原创

JAVA8系列教程-日期和时间

温馨提示:
本文最后更新于 2020年04月21日,已超过 959 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

开发人员社区中很大一部分一直在抱怨Date和Calendar类原因很多,例如难以理解,难以使用和不灵活。Date类甚至已经过时,并且Java文档建议使用Calendarclass代替Dateclass。最重要的是,日期比较是有问题的,过去我也遇到过这样的问题。

展望未来,JAVA 8(Lambda)有望发布新的日期和时间API /类(JSR-310),也称为ThreeTen,它将简单地改变您到目前为止的工作方式。这的一个关键部分是提供一个新的API,该API显着易于使用且不易出错。

它将提供一些非常需要的功能,例如:

  • 所有关键的公共类都是不可变的并且是线程安全的
  • 计算的其他领域可以采用的定义的术语和行为
我在2013年5月15日撰写了这篇文章。今天,在2014年3月18日,今天终于发布了Java 8,可供早期使用。我已经重新验证并验证了示例中的所有输出。与去年5月一样,它们像魅力一样工作。中只有遇到的变化TemporalAdjuster.java以前是班,现在是班@FunctionalInterface因此,我更正了相关示例并使用了“ TemporalAdjusters.java” 
目录

代表本地日期和时区的新类
代表时间戳和持续时间的新类
在现有枚举上添加了实用程序类
推出日期调整器
建立日期会更容易
模拟系统/机器时钟的新类
时区处理相关更改
日期格式更改
参考文献

代表本地日期和时区的新类

旨在取代Date类新的类LocalDateLocalTimeLocalDateTime

LocalDate

LocalDate类代表一个日期。没有时间或时区的表示。

LocalDate localDate = LocalDate.now();
System.out.println(localDate.toString());                //2013-05-15
System.out.println(localDate.getDayOfWeek().toString()); //WEDNESDAY
System.out.println(localDate.getDayOfMonth());           //15
System.out.println(localDate.getDayOfYear());            //135
System.out.println(localDate.isLeapYear());              //false
System.out.println(localDate.plusDays(12).toString());   //2013-05-27

LocalTime

 LocalTime类代表一个时间。没有日期或时区的表示。

//LocalTime localTime = LocalTime.now();     //toString() in format 09:57:59.744
LocalTime localTime = LocalTime.of(12, 20);
System.out.println(localTime.toString());    //12:20
System.out.println(localTime.getHour());     //12
System.out.println(localTime.getMinute());   //20
System.out.println(localTime.getSecond());   //0
System.out.println(localTime.MIDNIGHT);      //00:00
System.out.println(localTime.NOON);          //12:00

LocalDateTime

LocalDateTime类代表一个日期-时间。没有时区的表示。

LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime.toString());      //2013-05-15T10:01:14.911
System.out.println(localDateTime.getDayOfMonth()); //15
System.out.println(localDateTime.getHour());       //10
System.out.println(localDateTime.getNano());       //911000000

如果您希望使用区域信息的日期功能,那么拉姆达为您提供额外的3类类似于一个,即上面OffsetDateOffsetTimeOffsetDateTime时区偏移可以以“ +05:30”或“欧洲/巴黎”格式表示。这是通过使用另一个类来完成的ZoneId

OffsetDateTime offsetDateTime = OffsetDateTime.now();
System.out.println(offsetDateTime.toString());              //2013-05-15T10:10:37.257+05:30
 
offsetDateTime = OffsetDateTime.now(ZoneId.of("+05:30"));
System.out.println(offsetDateTime.toString());              //2013-05-15T10:10:37.258+05:30
 
offsetDateTime = OffsetDateTime.now(ZoneId.of("-06:30"));
System.out.println(offsetDateTime.toString());              //2013-05-14T22:10:37.258-06:30
 
ZonedDateTime zonedDateTime =
                ZonedDateTime.now(ZoneId.of("Europe/Paris"));
System.out.println(zonedDateTime.toString());               //2013-05-15T06:45:45.290+02:00[Europe/Paris]

代表时间戳和持续时间的新类

Instant

为了随时表示特定的时间戳,需要使用的类是InstantInstant类表示时间纳秒的精度瞬间。对一个对象的操作Instant包括与另一个对象进行比较,Instant以及增加或减少持续时间。

Instant instant = Instant.now();
System.out.println(instant.toString());                                 //2013-05-15T05:20:08.145Z
System.out.println(instant.plus(Duration.ofMillis(5000)).toString());   //2013-05-15T05:20:13.145Z
System.out.println(instant.minus(Duration.ofMillis(5000)).toString());  //2013-05-15T05:20:03.145Z
System.out.println(instant.minusSeconds(10).toString());                //2013-05-15T05:19:58.145Z

Duration

Duration 类是用Java语言首次带来的全新概念。它表示两个时间戳之间的时差。

Duration duration = Duration.ofMillis(5000);
System.out.println(duration.toString());     //PT5S
 
duration = Duration.ofSeconds(60);
System.out.println(duration.toString());     //PT1M
 
duration = Duration.ofMinutes(10);
System.out.println(duration.toString());     //PT10M
 
duration = Duration.ofHours(2);
System.out.println(duration.toString());     //PT2H
 
duration = Duration.between(Instant.now(), Instant.now().plus(Duration.ofMinutes(10)));
System.out.println(duration.toString());  //PT10M

Duration处理较小的时间单位,例如毫秒,秒,分钟和小时。它们更适合与应用程序代码进行交互。

Period

要与人互动,您需要获得更长的持续时间,这与Period类有关。

Period period = Period.ofDays(6);
System.out.println(period.toString());    //P6D
 
period = Period.ofMonths(6);
System.out.println(period.toString());    //P6M
 
period = Period.between(LocalDate.now(),
            LocalDate.now().plusDays(60));
System.out.println(period.toString());   //P1M29D

在现有枚举上添加了实用程序类

当前的Java SE平台使用int常量表示月份,星期几和am-pm等。现在,添加了许多额外的实用程序类,它们在这些枚举的基础上起作用。我以DayOfWeek这样的类为例该类是day枚举的包装,并且可以与其他类一致使用。

DayOfWeek

//day-of-week to represent, from 1 (Monday) to 7 (Sunday)
System.out.println(DayOfWeek.of(2));                    //TUESDAY
 
DayOfWeek day = DayOfWeek.FRIDAY;
System.out.println(day.getValue());                     //5
 
LocalDate localDate = LocalDate.now();
System.out.println(localDate.with(DayOfWeek.MONDAY));  //2013-05-13  i.e. when was monday in current week ?

其他如类 MonthMonthDayYearYearMonth等等。

日期调整器

日期调整器是日期处理工具中另一个美观实用的功能。它可以轻松解决以下问题:如何找到一个月的最后一天还是下一个工作日还是星期二一个星期?

让我们看一下代码。

LocalDate date = LocalDate.of(2013, Month.MAY, 15);                     //Today
         
LocalDate endOfMonth = date.with(TemporalAdjusters.lastDayOfMonth());
System.out.println(endOfMonth.toString());                              //2013-05-31
 
LocalDate nextTue = date.with(TemporalAdjusters.next(DayOfWeek.TUESDAY));
System.out.println(nextTue.toString());                                 //2013-05-21

创建日期对象

现在也可以使用构建器模式来创建日期对象构建器模式允许您使用单个零件来构建您想要的对象。这可以使用以“ at”为前缀的方法来实现。

//Builder pattern used to make date object
 OffsetDateTime date1 = Year.of(2013)
                        .atMonth(Month.MAY).atDay(15)
                        .atTime(0, 0)
                        .atOffset(ZoneOffset.of("+03:00"));
 System.out.println(date1);                                     //2013-05-15T00:00+03:00
 
//factory method used to make date object
OffsetDateTime date2 = OffsetDateTime.
                        of(2013, 5, 15, 0, 0, 0, 0, ZoneOffset.of("+03:00"));
System.out.println(date2);                                      //2013-05-15T00:00+03:00

模拟系统/机器时钟的新类

在新版本中提出了新的Clock模拟了系统时钟功能我最喜欢这个功能。原因是在进行单元测试时。您通常需要在将来的日期测试API。为此,我们已经将系统时钟转发到下一个日期,然后再次重新启动服务器并测试应用程序。

现在,无需这样做。使用Clock类来模拟这种情况。

Clock clock = Clock.systemDefaultZone();
System.out.println(clock);                      //SystemClock[Asia/Calcutta]
System.out.println(clock.instant().toString()); //2013-05-15T06:36:33.837Z
System.out.println(clock.getZone());            //Asia/Calcutta
 
Clock anotherClock = Clock.system(ZoneId.of("Europe/Tiraspol"));
System.out.println(anotherClock);                       //SystemClock[Europe/Tiraspol]
System.out.println(anotherClock.instant().toString());  //2013-05-15T06:36:33.857Z
System.out.println(anotherClock.getZone());             //Europe/Tiraspol
 
Clock forwardedClock  = Clock.tick(anotherClock, Duration.ofSeconds(600));
System.out.println(forwardedClock.instant().toString());  //2013-05-15T06:30Z

时区变更

与时区相关的处理由3个主要类别完成。这些是ZoneOffsetTimeZoneZoneRules

  • 所述ZoneOffset类表示一固定从UTC以秒的偏移。通常以“±hh:mm”格式的字符串表示。
  • TimeZone类表示,其中指定的时间带规则被定义的区域中的标识符。
  • ZoneRules是实际组定义时区偏移的变化规律。
//Zone rules
System.out.println(ZoneRules.of(ZoneOffset.of("+02:00")).isDaylightSavings(Instant.now()));
System.out.println(ZoneRules.of(ZoneOffset.of("+02:00")).isFixedOffset());

日期格式

日期格式主要通过两个类(即DateTimeFormatterBuilder和)支持DateTimeFormatterDateTimeFormatterBuilder在构建器模式上工作以构建自定义模式,DateTimeFormatter从而在其中提供必要的输入。

DateTimeFormatterBuilder formatterBuilder = new DateTimeFormatterBuilder();
formatterBuilder.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
                .appendLiteral("-")
                .appendZoneOrOffsetId();
DateTimeFormatter formatter = formatterBuilder.toFormatter();
System.out.println(formatter.format(ZonedDateTime.now()));

这些是我能够确定并进行的重大更改。

参考文献

正文到此结束