Java8 时间处理API(Date Time API)详解

在Java8之前,我们通常使用java.util.Datejava.util.Calendar类来处理日期和时间相关的操作,例如:

//创建一个Date对象
Date date = new Date();

//获取当前的年月日时分秒
int year = date.getYear() + 1900;
int month = date.getMonth() + 1;
int day = date.getDate();
int hour = date.getHours();
int minute = date.getMinutes();
int second = date.getSeconds();

//打印当前的日期和时间
System.out.println(year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second);

这种方式虽然可以实现功能,但是有一些缺点:

  • 可变性:DateCalendar类都是可变的,这意味着它们的值可以被修改,这可能会导致一些线程安全和逻辑错误的问题。
  • 可读性差:DateCalendar类的方法命名和设计都不够清晰和一致,例如getYear()方法返回的是从1900年开始的年份,而不是实际的年份;getMonth()方法返回的是从0开始的月份,而不是从1开始的月份;set(int field, int value)方法可以设置任意的字段和值,但是可能会导致不合理的结果,例如2月30日等。
  • 时区问题:DateCalendar类都不能很好地处理时区相关的问题,例如在不同的时区下显示或转换日期和时间。

为了解决这些问题,Java8引入了一套全新的日期和时间API,在java.time包中。这套API基于JSR-310规范设计,提供了一系列不可变且线程安全的类来表示日期、时间、时区、时段等概念。这套API有以下特点:

  • 不可变性:所有的日期和时间类都是不可变的,这意味着它们的值不能被修改,只能通过创建新的对象来表示不同的值。这样可以避免线程安全和逻辑错误的问题。
  • 可读性好:所有的日期和时间类都有清晰和一致的方法命名和设计,例如getYear()方法返回的是实际的年份;getMonth()方法返回的是一个枚举类型,而不是一个整数;with(TemporalAdjuster adjuster)方法可以根据指定的规则调整日期或时间,例如获取下一个周一等。
  • 时区支持:所有的日期和时间类都可以与时区相关联,例如使用ZonedDateTime类可以表示带有时区信息的日期和时间;使用ZoneId类可以表示时区标识符;使用ZoneOffset类可以表示时区偏移量。

Java8 Date Time API使用示例

为了方便理解Java8 Date Time API,我们可以将其分为四个层次:

  • 本地日期和时间:表示不带有时区信息的日期和时间,主要有以下几个类:
    • LocalDate:表示年月日,例如2021-10-31。
    • LocalTime:表示时分秒纳秒,例如23:59:59.999999999。
    • LocalDateTime:表示年月日时分秒纳秒,例如2021-10-31T23:59:59.999999999。
    • MonthDay:表示月日,例如10-31。
    • YearMonth:表示年月,例如2021-10。
    • Year:表示年,例如2021。
  • 带时区的日期和时间:表示带有时区信息的日期和时间,主要有以下几个类:
    • ZonedDateTime:表示带有时区信息的年月日时分秒纳秒,例如2021-10-31T23:59:59.999999999+08:00[Asia/Shanghai]。
    • OffsetDateTime:表示带有时区偏移量的年月日时分秒纳秒,例如2021-10-31T23:59:59.999999999+08:00。
    • OffsetTime:表示带有时区偏移量的时分秒纳秒,例如23:59:59.999999999+08:00。
    • ZoneId:表示时区标识符,例如Asia/Shanghai。
    • ZoneOffset:表示时区偏移量,例如+08:00。
  • 机器时间:表示以Unix时间戳的形式表示的日期和时间,主要有以下几个类:
    • Instant:表示一个瞬时点,以1970-01-01T00:00:00Z为基准,以秒和纳秒为单位,例如2021-10-31T15:59:59.999999999Z。
    • Duration:表示两个瞬时点之间的时间间隔,以秒和纳秒为单位,例如PT23H59M59.999999999S。
  • 人类时间:表示以人类可读的方式表示的日期和时间,主要有以下几个类:
    • Period:表示两个日期之间的时间间隔,以年月日为单位,例如P1Y2M3D。
    • ChronoUnit:表示时间单位,例如年、月、日、小时、分钟、秒等。
    • ChronoField:表示时间字段,例如年、月、日、小时、分钟、秒等。
    • DayOfWeek:表示星期几,例如MONDAY、TUESDAY等。
    • Month:表示月份,例如JANUARY、FEBRUARY等。

下面我们来看一些使用这些类的示例:

//创建一个本地日期
LocalDate date = LocalDate.of(2021, 10, 31);
System.out.println(date); //输出2021-10-31

//获取当前的本地日期
LocalDate today = LocalDate.now();
System.out.println(today); //输出当前的日期

//获取本地日期的年月日
int year = date.getYear();
int month = date.getMonthValue();
int day = date.getDayOfMonth();
System.out.println(year + "-" + month + "-" + day); //输出2021-10-31

//获取本地日期的星期几
DayOfWeek dow = date.getDayOfWeek();
System.out.println(dow); //输出SUNDAY

//获取本地日期的一些属性
boolean isLeapYear = date.isLeapYear(); //是否是闰年
boolean isBefore = date.isBefore(today); //是否在今天之前
boolean isAfter = date.isAfter(today); //是否在今天之后
boolean isEqual = date.isEqual(today); //是否等于今天
System.out.println(isLeapYear); //输出false
System.out.println(isBefore); //输出false或true,取决于当前日期
System.out.println(isAfter); //输出false或true,取决于当前日期
System.out.println(isEqual); //输出false或true,取决于当前日期

//调整本地日期
LocalDate nextDay = date.plusDays(1); //加一天
LocalDate nextWeek = date.plusWeeks(1); //加一周
LocalDate nextMonth = date.plusMonths(1); //加一个月
LocalDate nextYear = date.plusYears(1); //加一年
LocalDate firstDayOfMonth = date.with(TemporalAdjusters.firstDayOfMonth()); //获取当月第一天
LocalDate lastDayOfMonth = date.with(TemporalAdjusters.lastDayOfMonth()); //获取当月最后一天
LocalDate firstDayOfNextMonth = date.with(TemporalAdjusters.firstDayOfNextMonth()); //获取下个月第一天
System.out.println(nextDay); //输出2021-11-01
System.out.println(nextWeek); //输出
System.out.println(nextWeek); //输出2021-11-07 System.out.println(nextMonth); //输出2021-11-30 System.out.println(nextYear); //输出2022-10-31 System.out.println(firstDayOfMonth); //输出2021-10-01 System.out.println(lastDayOfMonth); //输出2021-10-31 System.out.println(firstDayOfNextMonth); //输出2021-11-01

        //创建一个本地时间 
        LocalTime time = LocalTime.of(23, 59, 59);
        System.out.println(time); //输出23:59:59

        //获取当前的本地时间 
        LocalTime now = LocalTime.now();
        System.out.println(now); //输出当前的时间

        //获取本地时间的时分秒纳秒 
        int hour = time.getHour();
        int minute = time.getMinute();
        int second = time.getSecond();
        int nano = time.getNano();
        System.out.println(hour + “:”+minute + “:”+second + “.”+nano); //输出23:59:59.0

        //调整本地时间 
        LocalTime nextSecond = time.plusSeconds(1); //加一秒 LocalTime nextMinute = time.plusMinutes(1); //加一分钟 LocalTime nextHour = time.plusHours(1); //加一小时 LocalTime withNano = time.withNano(999999999); //设置纳秒为999999999 System.out.println(nextSecond); //输出00:00:00 System.out.println(nextMinute); //输出00:00:59 System.out.println(nextHour); //输出00:59:59 System.out.println(withNano); //输出23:59:59.999999999

        //创建一个本地日期时间 
        LocalDateTime dateTime = LocalDateTime.of(2021, 10, 31, 23, 59, 59);
        System.out.println(dateTime); //输出2021-10-31T23:59:59

        //获取当前的本地日期时间 
        LocalDateTime nowDateTime = LocalDateTime.now();
        System.out.println(nowDateTime); //输出当前的日期时间

        //获取本地日期时间的年月日时分秒纳秒 
        int year = dateTime.getYear();
        int month = dateTime.getMonthValue();
        int day = dateTime.getDayOfMonth();
        int hour = dateTime.getHour();
        int minute = dateTime.getMinute();
        int second = dateTime.getSecond();
        int nano = dateTime.getNano();
        System.out.println(year + “ -”+month + “-”+day + " " + hour + “:”+minute + “:”+second + “.”+nano); //输出2021-10-31 23:59:59.0

        //调整本地日期时间 
        LocalDateTime nextSecond = dateTime.plusSeconds(1); //加一秒 LocalDateTime nextMinute = dateTime.plusMinutes(1); //加一分钟 LocalDateTime nextHour = dateTime.plusHours(1); //加一小时 LocalDateTime nextDay = dateTime.plusDays(1); //加一天 LocalDateTime nextWeek = dateTime.plusWeeks(1); //加一周 LocalDateTime nextMonth = dateTime.plusMonths(1); //加一个月 LocalDateTime nextYear = dateTime.plusYears(1); //加一年 LocalDateTime firstDayOfMonth = dateTime.with(TemporalAdjusters.firstDayOfMonth()); //获取当月第一天 LocalDateTime lastDayOfMonth = dateTime.with(TemporalAdjusters.lastDayOfMonth()); //获取当月最后一天 LocalDateTime firstDayOfNextMonth = dateTime.with(TemporalAdjusters.firstDayOfNextMonth()); //获取下个月第一天 System.out.println(nextSecond); //输出2021-11-01T00:00:00 System.out.println(nextMinute); //输出2021-11-01T00:00:59 System.out.println(nextHour); //输出2021-11-01T00:59:59 System.out.println(nextDay); //输出2021-11-01T23:59:59 System.out.println(nextWeek); //输出2021-11-07T23:59:59 System.out.println(nextMonth); //输出2021-11-30T23:59:59 System.out.println(nextYear); //输出2022-10-31T23:59:59 System.out.println(firstDayOfMonth); //输出2021-10-01T23:59:59 System.out.println(lastDayOfMonth); //输出2021-10-31T23:59:59 System.out.println(firstDayOfNextMonth); //输出2021-11-01T23:59:59

        //创建一个带时区的日期时间 
        ZonedDateTime zonedDateTime = ZonedDateTime.of(2021, 10, 31, 23, 59, 59, 0, ZoneId.of(“Asia / Shanghai”));
        System.out.println(zonedDateTime); //输出2021-10-31T23:59:59+08:00[Asia/Shanghai]

        //获取当前的带时区的日期时间 
        ZonedDateTime nowZonedDateTime = ZonedDateTime.now();
        System.out.println(nowZonedDateTime); //输出当前的带时区的日期时间

        //获取带时区的日期时间的年月日时分秒纳秒和时区信息 
        int year = zonedDateTime.getYear();
        int month = zonedDateTime.getMonthValue();
        int day = zonedDateTime.getDayOfMonth();
        int hour = zonedDateTime.getHour();
        int minute = zonedDateTime.getMinute();
        int second = zonedDateTime.getSecond();
        int nano = zonedDateTime.getNano();
        ZoneId zoneId = zonedDateTime.getZone();
        ZoneOffset zoneOffset = zonedDateTime.getOffset();
        System.out.println(year + “ -”+month + “-”+day + " " + hour + “:”+minute + “:”+second + “.”
        +nano + " " + zoneId + " " + zoneOffset); //输出2021-10-31 23:59:59.0 Asia/Shanghai +08:00

        //调整带时区的日期时间 
        ZonedDateTime nextSecond = zonedDateTime.plusSeconds(1); //加一秒 ZonedDateTime nextMinute = zonedDateTime.plusMinutes(1); //加一分钟 ZonedDateTime nextHour = zonedDateTime.plusHours(1); //加一小时 ZonedDateTime nextDay = zonedDateTime.plusDays(1); //加一天 ZonedDateTime nextWeek = zonedDateTime.plusWeeks(1); //加一周 ZonedDateTime nextMonth = zonedDateTime.plusMonths(1); //加一个月 ZonedDateTime nextYear = zonedDateTime.plusYears(1); //加一年 ZonedDateTime firstDayOfMonth = zonedDateTime.with(TemporalAdjusters.firstDayOfMonth()); //获取当月第一天 ZonedDateTime lastDayOfMonth = zonedDateTime.with(TemporalAdjusters.lastDayOfMonth()); //获取当月最后一天 ZonedDateTime firstDayOfNextMonth = zonedDateTime.with(TemporalAdjusters.firstDayOfNextMonth()); //获取下个月第一天 ZonedDateTime withZoneId = zonedDateTime.withZoneSameInstant(ZoneId.of(“Europe/London”)); //转换为另一个时区,保持瞬时点不变 System.out.println(nextSecond); //输出2021-11-01T00:00:00+08:00[Asia/Shanghai] System.out.println(nextMinute); //输出2021-11-01T00:00:59+08:00[Asia/Shanghai] System.out.println(nextHour); //输出2021-11-01T00:59:59+08:00[Asia/Shanghai] System.out.println(nextDay); //输出2021-11-01T23:59:59+08:00[Asia/Shanghai] System.out.println(nextWeek); //输出2021-11-07T23:59:59+08:00[Asia/Shanghai] System.out.println(nextMonth); //输出2021-11-30T23:59:59+08:00[Asia/Shanghai] System.out.println(nextYear); //输出2022-10-31T23:59:59+08:00[Asia/Shanghai] System.out.println(firstDayOfMonth); //输出2021-10-01T23:59:59+08:00[Asia/Shanghai] System.out.println(lastDayOfMonth); //输出2021-10-31T23:59:59+08:00[Asia/Shanghai] System.out.println(firstDayOfNextMonth); //输出2021-11-01T23:59:59+08:00[Asia/Shanghai] System.out.println(withZoneId); //输出2021-10-31T15:59:59+00:00[Europe/London]

        //创建一个机器时间 
        Instant instant = Instant.ofEpochSecond(1635705599);
        System.out.println(instant); //输出2021-10-31T15:59:59Z

        //获取当前的机器时间 
        Instant nowInstant = Instant.now();
        System.out.println(nowInstant); //输出当前的机器时间

        //获取机器时间的秒和纳秒 
        long epochSecond = instant.getEpochSecond();
        int nano = instant.getNano();
        System.out.println(epochSecond + “.”+nano); //输出1635705599.0
//调整机器时间
Instant nextSecond = instant.plusSeconds(1);//加一秒
        Instant nextMinute = instant.plusMinutes(1);//加一分钟
        Instant nextHour = instant.plusHours(1);//加一小时
        Instant nextDay = instant.plus(1, ChronoUnit.DAYS);//加一天
        Instant nextWeek = instant.plus(1, ChronoUnit.WEEKS);//加一周
        Instant nextMonth = instant.plus(1, ChronoUnit.MONTHS);// 加一个月
        Instant nextYear = instant.plus(1, ChronoUnit.YEARS);// 加一年
        System.out.println(nextSecond);// 输出2021-10-31T16:00:00Z
        System.out.println(nextMinute);// 输出2021-10-31T16:00:59Z
        System.out.println(nextHour);// 输出2021-10-31T16:59:59Z
        System.out.println(nextDay);// 输出2021-11-01T15:59:59Z
        System.out.println(nextWeek);// 输出2021-11-07T15:59:59Z
        System.out.println(nextMonth);// 输出2021-11-30T15:59:59Z
        System.out.println(nextYear);// 输出2022-10-31T15:59:59Z

        //创建一个人类时间
        Period period = Period.of(1, 2, 3);
        System.out.println(period); //输出P1Y2M3D

        //获取人类时间的年月日
        int years = period.getYears();
        int months = period.getMonths();
        int days = period.getDays();
        System.out.println(years + " years " + months + " months " + days + " days"); //输出1 years 2 months 3 days
        // 调整人类时间
        Period plusPeriod = period.plus(Period.of(1, 1, 1));//加上另一个时间间隔
        Period minusPeriod = period.minus(Period.of(0, 0, 1));//减去另一个时间间隔
        Period multipliedPeriod = period.multipliedBy(2);//乘以一个因子 
        Period normalizedPeriod = period.normalized();//标准化为年和月的形式
        System.out.println(plusPeriod);// 输出P2Y3M4D 
        System.out.println(minusPeriod); //输出P1Y2M2D 
        System.out.println(multipliedPeriod); //输出P2Y4M6D 
        System.out.println(normalizedPeriod); //输出P14M3D

Java8 Date Time API的优缺点分析对比

Java8 Date Time API是Java语言发展的一个重要的进步,它们为Java开发者提供了一套全新的日期和时间处理方式,可以使代码更加简洁和清晰,提高了开发效率和可读性。同时,它们也为Java引入了一些新的特性和概念,例如不可变性,时区支持,机器时间,人类时间等,使得Java更加丰富和强大。

但是,Java8 Date Time API也有一些缺点和限制,例如:

  • 兼容性问题:Java8 Date Time API只能在Java8或更高版本的环境中运行,如果需要在低版本的环境中运行,就需要使用其他的工具或者框架来支持。
  • 性能问题:Java8 Date Time API在运行时会生成一些额外的对象和方法,这可能会影响程序的性能和内存占用。虽然Java虚拟机会进行一些优化和缓存,但是仍然需要注意避免过度使用或者滥用这些特性。
  • 复杂性问题:Java8 Date Time API提供了很多不同的类和方法来表示不同的日期和时间概念,这可能会导致一些复杂性和混乱。例如,在不同的场景下需要使用不同的类和方法,例如LocalDateLocalTimeLocalDateTimeZonedDateTimeInstant等;在不同的类和方法之间需要进行转换,例如使用toInstant()ofInstant()withZoneSameInstant()等方法。

因此,在使用Java8 Date Time API时,需要根据具体的场景和需求来选择合适的方式,避免盲目地追求新特性而忽略了其他方面的影响。

Java8 Date Time API的使用场景和注意事项

Java8 Date Time API可以用来处理很多与日期和时间相关的操作,例如:

  • 获取当前的日期和时间
  • 解析和格式化日期和时间字符串
  • 计算日期和时间的差值和间隔
  • 调整日期和时间的值
  • 转换日期和时间的时区
  • 比较日期和时间的大小
  • 验证日期和时间的合法性

在使用Java8 Date Time API时,有一些注意事项,例如:

  • 使用不可变对象:所有的日期和时间类都是不可变的,这意味着它们的值不能被修改,只能通过创建新的对象来表示不同的值。这样可以避免线程安全和逻辑错误的问题。因此,在使用这些类时,不要试图修改它们的值,而是使用返回新对象的方法来进行操作。
  • 使用合适的类:Java8 Date Time API提供了很多不同的类来表示不同的日期和时间概念,这可能会导致一些复杂性和混乱。因此,在使用这些类时,要根据具体的场景和需求来选择合适的类。例如,在不需要时区信息的场景下,可以使用LocalDateLocalTimeLocalDateTime等类;在需要时区信息的场景下,可以使用ZonedDateTimeOffsetDateTimeOffsetTime等类;在需要以Unix时间戳的形式表示日期和时间的场景下,可以使用Instant类;在需要以人类可读的方式表示日期和时间的场景下,可以使用PeriodChronoUnit等类。
  • 使用正确的方法:Java8 Date Time API提供了很多不同的方法来对日期和时间进行操作,这可能会导致一些复杂性和混乱。因此,在使用这些方法时,要根据具体的场景和需求来选择正确的方法。例如,在调整日期和时间时,可以使用plus()minus()等方法来加减指定的值;可以使用with()等方法来设置指定的字段或者规则;可以使用truncatedTo()等方法来截断指定的单位;在转换日期和时间时,可以使用toInstant()ofInstant()等方法来转换为机器时间;可以使用withZoneSameInstant()等方法来转换为带时区的日期时间;在解析和格式化日期和时间字符串时,可以使用parse()format()等方法,并且指定合适的格式化器。

猜你喜欢

转载自blog.csdn.net/caicai250/article/details/131413687