一、时间线
在历史上,基本的时间单元‘秒’来自于地球围绕其轴心的自转。地球完成一次自转需要24小时或者86400秒。
Java日期和时间API规范要求Java使用如下时标:
- 每天都有86400秒
- 每天正午与官方时间准确匹配
- 其他时间也要以一种精确定义的方式与其紧密匹配
1.在Java中,一个
Instant
对象表示时间轴上的一个点(绝对时间),原点(元年)被规定为1970年1月1日的午夜。从原点开始,时间按照每天86400秒进行计算。向前向后分别以纳秒为单位。Instant的最小值(
Instant
.
MIN
)可以回退到10亿年前之久,最大值(
Instant
.
MAX
)表示1000000000年的12月31日
- Instant.now():返回当前的瞬时点
- Duration.between(start,end):计算两个瞬时点之间的时间距离
--------------------------------------------------------------------------------------------------------------------
Instant start=
Instant
.
now()
;
Thread.
sleep
(1000);
Instant end=
Instant
.
now()
;
Duration timeElapsed=
Duration
.
between(start,end)
;
long millis = timeElapsed.
toMillis()
;
--------------------------------------------------------------------------------------------------------------------
2.一个Duration对象表示两个瞬时点之间的时间量,可以通过调用
toNanos
、
toMillis
、
toSeconds
、
toMinutes
、
toHours
或者
toDays
方法,得到以各种时间单位来表示的
Duration
对象
Duration
对象在内部存储上需要多个long值,秒钟的值由一个long值保存,而纳秒的值由另一个int保存,大概300年的纳秒值才会导致long值溢出
3.时间
Instant
和
Duration
的数学操作
方法
|
描述
|
plus,minus
|
对当前Instant或者Duration添加或减少一段时间
|
plusNanos,plusMillis,
plusSeconds,plusMinutes,
plusHours,plusDays
|
根据指定的时间单位,对当前Instant或者Duration添加一段时间
|
minusNanos,minusMillis,
minusSeconds,minusMinutes,
minusHours,minusDays
|
根据指定的时间单位,对当前Instant或者Duration减少一段时间
|
multipliedBy,dividedBy
|
返回当前Duration与指定long值相乘或相除得到的一段时间,注意
这只能用于Duration不能用于Instant
|
isZero,isNegativ
|
检查Duration是否为0或负数
|
例子:检查某个算法是否比另一个算法至少快十倍
--------------------------------------------------------------------------------------------------------------------
Duration
timeElapsed2=
Duration
.
between(start2,end2)
;
boolean overTenTimesFaster=
timeElapsed.
multipliedBy
(10).
minus
(timeElapsed2).
isNegative
();
--------------------------------------------------------------------------------------------------------------------
4.
Instant
和
Duration
类都是不可变的,他们的所有方法,例如
multipliedBy
或者
minus
都会返回一个新的实例
二、本地日期
在新的JavaAPI中,有两种人类时间本地日期/时间和带时区的时间,例如
“June 14,1903”、“July 16,1969,09:32:00 EDT”,有许多计算是不需要时区的,并且在某些情况下,考虑时区可能会导致错误的结果,API设计者们不推荐使用带时区的时间,生日、假期、会议时间等,最好都用本地日期或事件来表示,除非真的希望表示绝对的瞬时点
1.
LocalDate
是一个带有年份、月份、当月天数的日期,要创建一个
LocalDate
可以使用静态方法
now
或
of
:
--------------------------------------------------------------------------------------------------------------------
LocalDate
today=
LocalDate
.
now()
;
LocalDate
alonzosBirthday=
LocalDate
.
of
(1903,6,14);
alonzosBirthday=
LocalDate
.
of
(1903,Month.JUNE,14);
--------------------------------------------------------------------------------------------------------------------
2.与
Date
不同,
LocalDate
使用与正常生活中一样的数字来表示月份,即1表示1月份,除此之外还可以使用Month枚举
3.LocalDate常用方法
方法
|
描述
|
now,of
|
静态方法,根据当前时间或指定的年、月、日来创建一个LocalDate对象
|
plusDays,plusWeeks,
plusMonths,plusYears
|
向当前LocalDate对象添加几天、几周、几个月或者几年
|
minusDays,minusWeeks,
minusMonths,minusYears
|
从当前LocalDate对象中减去几天、几周、几个月或者几年
|
plus,minus
|
添加或减少一个Duration或Period
|
withDayOfMonth
withDayOfYear
withMonth,withYear
|
将月份天数、年份天数、月份、年份修改为指定的值,并返回一个新的
LocalDate对象
|
getDayOfMonth
|
获得月份天数(1~31)
|
getDayOfYear
|
获得年份天数(1~366)
|
getDayWeek
|
获得星期几(返回一个DayOfWeek的枚举值)
|
getMonth,getMonthValue
|
获得月份,或者为一个Month枚举的值,或者是1~12之间的一个数字
|
getYear
|
获得年份,-999999999-999999999之间
|
Until
|
获得两个日期之间的Period对象,或者指定ChronoUnits数字
|
isBefore,isAfter
|
比较两个LocalDate
|
isLeapYear
|
如果当年为闰年,即当年的年份可以被4或400整除,但不能被100整除,
则返回true,该算法适用于以前的所有年份,即使从历史角度看并不准确
|
例子1
--------------------------------------------------------------------------------------------------------------------
LocalDate
programmersDay=
LocalDate
.
of
(2014,1,1).
plusDays
(255);
--------------------------------------------------------------------------------------------------------------------
例子2
--------------------------------------------------------------------------------------------------------------------
independenceDay.
until
(christmas); //返回5个月21天
independenceDay.
until
(christmas,
ChronoUnit
.
DAYS
); //返回174天
--------------------------------------------------------------------------------------------------------------------
例子3
--------------------------------------------------------------------------------------------------------------------
LocalDate
.
of
(1900,1,1).
getDayOfWeek()
.
getValue()
--------------------------------------------------------------------------------------------------------------------
4.两个瞬时点之间的距离时一个持续时间
Duration
,而对于本地日期,对应的对象就是时段
Period
,表示一段逝去的年、月或日。可以通过birthday.
plus
(
Period
.
ofYears
(1))或者birthday.
plusYears
(1)来获取下一年的生日日期,但如果时闰年,birthday.
plus
(
Duration
.
ofDays
(365))会导致错误的结果
5.
DayOfWeek
枚举提供了一些方便的方法
plus
和
minus
,用来计算模为7的星期几,例如:DayOfWeek.SATURDAY.
plus
(3)会返回DayOfWeek.TUESDAY
6.除了
LocalDate
,Java8中还提供了
MonthDay
、
YearMonth
和
Year
类来描述部分日期,例如
MonthDay
可以用来表示12月25日
三、日期校正器
1.编写代码时候,经常计算例如"每月的第一个周二"这样的日期。
TemporalAdjusters
类提供了许多静态方法来进行常用的校正。可以将一个校正方法的结果传递给
with
方法,与往常一样,
with
方法会返回一个新的
LocalDate
对象,而不会修改原有的对象
--------------------------------------------------------------------------------------------------------------------
LocalDate
firstTuesday=
LocalDate
.
of
(year,month,1)
.
with
(
TemporalAdjusters
.
nextOrSame
(
DayOfWeek
.
TUESDAY
));
--------------------------------------------------------------------------------------------------------------------
2.TemporalAdjusters类中的日期校正方法
方法
|
描述
|
next(weekday),previous(weekday)
|
指定工作日的前一天或后一天
|
nextOrSame(weekday)
previousOrSame(weekday)
|
从指定的日期开始,指定工作日的前一天或
后一天
|
dayOfWeekInMonth(n,weekday)
|
某月中的第n个工作日(weekday所指定)
|
lastInMonth(weekday)
|
某月中的最后一个工作日(weekday所指定)
|
firstDayOfMonth(),firstDayOfNextMonth()
firstDayOfNextYear(),lastDayOfMonth()
lastDayOfPreviousMonth(),lastDayOfYear()
|
3.可以通过
TemporalAdjuster
接口来实现自己的校正器
--------------------------------------------------------------------------------------------------------------------
TemporalAdjuster
NEXT_WORKDAY=w->{
LocalDate
result=(
LocalDate
)w;
do{
result=result.
plusDays
(1);
}while(
result
.
getDayOfWeek()
.
getValue()
>=6);
return result;
}
LocalDate
backToWork=today.
with
(NEXT_WORKDAY);
--------------------------------------------------------------------------------------------------------------------
在本例中,lambda表达式的参数类型为
Temporal
,必须被强制转换成
LocalDate
对象,我们可以通过
ofDateAdjuster
方法以及一个
UnaryOperator
<LocalDate>的lambda表达式来避免强制转换
--------------------------------------------------------------------------------------------------------------------
TemporalAdjuster
NEXT_WORKDAY=
TemporalAdjusters
.
ofDateAdjuster
(w->{
LocalDate
result=w;
do{
result=result.
plusDays
(1);
}while(
result
.
getDayOfWeek()
.
getValue()
>=6);
return result;
});
--------------------------------------------------------------------------------------------------------------------
四、本地时间
1.
LocalTime
表示一天中的某个时间,例如15:30:00,其实例可以通过
now
或者
of
方法来创建
--------------------------------------------------------------------------------------------------------------------
LocalTime
rightNow=
LocalTime
.
now
();
LocalTime
bedTime=
LocalTime
.
of
(22,30);
--------------------------------------------------------------------------------------------------------------------
2.LocalTime方法
方法
|
描述
|
now,of
|
静态方法,根据当前时间或者指定时间创建实例
|
plusHours,plusMinutes
plusSeconds,plusNanos
|
向当前LocalTime对象添加几个小时、几分钟、几秒或者
几微秒
|
minusHours,minusMinutes
minusSeconds,minusNanos
|
从当前LocalTime对象中减去几个小时、几分钟、几秒或
者几微妙
|
plus,minus
|
添加或减少一个Duration
|
withHour,withMinute
withSecond,withNano
|
将小时、分钟、秒钟、微秒修改为指定的值,并返回一个新的
LocalTime对象
|
getHour,getMinute
getSecond,getNano
|
获得当前LocalTime的小时、分钟、秒钟及微秒值
|
toSecondOfDay,toNanoOfDay
|
返回午夜时间与当前LocalTime之间相隔的秒钟数或微秒
数
|
isBefore,isAfter
|
比较两个LocalTime
|
3.
LocalTime
本身并不关心是AM还是PM,而是交给格式化程序来做这件事
4.
LocalDatetime
类表示一个日期和时间,该类适合用来存储确定时区中的某个时间点,例如某一次具体的课程或者活动安排。但是如果需要进行跨夏令时的时间计算,或者需要处理的用户处于不同的时区中,应该使用
ZonedDateTime
类
五、带时区的时间
1.因特网编号管理局(IANA)维护着一份全球所有已知时区的数据库,每年会更新几次,这些更新主要处理夏令时规则的改变,Java就使用了IANA数据库。每个时区都有一个ID,例如America/New_York或者Europe/Berlin,想获取素有可用的时区,可以调用
ZoneId
.
getAvailableIds
2.更具指定的时区ID,静态方法
ZoneId
.
of
(id)会返回一个
ZoneId
对象,可以通过local.
atZone
(zoneId)将一个
LocalDateTime
对象转换成一个
ZonedDateTime
对象,或者通过调用静态方法
ZonedDateTime
.
of
(year,month,day,hour,minute,second,nano,
zoneId
)
来创建一个
ZonedDateTime
对象
--------------------------------------------------------------------------------------------------------------------
ZoneDateTime
apollo11launch=
ZonedDateTime
.
of
(1969,7,16,9,32,0,0,
ZoneId
.
of
("America/New_York"));
--------------------------------------------------------------------------------------------------------------------
3.可以调用apollo11launch.
toInstant()
来获得对应的
Instant
对象,也可以通过调用
instant.
atZone(zonedId)
将一个
Instant
对象转换成一个指定时区的
ZonedDateTime
对象
4.ZonedDateTime的方法
方法
|
描述
|
now,of,ofInstant
|
静态方法,根据当前时间或指定的年、月、
日、分、秒、微秒(或者一个LocalDate、
LocalTime对象)和ZonedId,或者一个
Instant对象和ZoneId创建实例
|
plusDays,plusWeeks
plusMonths,plusYears
plusHours,plusMinutes
plusSeconds,plusNanos
|
向当前ZonedDateTime对象添加相应单位
的时间
|
minusDays,minusWeeks
minusMonths,minusYears
minusHours,minusMinutes
minusSecond,minusNanos
|
从当前ZonedDateTime对象中减去相应单位
的时间
|
plus,minus
|
添加或减少一个Duration或者Period
|
withDayOfMonth,withDayOfYear
withMonth,withYear,withHour
withMinute,withSecond,withNano
|
将相应单位的时间改为指定的值,并返回
一个新的ZonedDateTime对象
|
withZoneSameInstant
withZoneSameLocal
|
返回指定时区中的一个新ZonedDateTime对
象,它可以表示相同的瞬时点或者本地时间
|
getDayOfMonth
|
获得月份天数
|
getDayOfYear
|
获得年份天数
|
getDayOfWeek
|
获得星期几(返回一个DayOfWeek的枚举值)
|
getMonth,getMonthValue
|
获得月份,或者为一个Month枚举的值,或
者为1~12之间的数字
|
getYear
|
获得年份
|
getHour,getMinute
getSecond,getNano
|
获得ZonedDatetime对象的小时、分钟、秒钟
或者微秒数
|
getOffset
|
获得与UTC之间的时差,返回一个
ZoneOffset对象,时差在-12:00到+14:00
之间,会根据夏令时发生改变
|
toLocaldate,toLocalTime
toInstant
|
返回本地日期、本地时间、瞬时点
|
isBefore,isAfter
|
比较两个ZonedDateTime
|
注:UTC表示"世界协调时",是格林威治皇家天文台的时间
5.当调整一个跨夏令时的日期时,不要添加7天的Duration对象,而应该使用Period类,否则无法处理夏令时
--------------------------------------------------------------------------------------------------------------------
ZonedDateTime
nextMeeting=meeting.
plus
(
Duration
.
ofDays
(7)); //不能处理夏令时
ZonedDateTime
nextMeeting=meeting.
plus
(
Period
.
ofDays
(7));
--------------------------------------------------------------------------------------------------------------------
六、格式化和解析
DateTimeFormatter
类提供了三种格式化方法来打印日期/时间:
- 预定义的标准格式
- 语言环境相关的格式
- 自定义的格式
标准格式主要用于机器可读的时间戳如:1969-07-16-05:00,语言相关的格式是为了让人能够读懂日期和时间,这里只简单描述语言相关的格式。
DateTimeFormatter
类用来代替
DateFormat
,如果出于向后兼容的考虑,需要一个
DateFormat
的实例,可以通过调用
DateTimeFormatter
实例的
toFormat()
方法来维护
1.Java8对日期和时间各提供了4种风格,SHORT、MEDIUM、LONG和FULL
风格
|
日期
|
时间
|
SHORT
|
7/16/69
|
9:32 AM
|
MEDIUM
|
Jul 16,1969
|
9:32:00 AM
|
LONG
|
July 16,1969
|
9:32:00 AM EDT
|
FULL
|
Wednesday,July 16,1969
|
9:32:00 AM EDT
|
可以通过静态方法
ofLocalizedDate
、
ofLocalizedTime
和
ofLocalizedDateTime
来创建这类格式
--------------------------------------------------------------------------------------------------------------------
DateTimeFormatter
formatter=
DateTimeFormatter
.
ofLocalizedDateTime
(
FormatStyle
.
LONG
);
String formatted=formatter.
format
(apollo11launch);
--------------------------------------------------------------------------------------------------------------------
如果要更改其他的语言环境,只需要使用
withLocale
--------------------------------------------------------------------------------------------------------------------
formatted=formatter.
withLocale
(Locale.FRENCH).
formar
(apollo11launch);
--------------------------------------------------------------------------------------------------------------------
2.可以通过
ofPattern()
方法来自定义日期的格式
--------------------------------------------------------------------------------------------------------------------
formatted=
DateTimeFormatter
.
ofPattern
("E yyyy-MM-dd HH:mm");
//Wed 1969-07-16 09:32
--------------------------------------------------------------------------------------------------------------------
七、与遗留代码互操作
Instant <===>java.util.Date
Date.
from(instant)
date.
toInstant()
ZonedDateTime<===>java.util.GregorianCalendar
GregorianCalendar.
from(zonedDateTime)
cal.
toZonedDateTime()
Instant<===>java.sql.Timestamp
Timestamp.
from(instant)
timestamp.
toInstant()
LocalDateTime<===>java.sql.Timestamp
Timestamp.
valueOf(localDateTime)
timeStamp.
toLocalDateTime()
LocalDate<===>java.sql.Time
Date.
valueOf(localDate)
date.
toLocalDate()
LocalTime<===>java.sql.Time
Time.
valueOf(localTime)
date.
toLocalDate()
DateTimeFormatter<===>java.text.DateFormat
formatter.
toFormat()
java.util.TimeZone<===>ZoneId
Timezone.
getTimeZone(id)
timeZone.
toZoneId()
java.nio.file.attribute.FileTime<===>Instant
FileTime.
from(instant)
fileTime.
toInstant()