【问题标题】:Java util dateformat milliseconds for some timezones don't seem to be correct某些时区的 Java util dateformat 毫秒似乎不正确
【发布时间】:2013-12-24 03:02:31
【问题描述】:

据我了解,java.util.Date 将日期存储为从 1970 年 1 月 1 日 00:00 开始的毫秒数...

所以,我尝试了以下代码:

public void testDateFormatBehavior()
{
    DateFormat dfNoDay = new SimpleDateFormat(
            "MMM d H:m:s zzz yyyy"
            );
    // This one should be correct as IST = GMT+5.30
    String expStrDateBegIST1 = "Jan 01 05:30:01 IST 1970";
    // Instead, this one seems to do the conversion to
    // Jan 01 00:00:00 GMT 1970
    String expStrDateBegIST2 = "Jan 01 02:00:01 IST 1970";
    String expStrDateBegUTC = "Jan 01 00:00:01 GMT 1970";
    String expStrDateBegCET = "Jan 01 01:00:00 CET 1970";
    // Should convert to Jan 01 06:00:00 GMT 1970 as CST = GMT-6
    String expStrDateBegCST = "Jan 01 00:00:00 CST 1970"; 
    // This is EST, which is GMT+6... 
    String expStrDateBegEST = "Jan 01 10:00:00 EST 1970"; 
    try {
        Date dBegIST1 = dfNoDay.parse(expStrDateBegIST1);
        Date dBegIST2 = dfNoDay.parse(expStrDateBegIST2);
        Date dBegUTC = dfNoDay.parse(expStrDateBegUTC);
        Date dBegCET = dfNoDay.parse(expStrDateBegCET);
        Date dBegCST = dfNoDay.parse(expStrDateBegCST);
        Date dBegEST = dfNoDay.parse(expStrDateBegEST);
        System.out.println("IST1 milliseconds: " + dBegIST1.getTime());
        System.out.println("IST2 milliseconds: " + dBegIST2.getTime());
        System.out.println("UTC milliseconds: " + dBegUTC.getTime());
        System.out.println("CET milliseconds: " + dBegCET.getTime());
        System.out.println("CST milliseconds: " + dBegCST.getTime());
        System.out.println("EST milliseconds: " + dBegEST.getTime());
    } catch (ParseException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

输出:

IST1 milliseconds: 12601000
IST2 milliseconds: 1000
UTC milliseconds: 1000
CET milliseconds: 0
CST milliseconds: 21600000
EST milliseconds: 0

UTC 毫秒行是正确的,因为我们指定了从 1970 年 1 月 1 日开始的 00:00:01 秒。 CET 是正确的。 CST 是正确的,因为毫秒数是 1970 年 1 月 1 日之后的 6 小时。

但是,IST 转换很奇怪。

http://wwp.greenwichmeantime.com/to/ist/to-gmt/index.htm

IST 似乎是 GMT + 5:30。在我的 Java 代码中,它认为是 GMT + 2:00。

另外,EST 不正确。它认为 EST 是 GMT+10:00,而不是 GMT+6:00。 GMT+10:00 是美国东部标准时间,而不是美国东部标准时间。 http://wwp.greenwichmeantime.com/time-zone/australia/time-zones/eastern-standard-time/

是不是我做错了什么?

【问题讨论】:

    标签: java date


    【解决方案1】:

    Timezone javadoc 解释:

    三个字母的时区 ID

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


    问题在于您假设时区缩写始终指定特定时区。

    “IST”不明确:印度标准时间/爱尔兰标准时间/以色列标准时间。同样,“EST”可以表示美国东部标准时间或澳大利亚东部标准时间。


    Joda-time's timezone list 显示明确的 Olson 名称和当前时区偏移量。您最好使用 Joda-time 而不是 java.util 类,并使用 Olson 名称来识别时区。

    Should I use Java date and time classes or go with a 3rd party library like Joda Time? 讨论了许多人选择使用 Joda-time 的原因。

    【讨论】:

    • 仅测试 IST 日期。如果设置dfNoDay.setTimeZone(TimeZone.getTimeZone("IST"));,则检索正确的时间。
    • @SotiriosDelimanolis,当您使用不明确的标识符时,没有单一的“正确”时间。
    • 当然可以,但是为什么SimpleDateFormat 使用两种不同的方式来获取和设置TimeZone
    • @SotiriosDelimanolis,我不知道。我很久以前就停止使用java.util 的约会资料了,因为我遇到了太多奇怪的事情,不再记得细节了。
    • 请注意,答案链接到的 Joda-Time 时区列表略微过时。该页面描述了如何运行代码以查看当前列表。
    【解决方案2】:

    tl;博士

    ZonedDateTime.parse(   // Parse string as `ZonedDateTime` object.
        "1969-12-31T19:00-05:00[America/New_York]"   // This zone on that date is five hours behind UTC. 
    )                      // So last day of 1969 at 7 PM there is simultaneously first moment of January 1, 1970 in UTC (00:00:00).
    .toInstant()           // .toString() --> 1970-01-01T00:00:00Z
    .toEpochMilli()        // --> zero seconds since epoch, as we are *at* the epoch.
    

    0

    详情

    接受的Answer by Samuel 是正确的。

    避免使用旧的日期时间类

    现代方法使用 java.time 类取代了麻烦的旧旧日期时间类,例如 Date

    时区

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

    ZoneId z = ZoneId.of( "Africa/Tunis" ) ;  
    

    ISO 8601

    在将日期时间值序列化为文本时使用标准 ISO 8601 格式。

    java.time 类在解析/生成字符串时默认使用 ISO 8601 格式。所以不需要指定格式模式。

    ZonedDateTime 类明智地扩展了标准格式,将时区名称附加在方括号中。

    String input = "1970-01-01T05:30:01+05:30[Asia/Kolkata]" ;  // "Jan 01 05:30:01 IST 1970"
    ZonedDateTime zdt = ZonedDateTime.parse( input ) ;
    String output = zdt.toString() ;  // Generate string in standard ISO 8601 format.
    

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

    要通过 UTC 挂钟时间的镜头查看同一时刻,请提取 Instant我们预计在 UTC 日期前一秒,因为该日期的印度时间比 UTC 时间早五个半小时。

    Instant instant = zdt.toInstant() ;
    

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

    我建议避免将跟踪时间作为从 epoch 开始的计数,因为它不明确且容易出错。但是,如果您坚持,您可以获得自 1970 年 UTC 第一时刻 (1970-01-01T00:00:00Z) 以来的毫秒数。

    在这种情况下,我们预计结果为一千毫秒(1 秒)。

    long millisSinceEpoch = instant.toEpochMilli() ;
    

    1000

    查看北美东海岸时区的某个时刻:

    String input = "1970-01-01T10:00:00-05:00[America/New_York]";  // "Jan 01 10:00:00 EST 1970"
    ZoneId z = ZoneId.of( "America/New_York" );
    ZonedDateTime zdt = ZonedDateTime.parse( input );
    Instant instant = zdt.toInstant();
    long millisSinceEpoch = instant.toEpochMilli();
    

    zdt.toString(): 1970-01-01T10:00-05:00[美国/纽约]

    instant.toString(): 1970-01-01T15:00:00Z

    millisSinceEpoch: 54000000

    或者在同一区域的另一个时刻。在这里,我们在纪元参考前一天晚上 7 点尝试。那个日期的America/New_York 区域比UTC 晚了五个小时。因此,当调整为 UTC 时,我们正好落在午夜的钟声上,即纪元参考日期的第一刻,1970-01-01T00:00:00Z。这意味着1969-12-31T19:00-05:00[America/New_York] 距离纪元为零秒。

    String input = "1969-12-31T19:00-05:00[America/New_York]" ;
    ZoneId z = ZoneId.of( "America/New_York" );
    ZonedDateTime zdt = ZonedDateTime.parse( input );
    Instant instant = zdt.toInstant();
    long millisSinceEpoch = instant.toEpochMilli();
    

    zdt.toString(): 1969-12-31T19:00-05:00[美国/纽约]

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

    millisSinceEpoch: 0


    关于java.time

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

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

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

    使用符合JDBC 4.2 或更高版本的JDBC driver,您可以直接与您的数据库交换java.time 对象。不需要字符串或 java.sql.* 类。

    从哪里获得 java.time 类?

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

    【讨论】:

      猜你喜欢
      • 2023-03-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多