某些时区的Java util dateformat毫秒似乎不正确

时间:2013-12-24 03:02:32

标签: java date

根据我的理解,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是AEST,而不是EST。 http://wwp.greenwichmeantime.com/time-zone/australia/time-zones/eastern-standard-time/

我有什么问题吗?

2 个答案:

答案 0 :(得分:5)

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的原因。

答案 1 :(得分:0)

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。切勿使用诸如ESTIST之类的3-4字母缩写,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。

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

我建议避免将时间跟踪作为一个从历史算起的时间,因为它很模糊且容易出错。但是,如果你坚持,你可以获得自UTC 1970年第一时刻(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 [America / New_York]

     

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 [America / New_York]

     

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

     

millisSinceEpoch:0

关于 java.time

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

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

要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310

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

从哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如IntervalYearWeekYearQuartermore