【问题标题】:How to save DateTime as Date with TimeZone offset included?如何将 DateTime 保存为包含 TimeZone 偏移量的日期?
【发布时间】:2012-06-26 20:47:18
【问题描述】:

我有一些 DateTime,包括 TimeZone Europe/Vienna (+0200)。是通过这个方法获取的:

settlementService.getPendingPeriodStart()

然后用 toString 看起来像这样:

2012-06-01T00:00:00.000+02:00

现在我想将这个日期 2012-06-01 保存为 java.util.Date,所以我尝试了这样的操作:

transactionDate = settlementService.getPendingPeriodStart().withTime(0, 0, 0, 0).toDate();

但是结果是这样的:

2012 年 5 月 31 日星期四 22:00:00 UTC

将 DateTime 结果保存为 Date 包括时区偏移量的最佳方法是什么,因此 transactionDate 应该是 2012-06-01。我可以修改 GregorianCalendar,但这不是我喜欢的。这应该更容易,不是吗?

顺便说一下(如果不清楚的话)。本地系统在 UTC 上运行。这就是为什么结果是 Thu May 31 22:00:00 UTC 2012

【问题讨论】:

  • 您确定需要使用用户时区保存日期,而不是将其保存在集中时区并进行适当转换吗?
  • A Date 不能有时区,所以你真的在问不可能。

标签: java datetime datetimeoffset java-6 java.util.date


【解决方案1】:

用户的日期和时区是两个不同的东西。一个是日期,另一个是偏好或表示参数。

不要试图将它们存储在同一个字段中。

考虑到将它们有效地存储在一起(不损失精度)甚至是不可能的,因为日期可以存储在 long 中,只是因为它被指定为 UTC 日期。

您可以保存the time zone as an offset(经常建议将分钟保留在此偏移量 ([hh]:[mm]) 中,因为情况非常小)。

【讨论】:

    【解决方案2】:

    我想我找到了解决办法。 (如果您知道更好的解决方案,请告诉我)

    DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
    
    DateTime dateTimeWithTimeZone = new DateTime(DateTimeZone.forID("Europe/Vienna")).withDate(2012, 06, 01).withTime(0, 0, 0, 0);      
    Date dateWithTimeZoneIncluded = dateTimeWithTimeZone.toLocalDate().toDate();
    
    System.out.println(dateFormat.format(dateWithTimeZoneIncluded));
    

    结果如预期的那样是2012-06-01

    【讨论】:

      【解决方案3】:

      不幸的是,accepted answer 具有误导性。事实上,

      2012-06-01T00:00:00.000+02:00 = 2012-05-31T22:00:00Z
      

      右侧的Ztimezone designator,表示零时区偏移。它代表 Zulu 并指定 Etc/UTC 时区(时区偏移量为 +00:00 小时)。

      2012-06-01T00:00:00.000+02:00 写成2012-06-01,虽然只是一个函数调用,但对于任何依赖于时区的业务逻辑来说都是危险的,因为它在时区中可能有不同的日期和不同的偏移值,例如如上图所示。 2012-06-01 只是一个 LocalDate,应该用于跟踪出生日期、结婚日期等事件。

      java.time

      旧的日期时间 API(java.util 日期时间类型及其格式类型,SimpleDateFormat 等)已过时且容易出错。建议完全停止使用,改用java.timemodern date-time API*

      另外,下面引用的是Home Page of Joda-Time的通知:

      请注意,从 Java SE 8 开始,用户被要求迁移到 java.time (JSR-310) - JDK 的核心部分,它取代了这个项目。

      使用现代 API java.time 的解决方案:

      如何解析给定的日期时间字符串:

      给定的日期时间字符串有一个时区偏移量,因此它应该被解析为OffsetDateTime。由于现代日期时间 API 基于 ISO 8601,并且只要日期时间字符串符合 ISO 8601 标准,就不需要明确使用 DateTimeFormatter 对象。

      OffsetDateTime odt = OffsetDateTime.parse("2012-06-01T00:00:00.000+02:00"); // 2012-06-01T00:00+02:00
      

      如何从 UTC 中获取日期时间:

      有多种方法。最简单的方法是将其转换为 Instant,它代表时间线上的一个瞬时点,采用 UTC。

      Instant instant = odt.toInstant(); // 2012-05-31T22:00:00Z
      

      或者,

      OffsetDateTime odtUtc = odt.withOffsetSameInstant(ZoneOffset.UTC); // 2012-05-31T22:00Z
      

      如何摆脱java.util.Date

      如果需要 java.util.Date 的实例来自 OffsetDateTime 的实例,则可以使用 Date#from(Instant instant)

      Date date = Date.from(instant); // Thu May 31 23:00:00 BST 2012 <--In my timezone
      

      注意java.util.Date 对象不是像modern date-time types 那样的真实日期时间对象;相反,它表示自称为“纪元”的标准基准时间以来的毫秒数,即January 1, 1970, 00:00:00 GMT(或 UTC)。当您打印一个java.util.Date 的对象时,它的toString 方法会返回JVM 时区中的日期时间,根据这个毫秒值计算得出。如果您需要在不同的时区打印日期时间,则需要将时区设置为 SimpleDateFormat 并从中获取格式化字符串,例如

      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", Locale.ENGLISH);
      sdf.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));
      System.out.println(sdf.format(date)); // 2012-05-31T22:00:00.000Z
      

      如何从中取出日期部分:

      正如我已经解释过的,任何依赖于时区的业务逻辑都是危险的。但是,这只是一个简单的函数调用。

      LocalDate localDate = odt.toLocalDate(); // 2012-06-01
      

      Trail: Date Time了解更多关于java.timemodern date-time API*


      * 出于任何原因,如果您必须坚持使用 Java 6 或 Java 7,您可以使用 ThreeTen-Backport,它将大部分 java.time 功能向后移植到 Java 6 和 7 . 如果您正在为一个 Android 项目工作,并且您的 Android API 级别仍然不符合 Java-8,请检查 Java 8+ APIs available through desugaringHow to use ThreeTenABP in Android Project

      【讨论】:

        猜你喜欢
        • 2020-04-22
        • 1970-01-01
        • 1970-01-01
        • 2018-11-26
        • 1970-01-01
        • 1970-01-01
        • 2021-08-21
        • 1970-01-01
        • 2018-02-20
        相关资源
        最近更新 更多