Java 8时区转换

gue*_*t86 2 java timezone datetime datetime-conversion java-time

我知道有类似这样的问题,但我不太能找到与我相似的例子.我了解了LocalDateTime,ZonedDateTime但我不知道怎么告诉我ZonedDateTime假设解析日期的时区.

我正在从Java 7迁移到Java 8.在我的代码中,我有一个这样的方法:

public String changeTZ(String tzFrom, String tzTo, String dateToChange) {
    ZoneId zoneFrom = ZoneId.of(tzFrom);
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat.toPattern());
    LocalDateTime localtDateAndTime = LocalDateTime.parse(dateToChange, formatter);
    ZonedDateTime dateAndTimeINeed = ZonedDateTime.of(localtDateAndTime, zoneFrom );

    ZonedDateTime rezDate = dateAndTimeINeed.withZoneSameInstant(ZoneId.of(tzTo));

    return formatter.format(rezDate);
}
Run Code Online (Sandbox Code Playgroud)

示例用法是:

String rez = changeTZ("CEST", "UTC", "2017-08-10 14:23:58");
Run Code Online (Sandbox Code Playgroud)

正如你所看到的那样,它以字符串的形式接收日期时间,时区日期是tzFrom变量,而TZ我需要(toTo变量).

在Java 7中执行该操作的代码是荒谬而复杂的,因此我不会在此处进行讨论.你能告诉我如何在不改变方法接口(参数和返回类型)的情况下在Java 8中实现相同的功能吗?DST怎么样?它是在Java 8中自动处理的吗?

请注意,该方法还支持"CEST","CET","UTC"形式的时区以及"欧洲/伦敦"等标准时区.

Rob*_*per 5

此解决方案使用Hugo在评论中提到的IANA时区名称来获取ZoneId(此处提供更多详细信息).如果将其与CEST一起使用,将抛出异常.

public static String changeTZ(String tzFrom, String tzTo, String dateToChange){
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.of(tzFrom));
    ZonedDateTime zdt = ZonedDateTime.parse(dateToChange, dtf);
    DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.of(tzTo));  
    return zdt.format(dtf2);
}
Run Code Online (Sandbox Code Playgroud)

使用如下:

String rez = changeTZ("US/Alaska", "Europe/Berlin", "2017-08-10 14:23:58");
Run Code Online (Sandbox Code Playgroud)


小智 5

Java的8使用IANA时区的名称(总是在格式Region/City,如America/Sao_PauloEurope/Berlin)。避免使用简短的缩写(如CESTPST),因为它们含糊不清且不标准

实际上,这些名称不适用ZoneId主要是因为这种歧义(例如,CST 可以是“中央标准时间”、“古巴标准时间”或“中国标准时间”)。实际上,由于复古兼容性的原因,其中一些可能会起作用,但不能保证对所有这些都起作用。

我假设 CEST 是中欧夏令时。CEST 目前有许多不同的国家(和时区),因此如果您只是将“CEST”传递给 API,则 API 无法决定选择哪个时区。

这是因为时区包含一个地区在其历史上的所有不同偏移量。今天可能有很多国家/地区使用 CEST,但它们的历史在过去有所不同(有些可能在不同年份使用 DST,或者使用不同的偏移量然后更改等),这就是为什么它们每个都有一个时区。

但是,要使用这样的短名称(如CEST),您可以为每个名称定义一些默认值(这将是任意选择)并将这些选择放入映射中:

// map of custom zone names
Map<String, String> map = new HashMap<>();
// setting my arbitrary choices for each name
map.put("CEST", "Europe/Berlin"); // Berlin during DST period
map.put("CET", "Europe/Berlin"); // Berlin during non-DST period
// ... and so on
Run Code Online (Sandbox Code Playgroud)

然后你可以使用这个地图来创建ZoneId

// use the custom map to create the ZoneId
ZoneId zoneFrom = ZoneId.of(tzFrom, map);
...
// use the custom map to create the ZoneId
ZonedDateTime rezDate = dateAndTimeINeed.withZoneSameInstant(ZoneId.of(tzTo, map));
Run Code Online (Sandbox Code Playgroud)

我选择了Europe/Berlin,但当然您可以将其更改为您需要的任何时区。您可以通过调用获取可用时区列表(并选择最适合您系统的时区)ZoneId.getAvailableZoneIds()


使用上面的地图:

System.out.println(changeTZ("CEST", "UTC", "2017-08-10 14:23:58"));
Run Code Online (Sandbox Code Playgroud)

此代码输出:

2017-08-10 12:23:58

请注意,在14:23 CEST(我选择做Europe/Berlin)是UTC,这是正确的,因为12:23在八月柏林是DST(偏移+02:00)。

ZoneIdZonedDateTime类自动处理 DST 效果。您可以通过选择一月份的日期来检查这一点(当 DST 在柏林不生效时):

// January is not DST, so use CET
System.out.println(changeTZ("CET", "UTC", "2017-01-10 14:23:58"));
Run Code Online (Sandbox Code Playgroud)

输出是:

2017-01-10 13:23:58

月份柏林不在夏令时,所以偏移量为+01:00,然后柏林的 14:23 变为 UTC 的 13:23。


当然,理想的情况是始终使用全名(如Europe/Berlin),但如果您无法控制输入,则自定义地图是另一种选择。

Java 8 也有一个内置的预定义 map,但与任何其他预定义的东西一样,选择是任意的,不一定是你需要的。

  • 我会接受这个答案,因为它确实详细解释了事情。“CET”/“CEST”映射的额外积分:) (2认同)