简介
Java1.0有一个Date
类,事后证明其太过简单了,当Java1.1引入Calendar
之后,Date类中的大部分方法就被其弃用了。但是Calendar
的API还是有缺陷,它的实例是可以修改的,并且它没有处理诸如闰秒这样的问题。 第三次在Java SE 8中引入了java.time
API,它修正了过去的错误。并且应该会服役相当长一段时间。
时间线
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
对象,可以对其调用toNanos
、toMills
、getSeconds
、toMinutes
,toHours
和toDays
来获取Duration
按照传统单位度量的时间长度。(值得注意的是,转换为纳秒时,long类型只能存储大约300年的长度。)
TIP:Instance
和Duration
类都是不可修改的类,所以诸如multipliedBy
和minus
这样的方法都会返回一个新的实例。
日历类
除了上述的Date
与Time
类,Java还拥有一个时Calender
类。其代表了系统此刻日期对应的日历对象。
其主要有5个方法:
public get(int filed)
:获取日期中的某个字段信息。public void set(int field, int value)
:修改日历中的某个字段。public void add(int field, int amount)
:为某个字段增加或减少值。public final Date getTime()
:获取此刻日期对象。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)。getDayOfMonth
和getDayOfYear
表示分别返回月份日(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 | LocalDate nowDate = LocalDate.of(2000, 1, 1); |
创建自定义调整器
还可以通过实现TemporalAdjuster
接口来创建自定义的调整器。
例如:
1 | TempoalAdjuster NEXT_WEORKDAY = w -> { |
值得注意的是w原本是一个Temporal类型,我们需要将其转为LocalDate
对象(LocalDate
也实现了Temporal
接口)。
本地时间
LocalTime
表示当前时刻。如:15:30:00
。
创建LocalTime
可以使用of
和now
来创建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
也是拥有with
、minus
、plus
、get
、to
系列方法。
值得注意的是get
和to
系列方法的设计。
get()
可以传入TemporalField
对象来获取对应域(如时分秒)的值。也可以用get(Hour|Minutes|Second|Nano)
方法来获取对应域的值。to
方法有两个方法:public int toSecondOfDay()
:获取当前对应于一天中的多少秒。public long toNanoOfDay()
:获取当前对应于一天中的多少纳秒
时区时间
在Java中,用ZonedDateTime
来表示时区日期。其大部分方法都与LocalDateTime
(相当于LocalDate
与LocalTime
的结合)的方法相同。不同的是必须传入一个时区ID。这个ID可以从ZonedId
对象中获取。
例如:
1 | ZonedDateTime skipped = ZonedDateTime.of( |
时区转换
一个时区的时间与另外时区的时间是不同的,而且设计夏令时问题。所以一般使用withZoneSameLocal(ZoneId)
方法来进行转换。
例如:
1 | // 以中国时区获取当前时间: |
本地日期时间
LocalDateTime
LocalDateTime可以存储日期和时间,其中时间包括秒后10位。比如:2nd October 2007 at 13:45.30.123456789
其用法和LocalDate
基本一致。
格式化和解析
日期时间的格式化采用的是DateTimeFormatter
类。
DateTimeFormatter
类提供了是三种用于打印日期/时间值的格式器:
- 预定义的格式。
locale
相关的格式器。- 带有定制模式的格式器。
预定义的格式
下面是预定义的格式器
要使用标准的格式,可以世界调用其format
方法:
1 | LocalDateTime localDateTime = LocalDateTime.now(); |
标准格式器主要是为了机器可读的时间戳而设计的。
locale
相关的格式器
对于日期和时间而言,有4种locale相关的格式化风格,即SHORT
、MEDIUM
、LONG
和FULL
。见下表:
使用ofLocalizedDate
、ofLocalizedTime
和ofLocalizedDateTime
可以创建这种格式。如:
1 | DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG); |
这里使用了默认的locale,为了切换locale,可以直接使用withLocale
方法。
如:
1 | String reString2 = formatter.withLocale(Locale.CHINA).format(localDateTime); |
定制日期格式
在自定义格式中,规定了特定字母表达的日期中的部分,例如yyyy
表示2000
年这种格式。
所以,一个经典的例子如下:
1 | formatter = DateTimeFormatter.ofPattern("E yyyy-MM--dd HH:mm"); |
具体所有格式化符号如下表。
解析字符串
可以利用日期格式来解析对应的字符串。
如:
1 | LocalDate churchsBirthday = LocalDate.parse("1903-06-14"); |
第一个例子使用了标准的ISO_LOCAL_DATE
格式器,而第二个调用了一个自定义的格式器。