2016-12-02 31 views
2

GregorianCalendar的一个日期正在不一致:获取从一个GregorianCalendar

Calendar cal = new GregorianCalendar(2000, 0, 1); 
long testCalOne = cal.getTimeInMillis(); 
cal.setTimeZone(TimeZone.getTimeZone("UTC")); 
long testCalTwo = cal.getTimeInMillis(); 

Calendar cal2 = new GregorianCalendar(2000, 0, 1); 
cal2.setTimeZone(TimeZone.getTimeZone("UTC")); 
long testCalThree = cal2.getTimeInMillis(); 

System.out.println(testCalOne + ", " + testCalTwo + ", " + testCalThree); 

结果

946681200000, 946681200000, 946684800000 

这分别表示2000-01-01午夜GMT + 1,GMT + 1和UTC 。我的时区是相对于UTC的+1小时。

这里的问题是,getTimeInMillis应该返回自1970-01-01以来的UTC毫秒数。只有testCalThree是正确的。

另一个问题是setTimeZone看起来不工作,这取决于我之前是否调用了getTimeInMillis。

我的目标是将我从其他代码接收的日历作为参数,并获取UTC(java.util)日期以供进一步使用。

+1

如果您想保持理智,请切换到JodaTime或Java8新的日期/时间API。 – lexicore

+0

为什么还要使用'Calendar'? (没有违法) – Spotted

+0

不是我的代码,我只是从它的参数。 – Juryt

回答

0

我认为这是Calendar实现中的一个错误。 time被高速缓存,即,如果一旦重新使用了,除非发生某些变化

但不知何故设置时区不被认为是相关的变化。

您可以通过显式更改或清除某个字段强制重新计算时间,以毫秒为单位。所以这个:

Calendar cal = new GregorianCalendar(2000, 0, 1); 
    long testCalOne = cal.getTimeInMillis(); 
    cal.clear(Calendar.ZONE_OFFSET); 
    cal.setTimeZone(TimeZone.getTimeZone("UTC")); 
    long testCalTwo = cal.getTimeInMillis(); 

    Calendar cal2 = new GregorianCalendar(2000, 0, 1); 
    cal2.setTimeZone(TimeZone.getTimeZone("UTC")); 
    long testCalThree = cal2.getTimeInMillis(); 

    System.out.println(testCalOne + ", " + testCalTwo + ", " + testCalThree); 

给出:

946681200000, 946684800000, 946684800000 

如你所愿。在设置时区之前,我必须做的唯一事情是cal.clear(Calendar.ZONE_OFFSET)

无论如何,我想重复评论中的建议。如果您重视您的理智,请切换至JodaTime或Java8日期/时间。

+0

从技术上讲,它仍然不像预期的那样,因为getTimeInMillis应该考虑偏移量并将其作为UTC返回。因此,设置时区应该是不相关的。 但是,getTimeInMillis似乎被窃听,使得时区更改相关。 谢谢你的回答。 – Juryt

+0

如何设置时区应该不相关? 'getTimeInMillis()'返回1970-01-01 00:00:00 UTC和日历实例中的日期之间的毫秒数。设置时区是重大更改,因为日历在您的情况下包含_local_ date 2000-01-01 00:00:00。 2000-01-01 00:00:00 UTC与2000-01-01 00:00:00 UTC + 1不同。 – Archie

0

对此进一步思考后,代码可能按预期工作。根据是否在日历上调用了getter,setTimeZone的功能可能会有所不同。

在第一种情况下,它改变时区,但由于时间已经被获得并因此被初始化,所以UTC时间不变。

在第二种情况下,它表示我们在构造函数中传递的2001-1-1的初始化日期是UTC时间,应该被解析为UTC而不是本地时区。

如果是这样,那么结果是有道理的。

+0

这是无稽之谈。你调用* getter *的时刻应该完全不相关。我认为这是一个错误。 – lexicore

+0

在内部,日历很懒。它只会在需要时实际计算其内部字段。但是,您可以直接设置它们。这让我怀疑,对于这个班级,当你打电话给一个吸气剂是相关的。 (但反直觉和笨重。) – Juryt

0

TL;博士

myGregCal.toZonedDateTime() 
     .toInstant() 

......或者......

java.util.Date.from(
    myGregCal.toZonedDateTime() 
      .toInstant() 
) 

java.time

我的目标是拿我收到来自其他代码参数日历并获得UTC(java.util)进一步使用的日期。的CalendarDate

麻烦的旧日期,时间类现在是传统,由java.time类取代。幸运的是,您可以通过调用旧类的新方法来转换为java.time。

ZonedDateTime zdt = myGregCal.toZonedDateTime() ; 

对于UTC值,提取Instant。该类表示UTC时间轴上的时间,分辨率为纳秒。

Instant instant = zdt.toInstant() ; 

要生成表示标准ISO 8601格式的UTC值的字符串,请拨打toString

String output = instant.toString() ; 

如果您需要在生成的字符串等格式,转换为更灵活OffsetDateTime和使用DateTimeFormatter。搜索堆栈溢出了很多例子。

最好避免Date类。但是,如果你必须转换。与Instant类似,Date表示UTC中时间线上的一个点。但是Date限于毫秒分辨率。所以你风险数据丢失,从三位数的毫秒数与十位数的纳秒数之间的小数部分中删除数字。

java.util.Date utilDate = Date.from(instant) ; 

关于java.time

java.time框架是建立在Java 8和更高版本。这些类取代了日期时间类legacy,如java.util.Date,Calendar,& SimpleDateFormat

Joda-Time项目现在位于maintenance mode,建议迁移到java.time。请参阅Oracle Tutorial。并搜索堆栈溢出了很多例子和解释。规格是JSR 310

从何处获取java.time类?

ThreeTen-Extra项目与其他类扩展java.time。这个项目是未来可能增加java.time的一个试验场。您可以在这里找到一些有用的类,如Interval,YearWeek,YearQuartermore