为什么 SimpleDateFormat setTimeZone 不会为无效时区抛出错误

Why SimpleDateFormat setTimeZone doesn't throws error for invalid timezone

我正在使用 SimpleDateFormat 将时间的字符串类型转换为 unix 时间戳,但是当我将时区设置为非法值时,我仍然得到正确的结果,SimpleDateFormat.parse() 方法是如何定义的?如果我想有一个单元测试来测试失败案例,我该怎么做?

String str = "2016-06-21-10-19-22";

SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd-hh-mm-ss");
df.setTimeZone(TimeZone.getTimeZone("fewfefewf"));

Date date = df.parse(str);

long epoch = date.getTime();
System.out.println(epoch);

由于getTimeZone方法,如果无法理解指定的时区,returns默认GMT时区

the specified TimeZone, or the GMT zone if the given ID cannot be understood.

如果建议使用 TimeZone.getAvailableIDs 进行验证,并且可以轻松检查以验证输入时区是否有效

Arrays.stream(TimeZone.getAvailableIDs()).anyMatch(tz->tz.equals("fewfefewf"))

最终建议请使用 java-8 date time api 类 并开始远离 SimpleDateFormat

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss"); // make sure you use H for hours in format

LocalDateTime dateTime = LocalDateTime.parse(str,formatter);

java.time

我建议您使用 java.time,现代 Java 日期和时间 API,作为您的日期和时间工作。它的众多优点之一是,在许多情况下,它比 TimeZoneSimpleDateFormatDate 等旧 classes 提供更好的验证。这是我对你的代码的现代等价物的看法:

    String str = "2016-06-21-10-19-22";

    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd-hh-mm-ss");

    ZonedDateTime dateTime = LocalDateTime.parse(str, dtf)
            .atZone(ZoneId.of("fewfefewf"));

    long epoch = dateTime.toInstant().toEpochMilli();
    System.out.println(epoch);

运行 它产生:

Exception in thread "main" java.time.format.DateTimeParseException: Text '2016-06-21-10-19-22' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {MicroOfSecond=0, MinuteOfHour=19, NanoOfSecond=0, MilliOfSecond=0, SecondOfMinute=22, HourOfAmPm=10},ISO resolved to 2016-06-21 of type java.time.format.Parsed
    at java.base/java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:2017)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1952)
    at java.base/java.time.LocalDateTime.parse(LocalDateTime.java:492)
    at ovv.misc.MiscTest.<init>(MiscTest.java:72)
    at ovv.misc.MiscTest.main(MiscTest.java:61)
Caused by: java.time.DateTimeException: Unable to obtain LocalDateTime from TemporalAccessor: {MicroOfSecond=0, MinuteOfHour=19, NanoOfSecond=0, MilliOfSecond=0, SecondOfMinute=22, HourOfAmPm=10},ISO resolved to 2016-06-21 of type java.time.format.Parsed
    at java.base/java.time.LocalDateTime.from(LocalDateTime.java:461)
    at java.base/java.time.format.Parsed.query(Parsed.java:235)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1948)
    ... 3 more
Caused by: java.time.DateTimeException: Unable to obtain LocalTime from TemporalAccessor: {MicroOfSecond=0, MinuteOfHour=19, NanoOfSecond=0, MilliOfSecond=0, SecondOfMinute=22, HourOfAmPm=10},ISO resolved to 2016-06-21 of type java.time.format.Parsed
    at java.base/java.time.LocalTime.from(LocalTime.java:431)
    at java.base/java.time.LocalDateTime.from(LocalDateTime.java:457)
    ... 5 more

惊讶? java.time 已经发现了一个您没有询问的错误:在格式模式字符串中,从 00 到 23 的一天中的小时需要大写 HH。小写 hh 是时钟小时从 01 到 12 的 AM 或 PM。错误消息中要注意的详细信息是 HourOfAmPm=10 — 这不是您想要的。让我们再次修复并运行:

    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss");
Exception in thread "main" java.time.zone.ZoneRulesException: Unknown time-zone ID: fewfefewf
    at java.base/java.time.zone.ZoneRulesProvider.getProvider(ZoneRulesProvider.java:279)
    at java.base/java.time.zone.ZoneRulesProvider.getRules(ZoneRulesProvider.java:234)
    at java.base/java.time.ZoneRegion.ofId(ZoneRegion.java:120)
    at java.base/java.time.ZoneId.of(ZoneId.java:408)
    at java.base/java.time.ZoneId.of(ZoneId.java:356)
    at ovv.misc.MiscTest.<init>(MiscTest.java:73)
    at ovv.misc.MiscTest.main(MiscTest.java:61)

我相信这就是您所要求的。如果我们也修复此错误,代码 运行 将完美无缺:

    ZonedDateTime dateTime = LocalDateTime.parse(str, dtf)
            .atZone(ZoneId.of("America/Eirunepe"));

1466522362000

我输入了一个随机时区,但我想你知道你想要哪个时区。

其他答案已经很好地解释了你的代码出了什么问题。我知道没有办法说服旧的和设计不佳的 TimeZone class 来验证时区 ID 字符串。

Link: Oracle tutorial: Date Time 解释如何使用 java.time.