Java 日期转换 - UTC 到本地 - 工作方式因时区而异

Java Date Conversion - UTC to Local - works differently depending on the timezone

提问人:Joe 提问时间:9/20/2014 更新时间:9/20/2014 访问量:1384

问:

我在将字符串转换为 UTC 数据,然后转换为各种时区时遇到问题。我的程序似乎根据我是转换为 EST 还是 PST 而有所不同。这是我的代码:

    SimpleDateFormat utcFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    utcFormat.setTimeZone(java.util.TimeZone.getTimeZone("UTC"));

    Date date = utcFormat.parse("2014-08-18 17:00:17");

    SimpleDateFormat localFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    localFormat.setTimeZone(java.util.TimeZone.getTimeZone("PST"));

    System.out.println(localFormat.format(date));

如果我运行上面的代码,这是我的输出:

    2014-08-18 10:00:17

这反映了与提供的 UTC 时间 2014-08-18 17:00:17 的 7 小时偏移量。这是我所期望的。现在,如果我将该日期更改为 2014-11-18 17:00:17(将月份从 8 月更改为 11 月),则生成的输出如下:

    2014-11-18 09:00:17

据我所知,这也很好。输出反映了 UTC 的 8 小时偏移量,我相信这是因为 11 月不是夏令时,而 8 月是夏令时。

我遇到的问题是,如果我将时区从“PST”更改为“EST”,上面的相同代码的工作方式会有所不同。当我更改为 EST 时,无论我的日期是在 8 月还是 11 月,我都会获得相同的时间输出。

这是使用 EST 和 2014-08-18 17:00:17 的输出

    2014-08-18 12:00:17

这是使用 EST 和 2014-11-18 17:00:17 的输出

    2014-11-18 12:00:17

在这两种情况下,输出都表示与 UTC 的 5 小时偏移量,这仅在 11 月有意义,而在 8 月没有意义。

谁能向我解释我做错了什么?

Java 日期 UTC

评论


答:

-1赞 MirMasej 9/20/2014 #1

那是因为在保存之外,它的偏移是恒定的,并且它是夏令时的补充区域。ESTETEDT

因此,您应该使用来获取预期的行为。 更多维基百科ET

3赞 Dave Morrissey 9/20/2014 #2

您应该使用 or(这些是别名),而不是使用 。三个字母的时区缩写是模棱两可的,你无法确定你得到了什么。ESTAmerica/New_YorkUS/Eastern

评论

0赞 Basil Bourque 9/20/2014
实际上,是官方名称。是备用或别名。请参阅维基百科页面,其中列出了正确的时区名称。大多数官方的主要名称是 .America/New_YorkUS/EasternContinent/CityOrRegion
2赞 Compass 9/20/2014 #3

TimeZone 的文档

为了与 JDK 1.1.x 兼容,还支持其他一些三个字母的时区 ID(例如“PST”、“CTT”、“AST”)。但是,它们已被弃用,因为相同的缩写通常用于多个时区(例如,“CST”可能是美国“中部标准时间”和“中国标准时间”),然后 Java 平台只能识别其中一个时区。

而不是 ,会更清楚地了解您的意图。"EST""US/Eastern"

这些是受支持的 US 别名。

  • 美国/阿拉斯加
  • 美国/阿留申群岛
  • 美国/亚利桑那州
  • 美国/中部
  • 美国/东印第安纳州
  • 美国/东部
  • 美国/夏威夷
  • 美国/印第安纳-斯塔克
  • 美国/密歇根州
  • 美国/山地
  • 美国/太平洋地区
  • 美国/太平洋-新
  • 美国/萨摩亚
0赞 Alvin Bunk 9/20/2014 #4

@Compass是对的。 以下是您将使用的代码:

public static void main(String[] args) {
    SimpleDateFormat utcFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    utcFormat.setTimeZone(java.util.TimeZone.getTimeZone("UTC"));

    Date date = null;
    try {
        date = utcFormat.parse("2014-08-18 17:00:17");
    } catch (ParseException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    SimpleDateFormat localFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    localFormat.setTimeZone(java.util.TimeZone.getTimeZone("US/Eastern"));

    System.out.println(localFormat.format(date));
}
0赞 Basil Bourque 9/20/2014 #5

戴夫·莫里西(Dave Morrissey)的答案是正确的。

谁能向我解释我做错了什么?

是的。您正在使用一个可怕且令人困惑的日期时间库。

避免java.util.Date

java.util.Date 和 .日历类是出了名的麻烦,在设计和实现上都存在缺陷。使用一个像样的库。在 Java 中,这意味着 Joda-Time 或 Java 8 中的新 java.time 包(受 JSR 310 定义的 Joda-Time 的启发)。

时区

虽然 j.u.Date 没有时区,但在 Joda-Time 和 java.time 中,date-time 对象确实知道自己分配的时区。使这项工作变得更加容易和明智。

时区名称

使用正确的时区名称。避免使用 2、3 或 4 个字母的代码,因为它们既不是标准化的,也不是唯一的。这些专有名称中的大多数是 .Continent/CityOrRegion

夏令时

您不必担心夏令时。让日期时间库在那里完成繁重的工作。您需要做的就是确保您的图书馆使用的是新版本的时区数据库。政客们喜欢重新定义 DST。

国际标准化组织 8601

Joda-Time 和 java.time 都支持 ISO 8601 格式作为其解析和生成日期时间值的字符串表示形式的默认值。

Joda-Time 示例

以下是 Joda-Time 2.4 中的一些示例代码。此示例中的所有 DateTime 对象都表示宇宙历史上的同一同时时刻,但经过调整以显示每个位置的人所看到的挂钟时间

String inputRaw = "2014-08-18 17:00:17"; // Nearly in [ISO 8601][7] format.
String input = inputRaw.replace( " ", "T" );
DateTime dateTimeUtc = DateTime.parse( input, DateTimeZone.UTC );
DateTime dateTimeLosAngeles = dateTimeUtc.withZone( DateTimeZone.forID( "America/Los_Angeles" ) );
DateTime dateTimeNewYork = dateTimeUtc.withZone( DateTimeZone.forID( "America/New_York" ) );
DateTime dateTimeMontréal = dateTimeUtc.withZone( DateTimeZone.forID( "America/Montreal" ) );
DateTime dateTimeKolkata = dateTimeUtc.withZone( DateTimeZone.forID( "Asia/Kolkata" ) );