java8 -Date and Time

2019-10-10  本文已影响0人  bern85
为什么我们需要一个新的日期和时间库?

Java长期存在的一个问题是对日期和时间的支持非常不理想。
例如,现有的类(例如java.util.DateSimpleDateFormatter)不是线程安全的,导致用户可能出现并发问题 - 普通开发人员在编写日期处理代码时并不希望处理这样的问题。

一些日期和时间类的API设计也表现的差强人意。 例如,在Java 1.0中,对日期和时间的支持只能依赖java.util.Date类,但是年份的起始选择是1900年,月份的起始从0开始 - 不是很直观。如果你想创建2014年3月18号,你需要写如下代码:

Date date = new Date(114, 2, 18); 

输入如下:

Tue Mar 18 00:00:00 CST 2014

java1.1中对日期和时间的处理又引入了java.util.Calendar类。但是月份依旧是从0开始(不过,至少Calendar 取消了由1900年开始计算年份这一设计)。

正是因为java原生的日期处理存在这样或那样的问题,所以导致第三方的日期类库反而更加普及,例如Joda-Time。

为了解决这些问题并在JDK中提供更好的支持,为Java SE 8设计了新的日期和时间API。

由 Joda-Time的作者(Stephen Colebourne) 和oracle官方共同主导,再JSR 310 的规范下,新的日期时间处理类出现在Java SE 8的java.time软件包中。

核心理念

新的API包含三个核心思想:

LocalDate 和 LocalTime

开始接触新的日期和时间API时,你先碰到的可能是LocalDate类和LocalTime类。他们从默认时区的系统时钟获取当前日期。 还有一个名为LocalDateTime的复合类,它是LocalDateLocalTime的组合体。

对于由于时区产生的混淆,我们暂时可以放在一边(LocalDate不带时区信息)。当你不需要时区信息的时候,我们应该使用它们,如桌面JavaFX应用程序可能是其中之一的使用场景,这些类甚至可以用于具有一致性时区的分布式系统上。

创建LocalDate 和 LocalTime对象

新API中的所有核心类都是由静态工厂方法构建的。 当通过其传递年月日参数时,工厂方法为of; 从TemporalAccessor类型转换时,工厂方法为from。 还有将将字符串作为参数的parse方法。 如下所示:

       LocalDateTime timePoint = LocalDateTime.now();     // The current date and time
        LocalDate.of(2012, Month.DECEMBER, 12); // from values
        LocalDate.ofEpochDay(150);  // middle of 1970
        LocalTime.of(17, 18); // the train I took home today
        LocalTime.parse("10:15:30"); // From a String

Java SE 8日期类也可以根据标准Java getter约定用于获取值,如下所示:

LocalDate theDate = timePoint.toLocalDate();
Month month = timePoint.getMonth();
int day = timePoint.getDayOfMonth();
timePoint.getSecond();

你还可以更改对象值。 因为所有核心类在新API中都是不可变的,所以你如果想创建修改版,那么调用with、(而不是使用setter),返回修改后的新对象。也会根据不同的参数计算对应的值。如下所示:

         LocalDateTime timePoint = LocalDateTime.of(2014, 3, 18,10,0);
        // Set the value, returning a new object 
        LocalDateTime thePast = timePoint.withDayOfMonth(10).withYear(2010); //2010,3,10 10:00
        /* You can use direct manipulation methods, or pass a value and field pair */       
        LocalDateTime yetAnother = thePast.plusWeeks(3).plus(3,     
         ChronoUnit.WEEKS); // 2010,4,21 10:00

新API还具有调整器的概念 - 一个通用逻辑处理代码块的封装。 你可以设置一个或多个字段的WithAdjuster,也可以添加或减去某些字段的PlusAdjuster。 值类也可以充当调整器,在这种情况下,它们更新它们所代表的字段的值。 新API定义了一些常用的内置调整器,但如果你需要特定业务逻辑,则可以自定义调整器。如下所示:

        LocalDate timePoint = LocalDate.of(2014, 3, 18); //2014-03-18
        LocalDate foo = timePoint.with(lastDayOfMonth()); //2014-03-31
        LocalDate bar = timePoint.with(nextOrSame(DayOfWeek.SUNDAY)); //2014-03-23
        // Using value classes as adjusters
        LocalDate now = timePoint.with(LocalDate.now()); //当前时间

截断

新API通过提供表示日期,时间和日期与时间的类型来支持不同的精确的时间点,但显然还有更精细的的时间粒度(纳秒)。
truncatedTo方法截断返回原始日期时间的副本,其中小于指定单位的字段设置为零。如下所示:

        LocalDateTime dt1 = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45, 20); //2014-03-18T13:45:20
        LocalDateTime truncatedTime = dt1.truncatedTo(ChronoUnit.MINUTES); //2014-03-18T13:45

Time Zones

之前你看到的日期和时间的种类都不包含时区信息。时区的处理是新版日期和时间API新增 加的重要功能,使用新版日期和时间API时区的处理被极大地简化了。时区是按照一定的规则将区域划分成的标准时间相同的区间。在ZoneRules这个类中包含了 40个这样的实例。 时区由它们与通用协调时(UTC)的偏移量定义。 它们大致同步移动,但是按照指定的差异。

时区可以通过两个标识符来引用:缩写,例如“PLT,” 较长标识符,例如“Asia/Karachi.” 。在设计应用程序时,使用时区时发生偏移时,你应该考虑哪个方案更适合。

        LocalDateTime dateTime = LocalDateTime.of(2014, 3, 18,10,0);
        // You can specify the zone id when creating a zoned date time
        ZoneId id = ZoneId.of("Asia/Shanghai");
        ZonedDateTime zoned = ZonedDateTime.of(dateTime, id);
        assertEquals(id, ZoneId.from(zoned));
ZoneOffset newYorkOffset = ZoneOffset.of("-05:00"); 

Time Zone Classes

ZonedDateTime.parse("2007-12-03T10:15:30+01:00[Europe/Paris]");
ZonedDateTime

java.util.TimeZone是一个现有的时区类 - 但Java SE 8不使用它,因为所有JSR 310类都是不可变的,TimeZone是可变的。

Periods

Period表示诸如“3个月和1天”之类的值,其是时间线上的距离。 这与我们到目前为止所看到的其他类不同,其他类是时间轴上的点。

        LocalDate oldDate = LocalDate.of(2014, 3, 8);
        LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45);
        ZoneId romeZone = ZoneId.of("Asia/Shanghai");
        ZonedDateTime oldDateTime = dateTime.atZone(romeZone);
        // 3 years, 2 months, 1 day
        Period period = Period.of(3, 2, 1);
        // You can modify the values of dates using periods
        LocalDate newDate = oldDate.plus(period); //2017-05-09
        ZonedDateTime newDateTime = oldDateTime.minus(period); //2011-01-17T13:45+08:00[Asia/Shanghai]
        // Components of a Period are represented by ChronoUnit values
        assertEquals(1, period.get(ChronoUnit.DAYS));

Durations

Duration是时间线上按时间测量的距离,它与Period的作用类似,但具有不同的精度,如下所示:

        LocalDateTime today = LocalDateTime.now();
        LocalDateTime yesterday = today.minusDays(1);
        // A duration of 3 seconds and 5 nanoseconds
        Duration duration = Duration.ofSeconds(3, 5);
        Duration oneDay = Duration.between(today, yesterday);
        assertEquals(-1, oneDay.toDays());

可以在Duration实例上执行加法(plus),减法(minus)和 “with” 操作,还可以使用Duration修改日期或时间的值。

Chronologies

为了支持使用非ISO历法系统(non-ISO calendaring) 的开发人员的需求,Java SE 8引入了ChronologyChronology用于组织和识别日期的日历系统。java.time.chrono包中有大量的接口和实现类提供使用,接口如下:

Chronology:
ChronoLocalDate
ChronoLocalDateTime
ChronoZonedDateTime
ChronoPeriod

这些类纯粹适用于那些需要考虑本地化日历系统的高度国际化的应用程序,日期及时间API的设计者建议我们使用LocalDate。 一些日历系统甚至没有月或周的概念,并且需要定义一些特殊的日期字段,如Umm Al-Qura变量。

其他 API

Java SE 8还有一些其他常用的类。 如MonthDay,只包含Month和Day,适用于表示生日。 YearMonth类,只包含Year和Month,适用于信用卡开始日期和到期日期的情景。

Java SE 8中的JDBC将支持这些新类型,但没有对公共JDBC API进行更改。 现有的泛型setObject和getObject方法就足够了。

这些类型可以映射到实现了美国国家标准化组织(ANSI) SQL标准的数据库的日期类型;
如下所示:

ANSI SQL Java SE 8
DATE LocalDate
TIME LocalTime
TIMESTAMP LocalDateTime
TIME WITH TIMEZONE OffsetTime
TIMESTAMP WITH TIMEZONE OffsetDateTime

结论

Java SE 8在java.time包中提供了新的日期和时间API,在原生的Java API中提供高质量的日期和时间支持。
JSR 310

上一篇下一篇

猜你喜欢

热点阅读