用Joda和时区解析约会

时间:2011-01-07 11:24:45

标签: java datetime date timezone jodatime

我有两个时间戳,用两种不同的格式描述同一时刻。

2010-10-03 18:58:072010-10-03T16:58:07.000+02:00

我使用Joda-Time解析时间戳与两个不同的日期格式器。最后,我希望有两个DateTime对象,它们在同一时刻是相等的。

DateFormatter提供了几种控制时区和语言环境的方法,但我无法使其工作。

这是我想要的代码:

    final String date1 = "2010-10-03 18:58:07"; // Europe/Berlin local time
    final DateTimeFormatter formatter1 = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
    final DateTime dateTime1 = formatter1.parseDateTime(date1);

    final String date2 = "2010-10-03T16:58:07.000+02:00"; // Europe/Berlin local time with time zone
    final DateTimeFormatter formatter2 = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
    final DateTime dateTime2 = formatter2.parseDateTime(date2);

    Assert.assertTrue(dateTime1.isEqual(dateTime2));

4 个答案:

答案 0 :(得分:7)

如果您的默认时间是欧洲/柏林,2010-10-03 18:58:07对应2010-10-03T16:58:07.000 + 00:00。

您可能会误解字符串表示中的时区字段。您的时间戳2010-10-03T16:58:07.000 + 02:00意味着“在格林尼治标准时间偏离+2小时的时区是16:58:07”,或者用其他措辞“现在是16 :58:07在柏林“。我认为你预计这意味着它是格林尼治标准时间16:58:07?

答案 1 :(得分:4)

你的两个时间戳并不代表同一时刻(正如jambjo已经评论过的那样)。请参阅维基百科上的Time zone as offsets from UTC

另请参阅parseDateTime文档,了解其工作原理。如果您未提供任何时区,则将应用默认时区(如果您在那里,则为柏林时区UTC + 2)。所以:

  • 2010-10-03 18:58:07变为2010-10-03T18:58:07.000+02:00(柏林时间18:58,与UTC相差2小时,即UTC时间为16:58)。
  • 2010-10-03T16:58:07.000+02:00保持原样,因为提供了一个时区(即柏林16:58,与UTC相差2小时,即UTC时间14:58)

希望你明白了。您需要使用withZone方法调整时间以获得所需的结果。

答案 2 :(得分:1)

tl; dr

使用现代的 java.time 类取代 Joda-Time

LocalDateTime                                   // Represent a date and time-of-day without the context of a time zone or offset-from-UTC. *Not* a moment, *not* a point on the timeline.
.parse(                                         // Parse text into a date-time value.
    "2010-10-03 18:58:07".replace( " " , "T" )  // Replace SPACE in middle with a `T` to comply with ISO 8601 standard used by default in *java.time* when parsing/generating strings.
)                                               // Returns a `LocalDateTime` object.
.atZone(                                        // Assign the time zone we know for certain was intended for this input. 
    ZoneId.of( "Europe/Moscow" )                // Real time zones are named in `Continent/Region` format, never 2-4 letter codes such as CST, PST, IST, CEST, etc.
)                                               // Returns a `ZonedDateTime` object, a date with time-of-day and with a time zone assigned to determine a moment.
.toInstant()                                    // Adjust from time zone to UTC. 
.equals(
    OffsetDateTime                              // Represent a date and time-of-day with an offset-from-UTC but not a full time zone.
    .parse( "2010-10-03T16:58:07.000+02:00" )   // Parse a standard ISO 8601 string.
    .toInstant()                                // Adjust from offset to UTC (in other words, an offset of zero hours-minutes-seconds). 
)                                               // Returns `boolean`. 
  

true

详细信息

Answer by jarnbjo是正确的,因为您误解了offset-from-UTCtime zone值的含义。

现在,Joda-Time项目在2018年处于维护模式。该项目的主要作者斯蒂芬·科尔本(Stephen Colebourne)继续找到JSR 310,并编写了其实现,即java.time中的OpenJDK类。

首次输入

您的输入字符串2010-10-03 18:58:07几乎是标准的ISO 8601格式。为了符合要求,请将中间的空格替换为T

String input1 = "2010-10-03 18:58:07".replace( " " , "T" ) ;

该字符串缺少时区或UTC偏移量的任何指示。因此解析为LocalDateTime

LocalDateTime ldt = LocalDateTime.parse( input1 ) ;

此值 not 表示一个时刻,是 not 在时间轴上的一个点。如果没有时区或偏移量的上下文,则可能是世界各地时区范围约26-27小时内的许多时刻中的任何时刻。

在您的评论中,您揭示了输入字符串显然旨在表示Europe/Moscow时区中的日期和时间。因此,我们可以分配该区域来确定时刻,即时间轴上的一个点。

ZoneId zMoscow = ZoneId.of( "Europe/Moscow" ) ;
ZonedDateTime zdtMoscow = ldt.atZone( zMoscow ) ;  // Determine a moment by assigning a time zone.
  

zdtMoscow.toString():2010-10-03T18:58:07 + 04:00 [欧洲/莫斯科]

第二个输入

您的第二个输入2010-10-03T16:58:07.000+02:00符合标准ISO 8601格式。

此输入的UTC偏移量比UTC提前两个小时。因此,此字符串代表UTC中的14:58:07。

我们可以解析为OffsetDateTime来遵守给定的偏移量。

OffsetDateTime odt2 = OffsetDateTime.parse( "2010-10-03T16:58:07.000+02:00" ) ;
  

odt2.toString():2010-10-03T16:58:07 + 02:00

比较

这两个输入代表时间轴上的同一时刻,同一点吗?

比较的一种方法是将两者都调整为UTC。根据定义,Instant始终采用UTC。

提示:养成思考,工作,存储,交换和登录UTC的习惯。将UTC视为唯一的真实时间

Instant instant1 = zdtMoscow.toInstant() ;  // Adjust from time zone to UTC.
Instant instant2 = odt2.toInstant() ;       // Adjust from offset to UTC.

boolean equality = instant1.equals( instant2 );

运行时,我们看到结果的末尾带有Z。这表示UTC,并且发音为Zulu。而且,的确,我们看到这两个值表示同一时刻,在UTC几乎是下午3点。

  

instant1.toString():2010-10-03T14:58:07Z

     

instant2.toString():2010-10-03T14:58:07Z

     

平等:真实


关于 java.time

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

目前位于Joda-Timemaintenance mode项目建议迁移到java.time类。

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

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

在哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展了java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。您可能会在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

答案 3 :(得分:0)

我遇到了与您相同的问题,发现的最佳方法是解析数据。 我本来是这样的格式:2018-01-22 09:25:14.000 +0000

只需选择该列,然后在“数据”标签中单击“文本到列”即可。

我使用带空格的Delimited在3个不同的列中解析此格式。 所以最后,我有了;

颜色A 2018-01-22 B座09:25:14.000 C +0000