【问题标题】:Why does util.Date forwards the date instead of subtracting it?为什么 util.Date 转发日期而不是减去它?
【发布时间】:2017-06-15 06:40:46
【问题描述】:

我正在尝试将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。

谁能告诉我怎么了?

【问题讨论】:

  • 当伦敦 (UTC) 的时间为 0:00 时,印度 (IST) 的早上时间为 5:30。为什么你会期待别的?您正在解析日期字符串,并告诉系统将该日期字符串视为 UTC 时间。然后,您使用默认时区打印它,因此它将在该时区打印 same time
  • 您没有将 IST 转换为 UTC。您正在解析 UTC 并以系统默认值打印,这可能是 IST。
  • @Andreas 你的观点很有道理。但我建议不要将 UTC 称为“伦敦”时间。伦敦时间存在夏令时 (DST) 等异常,因此它实际上不是 UTC。这种用词不当会造成很多混乱。
  • @OleV.V.我同意这会很棒!但是,我只将其标记为重复。我没有否决它。尽管如此,我会说这个问题本身的质量相当好,所以我认为没有理由拒绝投票。但是,如果这是一个骗局,我认为最好将所有答案放在同一个帖子中。

标签: java timestamp java.util.date


【解决方案1】:

tl;博士

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

因为印度时间领先 UTC,而不是落后。

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

2017-01-01T05:30+05:30[亚洲/加尔各答]

使用 java.time

您正在使用麻烦的旧日期时间类,它们现在是遗留的,被 java.time 类所取代。

ISO 8601

您的输入字符串几乎是标准的ISO 8601 格式。要完全遵守,请将中间的空格替换为T。 java.time 类在解析/生成字符串时使用标准格式。所以不需要指定格式模式。

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

continent/region 的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用 3-4 个字母的缩写,例如 ESTIST,因为它们不是真正的时区,没有标准化,甚至不是唯一的 (!)。

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

zdt.toString(): 2017-01-01T05:30+05:30[亚洲/加尔各答]

看到这个code run live at IdeOne.com

印度时间领先 UTC

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

午夜 UTC = 5:30 AM 印度

因此,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 格式序列化为字符串。

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


关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

从哪里获得 java.time 类?

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

【讨论】:

    【解决方案2】:

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

    据我了解您的问题,您的日期时间字符串 2017-01-01 00:00:00 应以 IST(又名亚洲/加尔各答时间)解释,并且您希望将其转换为自纪元以来的秒数(而不是毫秒)。你在问为什么你得到一个不正确的结果。

    我认为答案相当平庸:当日期时间字符串为印度时间时,您不应在用于解析它的格式化程序上设置 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 的瞬间并返回 1483209000。请自行检查它是否正确。

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

    【讨论】:

      猜你喜欢
      • 2021-10-15
      • 2015-02-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-11
      • 2021-04-12
      • 2019-02-11
      • 1970-01-01
      • 2022-01-21
      相关资源
      最近更新 更多