IST映射到java.time库中的错误区域ID

IST映射到java.time库中的错误区域ID,java,java-time,Java,Java Time,我正试图从格式(yyyy-MM-dd-HH:MM-z)的字符串中解析ZoneDateTime。输入字符串(2019-08-29 00:00 IST)生成UTC时间戳 调试将我带到了IST的ZoneId映射到的位置 大西洋/雷克雅未克没有意义。它应该被映射到亚洲 timestamp = ZonedDateTime.parse(timeInput, DATE_WITH_TIMEZONE_FORMATTER) .toInstant().toEpochMilli() 在哪里 D

我正试图从格式(yyyy-MM-dd-HH:MM-z)的字符串中解析ZoneDateTime。输入字符串(2019-08-29 00:00 IST)生成UTC时间戳

调试将我带到了IST的ZoneId映射到的位置 大西洋/雷克雅未克没有意义。它应该被映射到亚洲

timestamp = ZonedDateTime.parse(timeInput, DATE_WITH_TIMEZONE_FORMATTER)
            .toInstant().toEpochMilli()
在哪里

DateTimeFormatter DATE_WITH_TIMEZONE_FORMATTER = DateTimeFormatter
      .ofPattern(DATE_TIME_WITH_TIMEZONE_PATTERN).withChronology(IsoChronology.INSTANCE);

我在这里遗漏了什么吗?

我不知道为什么你会得到冰岛,即使在主TZDB
IST
中,它只出现在爱尔兰标准时间(1968年之前错误地称为爱尔兰夏季时间)、以色列标准时间和印度标准时间。冰岛的出现可能是Java时区数据库中的一个错误

经过进一步调查,我发现只有当当前语言环境的语言设置为非空值时,问题才会出现。如果在没有指定语言的情况下创建区域设置,则会得到
Asia/Kolkata
,但如果存在该语言(任何语言),则会返回
Atlantic/Reykjavik
。这很可能是Java实现中的一个bug

    String input = "2019-08-29 00:00 IST";
    Locale loc = new Locale.Builder().setRegion("US").build(); // Note no language
    System.out.println(loc.toString());
    DateTimeFormatter DATE_WITH_TIMEZONE_FORMATTER = 
        DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm z").withLocale(loc);
    ZonedDateTime zdt = ZonedDateTime.parse(input, DATE_WITH_TIMEZONE_FORMATTER);
    System.out.println(zdt);
这就产生了

_US
2019-08-29T00:00+05:30[Asia/Kolkata]
但是改变

    Locale loc = new Locale.Builder().setLanguage("ta").build();
产生

ta
2019-08-29T00:00Z[Atlantic/Reykjavik]

无论如何,空时区
IST
在上下文之外是不明确的。为避免混淆,如果您希望
IST
始终是
Asia/Kolkata
,您可能必须在解析之前修改传入数据。

首先,如果有任何方法可以避免,请不要依赖三个字母的时区缩写。它们是模棱两可的,可能更多的时候是模棱两可的。当你说IST应该映射到亚洲时,它仍然保留了亚洲/特拉维夫和亚洲/加尔各答之间的选择(加上这两个别名,亚洲/耶路撒冷和亚洲/加尔各答)。在世界其他地方,IST可能意味着爱尔兰夏季时间,显然也意味着冰岛标准时间(或者类似的时间,这当然有道理)。这已经不是我第一次看到它被公认为大西洋/雷克雅未克了

如果无法避免必须解析
IST
,请通过
DateTimeFormatterBuilder
的两个参数
appendZoneText
方法控制解释。它接受一组首选区域:

DateTimeFormatter DATE_WITH_TIMEZONE_FORMATTER = new DateTimeFormatterBuilder()
        .appendPattern("yyyy-MM-dd HH:mm ")
        .appendZoneText(TextStyle.SHORT, Collections.singleton(ZoneId.of("Asia/Kolkata")))
        .toFormatter(Locale.ENGLISH);
在我放置的
Asia/Kolkata
中替换您的首选区域。其余的不应该引起麻烦:

    String timeInput = "2019-08-29 00:00 IST";
    ZonedDateTime zdt = ZonedDateTime.parse(timeInput, DATE_WITH_TIMEZONE_FORMATTER);
    System.out.println("Parsed ZonedDateTime: "+ zdt);
    long timestamp = zdt
            .toInstant().toEpochMilli();
    System.out.println("timestamp: " + timestamp);
输出:


链接:(您会注意到列表中IST出现了三次,而且许多其他缩写也不明确)。

好奇。。。我尝试了一下,得到了同样的结果(无论是否添加
等时学
)。进一步调查。。。(我猜你的意思是
HH:mm
,因为
HH:mm
无效)。是的,在我的代码中是HH:mm。谢谢你在帖子中指出错误。我已经编辑过了。Java的版本和供应商是什么?这是一个很好的例子,说明了为什么不应该使用2-4个字符的伪区域(如
IST
)来交换文本日期时间类型。使用诸如
亚洲/加尔各答
大西洋/雷克雅未克
,以及
欧洲/都柏林
。谢谢,但它引入了对区域设置的依赖。对我来说,事先没有办法知道语言环境,我唯一的信息就是输入字符串。我可以亲自创建一个从时区字符串到语言环境的映射,并在解析输入时使用该映射。此外,我假设在出现歧义的情况下,库应该通过解析失败来指示歧义。可能它没有失败的原因是默认的语言环境是X(例如en_US),并且在该语言环境中,IST并不含糊不清。我的假设正确吗?只是猜测一下:这种行为可能会随着Java(至少是OpenJDK)的最新版本切换到默认使用作为区域设置定义源而有所不同。看,谢谢。对我来说,这似乎违反直觉,我需要指定一个ZoneId,甚至可以指定时区名称。对我来说,没有办法事先知道分区。我将从时区字符串映射到Id,并设置一组不含糊的规则。@tswapnil您需要了解
IST
不是时区。tzdb中定义并由IANA维护的数据确实是唯一且明确的<代码>列表
不在该列表中。避免使用这些2-4个字符的伪区域。学习仅使用标准ISO 8601格式以文本方式交换日期时间值,以避免这些问题。
Parsed ZonedDateTime: 2019-08-29T00:00+05:30[Asia/Kolkata]
timestamp: 1567017000000