【问题标题】:CET to GMT function in Java [duplicate]Java中的CET到GMT功能[重复]
【发布时间】:2018-06-09 10:53:27
【问题描述】:

我正在尝试将 CET 时间格式转换为 GMT。 原因是我从 XML 获取日期并将其插入到另一个使用 GMT 日期格式的系统中。所以我插入的任何日期都是过去的一天。

这里我从 XML 中获取并将其插入到 Java 对象中:

Date dob = DateUtils.fromXMLGregorianCalendar(customer.getDateOfBirth());
toYYYYMMDD(dob);
CETtoGMT(dob);
personalAccount.setBirthDate(dob);

我的功能:

public static String toYYYYMMDD(Date day) {
    SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
    String date = formatter.format(day);
    return date;
}
public String CETtoGMT(Date cetDate) {
    SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
    formatter.setTimeZone(TimeZone.getTimeZone("GMT"));
    String date = formatter.format(cetDate);
    return date;
}

我不确定我做错了什么,但我的测试表明我使用奥斯陆时区作为对象个人帐户出生日期:

Sun Feb 24 00:00:00 CET 1990
cdate = {Gregorian$Date@3163} "1990-02-24T00:00:00.000+0100"
 cachedYear = 1990
 cachedFixedDateJan1 = 724642
 cachedFixedDateNextJan1 = 725007
 era = null
 year = 1990
 month = 2
 dayOfMonth = 24
 dayOfWeek = 1
 leapYear = false
 hours = 0
 minutes = 0
 seconds = 0
 millis = 0
 fraction = 0
 normalized = true
 zoneinfo = {ZoneInfo@3165} "sun.util.calendar.ZoneInfo[id="Europe/Oslo",offset=3600000,dstSavings=3600000,useDaylight=true,transitions=141,lastRule=java.util.SimpleTimeZone[id=Europe/Oslo,offset=3600000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]]"
 zoneOffset = 3600000
 daylightSaving = 0
 forceStandardTime = false
 locale = null

【问题讨论】:

  • 在'toYYYYMMDD'函数中你忘记设置时区了。顺便说一句,如果您创建一个单元测试并在 IDE 中调试它,这对您来说将成为一个小问题。
  • 您应该记录问题的重要部分,例如DateUtils 是什么。我可能会猜到 Apache Commons Lang,但你不应该让我们猜。

标签: java date


【解决方案1】:

tl;博士

customer.getDateOfBirth()                     // Assuming this call returns a `XMLGregorianCalendar`. 
    .toGregorianCalendar​()                    // Convert `XMLGregorianCalendar` to the legacy class `GregorianCalendar`, en route to a `ZonedDateTime` in next line.
    .toZonedDateTime()                        // Convert from `GregorianCalendar` to `ZonedDateTime`, from legacy class to modern class.
    .toOffsetDateTime()                       // Convert to `OffsetDateTime` to better document that we are moving to an offset-from-UTC rather than a full time zone.
    .withOffsetSameInstant( ZoneOffset.UTC )  // Adjust into UTC. Same moment in time, same point on the timeline, but viewed through a different wall-clock time.
    .toLocalDate()                            // Extract a date-only value, without time zone and without time-of-day.
    .toString()                               // Generate a string in standard ISO 8601 format YYYY-MM-DD.
;

Date 是 UTC,但它的 toString 不是

java.util.Date 类代表 UTC 中的一个时刻。不幸的是,它的toString 方法在生成字符串时会混淆地应用 JVM 的当前默认时区。这已经在 Stack Overflow 上解释过很多很多次了。

避免遗留类

Date 是现在遗留下来的麻烦的旧日期时间类之一,完全被现代 java.time 类所取代。

java.time

您可以通过调用添加到旧类的新方法来转换旧类。如果您的代码返回 XMLGregorianCalendar 对象,请先转换为 GregorianCalendar

GregorianCalendar gc = customer.getDateOfBirth().toGregorianCalendar​() ;  // Returning a XMLGregorianCalendar, then converting to a GregorianCalendar.

转换成java.time。 GregorianCalendar​ 对应的是ZonedDateTime

ZonedDateTime zdt = gc.toZonedDateTime() ;

您的目标是 UTC。所以转换为OffsetDateTime,然后调整为UTC。

OffsetDateTime odt = zdt.toOffsetDateTime().withOffsetSameInstant( ZoneOffset.UTC ) ;

显然您想要该 UTC 调整的仅日期,因此提取 LocalDate 来表示没有时间和时区的日期。

LocalDate ld = odt.toLocalDate() ;

为了让所有读者都清楚……确定日期需要一个时区。对于任何给定的时刻,日期在全球范围内都会有所不同。例如,Paris France 中午夜后几分钟是新的一天,而 Montréal Québec 中仍然是“昨天”。我们必须通过所有这些步骤来保存重要的时区(或与 UTC 的偏移)信息,以便获得正确的日期。

字符串

您想要的输出似乎是标准ISO 8601 格式的字符串。 java.time 类在解析/生成字符串时默认使用标准格式。所以不需要指定格式模式。

String output = ld.toString() ;

时区

顺便说一句,切勿使用CET 或其他此类 3-4 个字母的伪区域。这些不是真正的时区,不是标准化的,甚至不是唯一的(!)。

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

ZoneId z = ZoneId.of( "Europe/Paris" );  // Or "Europe/Berlin" or "Africa/Tunis" etc.
ZonedDateTime zdt = zdt.withZoneSameInstant( z ) ;

关于java.time

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

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

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

从哪里获得 java.time 类?

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

【讨论】:

  • 谢谢。我使用集成,所以我必须支持不同系统的不同格式。我最初的目标不是将它转换为字符串,而是转换为 java.util.Calendar。
  • @Solo 如果可能的话,我建议在将日期时间值作为文本交换时仅使用标准 ISO 8601 格式。对于分区值,ZonedDateTime 类通过在方括号中附加区域名称来明智地扩展标准。通常最好交换 UTC 值,在 java.time 中表示为 Instant
猜你喜欢
  • 2021-08-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-21
  • 2012-12-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多