核心技术Ⅱ:时间与日期 API

来自Wikioe
跳到导航 跳到搜索


关于

相关类:

  1. Date、Time:
  2. LocalDate、LocalTime、LocalDateTime:
  3. ZonedDateTime:
  • “LocalDateTime”,适合存储固定时区的时间点,如用于课程安排、日常安排;
  • “ZonedDateTime”,适合于计算跨越夏令时,或处理不同时区;

时间线(Date、Time)

Java 的“Date”和“Time”API规范要求Java使用的时间尺度为:

  1. 每天 86400 秒;
  2. 每天正午与官方时间精确匹配;
  3. 在其他时间点上,以精确定义的方式与官方时间接近匹配;

Instant表示时间线上的某个点

  1. “新纪元”:时间线原点(穿过伦敦格林威治皇家天文台的本初子午线所处时区的1970年1月1日的午夜),从该原点开始,时间按照每天86400秒向前或向回度量,精确到纳秒。
  2. Instant的值向回可追溯lO亿年(“Instant.MIN”),最大的值“Instant.MAX”是公元I000000000年的12月31口。
  3. 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两个时刻之间的时间量

  1. Duration对象的内部存储所需的空间超过了一个1ong的值,因此秒数存储在一个1ong中,而纳秒数存储在一个额外的int中。
    1. 如果想要让计算精确到纳秒级,那么需要整个Duration的存储内容;
    2. 如果不要求这么高的精度,可以用1ong的值来执行计算,然后直接调用“toNanos”。
  2. 通过调用“toNanos”、“toMi11is”、“getSeconds”、“toMinutes”、“toHours”、“toDays”,获得Duration按照传统单位度址的时间长度。

时间的Instant和Duration的运算.png

  • “Instant”和“Duration”类都是不可修改的类,所以诸如“multipliedBy”和“minus”这样的方法都会返回一个新的实例。

本地时间线(LocalDate、LocalTime、LocalDateTime)

本地时间(LocalDate)

LocalDate:带有年、月、日的日期。

  1. 构建LocalDate对象,可以使用“now”或“of”静态方法:
    LocalDatetoday = LocalDate.now();   //Today'sdate
    LocalDatealonzosBirthday = Loca1Date.of(1903,6,14);
    alonzosBirthday = Loca1Date.of(1903,Month.JUNE,14);
       //UsestheMonthenumeration
    
  2. 月份使用通常的月份数字(从1开始),或者使用“Month”类枚举。(UNlX和“java.util.Date”的月从0开始而年从1900开始);
  3. LocalDate的两个时间点之间的时长“Period”;(类比于“Instant”之间的“Duration”)
    (类比于“Instant”之间的“Duration”)
    // 获取下一年的生日
    birthday.plus(Period.ofYears(l)); 
    birthday.plusYears(l);
    
    birthday.plus(Duration.ofDays(365)); // 在闰年不会产生正确结果的。
    

LocalDate的方法.png 如:

// 程序员日是每年的第 256 天
LocalDate programmersDay = Loca1Date.of(2014, 1, 1).plusDays(255);
  1. util”方法会产生两个本地日期之间的时长。
    independenceDay.unti1(christmas)   // 会产生5个月21天的一段时长。
    // 这实际上并不是很有用,因为每个月的天数不尽相同。为了确定到底有多少天,可以使用:
    independenceOay.until(christmas,ChronoUnit.DAYS)   //174days
    
  2. 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”类提供了大量用于常见调整的静态方法:

TemporalAdjusters类中的日期调整器.png
  • 将调整方法的结果传递给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”。

  1. now”或“of”方法创建其实例:
    LocalTime rightNow = LocalTime.now(); 
    LocalTime bedtime= Loca1Time.of(22, 30); // or Loca1Time.of(22, 30, 0)
    
  2. plus”和“minus”操作是按照一天24小时循环操作的:
    LocalTime rightNow = LocalTime.now(); 
    LocalTime bedtime= Loca1Time.of(22, 30); // or Loca1Time.of(22, 30, 0)
    

LocaTime的方法.png

  • LocalTime 自身并不关心 AM/PM,而是交给格式器“DateTimeFormatter”解决。

时区时间(ZonedDateTime)

互联网编码分配管理机构(Internet Assigned Numbers Authority, lANA)保存若一个数据库,里面存储若世界上所有已知的时区(www.iana.org/time-zones), 它每年会更新数次,而批量更新会处理夏令时的变更规则。Java使用了IANA 数据库。

  • UTC代表“协调世界时”,是不考虑夏令时的格林威冶皇家天文台时间。
  • 当夏令时开始时,时钟要向前拨快一小时;夏令时结束时,时钟要向回拨慢一小时;
  1. 每个时区都有一个ID, 例如“America/New_York”和“Europe/Berlin”。可以通过调用“ZoneId.getAvailableZonelds”获取所有可用时区;
  2. 调用静态方法“ZoneId.of(id)”可以产生一个给定一个时区ID的ZoneId对象;
  3. 通过调用“local.atZone(zoneId)”将“LocalDateTime”对象转换为“ZonedDateTime”对象;
  4. 通过调用静态方法“ZonedDateTime.of(year,month,day ,hour,minute,se cond,nano,zoneld)”来构造一个“ZonedDateTime”对象;
  5. 调整跨越夏令时边界的日期时特别注意(使用“Period”而非“Duration”):
    // 例如,如果你将会议设置在下个星期,不要直接加上一个7 天的“Duration”:
    
    ZonedDatelime nextMeeting = meeting.plus(Duration.ofDays(7));􀀁
    //􀀁Caution!􀀁Won't􀀁work􀀁with􀀁daylight􀀁savings􀀁time􀀁
    
    ZonedDatelime nextMeeting = meeting.plus(Period.ofDays(7));􀀁 
    //􀀁OK􀀁
    
ZonedDateTime 的方法.png
  • 还有一个“OffsetDateTime”类,表示与UTC具有偏移量的时间,但是没有时区规则的束缚。(应用于某些网络协议等)

格式化和解析(DateTimeFormatter)

格式化

DateTimeFormatter类提供了三种用于打印日期/时间值的格式器:

  1. 预定义的格式器
    // 使用标准的格式器,可以直接调用其 format 方法
    String formatted = DateTimeFormatter.ISO_OFFS_DATE_TIME.format(apollo111aunch);􀀁// 1969-07-16T09:32:00-04:00"􀀁
    
    预定义的格式器.png
  2. 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
    
    Locale 相关的格式化风格.png
  3. 带有定制模式的格式器
    // 定制日期格式
    formatter = DateTimeFormatter.ofPattern("E yyyy-MM-dd HH:mm");􀀁 // Wed 1969-07-16 09:32
    
    常用的日期、时间格式的格式化符号.png

解析

解析字符串中的日期/时间值,可以使用众多的静态“parse”方法之一:

  1. 使用标准的“ISO_LOCAL_DATE”格式器:
    LocalDate churchsBirthday = LocalDate.parse("1903-06-14");
    
  2. 使用定制的格式器:
    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 对这些转换进行了总结

  1. Instant类近似于“java.util.Date”:
    在 JavaSE8 中,这个类有两个额外的方法,
    1. 将“Date”转换为“Instant”的“toInstant”方法,
    2. 以及反方向转换的静态的“from”方法。
  2.  ZonedDateTime近似于“java.util.GregorianCalendar”:
    在 JavaSE8 中,这个类有细粒度的转换方法,
    1. toZonedDateTime”方法可以将“GregorianCalendar”转换为“ZonedDateTime”,而静态的“from”方法可以执行反方向的转换。
  3. 另一个可用千日期和时间类的转换集位于“java.sql”包中。
    可以传递一个“DateTimeFormatter”给使用“java.text.Format”的遗留代码。

Java.time 类与遗留类之间的转换.png