核心技术Ⅱ:时间与日期 API
跳到导航
跳到搜索
关于
相关类:
- Date、Time:
- LocalDate、LocalTime、LocalDateTime:
- ZonedDateTime:
- “LocalDateTime”,适合存储固定时区的时间点,如用于课程安排、日常安排;
- “ZonedDateTime”,适合于计算跨越夏令时,或处理不同时区;
时间线(Date、Time)
Java 的“Date”和“Time”API规范要求Java使用的时间尺度为:
- 每天 86400 秒;
- 每天正午与官方时间精确匹配;
- 在其他时间点上,以精确定义的方式与官方时间接近匹配;
Instant:表示时间线上的某个点。
- “新纪元”:时间线原点(穿过伦敦格林威治皇家天文台的本初子午线所处时区的1970年1月1日的午夜),从该原点开始,时间按照每天86400秒向前或向回度量,精确到纳秒。
- Instant的值向回可追溯lO亿年(“Instant.MIN”),最大的值“Instant.MAX”是公元I000000000年的12月31口。
- Instant对象用作时间戳:“Instant.now()”当前的时刻;“equals”、“compareTo”比较两个Instant对象;“Duration.between”得到两个时刻之间的时间差。
Instant start = Instant.now();
runAlgorithm();
Instant end = Instant.now();
Duration timeElapsed = Duration.between(start, end);
long millis = timeElapsed.toMillis();
Duration:两个时刻之间的时间量。
- Duration对象的内部存储所需的空间超过了一个1ong的值,因此秒数存储在一个1ong中,而纳秒数存储在一个额外的int中。
- 如果想要让计算精确到纳秒级,那么需要整个Duration的存储内容;
- 如果不要求这么高的精度,可以用1ong的值来执行计算,然后直接调用“toNanos”。
- 通过调用“toNanos”、“toMi11is”、“getSeconds”、“toMinutes”、“toHours”、“toDays”,获得Duration按照传统单位度址的时间长度。
- “Instant”和“Duration”类都是不可修改的类,所以诸如“multipliedBy”和“minus”这样的方法都会返回一个新的实例。
本地时间线(LocalDate、LocalTime、LocalDateTime)
本地时间(LocalDate)
LocalDate:带有年、月、日的日期。
- 构建LocalDate对象,可以使用“now”或“of”静态方法:
LocalDatetoday = LocalDate.now(); //Today'sdate LocalDatealonzosBirthday = Loca1Date.of(1903,6,14); alonzosBirthday = Loca1Date.of(1903,Month.JUNE,14); //UsestheMonthenumeration
- 月份使用通常的月份数字(从1开始),或者使用“Month”类枚举。(UNlX和“java.util.Date”的月从0开始而年从1900开始);
- LocalDate的两个时间点之间的时长“Period”;(类比于“Instant”之间的“Duration”)
- (类比于“Instant”之间的“Duration”)
// 获取下一年的生日 birthday.plus(Period.ofYears(l)); birthday.plusYears(l); birthday.plus(Duration.ofDays(365)); // 在闰年不会产生正确结果的。
// 程序员日是每年的第 256 天
LocalDate programmersDay = Loca1Date.of(2014, 1, 1).plusDays(255);
- “util”方法会产生两个本地日期之间的时长。
independenceDay.unti1(christmas) // 会产生5个月21天的一段时长。 // 这实际上并不是很有用,因为每个月的天数不尽相同。为了确定到底有多少天,可以使用: independenceOay.until(christmas,ChronoUnit.DAYS) //174days
- “getDayOfWeek”会产生星期日期,即“DayOfWeek”枚举的某个值。
- “DayOfWeek.MONDAY”的枚举值为1,而“DayOfWeek.SUNDAY”的枚举值为7;
- “DayOfWeek”枚举具有便捷方法“plus”和“minus”,以7为模计算星期日期;
- 有些方法可能会创建出并不存在的日期。例如,在1月31日上加上1个月不应该产生2月31日。
- (这些方法并不会抛出异常,而是会返回该月有效的最后一天)
Loca1Date.of(2016,1,31).plusMonths(l); Loca1Date.of(2016,3,31).minusMonths(l); // 都将产生2016年2月29日。
- 除了LocalDate之外,还有“MonthDay”、“YearMonth”和“Year”类可以描述部分日期。
- 例如,12月25日(没有指定年份)可以表示成一个“MonthDay”对象;
日期调整器(TemporalAdjusters)
用于计算诸如“每个月的第一个星期二”这样的日期;
“TemporalAdjusters”类提供了大量用于常见调整的静态方法:
- 将调整方法的结果传递给with方法:
- (with 方法会返回一个新的 LocalDate对象,而不会修改原来的对象)
// 某个月的第一个星期二可以像下面这样计箕: LocalDate firstTuesday = LocalDate.of(year, month, 1).with( TemporalAdjusters.nextOrSame(DayOfWeek.TUESDAY));
“TemporalAdjuster”接口:通过实现该接口来创建自己的调整器:
// 用于计算下一个工作日的调整器 TemporalAdjuster NEXT_WDRKDAY = w -> { LocalDate result = (Loca1Date) w; do { result = result.plusDays(l); } whi1e (resu1t.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; // No cast do { resu1t = result.plusDays(1); } while (result.getDayOfWeek().getValue() >= 6); return result; };
本地时间(LocalTime)
LocalTime 表示当日时刻,例如“15:30:00”。
- “now”或“of”方法创建其实例:
LocalTime rightNow = LocalTime.now(); LocalTime bedtime= Loca1Time.of(22, 30); // or Loca1Time.of(22, 30, 0)
- “plus”和“minus”操作是按照一天24小时循环操作的:
LocalTime rightNow = LocalTime.now(); LocalTime bedtime= Loca1Time.of(22, 30); // or Loca1Time.of(22, 30, 0)
- LocalTime 自身并不关心 AM/PM,而是交给格式器“DateTimeFormatter”解决。
时区时间(ZonedDateTime)
互联网编码分配管理机构(Internet Assigned Numbers Authority, lANA)保存若一个数据库,里面存储若世界上所有已知的时区(www.iana.org/time-zones), 它每年会更新数次,而批量更新会处理夏令时的变更规则。Java使用了IANA 数据库。
- UTC代表“协调世界时”,是不考虑夏令时的格林威冶皇家天文台时间。
- 当夏令时开始时,时钟要向前拨快一小时;夏令时结束时,时钟要向回拨慢一小时;
- 每个时区都有一个ID, 例如“America/New_York”和“Europe/Berlin”。可以通过调用“ZoneId.getAvailableZonelds”获取所有可用时区;
- 调用静态方法“ZoneId.of(id)”可以产生一个给定一个时区ID的ZoneId对象;
- 通过调用“local.atZone(zoneId)”将“LocalDateTime”对象转换为“ZonedDateTime”对象;
- 通过调用静态方法“ZonedDateTime.of(year,month,day ,hour,minute,se cond,nano,zoneld)”来构造一个“ZonedDateTime”对象;
- 调整跨越夏令时边界的日期时特别注意(使用“Period”而非“Duration”):
// 例如,如果你将会议设置在下个星期,不要直接加上一个7 天的“Duration”: ZonedDatelime nextMeeting = meeting.plus(Duration.ofDays(7)); //Caution!Won'tworkwithdaylightsavingstime ZonedDatelime nextMeeting = meeting.plus(Period.ofDays(7)); //OK
- 还有一个“OffsetDateTime”类,表示与UTC具有偏移量的时间,但是没有时区规则的束缚。(应用于某些网络协议等)
格式化和解析(DateTimeFormatter)
格式化
DateTimeFormatter类提供了三种用于打印日期/时间值的格式器:
- 预定义的格式器:
- Locale相关的格式器:标准格式都主要是为了机器刻度的时间戳而设计的。为了向人类读者表示日期和时间,可以使用 Locale 相关的格式器。
// 创建格式器:静态方法“ofLocalizedDate”、“ofLocalizedTime”和“ofLoca1izedDateTime”。 DateTimeFormatter formatter = DateTimeFormatter.ofLoca1izedDateTime(FormatStyle.LONG); String formatted = formatter.format(apollo11launch); // July 16, 1969 9:32:00 AM EDT // 切换到不同的Locale:使用“withLocale”方法 formatted = formatter .wi thlocale(Local e. FR杻CH) .format(apollo11launch); // 按照不同的 Locale 和格式,给出星期日期和月份的名字。 //(“DayOfWeek”和“Month”枚举都有“getDisplayName”方法) for (DayOfWeek w : DayOfWeek.valuesO) System.out. pri nt(w.getDi spl ayName(TextStyle.SHORT, Locale. ENGLISH) + " "); // Prints Mon Tue Wed Thu Fri Sat Sun
- 带有定制模式的格式器:
解析
解析字符串中的日期/时间值,可以使用众多的静态“parse”方法之一:
- 使用标准的“ISO_LOCAL_DATE”格式器:
LocalDate churchsBirthday = LocalDate.parse("1903-06-14");
- 使用定制的格式器:
ZonedDateTime apollo11launch = ZonedDateTime.parse("1969-07-16 03:32:00-0400", DateTimeFonnatter.ofPattern("yyyy-MM-dd HH:mm:ssxx"));
与遗留代码
作为全新的创造,Java Date 和 Time API 必须能够与已有类之间进行互操作, 特别是“java.util.Date”、“java.util.GregorianCalendar”和“java.sql.Date/Time/Timestamp”:
类似地 ,, 你还 表6-9 对这些转换进行了总结
- Instant类近似于“java.util.Date”:
- 在 JavaSE8 中,这个类有两个额外的方法,
- 将“Date”转换为“Instant”的“toInstant”方法,
- 以及反方向转换的静态的“from”方法。
- ZonedDateTime近似于“java.util.GregorianCalendar”:
- 在 JavaSE8 中,这个类有细粒度的转换方法,
- “toZonedDateTime”方法可以将“GregorianCalendar”转换为“ZonedDateTime”,而静态的“from”方法可以执行反方向的转换。
- 另一个可用千日期和时间类的转换集位于“java.sql”包中。
- 可以传递一个“DateTimeFormatter”给使用“java.text.Format”的遗留代码。