java核心技术-II-6-日期和时间API

简介

Java1.0有一个Date类,事后证明其太过简单了,当Java1.1引入Calendar之后,Date类中的大部分方法就被其弃用了。但是Calendar的API还是有缺陷,它的实例是可以修改的,并且它没有处理诸如闰秒这样的问题。 第三次在Java SE 8中引入了java.timeAPI,它修正了过去的错误。并且应该会服役相当长一段时间。

时间线

Java的Date和Time API规范要求Java使用的时间尺度为:

  • 每天86400秒。
  • 每天正午与官方时间精确匹配。
  • 在其他时间点上,以精确定义的方式与官方时间接近匹配。

在Java中,Instance类表示时间线上的某个点。被称为“新纪元”的时间线远点被设置为穿过伦敦格林尼治皇家天文台的本初子午线所处时区的1970年1月1日的午夜。这与UNIX/POSIX时间使用的惯例相同。从该原点开始,时间按照每天86400秒向前或向回度量,精确到纳秒。其有以下API:

  • Instance.MIN():最小值,表示10亿年前。
  • Instance.MAX():最大值,是公元1000 000 000年的12月21日。
  • Instance.now():给出当前的时刻。
  • Instance.between(start, end):表示两个时间点的差距。返回的是一个Duriation对象,可以对其调用toNanostoMillsgetSecondstoMinutestoHourstoDays来获取Duration按照传统单位度量的时间长度。(值得注意的是,转换为纳秒时,long类型只能存储大约300年的长度。)

TIP:InstanceDuration类都是不可修改的类,所以诸如multipliedByminus这样的方法都会返回一个新的实例。

日历类

除了上述的DateTime类,Java还拥有一个时Calender类。其代表了系统此刻日期对应的日历对象。

其主要有5个方法:

  1. public get(int filed):获取日期中的某个字段信息。
  2. public void set(int field, int value):修改日历中的某个字段。
  3. public void add(int field, int amount):为某个字段增加或减少值。
  4. public final Date getTime():获取此刻日期对象。
  5. public long getTimeInMillis():获取此刻时间毫秒值。

本地日期

在Java API中有两种人类时间,本地日期/时间和时区时间。本地日期/时间包含日期和当天的时间。但是与时区信息没有和关系。例如2021年1月1日就是一个本地日期,因为整个日期既没有当天的时间,也没有时区信息,因此它不对应精确的时刻。与之对应的是,2021年1月1日 06:11:11 UTC+8 是一个时区日期/时间,表示的是时间线上的一个精确的时刻。

LocalDate对象是带有年月日的日期,用于表示本地日期。

创建LocalDate

可以使用以下两个API构建LocalDate对象

  • public static LocalDate now():获取当前的本地日期对象。
  • public static LocalDate of(int year, int month, int dayOfMonth):获取指定日期的本地日期对象。

改变LocalDate

LocalDate有众多的方法用来通过一个LocalDate构建新的LocalDate对象。(注意LocalDate是不可修改,所以以下方法都是返回新的类。)

  • public LocalDate plus(TemporalAmount amountToAdd):产生一个新对象,在原来对象上加一个Peroid对象,参数一般是一个Period对象(Period实现了TemporalAmount 接口)。
  • public LocalDate minus(TemporalAmount amountToAdd):产生一个新对象,在原来对象上减一个Peroid对象,参数一般是一个Period对象(Period实现了TemporalAmount 接口)。
  • public LocalDate (plus|minus)(Days|Weeks|Months|Years)(long number) :产生一个新对象,在原来对象上加或减指定的时间单位获得。
  • public LocalDate with(TemporalAdjuster adjuster):产生一个新对象,按照Peroid对象修改原对象中的数据。
  • with(Year|Month|DayOfMonth|DayOfYear)(int xxx):产生一个新对象,修改原对象中的数据,注意DayOfMonth表示月份日期(如2022年6月6日),DayOfYear表示年日期(如2022年第5天)。
  • get(Year|Month|MonthValue|DayOfMonth|DayOfYear)():获取当前对象的对应数据。注意getMonth返回的是Month对象,而getMonthValue返回的是月份的数字(1-12)。getDayOfMonthgetDayOfYear表示分别返回月份日(1-31)和年日期(1-366)。
  • public String format(DateTimeFormatter formatter):返回格式化的日期。

间隔

本地日期的时长间隔为Period对象(与Instance对象的Duration对象相似)。可以通过以下API创建Period对象:

  • public static Period of(int years, int months, int days)
  • public static Period of(Year|Month|Weeks|Days)(int xxx)

并且其也有withXXX来修改数据,plusXXX方法来加数据,minusXXX方法来减数据。

然后可以使用LocalDate的重载方法来使用这个Peroid来改变原数据。

日期调整器

有些时候需要计算例如“每月的第一个周二”这样的日期。TemporalAdjusters类提供了一些方法来用于计算,具体API如下:

  • public static TemporalAdjuster firstDayOfMonth()
  • public static TemporalAdjuster lastDayOfMonth()
  • public static TemporalAdjuster firstDayOfNextMonth()
  • public static TemporalAdjuster firstDayOfYear()
  • public static TemporalAdjuster lastDayOfYear()
  • public static TemporalAdjuster firstDayOfNextYear()
  • public static TemporalAdjuster firstInMonth(DayOfWeek dayOfWeek)
  • public static TemporalAdjuster lastInMonth(DayOfWeek dayOfWeek)
  • public static TemporalAdjuster dayOfWeekInMonth(int ordinal, DayOfWeek dayOfWeek)
  • public static TemporalAdjuster next(DayOfWeek dayOfWeek)
  • public static TemporalAdjuster nextOrSame(DayOfWeek dayOfWeek)
  • public static TemporalAdjuster previous(DayOfWeek dayOfWeek)
  • public static TemporalAdjuster previousOrSame(DayOfWeek dayOfWeek)

(这些方法看名字就可以很明显的看出其功能,就不再介绍)

注意这些方法都返回了一个TemporalAdjuster方法,我们需要将其传递给LocalDate.with方法以获得一个新的LocalDate对象。

例如:

1
2
3
4
LocalDate nowDate = LocalDate.of(2000, 1, 1);
TemporalAdjuster a = TemporalAdjusters.firstDayOfNextMonth();
LocalDate newDate = nowDate.with(a);
System.out.println(newDate); //2000-02-01

创建自定义调整器

还可以通过实现TemporalAdjuster接口来创建自定义的调整器。

例如:

1
2
3
4
5
6
7
8
TempoalAdjuster NEXT_WEORKDAY = w -> {
LocalDate res = w;
do{
res = res.plusDays(1);
}
while(res.getDayOfWeek().getValue() >= 6);
return res;
}

值得注意的是w原本是一个Temporal类型,我们需要将其转为LocalDate对象(LocalDate也实现了Temporal接口)。

本地时间

LocalTime表示当前时刻。如:15:30:00

创建LocalTime

可以使用ofnow来创建LocalTime对象。

  • public static LocalTime now()
  • public static LocalTime of(int hour, int minute)
  • public static LocalTime of(int hour, int minute, int second)
  • public static LocalTime of(int hour, int minute, int second, int nanoOfSecond)

用法基本与LocalDate一致,只是参数不同。

操作

LocalTime也是拥有withminusplusgetto系列方法。

值得注意的是getto系列方法的设计。

  • get()可以传入TemporalField对象来获取对应域(如时分秒)的值。也可以用get(Hour|Minutes|Second|Nano)方法来获取对应域的值。
  • to方法有两个方法:
    • public int toSecondOfDay():获取当前对应于一天中的多少秒。
    • public long toNanoOfDay():获取当前对应于一天中的多少纳秒

时区时间

在Java中,用ZonedDateTime来表示时区日期。其大部分方法都与LocalDateTime(相当于LocalDateLocalTime的结合)的方法相同。不同的是必须传入一个时区ID。这个ID可以从ZonedId对象中获取。

例如:

1
2
3
4
5
ZonedDateTime skipped = ZonedDateTime.of(
LocalDate.of(2013, 3, 31),
LocalTime.of(2, 30),
ZonedId.of("Europe/Berlin")
);

时区转换

一个时区的时间与另外时区的时间是不同的,而且设计夏令时问题。所以一般使用withZoneSameLocal(ZoneId)方法来进行转换。

例如:

1
2
3
4
5
6
// 以中国时区获取当前时间:
ZonedDateTime Shanghai = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
// 转换为纽约时间:
ZonedDateTime NewYork = Shanghai.withZoneSameInstant(ZoneId.of("America/New_York"));
System.out.println(Shanghai);
System.out.println(NewYork);

本地日期时间

LocalDateTime

LocalDateTime可以存储日期和时间,其中时间包括秒后10位。比如:2nd October 2007 at 13:45.30.123456789

其用法和LocalDate基本一致。

格式化和解析

日期时间的格式化采用的是DateTimeFormatter类。

DateTimeFormatter类提供了是三种用于打印日期/时间值的格式器:

  • 预定义的格式。
  • locale相关的格式器。
  • 带有定制模式的格式器。

预定义的格式

下面是预定义的格式器

预定义的格式器

要使用标准的格式,可以世界调用其format方法:

1
2
LocalDateTime localDateTime = LocalDateTime.now();
String formatted = DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(localDateTime); //20220216

标准格式器主要是为了机器可读的时间戳而设计的。

locale相关的格式器

对于日期和时间而言,有4种locale相关的格式化风格,即SHORTMEDIUMLONGFULL。见下表:

locale相关的格式化风格

使用ofLocalizedDateofLocalizedTimeofLocalizedDateTime可以创建这种格式。如:

1
2
3
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG);
String reString1 = formatter.format(localDateTime);
System.out.println(reString1); //February 16, 2022

这里使用了默认的locale,为了切换locale,可以直接使用withLocale方法。

如:

1
2
String reString2 = formatter.withLocale(Locale.CHINA).format(localDateTime);
//2002年2月16日

定制日期格式

在自定义格式中,规定了特定字母表达的日期中的部分,例如yyyy表示2000年这种格式。

所以,一个经典的例子如下:

1
formatter = DateTimeFormatter.ofPattern("E yyyy-MM--dd HH:mm");

具体所有格式化符号如下表。

常用的日期时间格式的格式化符号

解析字符串

可以利用日期格式来解析对应的字符串。

如:

1
2
LocalDate churchsBirthday = LocalDate.parse("1903-06-14");
ZonedDateTime apollolllauch = ZonedDateTime.parse("1969-07-16 03:32:00-0400", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssxx"));

第一个例子使用了标准的ISO_LOCAL_DATE格式器,而第二个调用了一个自定义的格式器。

Powered by Hexo and Hexo-theme-hiker

Copyright © 2019 - 2024 My Wonderland All Rights Reserved.

UV : | PV :