Java date

作者: 赵阳_c149 | 来源:发表于2019-11-04 13:39 被阅读0次

    时间像是一个箭头,可以很容易的设置开始点,然后向前数或者向后数。但是为什处理时间这么难呢,因为你得让人看的懂啊。比方说,我告诉你“我们将于1523793600开一次会”,你是觉得自己理解能力差还是会觉得我不正常?

    历史

    在Java 1.0中,设计者提供了Date接口处理时间,但是事实证明这是一个“naïve”的设计,1.1之后大部分的方法都被废弃了。

    Java 1.1引入了Calendar方法,结果也不咋地,他的实例是mutable的,而且无法处理闰秒(Leap seconds)这样的问题。

    于是在Java 8中,对处理时间的接口进行了第三次设计,一次“charm”的设计。引入了java.time API。本文主要介绍java.time的基本概念和使用方式。

    时间的定义

    在英语中(学编程咋能不学英语 ;-<),时间有两个概念Date和Time,Date指的是日期如2019年11月4日,而Time指的是一天内的“时间”,如上午10:46分。
    开始,人类主要是通过地球的自转来确定时间,将地自转一圈的时间等分为86,400份,定为一秒。然而,地球会抖动,是的,抖动。想象一下周末你用洗衣机甩干衣服,由于滚筒里的衣服放的不均匀,在滚筒滚动的时候,就会发生抖动。不要说你家的高级西门子洗衣机几乎没有抖动,他那么贵是有原因的。所以说根据地球自转计算出来的秒并不准确。好在,人类发现了一种化学元素,铯原子-133,根据这种原子的一种内在不变的特性,可以精确的定义秒的长度。一些介绍相对论等前沿物理学(如《时间简史》)的科普书中经常提到“原子钟”,全名就是铯原子种。

    这样,时间准了。但是地球还是会抖动啊,铯原子钟计算出来的86,400秒,也就是“1天”,仍然需要和地球的自转同步,因此也就有了闰秒。通常情况下,计算机通过“smoothing”的方式将“一天”和地球的自转同步,计算机上的“一天”在闰秒发生之前被智能的稍稍减慢或者加快,使得“一天”恒定是86,400秒。


    以上内容可看可不看!


    Java中“时间点”的定义

    在java中,一个Instant代表时间线上的一点。时间线的起点被称为“epoch”(新纪元),被设置为子午线上(位于伦敦的格林尼治皇家天文台)的1970年1月1日的午夜,这和UNIX/POSIX时间中的约定是一致的。从这个时间开始,每天的时间定为86,400秒,精确到纳秒(nanosecond 十亿分之一秒)。Instant可以回溯到10亿年前(Instant.MIN),(地球的年龄是45年,不够用啊。。。)最大的时间,是10年后的12月31日(Instant.MAX)。

    static方法Instant.now()返回当前的时间点。可以用equalscompareToInstant进行比较,所以你可以将Instant当作timestamp使用。

    也可以用Duration.between得到两个Instant的间隔。

    Instant start = Instant.now();
    runAlgorithm();
    Instant end = Instant.now();
    Duration timeElapsed = Duration.between(start, end);
    long millis = timeElapsed.toMillis();
    

    Duration是两个Instant之间的间隔量,可以将其转换为人类熟悉的形式:

    toNanos, toMillis, toSeconds, toMinutes, toHours, or toDays
    

    如果需要精确到纳秒,就需要考虑溢出问题。一个long型变量可以保存将尽300年的纳米秒。如果相差过大,可以用Duration来做:

    boolean overTenTimesFaster = timeElapsed.toNanos() * 10 < timeElapsed2.toNanos();
    

    InstantDuration是immutable。

    Local Date

    上一节主要讲了绝对时间,现在我们来看人类读的懂的时间。在java API中,有两中人类读的懂的时间,local date/timezoned date/timeLocal date/time仅包含date和time信息,而没有zone信息,因此也就无法同一个精确的Instant时间关联起来,例如June 14,1903 (lambda的发明者Alonzo Church的生日)。与之相反,zoned date/time,July 16,1969, 09:32:00 EDT(阿波罗11号的发射日),可以精确的代表时间线上的一个Instant时间。

    在一些存在夏令时(提出者是本杰明·富兰克林,你没看错,我也没写错,就是那个被印在100美元钞票上的神人)的国家和地区(例如现在的美帝和多年前的中国,哈,暴漏了我的年龄),没有必要使用zoned date/time。因为如果你将和女朋友约会的时间定为早上10:00,那么你不能因为夏令时来到,就晚到一个小时(你可以试试)。由此,API的设计者建议,在不需要使用zonde 时间的情况下,就不要乱用。如生日,节日,开会时间,最好还是用local date/time。

    好,就让我们看看如何构造和使用lcoal date/time。Java API提供了LocalDate类,这是一个描述date(日期)的对象,顾名思义,就是由年、月、日构成,可以用static方法now()或者of()创建一个实例:

    LocalDate today = LocalDate.now(); // Today’s date
    LocalDate alonzosBirthday = LocalDate.of(1903, 6, 14);
    alonzosBirthday = LocalDate.of(1903, Month.JUNE, 14);
       // Uses the Month enumeration
    

    在UNIX和java.util.Date中,月份是从0开始的,年是从1900年开始数的,为啥?你去问C程序猿!与之相比,LocalDate正常了一些,月份从1开始算,而且还提供了Month enumeration。

    比方说,如果你想知道程序员日(一年的第256天,多么自恋的程序员,除了他们,谁想知道这个问题。。。),

    LocalDate programmersDay = LocalDate.of(2014, 1, 1).plusDays(255);
       // September 13, but in a leap year it would be September 12
    

    主要到Instant之间的间隔是一个Duration,而LocalDate之间的间隔是一个Period,代表逝去的年、月、日:

    birthday.plus(Period.ofYears(1)) 
    birthday.plusYears(1)
    

    作用相同。

    birthday.plus(Period.ofDays(365))
    

    在非闰年的的时候也行
    要想知道两个local date的间隔:

    independenceDay.until(christmas)
    

    java8和java9还提供了更多的工具类,想了解更多的话可以参照官方文档。

    Local Time

    Java API提供了LocalTime类,这是一个描述time(时间,入15:30:00)的对象,可以用static方法now()或者of()创建一个实例:

    LocalTime rightNow = LocalTime.now();
    LocalTime bedtime = LocalTime.of(22, 30); // or LocalTime.of(22, 30, 0)
    

    还有一个LocalDateTime类,用来代表date和time,主要用来在一个固定的time zone下存储时间点。

    Zoned 时间

    上帝说“世界还不够复杂”,于是就有了时区。这是一个百分百的人工产物。在一个理性的世界中,我们都得服从格林尼治时间,一些人在“凌晨”两点吃午饭,还有一些人在“夜里”22:00吃,你为啥知道到饭点了?因为你饿了。想想吧,我天朝上国幅员辽阔,跨越4个时区。。。而其他地方的人也不好过,他们的时间考虑了时区,但是每当跨时区的时候,都要重调时间,这很麻烦,更烦的是,还要考虑夏/冬令时!

    然而,这就是人生,总得微笑着活下去。

    IANA(internet assigned numbers authority)维护着一个数据库,保存了世界上的时区信息(www.iana.org/time-zones),每年都要更新几次。Java用的就是这个数据库。

    在Java中,每个时区都有一个ID

    ZonedDateTime apollo11launch  = ZonedDateTime.of(1969, 7, 16, 9, 32, 0, 0,
       ZoneId.of("America/New_York"));
       // 1969-07-16T09:32-04:00[America/New_York]
    

    ZonedDateTime和Instant也可以相互转换:

    instant.atZone(ZoneId.of("UTC")) 
    
    apollo11launch.toInstant
    

    格式化

    DateTimeFormatter类提供了3种formatter打印date/time

    • 预定义的标准formatter,主要方便机器读取。
    String formatted = DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(apollo11launch);
       // 1969-07-16T09:32:00-04:00"
    

    主要是从一些常量中选择需要的格式。

    • Locale-specific formatter
      方便人类阅读,主要有四种形式:human short,medium,long,full。
      通过调用static方法创建:ofLocalizedDate, ofLocalizedTime, and ofLocalizedDateTime。
    DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);
    String formatted = formatter.format(apollo11launch);
      // July 16, 1969 9:32:00 AM EDT
    

    上述方法使用系统默认的locale信息,还可以指定特殊的locale信息:

    formatted = formatter.withLocale(Locale.FRENCH).format(apollo11launch);
       // 16 juillet 1969 09:32:00 EDT
    
    • 自定义的pattern
      formatter = DateTimeFormatter.ofPattern("E yyyy-MM-dd HH:mm");

    同遗留代码交互

    java.time API 还提供了一些方法,一遍java 8之后的代码同之前的代码进行交互。可以查看相关文档。

    【1】本文大部分内容来自于《Core Java,Volume II —— Advanced Features, 11th Edition》by Cay S. Horstmann,Prentice Hall 2019年出版,第六章,‘The Date and Time API’。

    相关文章

      网友评论

        本文标题:Java date

        本文链接:https://www.haomeiwen.com/subject/fuvrbctx.html