【问题标题】:Parse ISO8601 date string to date with UTC Timezone使用 UTC 时区解析 ISO8601 日期字符串到日期
【发布时间】:2014-11-14 07:44:24
【问题描述】:

我正在尝试对 JavaScript 应用程序的日期进行序列化/反序列化。

服务器端,我用的是Java,上面安装了JodaTime。我发现了如何使用 UTC 时区序列化为 ISO,但不知道如何进行反向操作。

这是我的代码

public static String getIsoDate( Date date )
{
    SimpleDateFormat  dateToIsoDateString = new SimpleDateFormat( ISO_8601_DATE_FORMAT );
    TimeZone tz = TimeZone.getTimeZone("UTC");
    dateToIsoDateString.setTimeZone( tz );
    return dateToIsoDateString.format( date );
}

// this will return a date with GMT timezone
public static Date getDateFromIsoDateString( String iso8601date )
{
    DateTimeFormatter jodaParser = ISODateTimeFormat.dateTimeNoMillis();
    return jodaParser.parseDateTime( iso8601date ).toDate();
}

我不介意使用或不使用 Joda,只需要一个快速且有效的解决方案,

【问题讨论】:

标签: java javascript mongodb date jodatime


【解决方案1】:

Java8 原生解决方案

Date.from(ZonedDateTime.parse("1994-11-05T08:15:30+05:30").toInstant())

Date.from(ZonedDateTime.parse("1994-11-05T13:15:30Z").toInstant())

【讨论】:

  • ZonedDateTime 不是在这里使用的合适的类。首先,使用OffsetDateTime.parse。第二个,Instant.parse。正如我已经在my Answer 中发布的那样。
  • 但是,根据docs.oracle.com/javase/8/docs/api/java/time/ZonedDateTime.html "在 ISO-8601 日历系统中具有时区的日期时间,例如 2007-12-03T10:15:30+01:00 Europe/巴黎。”我希望 ZonedDateTime 也是一个有效的解决方案,不是吗?两种方法的输出相同。
  • 请注意该引用中的官方时区名称Europe/Paris。我们正在讨论的输入字符串缺少那个时区,并且只有一个与 UTC 的偏移量。偏移量只是几个小时、分钟、秒。时区更多,是特定地区人民使用的偏移量的过去、现在和未来变化的历史。您的代码有效,因为 java.time 是使用 ZoneOffset 作为 ZoneId 的子类实现的。该代码有效但具有误导性,表明实际上我们根本没有时区。阅读OffsetDateTime 类。
【解决方案2】:

如果您使用的是 Java 7 或更早版本,可以参考此post

如果您使用的是 Java 8,您可以这样做:

    DateTimeFormatter timeFormatter = DateTimeFormatter.ISO_DATE_TIME;
    TemporalAccessor accessor = timeFormatter.parse("2015-10-27T16:22:27.605-07:00");

    Date date = Date.from(Instant.from(accessor));
    System.out.println(date);

更新

@BasilBourque 在评论中指出,TemporalAccessor 是 java 框架级接口,不建议在应用程序代码中使用,建议使用具体类而不是接口。

此接口是框架级接口,不应在应用程序代码中广泛使用。相反,应用程序应该创建和传递具体类型的实例,例如 LocalDate。造成这种情况的原因有很多,其中一部分是该接口的实现可能在 ISO 以外的日历系统中。有关这些问题的更全面讨论,请参阅 ChronoLocalDate。

有一些具体的类可供使用,例如LocalDateLocalDateTimeOffsetDateTimeZonedDateTime 等。

DateTimeFormatter timeFormatter = DateTimeFormatter.ISO_DATE_TIME;

OffsetDateTime offsetDateTime = OffsetDateTime.parse("2015-10-27T16:22:27.605-07:00", timeFormatter);

Date date = Date.from(Instant.from(offsetDateTime));
System.out.println(date);

【讨论】:

  • java.time 类不鼓励使用接口/超类。具体类OffsetDateTime 类在这里是合适的。 OffsetDateTime.parse( "2015-10-27T16:22:27.605-07:00" )
  • @BasilBourque 你是对的。文档建议不要在应用程序代码中使用框架级接口。更新了答案。
【解决方案3】:

tl;博士

OffsetDateTime.parse( "2015-10-27T16:22:27.605-07:00" )
.toInstant()
.toString()

2015-10-27T23:22:27.605Z

详情

您的问题不明确和具体。也许这些小例子会有所帮助。将旧的 java.util.Date 和 .Calendar 类与 Joda-Time 混合使用可能会让您感到困惑。 Joda-Time 完全替换那些类而不是增加。

java.time

现代的 java.time 类取代了 Java 中遗留的日期时间类以及提供灵感的 Joda-Time 库。

OffsetDateTime

OffsetDateTime 类表示时间轴上的一个时刻,其与 UTC 的特定偏移量决定了它的挂钟时间。

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

OffsetDateTime odt = OffsetDateTime.parse( "2015-10-27T16:22:27.605-07:00" ) ;

Instant

要从 UTC 后 7 小时的偏移量调整到 UTC 本身,我们需要在时间上增加 7 小时,并在需要时翻转日期。 OffsetDateTime 类可以为我们完成这项工作。使用 ZoneOffset.UTC 常量指定 UTC。

OffsetDateTime odtUtc = odt.withOffsetSameInstant( ZoneOffset.UTC ) ;

odtUtc.toString(): 2015-10-27T23:22:27.605Z

如果您在代码中大量使用此 UTC 值,并且通常应该如此,那么您可能会发现使用 Instant 对象更清晰。根据定义,Instant总是在 UTC 中。我们可以从OffsetDateTime 中提取一个Instant

Instant instant = odt.toInstant() ;

instant.toString(): 2015-10-27T23:22:27.605Z

请注意,我们上面的所有三个对象(odtodtUtcinstant)都代表相同的同时时刻,时间轴上的相同点。唯一不同的是他们的挂钟时间。

ZonedDateTime

顺便说一句,如果您想看到同一时刻调整到某个地区的人们使用的挂钟时间,请通过ZoneId 指定时区以获取ZonedDateTime 对象。

ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;

zdt.toString(): 2015-10-27T19:22:27.605-04:00[美国/蒙特利尔]

java.time中使用具体类

Answer by always_a_rookie_to_learn 与上述方法类似,但使用接口TemporalAccessor。通常,在 Java 中使用更高的接口和超类是一个好主意。但不是在这里。 java.time 文档解释说,他们的设计旨在让我们在应用程序中使用较低的更具体的类。通常,这些抽象仅供框架内部使用。

在这个问题的具体情况下,OffsetDateTime 类是合适的,而不是TemporalAccessor


关于java.time

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

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

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

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

从哪里获得 java.time 类?

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


乔达时间

更新:Joda-Time 项目处于维护模式,它建议迁移到 java.time 类。此部分保留完整的历史记录。

对于字符串,Joda-Time 默认为ISO 8601,包括解析和生成。 Joda-Time 具有 ISO 8601 的内置默认解析器,因此只需将您的兼容字符串传递给构造函数或静态 parse 方法。

java.util.Date date = new DateTime( "2010-01-01T12:00:00+01:00Z" ).toDate();

尽可能避免使用 java.util.Date 和 .Calendar,并坚持使用 Joda-TimeDateTime 等类。仅在其他类需要时使用 .Date。

DateTime dateTimeUtc = new DateTime( someJavaDotUtilDotDate, DateTimeZone.UTC ); // Joda-Time can convert from java.util.Date type which has no time zone.
String output = dateTime.toString(); // Defaults to ISO 8601 format.
DateTime dateTimeUtc2 = new DateTime( output, DateTimeZone.UTC ); // Joda-Time can parse ISO 8601 strings.

为了演示,调整到用户期望的时区。

DateTime dateTimeMontréal = dateTimeUtc.withZone( DateTimeZone.forID( "America/Montreal" ) );

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-07-18
    • 2011-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多