LocalDate 不一致
LocalDate inconsistency
我正在尝试从 LocalDate
对象 (java.time.LocalDate
) 生成一个 Date
对象 (java.util.Date
),其中我有以下条件:
- 允许可以从
Date
对象中减去一定天数的参数
- 将日期和时间设置为当前 UTC 日期和时间
- 时间在一天的开始,即
00:00:00
- 时区戳(即 CDT 或 UTC)无关紧要,因为我将其从
String
中删除
为了满足这个标准,我创建了一个测试程序,但是当我修改 LocalDate
的某个 属性 时,我得到了有趣的结果。请参阅下面的代码:
public static void main (String args[]) {
Long processingDaysInPast = 0L;
LocalDate createdDate1 = LocalDate.now(Clock.systemUTC()).minusDays(processingDaysInPast);
LocalDate createdDate2 = LocalDate.now(Clock.systemUTC()).minusDays(processingDaysInPast);
System.out.println(createdDate1);
System.out.println(createdDate1.atStartOfDay().toInstant(ZoneOffset.UTC));
System.out.println(Date.from(createdDate1.atStartOfDay().toInstant(ZoneOffset.UTC)));
System.out.println((createdDate2.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()));
System.out.println(Date.from(createdDate2.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()));
}
输出:
2017-08-14
2017-08-14T00:00:00Z
Sun Aug 13 19:00:00 CDT 2017
2017-08-14
2017-08-14T05:00:00Z
Mon Aug 14 00:00:00 CDT 2017
当我添加值 Date.from(createdDate1.atStartOfDay().toInstant(ZoneOffset.UTC))
时,我得到了日期的预期输出,带有 00:00:00
时间字段。但是,如果我不加这个参数,比如:Date.from(createdDate2.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant())
我得到的结果是前一天,在19:00:00
这是为什么?
我的主要目标是能够捕获一个 Date
对象,使用当前 UTC 日期,并将时间清零 (StartOfDay
)。
当你这样做时:
createdDate2.atStartOfDay().atZone(ZoneId.systemDefault())
首先,createdDate2.atStartOfDay()
returns一个LocalDateTime
,相当于2017-08-14
午夜。 LocalDateTime
不支持时区。
当您调用 atZone(ZoneId.systemDefault())
时,它会在 系统的默认时区(ZoneId.systemDefault()
)。在你的情况下,默认时区不是 UTC(它是 "CDT",所以它在 CDT 午夜 - 只需执行 System.out.println(ZoneId.systemDefault())
来检查你的默认时区是什么)。
要获取 UTC 午夜的日期,您可以将默认时区 (ZoneId.systemDefault()
) 替换为 UTC (ZoneOffset.UTC
):
Date.from(createdDate2.atStartOfDay().atZone(ZoneOffset.UTC).toInstant())
或(更短的版本):
Date.from(createdDate2.atStartOfDay(ZoneOffset.UTC).toInstant())
当然你也可以像 createdDate1
:
Date.from(createdDate2.atStartOfDay().toInstant(ZoneOffset.UTC))
它们都是等价的,结果是 UTC 午夜。
快速说明:像 CDT
或 PST
这样的短时区名称不是真正的时区。
API 使用 IANA timezones names(格式总是 Region/City
,如 America/Chicago
或 Europe/Berlin
)。
避免使用 3 个字母的缩写(如 CDT
或 PST
),因为它们是 ambiguous and not standard.
有lots of different timezones可以用CDT
作为缩写。发生这种情况是因为时区是一个区域在历史期间拥有、拥有和将拥有的所有不同偏移量的集合。仅仅因为今天很多地方使用CDT
,并不意味着它们在过去的同一时期都使用过,也不意味着将来会被所有人使用。由于历史不同,为每个地区创建了一个时区。
我正在尝试从 LocalDate
对象 (java.time.LocalDate
) 生成一个 Date
对象 (java.util.Date
),其中我有以下条件:
- 允许可以从
Date
对象中减去一定天数的参数 - 将日期和时间设置为当前 UTC 日期和时间
- 时间在一天的开始,即
00:00:00
- 时区戳(即 CDT 或 UTC)无关紧要,因为我将其从
String
中删除
为了满足这个标准,我创建了一个测试程序,但是当我修改 LocalDate
的某个 属性 时,我得到了有趣的结果。请参阅下面的代码:
public static void main (String args[]) {
Long processingDaysInPast = 0L;
LocalDate createdDate1 = LocalDate.now(Clock.systemUTC()).minusDays(processingDaysInPast);
LocalDate createdDate2 = LocalDate.now(Clock.systemUTC()).minusDays(processingDaysInPast);
System.out.println(createdDate1);
System.out.println(createdDate1.atStartOfDay().toInstant(ZoneOffset.UTC));
System.out.println(Date.from(createdDate1.atStartOfDay().toInstant(ZoneOffset.UTC)));
System.out.println((createdDate2.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()));
System.out.println(Date.from(createdDate2.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()));
}
输出:
2017-08-14
2017-08-14T00:00:00Z
Sun Aug 13 19:00:00 CDT 2017
2017-08-14
2017-08-14T05:00:00Z
Mon Aug 14 00:00:00 CDT 2017
当我添加值 Date.from(createdDate1.atStartOfDay().toInstant(ZoneOffset.UTC))
时,我得到了日期的预期输出,带有 00:00:00
时间字段。但是,如果我不加这个参数,比如:Date.from(createdDate2.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant())
我得到的结果是前一天,在19:00:00
这是为什么?
我的主要目标是能够捕获一个 Date
对象,使用当前 UTC 日期,并将时间清零 (StartOfDay
)。
当你这样做时:
createdDate2.atStartOfDay().atZone(ZoneId.systemDefault())
首先,createdDate2.atStartOfDay()
returns一个LocalDateTime
,相当于2017-08-14
午夜。 LocalDateTime
不支持时区。
当您调用 atZone(ZoneId.systemDefault())
时,它会在 系统的默认时区(ZoneId.systemDefault()
)。在你的情况下,默认时区不是 UTC(它是 "CDT",所以它在 CDT 午夜 - 只需执行 System.out.println(ZoneId.systemDefault())
来检查你的默认时区是什么)。
要获取 UTC 午夜的日期,您可以将默认时区 (ZoneId.systemDefault()
) 替换为 UTC (ZoneOffset.UTC
):
Date.from(createdDate2.atStartOfDay().atZone(ZoneOffset.UTC).toInstant())
或(更短的版本):
Date.from(createdDate2.atStartOfDay(ZoneOffset.UTC).toInstant())
当然你也可以像 createdDate1
:
Date.from(createdDate2.atStartOfDay().toInstant(ZoneOffset.UTC))
它们都是等价的,结果是 UTC 午夜。
快速说明:像 CDT
或 PST
这样的短时区名称不是真正的时区。
API 使用 IANA timezones names(格式总是 Region/City
,如 America/Chicago
或 Europe/Berlin
)。
避免使用 3 个字母的缩写(如 CDT
或 PST
),因为它们是 ambiguous and not standard.
有lots of different timezones可以用CDT
作为缩写。发生这种情况是因为时区是一个区域在历史期间拥有、拥有和将拥有的所有不同偏移量的集合。仅仅因为今天很多地方使用CDT
,并不意味着它们在过去的同一时期都使用过,也不意味着将来会被所有人使用。由于历史不同,为每个地区创建了一个时区。