旧API的问题:
在Java 1.0中,对日期和时间的支持只能依赖java.util.Date类。 存在的问题有:
- 年份的起始选择是1900年
- 月份的起始从0开始
在Java 1.1中, Date类中的很多方法被废弃了,取而代之的是java.util.Calendar类 ,仍然有很多问题:
- 月份依旧是从0开始计算
- 同时存在Date和Calendar这两个类,增加了程序员的疑惑
- 有的特性只在某一个类有提供,比如DateFormat只在Date类里有
- DateFormat不是线程安全的
- Date和Calendar类都是可变的
所有这些缺陷和不一致导致用户们转投第三方的日期和时间库,比如Joda-Time。为了解决这些问题, Oracle决定在原生的Java API中提供高质量的日期和时间支持。所以,你会看到Java 8在java.time包中整合了很多Joda-Time的特性。
先给出几个新API的示例,可以看到,Java8中对时间、日期的操作是非常优雅和易于理解的。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 
 | LocalDate today = LocalDate.now(); 
 LocalDate today = LocalDate.now();
 LocalDate someDay = LocalDate.of(2021, Month.MAY, 14);
 Period period = Period.between(today, someDay);
 
 System.out.printf("interval : %d years, %d months, %d days ", period.getYears(), period.getMonths(), period.getDays());
 
 
 LocalDate tomorrow = today.plusDays(1);
 
 LocalDate secondDay = today.withDayOfMonth(1);
 
 LocalDate lastDay = today.with(TemporalAdjusters.lastDayOfMonth());
 
 LocalDate firstMonday = today.with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY));
 
 
 LocalTime now = LocalTime.now();
 
 LocalTime then = now.plusHours(2);
 
 long window = Duration.ofMinutes(45).toMillis();
 
 | 
时间、日期、时间戳
LocalDate
该类的实例是一个不可变对象,它只包含日期,并不含时间。另外,它不附带时区信息。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | LocalDate date = LocalDate.of(2014, 3, 18);
 int year = date.getYear();
 Month month = date.getMonth();
 int day = date.getDayOfMonth();
 DayOfWeek dow = date.getDayOfWeek();
 int len = date.lengthOfMonth();
 boolean leap = date.isLeapYear();
 
 LocalDate today = LocalDate.now();
 
 | 
LocalTime
一天中的时间,比如13:45:20,可以使用LocalTime类表示 :
| 12
 3
 4
 
 | LocalTime time = LocalTime.of(13, 45, 20);int hour = time.getHour();
 int minute = time.getMinute();
 int second = time.getSecond();
 
 | 
LocalDate和LocalTime都可以通过字符串创建:
| 12
 
 | LocalDate date = LocalDate.parse("2014-03-18");LocalTime time = LocalTime.parse("13:45:20");
 
 | 
LocalDateTime
LocalDateTime,是LocalDate和LocalTime的合体。它同时表示了日期和时间,但不带有时区信息,你可以直接创建,也可以通过合并日期和时间对象构造,如下所示。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | 
 LocalDateTime dt1 = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45, 20);
 LocalDateTime dt2 = LocalDateTime.of(date, time);
 
 LocalDateTime dt3 = date.atTime(13, 45, 20);
 LocalDateTime dt4 = date.atTime(time);
 LocalDateTime dt5 = time.atDate(date);
 
 LocalDate date1 = dt1.toLocalDate();
 LocalTime time1 = dt1.toLocalTime();
 
 | 
时间戳类Instant
Unix元年时间(UTC时区1970年1月1日午夜时分)开始所经历的秒数
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | Instant.now();
 
 
 
 
 
 Instant.ofEpochSecond(3);
 Instant.ofEpochSecond(3, 0);
 Instant.ofEpochSecond(2, 1_000_000_000);
 Instant.ofEpochSecond(4, -1_000_000_000);
 
 | 
可以通过Duration和Period类使用Instant 。
Duration 、Period
Duration类主要用于以秒和纳秒衡量时间的长短 。
| 12
 3
 4
 
 | Duration d1 = Duration.between(time1, time2);
 Duration d1 = Duration.between(dateTime1, dateTime2);
 Duration d2 = Duration.between(instant1, instant2);
 
 | 
- 由于LocalDateTime和Instant是为不同的目的而设计的,一个是为了便于人阅读使用,另一个是为了便于机器处理,所以你不能将二者混用。如果你试图在这两类对象之间创建duration,会触发一个DateTimeException异常。
- Duration类主要用于以秒和纳秒衡量时间的长短,所以不能向between方法传递LocalDate对象做参数。
使用Period类,得到两个LocalDate之间的时长
| 12
 
 | Period tenDays = Period.between(LocalDate.of(2014, 3, 8),LocalDate.of(2014, 3, 18));
 
 | 
最后, Duration和Period类都提供了很多非常方便的工厂类,直接创建对应的实例 。
| 12
 3
 4
 5
 6
 
 | Duration threeMinutes = Duration.ofMinutes(3);Duration threeMinutes = Duration.of(3, ChronoUnit.MINUTES);
 
 Period tenDays = Period.ofDays(10);
 Period threeWeeks = Period.ofWeeks(3);
 Period twoYearsSixMonthsOneDay = Period.of(2, 6, 1);
 
 | 
操纵、解析和格式化日期
截至目前,我们介绍的这些日期/时间对象都是不可变的 。
如果你已经有一个LocalDate对象,想要创建它的一个修改版副本,最直接也最简单的方法是使用withAttribute方法。
| 12
 3
 4
 
 | 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对象
| 12
 3
 4
 
 | 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对象
| 12
 3
 4
 
 | import static java.time.temporal.TemporalAdjusters.*;LocalDate date1 = LocalDate.of(2014, 3, 18);
 LocalDate date2 = date1.with(nextOrSame(DayOfWeek.SUNDAY));
 LocalDate date3 = date2.with(lastDayOfMonth());
 
 | 
另外,还也已创建自定义的TemporalAdjuster,例如获得当前日期的下一个工作日。见《Java 8 in Action》。
打印输出及解析日期、时间对象
和老的java.util.DateFormat相比较,所有的DateTimeFormatter实例都是线程安全
的。所以,你能够以单例模式创建格式器实例,就像DateTimeFormatter预定义的那些格式器常量,并能在多个线程间共享这些实例。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 
 | LocalDate date = LocalDate.of(2014, 3, 18);
 String s1 = date.format(DateTimeFormatter.BASIC_ISO_DATE);
 String s2 = date.format(DateTimeFormatter.ISO_LOCAL_DATE);
 
 LocalDate date1 =
 LocalDate.parse("20140318",DateTimeFormatter.BASIC_ISO_DATE);
 LocalDate date2 =
 LocalDate.parse("2014-03-18",DateTimeFormatter.ISO_LOCAL_DATE);
 
 
 DateTimeFormatter formatter =
 DateTimeFormatter.ofPattern("dd/MM/yyyy");
 LocalDate date1 = LocalDate.of(2014, 3, 18);
 String formattedDate = date1.format(formatter);
 LocalDate date2 = LocalDate.parse(formattedDate, formatter);
 
 
 DateTimeFormatter italianFormatter =
 DateTimeFormatter.ofPattern("d. MMMM yyyy", Locale.ITALIAN);
 LocalDate date1 = LocalDate.of(2014, 3, 18);
 String formattedDate = date.format(italianFormatter);
 LocalDate date2 = LocalDate.parse(formattedDate, italianFormatter);
 
 | 
最后,如果你还需要更加细粒度的控制, DateTimeFormatterBuilder类还提供了更复杂
的格式器,你可以选择恰当的方法,一步一步地构造自己的格式器。另外,它还提供了非常强大的解析功能,比如区分大小写的解析、柔性解析(允许解析器使用启发式的机制去解析输入,不精 确 地 匹 配 指 定 的 模 式 )、 填 充 ,以及在格式器中指 定可选节 。比 如 , 你可以通 过DateTimeFormatterBuilder 自己编程实现italianFormatter,代码清单如下。
| 12
 3
 4
 5
 6
 7
 8
 
 | DateTimeFormatter italianFormatter = new DateTimeFormatterBuilder().appendText(ChronoField.DAY_OF_MONTH)
 .appendLiteral(". ")
 .appendText(ChronoField.MONTH_OF_YEAR)
 .appendLiteral(" ")
 .appendText(ChronoField.YEAR)
 .parseCaseInsensitive()
 .toFormatter(Locale.ITALIAN);
 
 | 
时区
日期和时间的类都不包含时区信息。
时区的处理是新版日期和时间API新增加的重要功能,使用新版日期和时间API时区的处理被极大地简化了。新的java.time.ZoneId类是老版java.util.TimeZone的替代品。
可以通过调用ZoneId的getRules()得到指定时区。每个特定的ZoneId对象都由一个地区ID标识,比如:
ZoneId romeZone = ZoneId.of("Europe/Rome);
地区ID都为“{区域}/{城市}”的格式,这些地区集合的设定都由英特网编号分配机构(IANA)的时区数据库提供。
还可以通过Java 8的新方法toZoneId将一个老的时区对象转换为ZoneId:
ZoneId zoneId = TimeZone.getDefault().toZoneId();
一旦得到一个ZoneId对象,你就可以将它与LocalDate、 LocalDateTime或者是Instant对象整合起来,构造为一个ZonedDateTime实例,它代表了指定时区的时间点,代码清单如下所示。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 
 | LocalDate date = LocalDate.of(2014, Month.MARCH, 18);
 ZonedDateTime zdt1 = date.atStartOfDay(romeZone);
 LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45);
 ZonedDateTime zdt2 = dateTime.atZone(romeZone);
 Instant instant = Instant.now();
 ZonedDateTime zdt3 = instant.atZone(romeZone);
 
 
 LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45);
 Instant instantFromDateTime = dateTime.toInstant(romeZone);
 
 Instant instant = Instant.now();
 LocalDateTime timeFromInstant = LocalDateTime.ofInstant(instant, romeZone);
 
 | 
 
    
    
        
Comment