从 GregorianCalendar 获取日期

Getting a Date from a 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

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

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

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

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

我认为这是 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 date/time.

经过进一步思考,代码可能会按预期工作。 setTimeZone 的功能可能会有所不同,具体取决于 getter 是否已在日历上调用。

在第一种情况下,它改变了时区,但是因为时间已经被获取并因此被初始化,所以UTC时间没有改变。

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

如果是这样,那么结果就有意义了。

tl;博士

myGregCal.toZonedDateTime()
         .toInstant()

……或者……

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

java.time

My goal is to take a Calendar I receive as parameter from other code and get a UTC (java.util) Date for further use.

CalendarDate 的麻烦的旧日期时间 classes 现在是遗留的,被 java.time classes 取代。幸运的是,您可以通过在旧 classes 上调用新方法来转换 to/from java.time。

ZonedDateTime zdt = myGregCal.toZonedDateTime() ;

对于 UTC 值,提取一个 Instant。 class 表示 UTC 时间轴上的一个时刻,分辨率为纳秒。

Instant instant = zdt.toInstant() ;

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

String output = instant.toString() ;

如果您需要在生成的字符串中使用其他格式,请转换为更灵活的 OffsetDateTime 并使用 DateTimeFormatter。在 Stack Overflow 中搜索许多示例。

最好避开Dateclass。但如果你必须,转换。与 Instant 一样,Date 表示 UTC 时间轴上的一个点。但是 Date 仅限于毫秒分辨率。因此,您 冒着数据丢失的风险 ,从毫秒的 3 位数字与纳秒的 9 位数字之外的秒的小数部分中删除数字。

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

关于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,建议迁移到java.time。

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

从哪里获得java.time classes?

  • Java SE 8 and SE 9 及更高版本
    • 内置。
    • 标准 Java API 的一部分,带有捆绑实施。
    • Java 9 添加了一些小功能和修复。
  • Java SE 6 and SE 7
  • Android
    • ThreeTenABP项目专门为Android改编了ThreeTen-Backport(上面提到的)。
    • 参见

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.