如何正确处理时间
现实生活的世界里,时间是不断向前的,如果向前追溯时间的起点,可能是宇宙出生时,又或是是宇宙出现之前,
但肯定是我们目前无法找到的,我们不知道现在距离时间原点的精确距离。所以我们要表示时间,
就需要人为定义一个原点。
原点被规定为,格林威治时间(GMT)1970年1月1日的午夜 为起点,之于为啥是GMT时间,大概是因为本初子午线在那的原因吧。
Java中的时间
如果你跟你朋友说:“我们 1484301456 一起去吃饭,别迟到!”,而你朋友能马上理解你说的时间,表示时间就会很简单,
只需要一个long值来表示原点的偏移量,这是个绝对时间,在世界范围内都适用。但实际上我们不能马上理解这串数字,
而且我们需要不同的时间单位来表示时间的跨度,比如一个季度是3个月,一个月有30天等。
你可以跟朋友约好“明天这个时候再见面”,你朋友很容易理解明天的意思,但要是没有’天’这个单位,
他就需要在那串数字上加上86400(一天是86400秒)。
Java三次引入处理时间的API,JDK1.0中包含了一个Date
类,但大多数方法在java1.1引入Calendear
类之后被弃用了。
它的实例都是可变的,而且它的API很难使用,比如月份是从0开始这种反人类的设置。
java8引入的java.time
API 已经纠正了之前的问题。它已经完全实现了JSR310
规范。
java8时间API介绍及使用
在新的时间API中,Instant
表示一个精确的时间点,Duration
和Period
表示两个时间点之间的时间量。 LocalDate
表示日期,即xx年xx月xx日,即不包括时间也不带时区。LocalTime
与LocalDate
类似,
但只包含时间。LocalDateTime
则包含日期和时间。ZoneDateTime
表示一个带时区的时间。 DateTimeFormatter
提供格式化和解析功能。下面详细的介绍使用方法。
Instant
Instant
表示一个精确的时间,时间数轴就是由无数个时间点组成,数轴的原点就是上面提
到的1970-1-1 00:00:00
,Instant
由两部分组成,一是从原点开始到指定时间点的秒数s,
二是距离该秒数s的纳秒数。
使用静态方法Instant.now()
可以获取当前的时间点,该方法默认使用的是UTC(协调世界时——由原子钟提供)时间,可以使用equeal
和 compareTo
来比较两个时间点的值。
计算某段代码执行时间可以使用下面的方式:
Duration对象表示两个时间点之间的距离,通过类似toMillis()
toDays()
getSeconds()
等方法,
得到各种时间单位表示的Duration对象。如果确实需要使用纳秒来做一些计算,可以调用toNanos()
获得一个long类型的值,该值表示距离原点的纳秒值。大概300年的纳秒值会导致long值溢出。
Duration内部使用一个long类型来保存秒钟的值,使用一个int来保存纳秒的值,与Instant类似,
这个纳秒保存的是距离该秒钟的纳秒值.
Instant与Duration都可以进行一些运算,来调整表示的时间,比如:plus()
minus
方法,
表示增加或减少一段时间,plusSeconds()
minusSeconds()
plusXxx()
等表示增加或减少相应时间单位的一段时间。
Duration可以进行multipliedBy()
乘法和dividedBy()
除法运算。negated()
做取反运算,即1.2秒取反后为-1.2秒。
非常重要的是,Instant 和 Duration类都是不可变的,他们的所有方法都返回一个新的实例。不可变类有很多优点:
不可变类使用起来不容易出错,其本质上是线程安全的,对象可以被自由的共享,而不用担心被某个方法修改。
LocalDate(本地日期)
上面介绍的Instant是一个绝对的准确时间点,是人类不容易理解的时间,现在介绍人类使用的时间。
LocalDate 表示像 2017-01-01
这样的日期。它包含有年份、月份、当月天数,它不不包含一天中的时间,
以及时区信息。由于上面的这些特点,所以LocalDate不能表示一个准确的时间点,即Instant。
有很多时间的计算是不需要时区的,而且有一些情况下使用时区会导致一些问题,例如你在中国设置了一个 2017-01-01 UT+8:00
的放假提醒,但之后你去了美国,到了2017-01-01 UT+8:00
时间时你收到了提醒,
但是此时美国还没到放假的时间。
API的设计者推荐使用不带时区的时间,除非真的希望表示绝对的时间点。
可以使用静态方法now()
和of()
创建LocalDate。java.util.Date
使用0作为月份的开始,年份从1990年开始算起,
而新的API中完全是用生活中一样的方式来表示年和月份。
可以通过一些方法对日期做一些运算。
上面讲过Duration表示的是Instant对应的时间段,LocalDate对应的表示时间段的是Period,
Period内部使用三个int值分表表示年、月、日。
Duration和Period都是TemporalAmount接口的实现,该接口表示时间量。
LocalDate 也可以增加或减少一段时间:
使用until获得两个日期之间的Period对象
LocalDate提供了一些测试方法: isBefore
isAfter
比较两个LocalDate,isLeapYear
判断是否是闰年。
LocalDate还提供了各种getXxx方法来返回所需要的数据,其中getDayOfWeek()
返回DayOfWeek
枚举。 DayOfWeek
提供了plus
minus
来方便计算星期。
除了LocalDate,Java8还提供了Year
MonthDay
YearMonth
来表示部分日期,例如MonthDay
可以表示1月1日。
日期校正器TemporalAdjuster
如果想找到某个月的第一个周五,或是某个月的最后一天,像这样的日期就可以使用TemporalAdjuster
来进行日期调整。 TemporalAdjusters
提供一些静态方法,返回常用的TemporalAdjuster
。
这是上面例子对应的当月日历
LocalTime(本地时间)
LocalTime表示一天中的某个时间,例如18:00:00
。LocaTime与LocalDate类似,他们也有相似的API。
需要注意的是:LocalTime本身不关心是AM还是PM,而是格式化程序来负责这个事情。
LocalDateTime(本地日期时间)
LocalDateTime表示一个日期和时间,它适合用来存储确定时区的某个时间点。不适合跨时区的问题。
若需要处理跨时区的时间,需要使用ZonedDateTime.
ZonedDateTime(带时区的时间)
时区(Time Zone)是地球上的区域使用同一个时间定义。1884年在华盛顿召开国际经度会议时,
为了克服时间上的混乱,规定将全球划分为24个时区。
由于实用上常常1个国家,或1个省份同时跨着2个或更多时区,为了照顾到行政上的方便,
常将1个国家或1个省份划在一起。所以时区并不严格按南北直线来划分,而是按自然条件来划分。
Java使用ZoneId
来标识不同的时区.
我在测试的时候一共有590个时区可用,但要知道,这个时区的个数不是固定的。
IANA(Internet Assigned Numbers Authority,因特网拨号管理局)维护着一份全球所有已知的时区数据库,
每年会更新几次,主要处理夏令时规则的改变。Java使用了IANA的数据库。
创建ZonedDateTime
LocalDateTime转换为ZonedDateTime
ZonedDateTime的一些方法
ZonedDateTime的许多方法与LocalDateTime、LocalDate、LocalTime类似,下面简单介绍几个方法的使用。
有一些国家和地区使用夏令时,处理起来需要注意,但在中国没有该问题,
需要注意的是使用plus()
时要用Period
对象表示的时间量,而不应该用Duration
表示的时间量, Duration
不能处理夏令时。
格式化和解析 DateTimeFormatter
DateTimeFormatter
是不可变类,而SimpleDateFormat
是非线程安全的,是一个常见的坑。
格式化
DateTimeFormatter使用了三种格式化方法来打印日期和时间
- 预定义的标准格式
DateTimeFormatter
预定义了一些格式,可以直接调用format方法
语言环境相关的格式化风格
根据当前操作系统语言环境,有SHORET
MEDIUM
LONG
FULL
四种不同的风格来格式化。
可以通过DateTimeFormatter
的静态方法ofLocalizedDate
ofLocalizedTime
ofLocalizedDateTime
上面的方法都使用的是默认的语言环境,如果想改语言环境,需要使用withLocale
方法来改变。
使用自定义模式格式化
解析
遗留代码相互操作
Instant
类似于java.util.Date
ZonedDateTime
类似于java.util.GregorianCalendar
转载于:
如何正确处理时间
现实生活的世界里,时间是不断向前的,如果向前追溯时间的起点,可能是宇宙出生时,又或是是宇宙出现之前,
但肯定是我们目前无法找到的,我们不知道现在距离时间原点的精确距离。所以我们要表示时间,
就需要人为定义一个原点。
原点被规定为,格林威治时间(GMT)1970年1月1日的午夜 为起点,之于为啥是GMT时间,大概是因为本初子午线在那的原因吧。
Java中的时间
如果你跟你朋友说:“我们 1484301456 一起去吃饭,别迟到!”,而你朋友能马上理解你说的时间,表示时间就会很简单,
只需要一个long值来表示原点的偏移量,这是个绝对时间,在世界范围内都适用。但实际上我们不能马上理解这串数字,
而且我们需要不同的时间单位来表示时间的跨度,比如一个季度是3个月,一个月有30天等。
你可以跟朋友约好“明天这个时候再见面”,你朋友很容易理解明天的意思,但要是没有’天’这个单位,
他就需要在那串数字上加上86400(一天是86400秒)。
Java三次引入处理时间的API,JDK1.0中包含了一个Date
类,但大多数方法在java1.1引入Calendear
类之后被弃用了。
它的实例都是可变的,而且它的API很难使用,比如月份是从0开始这种反人类的设置。
java8引入的java.time
API 已经纠正了之前的问题。它已经完全实现了JSR310
规范。
java8时间API介绍及使用
在新的时间API中,Instant
表示一个精确的时间点,Duration
和Period
表示两个时间点之间的时间量。 LocalDate
表示日期,即xx年xx月xx日,即不包括时间也不带时区。LocalTime
与LocalDate
类似,
但只包含时间。LocalDateTime
则包含日期和时间。ZoneDateTime
表示一个带时区的时间。 DateTimeFormatter
提供格式化和解析功能。下面详细的介绍使用方法。
Instant
Instant
表示一个精确的时间,时间数轴就是由无数个时间点组成,数轴的原点就是上面提
到的1970-1-1 00:00:00
,Instant
由两部分组成,一是从原点开始到指定时间点的秒数s,
二是距离该秒数s的纳秒数。
使用静态方法Instant.now()
可以获取当前的时间点,该方法默认使用的是UTC(协调世界时——由原子钟提供)时间,可以使用equeal
和 compareTo
来比较两个时间点的值。
计算某段代码执行时间可以使用下面的方式:
Duration对象表示两个时间点之间的距离,通过类似toMillis()
toDays()
getSeconds()
等方法,
得到各种时间单位表示的Duration对象。如果确实需要使用纳秒来做一些计算,可以调用toNanos()
获得一个long类型的值,该值表示距离原点的纳秒值。大概300年的纳秒值会导致long值溢出。
Duration内部使用一个long类型来保存秒钟的值,使用一个int来保存纳秒的值,与Instant类似,
这个纳秒保存的是距离该秒钟的纳秒值.
Instant与Duration都可以进行一些运算,来调整表示的时间,比如:plus()
minus
方法,
表示增加或减少一段时间,plusSeconds()
minusSeconds()
plusXxx()
等表示增加或减少相应时间单位的一段时间。
Duration可以进行multipliedBy()
乘法和dividedBy()
除法运算。negated()
做取反运算,即1.2秒取反后为-1.2秒。
非常重要的是,Instant 和 Duration类都是不可变的,他们的所有方法都返回一个新的实例。不可变类有很多优点:
不可变类使用起来不容易出错,其本质上是线程安全的,对象可以被自由的共享,而不用担心被某个方法修改。
LocalDate(本地日期)
上面介绍的Instant是一个绝对的准确时间点,是人类不容易理解的时间,现在介绍人类使用的时间。
LocalDate 表示像 2017-01-01
这样的日期。它包含有年份、月份、当月天数,它不不包含一天中的时间,
以及时区信息。由于上面的这些特点,所以LocalDate不能表示一个准确的时间点,即Instant。
有很多时间的计算是不需要时区的,而且有一些情况下使用时区会导致一些问题,例如你在中国设置了一个 2017-01-01 UT+8:00
的放假提醒,但之后你去了美国,到了2017-01-01 UT+8:00
时间时你收到了提醒,
但是此时美国还没到放假的时间。
API的设计者推荐使用不带时区的时间,除非真的希望表示绝对的时间点。
可以使用静态方法now()
和of()
创建LocalDate。java.util.Date
使用0作为月份的开始,年份从1990年开始算起,
而新的API中完全是用生活中一样的方式来表示年和月份。
可以通过一些方法对日期做一些运算。
上面讲过Duration表示的是Instant对应的时间段,LocalDate对应的表示时间段的是Period,
Period内部使用三个int值分表表示年、月、日。
Duration和Period都是TemporalAmount接口的实现,该接口表示时间量。
LocalDate 也可以增加或减少一段时间:
使用until获得两个日期之间的Period对象
LocalDate提供了一些测试方法: isBefore
isAfter
比较两个LocalDate,isLeapYear
判断是否是闰年。
LocalDate还提供了各种getXxx方法来返回所需要的数据,其中getDayOfWeek()
返回DayOfWeek
枚举。 DayOfWeek
提供了plus
minus
来方便计算星期。
除了LocalDate,Java8还提供了Year
MonthDay
YearMonth
来表示部分日期,例如MonthDay
可以表示1月1日。
日期校正器TemporalAdjuster
如果想找到某个月的第一个周五,或是某个月的最后一天,像这样的日期就可以使用TemporalAdjuster
来进行日期调整。 TemporalAdjusters
提供一些静态方法,返回常用的TemporalAdjuster
。
这是上面例子对应的当月日历
LocalTime(本地时间)
LocalTime表示一天中的某个时间,例如18:00:00
。LocaTime与LocalDate类似,他们也有相似的API。
需要注意的是:LocalTime本身不关心是AM还是PM,而是格式化程序来负责这个事情。
LocalDateTime(本地日期时间)
LocalDateTime表示一个日期和时间,它适合用来存储确定时区的某个时间点。不适合跨时区的问题。
若需要处理跨时区的时间,需要使用ZonedDateTime.
ZonedDateTime(带时区的时间)
时区(Time Zone)是地球上的区域使用同一个时间定义。1884年在华盛顿召开国际经度会议时,
为了克服时间上的混乱,规定将全球划分为24个时区。
由于实用上常常1个国家,或1个省份同时跨着2个或更多时区,为了照顾到行政上的方便,
常将1个国家或1个省份划在一起。所以时区并不严格按南北直线来划分,而是按自然条件来划分。
Java使用ZoneId
来标识不同的时区.
我在测试的时候一共有590个时区可用,但要知道,这个时区的个数不是固定的。
IANA(Internet Assigned Numbers Authority,因特网拨号管理局)维护着一份全球所有已知的时区数据库,
每年会更新几次,主要处理夏令时规则的改变。Java使用了IANA的数据库。
创建ZonedDateTime
LocalDateTime转换为ZonedDateTime
ZonedDateTime的一些方法
ZonedDateTime的许多方法与LocalDateTime、LocalDate、LocalTime类似,下面简单介绍几个方法的使用。
有一些国家和地区使用夏令时,处理起来需要注意,但在中国没有该问题,
需要注意的是使用plus()
时要用Period
对象表示的时间量,而不应该用Duration
表示的时间量, Duration
不能处理夏令时。
格式化和解析 DateTimeFormatter
DateTimeFormatter
是不可变类,而SimpleDateFormat
是非线程安全的,是一个常见的坑。
格式化
DateTimeFormatter使用了三种格式化方法来打印日期和时间
- 预定义的标准格式
DateTimeFormatter
预定义了一些格式,可以直接调用format方法
语言环境相关的格式化风格
根据当前操作系统语言环境,有SHORET
MEDIUM
LONG
FULL
四种不同的风格来格式化。
可以通过DateTimeFormatter
的静态方法ofLocalizedDate
ofLocalizedTime
ofLocalizedDateTime
上面的方法都使用的是默认的语言环境,如果想改语言环境,需要使用withLocale
方法来改变。
使用自定义模式格式化
解析
遗留代码相互操作
Instant
类似于java.util.Date
ZonedDateTime
类似于java.util.GregorianCalendar