简述Date历史
JAVA1.0时代,对日期和时间的支持只能依赖java.util.Date类。该类只能以毫秒的精度来表示时间。该类在使用时存在如下不适合的地方:年份的起始选择是1900年、月份的起始从0开始、toString的返回值中甚至还包含了JVM的默认时区CET。
JAVA1.1时代,Date类中的很多方法被废弃了,取而代之的是java.util.Calendar类。Calendar类也有类似的问题:月份依旧是从0开始计算(不过,至少Calendar类拿掉了由1900年开始计算年份这一设计)。
另外,DateFormat方法不是线程安全的。
最后就是Date和Calendar类都是可以变的。
LocalDate和LocalTime
LocalDate类的实例是一个不可变对象,它只提供日期信息,不提供时间信息。另外,它也不附带任何与时区相关的信息。用户可以通过静态工厂方法LocalDate.of创建一个LocalDate实例。
LocalTime类的实例是一个不可变对象,它只提供时间信息,不提供日期信息。另外,它也不附带任何与时区相关的信息。用户可以通过静态工厂方法LocalTime.of创建一个LocalTime实例。
LocalDate/LocalTime可以通过LocalDate.parse/LocalTime.
parse传入字符串/DateTimeFormatter来解析,不过,一旦传递的字符串参数无法被解析为合法的LocalDate/LocalTime对象,这两个parse方法都会抛出一个继承自RuntimeException的DateTimeParseException异常。
LocalDateTime
LocalDateTime是LocalDate和LocalTime的合体。它同时表示了日期和时间,但不带有时区信息。
可以用LocalDateTime.of直接创建,也可以通过LocalDateTime.of合并日期和时间对象来构造。
可以通过LocalTime.atDate/LocalDate.atTime创建一个LocalDateTime对象。
可以使用toLocalDate/toLocalTime方法,从LocalDateTime中提取LocalDate/LocalTime组件。
机器的日期Instant
从计算机的角度来看,建模时间最自然的格式是表示一个持续时间段上某个点的单一大整型数。这也是新的java.time.Instant类对时间建模的方式,基本上它是以Unix元年时间(传统的设定为UTC时区1970年1月1日午夜时分)开始所经历的秒数进行计算。
可以通过向静态工厂方法Instant.ofEpochSecond传递一个代表秒数的值来创建一个该类的实例。
静态工厂方法Instant.ofEpochSecond还有一个增强的重载版本,它接收第二个以纳秒为单位的参数值,对传入作为秒数的参数进行调整(增/减)。重载的版本会调整纳秒参数,确保保存的纳秒分片在0到999 999 999之间。
静态工厂方法Instant.now可以获取当前时刻的时间戳。
最后的最后,Instant设计初衷是为了便于机器使用。它包含的是由秒、纳秒所构成的数字。所以,它无法处理那些我们非常容易理解的时间单位。
差值Duration/Period
Duration:比较两个LocalTimes对象、两个LocalDateTimes对象、两个Instant对象之间的差值。
Period:比较N个LocalTimes对象、N个LocalDateTimes对象、N个Instant对象之间的差值。
Duration和Period类都提供了很多非常方便的工厂类,直接创建对应的实例,而不需要通过比较两个对象来生成:
Duration.ofMinutes(3);返回Duration对象
Duration.of(3, ChronoUnit.MINUTES);返回Duration对象
Period.ofDays(10);返回Period对象
Period.ofWeeks(3);返回Period对象
Period.of(2, 6, 1);返回Period对象
修改对象
如果你已经有一个LocalDate对象,想要创建它的一个修改版,最直接也最简单的方法是使用withAttribute方法。 withAttribute方法会创建对象的一个副本,并按照需要修改它的属性。注意:所有withAttribute方法都返回一个修改了属性的对象。它们都不会修改原来的对象。
LocalDate date1 = LocalDate.of(2014, 3, 18);
LocalDate date2 = date1.withYear(2011);
LocalDate date3 = date2.withDayOfMonth(25);
LocalDate date4 = date3.with(ChronoField.MONTH_OF_YEAR, 9);
LocalDate/LocalTime/LocalDateTime/Instant都实现了Temporal接口,所有的日期和时间API类都实现了get/with方法,使用get和with方法,可以将Temporal对象值的读取和修改区分开。如果Temporal对象不支持请求访问的字段,它会抛出一个UnsupportedTemporalTypeException异常。
与get和with方法类似,plus和minus方法都声明于Temporal接口中。通过这些方法,对TemporalUnit对象加上或者减去一个数字,我们能非常方便地将Temporal对象前溯或者回滚至某个时间段,通过ChronoUnit枚举我们可以非常方便地实现TemporalUnit接口。
LocalDate date1 = LocalDate.of(2014, 3, 18);
LocalDate date2 = date1.plusWeeks(1);
LocalDate date3 = date2.minusYears(3);
LocalDate date4 = date3.plus(6, ChronoUnit.MONTHS);
TemporalAdjuster
在使用with方法时,为了更加方便的跳转到某个特定日期,可以使用TemporalAdjuster。API里面包含了很多封装好了大 量 预 定 义 的TemporalAdjuster。可以通过TemporalAdjuster类的静态工厂方法访问:
dayOfWeekInMonth 创建一个新的日期,它的值为同一个月中每一周的第几天
firstDayOfMonth 创建一个新的日期,它的值为当月的第一天
firstDayOfNextMonth 创建一个新的日期,它的值为下月的第一天
firstDayOfNextYear 创建一个新的日期,它的值为明年的第一天
firstDayOfYear 创建一个新的日期,它的值为当年的第一天
firstInMonth 创建一个新的日期,它的值为同一个月中,第一个符合星期几要求的值
lastDayOfMonth 创建一个新的日期,它的值为当月的最后一天
lastDayOfNextMonth 创建一个新的日期,它的值为下月的最后一天
lastDayOfNextYear 创建一个新的日期,它的值为明年的最后一天
lastDayOfYear 创建一个新的日期,它的值为今年的最后一天
lastInMonth 创建一个新的日期,它的值为同一个月中,最后一个符合星期几要求的值
next/previous创建一个新的日期,并将其值设定为日期调整后或者调整前,第一个符合指定星期几要求的日期
nextOrSame/previousOrSame创建一个新的日期,并将其值设定为日期调整后或者调整前,第一个符合指定星期几要求的日期,如果该日期已经符合要求,直接返回该对象
格式化日期
新的java.time.format包就是为这个目的而设计的。最重要的类是DateTimeFormatter。创建格式器最简单的方法是通过它的静态工厂方法以及常量。
比如BASIC_ISO_DATE和ISO_LOCAL_DATE这样的常量是 DateTimeFormatter 类的预定义实例。所有的DateTimeFormatter实例都能用于以一定的格式创建代表特定日期或时间的字符串:
LocalDate date = LocalDate.of(2014, 3, 18);
String s1 = date.format(DateTimeFormatter.
BASIC_ISO_DATE);其日期格式为:20140318
String s2 = date.format(DateTimeFormatter.
ISO_LOCAL_DATE);其日期格式为:2014-03-18
注意:所有的DateTimeFormatter实例都是线程安全的。因此能够以单例模式创建格式器实例,能在多个线程间共享实例。
DateTimeFormatter类还支持一个静态工厂方法,它可以按照某个特定的模式创建格式器:DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
DateTimeFormatter italianFormatter =DateTimeFormatter
.ofPattern("d. MMMM yyyy", Locale.ITALIAN);
如果你还需要更加细粒度的控制,DateTimeFormatterBuilder类还提供了更复杂的格式器。它提供了非常强大的解析功能,比如区分大小写的解析、柔性解析(允许解析器使用启发式的机制去解析输入,不精确地匹配指定的模式)、填充,以及在格式器中指定可选节 。
处理不同的时区
时区的处理是新版日期和时间API新增加的重要功能,使用新版日期和时间API时区的处理被极大地简化了。
新的java.time.ZoneId类是老版java.util.TimeZone的替代品。
跟其他日期和时间类一样, ZoneId类也是无法修改的。ZoneId对象可以将与LocalDate、 LocalDateTime或者是Instant对象整合起来。
注意
JAVA8 | JAVA8之前 |
一周从周一开始,数字从1-7 | 一周从周日开始,数字从1-7 |
月份从1月开始,数字从1-12 | 月份从1月开始,数字从0-11 |