08-Java8新时间与日期

发布于:2022-12-27 ⋅ 阅读:(844) ⋅ 点赞:(0)

Java8新时间与日期类

1. 旧的时间日期类

1.1 JDK1.0

public class Date implements java.io.Serializable, Cloneable, Comparable<Date>

1.2 JDK1.1

public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar> 
abstract public class TimeZone implements Serializable, Cloneable

1.3 缺点

  • 部分方法不实用,已被弃用
  • 存在线程不安全问题

1.4 线程不安全例子

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.concurrent.*;

public class TestSimpleDateFormat {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        Callable<Date> call = () -> sdf.parse("20161218");
        ExecutorService pool = Executors.newFixedThreadPool(10);
        ArrayList<Future<Date>> results = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            results.add(pool.submit(call));
        }
        for(Future<Date> item: results){
            System.out.println(item.get());
        }
    }
}
Thu Jun 18 00:00:00 CST 2201
Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.NumberFormatException: multiple points
	at java.util.concurrent.FutureTask.report(FutureTask.java:122)
	at java.util.concurrent.FutureTask.get(FutureTask.java:192)
	at se.newdate.TestSimpleDateFormat.main(TestSimpleDateFormat.java:18)
Caused by: java.lang.NumberFormatException: multiple points
	at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1890)
	at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
	at java.lang.Double.parseDouble(Double.java:538)
	at java.text.DigitList.getDouble(DigitList.java:169)
	at java.text.DecimalFormat.parse(DecimalFormat.java:2089)
	at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1867)
	at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
	at java.text.DateFormat.parse(DateFormat.java:364)
	at se.newdate.TestSimpleDateFormat.lambda$main$0(TestSimpleDateFormat.java:11)

1.5 使用ThreadLocal解决线程不安全

public class TestSimpleDateFormat {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<Date> call = () -> DateFormatThreadLocal.convert("20161218");
        ExecutorService pool = Executors.newFixedThreadPool(10);
        ArrayList<Future<Date>> results = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            results.add(pool.submit(call));
        }
        for(Future<Date> item: results){
            System.out.println(item.get());
        }
        pool.shutdown();
    }
}

class DateFormatThreadLocal {
    private static final ThreadLocal<DateFormat> df = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMdd"));

    public static Date convert(String source) throws ParseException {
        return df.get().parse(source);
    }
}
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016

Process finished with exit code 0

1.6 使用线程安全的LocalDate

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.concurrent.*;

public class TestSimpleDateFormat {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<LocalDate> call = () -> LocalDate.parse("20161218", DateTimeFormatter.ofPattern("yyyyMMdd"));
        ExecutorService pool = Executors.newFixedThreadPool(10);
        ArrayList<Future<LocalDate>> results = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            results.add(pool.submit(call));
        }
        for(Future<LocalDate> item: results){
            System.out.println(item.get());
        }
        pool.shutdown();
    }
}
2016-12-18
2016-12-18
2016-12-18
2016-12-18
2016-12-18
2016-12-18
2016-12-18
2016-12-18
2016-12-18
2016-12-18

Process finished with exit code 0

2. 新时间日期类

2.1 概述

  • LocalDate、LocalTime、LocalDateTime类的实例是不可变的对象,分别表示使用ISO-8601日历系统的日期、时间、日期和时间。
  • 提供了简单的日期或时间,并不包含当前的时间信息,也不包含与时区相关的信息。
  • ISO-8601日历系统是国际标准化组织制定的现代公民的日期和时间的表示法

2.2 本地时间

import java.time.LocalDateTime;

public class TestLocalDateTime {

    public static void main(String[] args) {
        LocalDateTime now = LocalDateTime.now();
        System.out.println("获取当前系统时间:LocalDateTime.now() ==> " + now);

        LocalDateTime ldt = LocalDateTime.of(2015, 10, 19, 13, 22, 33);
        System.out.println("指定时间:LocalDateTime.of(2015, 10, 19, 13, 22, 33) ==> " + ldt);

        LocalDateTime localDateTime = now.plusYears(2);
        System.out.println("当前时间+2年:now.plusYears(2) ==> " + localDateTime);

        LocalDateTime localDateTime1 = now.minusMinutes(2);
        System.out.println("当前时间-2分钟:now.minusMinutes(2) ==> " + localDateTime1);

        System.out.println(now.getYear());
        System.out.println(now.getMonthValue());
        System.out.println(now.getDayOfMonth());
        System.out.println(now.getHour());
        System.out.println(now.getMinute());
        System.out.println(now.getSecond());
    }
}
获取当前系统时间:LocalDateTime.now() ==> 2021-08-04T07:15:45.764
指定时间:LocalDateTime.of(2015, 10, 19, 13, 22, 33) ==> 2015-10-19T13:22:33
当前时间+2年:now.plusYears(2) ==> 2023-08-04T07:15:45.764
当前时间-2分钟:now.minusMinutes(2) ==> 2021-08-04T07:13:45.764
2021
8
4
7
15
45

2.3 时间戳

  • 以 Unix 元年:1970年1月1日0时0分0秒至今的毫秒值
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;

public class TestLocalDateTime {

    public static void main(String[] args) {
        Instant now = Instant.now();
        System.out.println("默认获取UTC时区:Instant.now() ==> " + now);

        OffsetDateTime offsetDateTime = now.atOffset(ZoneOffset.ofHours(8));
        System.out.println("带偏移量的时间:now.atOffset(ZoneOffset.ofHours(8)) ==> " + offsetDateTime);

        long l = now.toEpochMilli();
        System.out.println("转时间戳:now.toEpochMilli() ==> " + l);

        Instant instant = Instant.ofEpochSecond(60);
        System.out.println("对Unix元年+60秒:Instant.ofEpochSecond(60) ==> " + instant);
    }
}
默认获取UTC时区:Instant.now() ==> 2021-08-04T02:25:29.498Z
带偏移量的时间:now.atOffset(ZoneOffset.ofHours(8)) ==> 2021-08-04T10:25:29.498+08:00
转时间戳:now.toEpochMilli() ==> 1628043929498
对Unix元年+60秒:Instant.ofEpochSecond(60) ==> 1970-01-01T00:01:00Z

2.4 时间间隔

  • Duration:计算两个时间之间的间隔
import java.time.*;

public class TestLocalDateTime {

    public static void main(String[] args) {
        Instant before = Instant.now();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {

        }
        Instant last = Instant.now();
        Duration between = Duration.between(before, last);
        System.out.println("计算时间戳之间的间隔:Duration.between(before, last) ==> " + between.toMillis());

        LocalTime lt1 = LocalTime.now();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {

        }
        LocalTime lt2 = LocalTime.now();
        Duration between1 = Duration.between(lt1, lt2);
        System.out.println("计算LocalTime之间的间隔:Duration.between(lt1, lt2) ==> " + between1.toMillis());
    }
}
计算时间戳之间的间隔:Duration.between(before, last) ==> 1013
计算LocalTime之间的间隔:Duration.between(lt1, lt2) ==> 1011
  • Period:计算两个日期之间的间隔
import java.time.*;

public class TestLocalDateTime {

    public static void main(String[] args) {
        LocalDate ld1 = LocalDate.of(2015,1,1);
        LocalDate ld2 = LocalDate.now();
        Period between1 = Period.between(ld1, ld2);
        System.out.println("计算LocalDate之间的间隔:Period.between(ld1, ld2) ==> " +
                between1.getYears() + "年" +
                between1.getMonths() + "月" +
                between1.getDays() + "天");
    }
}
计算LocalDate之间的间隔:Period.between(ld1, ld2) ==> 6年7月3天

2.5 时间校正器

  • TemporalAdjuster:时间校正器。有时我们需要获取如 将日期调整到下一个周日等操作
  • TemporalAdjusters:该类通过静态方法提供大量常用TemporalAdjuster的实现
import java.time.*;
import java.time.temporal.TemporalAdjusters;

public class TestLocalDateTime {

    public static void main(String[] args) {
        LocalDateTime now = LocalDateTime.now();
        System.out.println("校正之前:" + now);

        LocalDateTime with = now.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
        System.out.println("下一个周日:now.with(TemporalAdjusters.next(DayOfWeek.SUNDAY)) ==> " + with);

        // 自定义:下一个工作日
        LocalDateTime with1 = now.with(l -> {
            LocalDateTime tmp = (LocalDateTime) l;
            DayOfWeek dayOfWeek = tmp.getDayOfWeek();
            if (dayOfWeek.equals(DayOfWeek.FRIDAY)) {
                tmp.plusDays(3);
            } else if (dayOfWeek.equals(DayOfWeek.SATURDAY)) {
                tmp.plusDays(2);
            } else {
                tmp.plusDays(1);
            }
            return tmp;
        });

        System.out.println("自定义计算下一个工作日:" + with1);
    }
}
校正之前:2021-08-04T10:51:48.043
下一个周日:now.with(TemporalAdjusters.next(DayOfWeek.SUNDAY)) ==> 2021-08-08T10:51:48.043
自定义计算下一个工作日:2021-08-04T10:51:48.043

2.6 时间日期格式化

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class TestLocalDateTime {

    public static void main(String[] args) {
        DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE;
        LocalDateTime now = LocalDateTime.now();
        String format = now.format(formatter);
        System.out.println("DateTimeFormatter.ISO_DATE 格式化方式:" + format);

        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
        String format1 = now.format(dateTimeFormatter);
        System.out.println("自定义格式化:DateTimeFormatter.ofPattern(\"yyyy年MM月dd日 HH:mm:ss\") ==> " + format1);

        LocalDateTime parse = now.parse(format1, dateTimeFormatter);
        System.out.println("自定义格式转回LocalDateTime:now.parse(format1, dateTimeFormatter) ==> " + parse);
    }
}
DateTimeFormatter.ISO_DATE 格式化方式:2021-08-04
自定义格式化:DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss") ==> 2021年08月04日 11:02:12
自定义格式转回LocalDateTime:now.parse(format1, dateTimeFormatter) ==> 2021-08-04T11:02:12

2.7 时区处理

  • Java8加入对时区的支持,带时区的时间:ZonedDate ZonedTime ZonedDateTime
  • 每个时区对应有一个ID,地区ID都为{区域}/{城市}的格式
  • 例如:Asia/Shanghai 等
  • ZonedId:该类中包含了所有的时区信息
    • getAvailableZoneIds():可以获取所有时区的信息
    • of(id):用指定的时区信息获取ZoneId对象

2.7.1 查看所有时区

import java.time.ZoneId;
import java.util.Set;

public class TestLocalDateTime {

    public static void main(String[] args) {
        Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
        for (String str: availableZoneIds){
            System.out.println(str);
        }
    }
}
Asia/Aden
America/Cuiaba
Etc/GMT+9
Etc/GMT+8
Africa/Nairobi
America/Marigot
Asia/Aqtau
Pacific/Kwajalein
America/El_Salvador
Asia/Pontianak
...

2.7.2 获取指定时区当前时间

import java.time.LocalDateTime;
import java.time.ZoneId;

public class TestLocalDateTime {

    public static void main(String[] args) {
        LocalDateTime now = LocalDateTime.now(ZoneId.of("America/Marigot"));
        System.out.println("获取时区\"America/Marigot\"的当前时间:LocalDateTime.now(ZoneId.of(\"America/Marigot\")) ==> " + now);
    }
}
获取时区"America/Marigot"的当前时间:LocalDateTime.now(ZoneId.of("America/Marigot")) ==> 2021-08-03T23:15:20.208

2.7.3 获取指定时区时间偏移

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class TestLocalDateTime {

    public static void main(String[] args) {
        LocalDateTime now1 = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
        ZonedDateTime zonedDateTime = now1.atZone(ZoneId.of("Asia/Shanghai"));
        System.out.println(zonedDateTime);
    }
}
2021-08-04T11:21:01.579+08:00[Asia/Shanghai]

网站公告

今日签到

点亮在社区的每一天
去签到