为什么 util.Date 转发日期而不是减去日期?

Why does util.Date forwards the date instead of subtracting it?

我正在尝试将 IST 转换为 UTC 纪元 Java 但不是从 IST 中减去 5.30 小时,而是在 IST

中增加 5.30 小时
public static long convertDateToEpochFormat(String date) {
    Date convertedDate = null;
    try {
        LOGGER.info(date);
        DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
        LOGGER.info(date);
        convertedDate = formatter.parse(date);
        LOGGER.info(convertedDate);
    } catch (ParseException e) {
        e.printStackTrace();
    }
    return convertedDate.getTime() / 1000L;
}

我得到的日志语句是:

2017-01-01 00:00:00
2017-01-01 00:00:00
Sun Jan 01 05:30:00 IST 2017

由于 UTC 转换,理想情况下应该是 12 月 31 日 18:30:00。

谁能告诉我怎么了?

tl;博士

Why does util.Date forwards the date instead of subtracting it?

因为印度时间比 UTC 提前,而不是落后。

Instant.parse(
    "2017-01-01 00:00:00".replace( " " , "T" ) + "Z" 
).atZone(
    ZoneId.of( "Asia/Kolkata" )
).toString()

2017-01-01T05:30+05:30[Asia/Kolkata]

使用java.time

您正在使用麻烦的旧日期时间 类,现在已成为遗留问题,已被 java.time 类.

取代

ISO 8601

您输入的字符串几乎是标准 ISO 8601 格式。要完全遵守,请将中间的 SPACE 替换为 T。 java.time 类 在 parsing/generating 字符串时使用标准格式。因此无需指定格式模式。

String input = "2017-01-01 00:00:00".replace( " " , "T" ) ;

如果该输入要表示 UTC 中的时刻,请附加一个 ZZulu 的缩写,表示 UTC。

String input = "2017-01-01 00:00:00".replace( " " , "T" ) + "Z" ;  // Assuming this input was intended to be in UTC.

2017-01-01T00:00:00Z

如果可能,在将日期时间值序列化为字符串时,首先使用 ISO 8601 格式。

Instant

将该输入字符串解析为 Instant,UTC 时间轴上的一个时刻,分辨率为纳秒。

Instant instant = Instant.parse( input ) ;

instant.toString(): 2017-01-01T00:00:00Z

ZonedDateTime

您似乎希望将此值调整为印度时间。应用 ZoneId 得到 ZonedDateTime.

指定 proper time zone name in the format of continent/region, such as America/Montreal, Africa/CasablancaPacific/Auckland。切勿使用 ESTIST 等 3-4 字母缩写,因为它们 不是 真实时区,未标准化,甚至不是唯一的(!)。

ZoneId z = ZoneId.of( "Asia/Kolkata" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;

zdt.toString(): 2017-01-01T05:30+05:30[Asia/Kolkata]

看到这个 code run live at IdeOne.com

印度时间比 UTC

您的问题预计印度时间会倒退,落后于 UTC 值。这是没有意义的。印度时间比 UTC 早 ,不晚于 UTC。美洲向西时,时区 落后于 UTC。格林威治本初子午线以东是 UTC 的偏移 ahead。在现代,ISO 8601 和大多数其他协议用 plus 符号标记此类偏移量:+05:30。请注意,一些旧协议做了相反的事情(使用负号)。

UTC 午夜 = 5:30 印度上午

所以 UTC 的午夜,00:00:00 在本初子午线,同时是印度早上 5 点 30 分。

所以这三个都代表同一时刻,时间轴上的同一点:

  • 2017-01-01T00:00:00Z
  • 2017-01-01T05:30+05:30[Asia/Kolkata]
  • 2016-12-31T16:00-08:00[America/Los_Angeles]

避免从纪元开始计数

不要不要将时间处理为从纪元开始的整数计数,就像您通过从问题中看到的方法返回long所做的那样。在您的 Java 代码中,使用日期时间对象传递日期时间值,特别是 java.time 对象。在 Java 代码之外传递日期时间值时,使用实用的 ISO 8601 格式序列化为字符串。

依赖于从纪元开始计数的整数值令人困惑,难以调试,无法被人类阅读,并且会导致挫败感和错误(更糟糕的是:未观察到错误)。


关于java.time

java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

Joda-Time project, now in maintenance mode, advises migration to the java.time 类.

要了解更多信息,请参阅 Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310

java.time类在哪里获取?

ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

不仅正确,而且信息量很大。我已经投票了。我会尝试一点点不同的角度。

据我了解你的问题,你的日期时间字符串 2017-01-01 00:00:00 应该在 IST 中解释,又名 Asia/Kolkata 时间,你想将它转换为自纪元以来的秒数(而不是毫秒) .你在问为什么你得到的结果不正确。

我认为答案相当平庸:当日期时间字符串是印度时间时,您不应该在用于解析它的格式化程序上设置 UTC 时间。这肯定会得到不正确的结果(如果您要将日期时间 格式化 为 UTC,您最好在用于格式化的格式化程序上将 UTC 设置为时区,但这是一个不同的故事)。

我同意 Basil Bourque 的观点,您应该避免使用过时的 类 DateSimpleDateFormat。所以这是我的建议(假设你确实需要纪元秒并且不能像 Basil Bourque 推荐的那样使用 Instant)。

private static DateTimeFormatter parseFormatter = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");

public static long convertDateToEpochFormat(String date) {
    return LocalDateTime.parse(date, parseFormatter)
            .atZone(ZoneId.of("Asia/Kolkata"))
            .toInstant()
            .getEpochSecond();
}

这会将您的示例字符串转换为 2016-12-31T18:30:00Z 和 return 1483209000 的瞬间。请自行检查它是否正确。

我一直在假设 IST 你指的是印度标准时间。请注意,三个和四个字母的时区缩写是不明确的。例如,我的 JVM 认为 IST 表示以色列标准时间。如果您的意图相同,请将 Asia/Jerusalem 替换为 Asia/Kolkata。如果您指的是爱尔兰标准时间(另一种 recognized/semi-official 解释),请使用 Europe/Dublin。您当然会在每种情况下得到不同的输出。