java8时间类

java8时间类

优势

  1. 线程安全
    • 新的日期/时间API中,所有的类都是不可变的。
  2. 年月日使用贴近日常习惯
    • Data类型查看年份要加1900,月是从0开始,日期是从1开始
    • Calendar类型年份不必转换,月份仍然要加1,查看星期几时返回的1代表周日
    • 转换日期时对夏令时有优化,只需要修改ZonedDateTime的时区即可

时间单位说明

中国标准时间(China Standard Time) CST UTC+8 GMT+8

  • UT Universal Time 世界时。根据原子钟计算出来的时间。
  • GMT Greenwich Mean Time 格林尼治标准时间。这是以英国格林尼治天文台观测结果得出的时间,这个地方的当地时间过去被当成世界标准的时间。
  • UTC Coordinated Universal Time 协调世界时。每隔几年都会给世界时+1秒,让(UT)和(GMT)相差不至于太大。并将得到的时间称为UTC
  • 协调世界时不与任何地区位置相关,也不代表此刻某地的时间,所以在说明某地时间时要加上时区

常用类介绍

java8常用时间类

Class类 说明
ZoneId 时区
Instant 时刻(直接打印会显示UTC+0GMT+0时区)
LocalDate 年月日,不含时区信息
LocalTime 时分秒,不含时区信息
LocalDateTime 年月日时分秒,不含时区信息
ZonedDateTime 年月日时分秒及时区
Duration 时差,一般用于换算时分秒(注意,换算成秒会将所有时间全部转化成该类型)
Period 时差,一般用于换算年月日(注意,换算成秒不会将所有时间全部转化成该类型)

使用DEMO

public static void main(String[] args) {
    //ZoneId 是时区相关类
    //北京时区
    ZoneId beijing = ZoneId.of("UTC+8");
    //美国夏威夷州时区
    ZoneId hawaii = ZoneId.of("UTC-10");

    System.out.println("//--------------------------------------------------------------------------------");
    //不含有时区的时间相关类
    System.out.println("不含有时区的时间相关类");
    //Instant  时刻   用来代替时间戳     有年月日时分秒信息     也可以理解成有时区(打印时有时区标识),但只能是UTC+0 GMT+0时区
    Instant instant = Instant.now();
    System.out.println("Instant.now()" + instant);
    System.out.println("Instant.of() " + Instant.ofEpochSecond(1656599684));
    System.out.println("Instant.of() " + Instant.ofEpochMilli(1656599721234L));

    //LocalDateTime 有年月日时分秒信息     不含有时区   体现在打印没有表示时区的后缀
    //LocalDate,LocalTime 与LocalDateTime同理
    LocalDateTime localDateTime = LocalDateTime.now();
    LocalDate localDate = LocalDate.now();
    LocalTime localTime = LocalTime.now();
    System.out.println("LocalDateTime.now()" + localDateTime);
    System.out.println("LocalDateTime.of()" + LocalDateTime.ofInstant(instant, beijing));
    System.out.println("LocalDateTime.of()有时区问题" + LocalDateTime.ofEpochSecond(1656599684, 0, ZoneOffset.UTC));
    System.out.println("LocalDateTime.of()" + LocalDateTime.of(2022, 6, 29, 0, 0, 0));
    System.out.println("LocalDate,LocalTime 与LocalDateTime同理" + localDate + localTime);

    System.out.println("//--------------------------------------------------------------------------------");

    System.out.println("含有时区的时间相关类");

    //ZonedDateTime 含有时区的时间相关类,由LocalDateTime和ZoneId组成
    //ZonedDateTime 通过Instant或LocalDateTime增加时区而来
    ZonedDateTime zonedDateTime = instant.atZone(beijing);
    System.out.println("ZonedDateTime通过Instant转换而来(1):" + zonedDateTime);
    zonedDateTime = instant.atZone(hawaii);
    System.out.println("ZonedDateTime通过Instant转换而来(2):" + zonedDateTime);

    //ZonedDateTime带有时区的当前时间  LocalDateTime拼接上时区后缀
    zonedDateTime = localDateTime.atZone(hawaii);
    System.out.println("ZonedDateTime通过LocalDateTime拼接上时区后缀而来:" + zonedDateTime);

    System.out.println("//--------------------------------------------------------------------------------");

    //Instant常用时间操作
    System.out.println("Instant常用时间操作");
    System.out.println("获取时间戳(秒): " + instant.getEpochSecond());
    System.out.println("获取时间戳(毫秒): " + instant.toEpochMilli());
    System.out.println("取整: " + instant.truncatedTo(ChronoUnit.HOURS));

    //LocalDateTime,ZonedDateTime常用时间操作,二者共同的操作
    System.out.println("LocalDateTime,ZonedDateTime常用时间操作,二者共同的操作");

    System.out.println("获取时间戳(秒): " + localDateTime.toInstant(ZoneOffset.UTC));

    System.out.println("取日期" + localDateTime.getDayOfMonth());
    System.out.println("取星期" + localDateTime.getDayOfWeek());
    System.out.println("取日期加" + localDateTime.plusDays(1));
    System.out.println("取日期减" + localDateTime.minusDays(1));

    System.out.println("调整日期(时分秒不变)" + zonedDateTime.withDayOfMonth(1));
    System.out.println("调整日期(时分秒不变)" + zonedDateTime.withDayOfYear(1));

    System.out.println("日期比大小" + localDateTime.isAfter(localDateTime.plusDays(1)));
    System.out.println("计算时差");
    Duration duration = Duration.between(localDateTime, localDateTime
            .minusWeeks(10).plus(10, ChronoUnit.HOURS).plus(20, ChronoUnit.MINUTES));
    System.out.println("计算时差,一共相差天(差值全部转化)" + duration.toDays());
    System.out.println("计算时差,一共相小时(差值全部转化)" + duration.toHours());
    System.out.println("计算时差,一共相分钟(差值全部转化)" + duration.toMinutes());
    System.out.println("计算时差格式化" + format(duration));
    System.out.println("当天3点" + localDate.atTime(LocalTime.of(3, 0, 0)));

    Period period = Period.between(LocalDate.now(), LocalDate.of(2000, 12, 1));
    String s = String.format("计算时差相差%d年%02d月%02d日", period.getYears(), period.getMonths(), period.getDays());
    System.out.println(s);

    //LocalDate常用时间操作
    System.out.println("LocalDate常用时间操作");
    // 本月第一天
    System.out.println("本月第1天:" + localDate.with(TemporalAdjusters.firstDayOfMonth()));
    System.out.println("本月最后1天:" + localDate.with(TemporalAdjusters.lastDayOfMonth()));
    System.out.println("下月第1天:" + localDate.with(TemporalAdjusters.firstDayOfNextMonth()));
    System.out.println("本月第1个周一:" + localDate.with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY)));
    System.out.println("本月第2个周二:" + localDate.with(TemporalAdjusters.dayOfWeekInMonth(2, DayOfWeek.TUESDAY)));
    System.out.println("本周三:" + localDate.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).with(TemporalAdjusters.nextOrSame(DayOfWeek.WEDNESDAY)));
    System.out.println("下一个周一(不算今天否则加上OrSame):" + localDate.with(TemporalAdjusters.next(DayOfWeek.MONDAY)));
    System.out.println("上一个周一(不算今天否则加上OrSame):" + localDate.with(TemporalAdjusters.previous(DayOfWeek.MONDAY)));
    System.out.println("当天0点:" + localDate.atStartOfDay());
    System.out.println("是否是闰年:" + localDate.isLeapYear());
    System.out.println("//--------------------------------------------------------------------------------");

    //DateTimeFormatter,用于【格式化时间为字符串】或【转字符串为时间】
    System.out.println("格式化操作");
    DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    //时间格式转字符串格式 dateTimeFormatter.format或者DateTime.format都行
    System.out.println("时间转字符串(Formatter.format):" + dateTimeFormatter.format(localDateTime));
    //会去掉时区(除非指定显示时区)
    System.out.println("时间转字符串,会去掉时区(DateTime.format):" + zonedDateTime.format(dateTimeFormatter) + "  " + zonedDateTime);

    //字符串转时间
    LocalDateTime parseLocalDateTime = LocalDateTime.parse("2022-06-29T23:41:03");
    System.out.println("字符串转时间:" + parseLocalDateTime);

    //字符串转时间,使用自定义格式
    parseLocalDateTime = LocalDateTime.parse("2022-06-29 03:41:03", dateTimeFormatter);
    System.out.println("字符串转时间,使用自定义格式:" + parseLocalDateTime);

    System.out.println("夏威夷时区" + zonedDateTime);
    zonedDateTime = zonedDateTime.withZoneSameInstant(beijing);
    System.out.println("夏威夷转北京时区" + zonedDateTime);
}

使用Mybatis需要注意的点

  • 使用LocalDateTime,会使用数据库设置的时区,而不是Java应用的时区。

    • 在设置JDBC链接添加&serverTimezone=Asia/Shanghai
  • Java应用与数据库对应关系

    数据库类型 对应Java类型
    DATETIME LocalDateTime
    TIMESTAMP LocalDateTime
    DATE LocalDate
    TIME LocalTime

心得

  1. 为什么时间戳转LocalDataTime需要时区信息呢?LocalDataTime毕竟不带时区信息。

    假设不需要带有时区信息,那么时间戳转LocalDateTime应该就是默认的UTC时区对应的LocalDateTime,但这往往不是人们关心的。

    当人们使用LocalDataTime更关心自己当前时区的时间,故时间戳在转换成LocalDateTime时需要加上时区信息的操作才更为合理。

    但这不代表LocalDataTime需要保存时区信息,他只是一个时间值。

  2. 为什么时间戳转Instant不需要时区信息呢?

    Instant也可以理解成有时区(打印时有时区标识),但只能是UTC+0 GMT+0时区,故在存储时不需要加上时区信息也不会造成歧义。

    当人们使用Instant时往往会将其转换成其他可以使用本时区信息的类,如LocalDataTimeZonedDateTime,在转换的过程中加入时区信息即可。