SimpleDateFormat 解析方法第一次起作用,第二次被忽略

SimpleDateFormat parse method works first time, is ignored second time

我有一个日期时间,我首先将其转换为当地时间,然后再转换为另一个时区。第一次转换没有问题,但第二次转换被忽略。有什么问题?

String input = "2020-05-20 01:10:05";
SimpleDateFormat localFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
localFormat.setTimeZone(TimeZone.getTimeZone(ZoneId.systemDefault()));

try {
    Date date = localFormat.parse(input);
    System.out.println(date); //Wed May 20 01:10:05 PDT 2020 <--- Logs this (Expected)

    SimpleDateFormat estFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    estFormat.setTimeZone(TimeZone.getTimeZone("America/New_York"));
    
    String newDate = estFormat.format(date);
    System.out.println(newDate); //2020-05-20 04:10:05 <--- Logs this (Expected)

    Date dateEst = estFormat.parse(newDate);
    System.out.println(dateEst); //Wed May 20 01:10:05 PDT 2020 <--- Logs this (Unexpected) Should be Wed May 20 04:10:05 EST 2020

}catch (Exception e){
    e.printStackTrace();
}

似乎在尝试转换为 America/New_York 时区时忽略了第二个 estFormat.parse()

首先,除非您要维护遗留代码,否则不要使用 Date

其次,SimpleDateFormat 正确解析了日期。您没有像在上一个(正确的)示例中那样使用 estFormat 来格式化日期。尝试:

System.out.println(estFormat.format(dateEst));

This 很好地概述了 Java 中与日期相关的不同类型的实体,值得一读。以下是您可能会觉得有用的摘录:

The java.util.Date has no concept of time zone, and only represents the number of seconds passed since the Unix epoch time – 1970-01-01T00:00:00Z. But, if you print the Date object directly, the Date object will be always printed with the default system time zone.

我注意到一件事,第一个变量的类型是字符串,第二个变量的类型是日期。所以可能类型转换在这里造成了问题。没有什么比忽略第二次更好的了。

java.time

我强烈建议您使用 java.time,现代 Java 日期和时间 API,作为您的日期和时间工作。一旦习惯了稍微不同的思维方式,您可能还会发现生成的代码更清晰、更自然。我们先把常量的东东定义为常量:

private static final DateTimeFormatter FORMATTER
        = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.ROOT);
private static final ZoneId FROM_ZONE = ZoneId.of("America/Los_Angeles");
private static final ZoneId TO_ZONE = ZoneId.of("America/New_York");

有了这些我们就可以做我们的工作了:

    String input = "2020-05-20 01:10:05";
    
    ZonedDateTime fromDateTime
            = LocalDateTime.parse(input, FORMATTER).atZone(FROM_ZONE);
    System.out.println("From:      " + fromDateTime);
    
    ZonedDateTime toDateTime = fromDateTime.withZoneSameInstant(TO_ZONE);
    System.out.println("To:        " + toDateTime);
    
    String formatted = toDateTime.format(FORMATTER);
    System.out.println("Formatted: " + formatted);

输出为:

From:      2020-05-20T01:10:05-07:00[America/Los_Angeles]
To:        2020-05-20T04:10:05-04:00[America/New_York]
Formatted: 2020-05-20 04:10:05

编辑:

How would I get it to have EST 2020 at the end?

对于大多数用途来说,一个不错的选择是使用本地化格式化程序:

private static final DateTimeFormatter TARGET_FORMATTER
        = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG)
                .withLocale(Locale.US);

像这样:

    String formatted = toDateTime.format(TARGET_FORMATTER);
Formatted: May 20, 2020 at 4:10:05 AM EDT

一个细节,我们最后没有得到 EST,因为纽约和北美东海岸的大部分地区使用夏令时 (DST),因此在 5 月是东部夏令时 EDT。

出了什么问题?

您似乎希望 Date 对象携带解析它的格式化程序的时区 America/New_York。老式的 Date 对象无法做到这一点。这只是一个愚蠢的时间点,没有任何时区或其他附加信息。令许多人感到困惑的是,它的 toString 方法使用 JVM 的默认时区来呈现返回的字符串,从而给人一种时区存在的错误印象。相比之下,现代 ZonedDateTime,顾名思义,确实拥有一个时区。

Link

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