Joda Time用时区解析日期并保留该时区

时间:2013-05-28 14:34:40

标签: java date jodatime

我想解析一个使用特定时区创建的日期,将其转换为格式并返回。转换有效,但时区偏移始终设置为+0000,并根据需要添加/减去时差。如何让它格式化并保持偏移正确?

我期待这个:2012-11-30T12:08:56.23 + 07:00

但得到这个:2012-11-30T05:08:56.23 + 00:00

实现:

public static final String ISO_8601_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSZZ";

public static String formatDateToISO8601Standard(Date date) {
    DateTime dateTime = new DateTime(date);
    DateTimeFormatter df = DateTimeFormat.forPattern(ISO_8601_DATE_FORMAT);
    return dateTime.toString(df);
}

测试类:

private static final String DATE_WITH_TIMEZONE = "30 11 2012 12:08:56.235 +0700";
private static final String EXPECTED_DATE_WITH_TIMEZONE = "2012-11-30T12:08:56.23+07:00";

@Test public void testFormattingDateWithSpecificTimezone() throws Exception {
    String result = JodaDateUtil.formatDateToISO8601Standard(createDate(DATE_WITH_TIMEZONE));
    assertEquals("The date was not converted correctly", EXPECTED_DATE_WITH_TIMEZONE, result); }

private Date createDate(String dateToParse) throws ParseException {
    DateTimeFormatter df = DateTimeFormat.forPattern("dd MM yyyy HH:mm:ss.SSS Z");
    DateTime temp = df.parseDateTime(dateToParse);
    Date date = temp.toDate();
    return date; }

4 个答案:

答案 0 :(得分:37)

基本上,一旦解析了[在createDate()方法中的日期字符串],就会丢失原始区域。 Joda-Time将允许您使用任何区域格式化日期,但您需要保留原始区域。

在createDate()方法中,DateTimeFormatter“df”可以返回字符串上的区域。您需要使用withOffsetParsed()方法。然后,当您拥有DateTime时,请致电getZone()。如果您将此区域保存在某处或以某种方式将其传递给格式化例程,则可以通过创建DateTimeFormatter“withZone”并在该格式上指定所需区域来使用它。

作为演示,这里是一个方法中的一些示例代码。希望它能帮助您按照希望的方式更改代码。

  public static void testDate() 
  {
    DateTimeFormatter df = DateTimeFormat.forPattern("dd MM yyyy HH:mm:ss.SSS Z");
    DateTime temp = df.withOffsetParsed().parseDateTime("30 11 2012 12:08:56.235 +0700");
    DateTimeZone theZone = temp.getZone();

    Date date = temp.toDate();

    DateTime dateTime = new DateTime(date);
    DateTimeFormatter df2 = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSZZ");
    DateTimeFormatter df3 = df2.withZone(theZone);

    System.out.println(dateTime.toString(df2));
    System.out.println(dateTime.toString(df3));

  }

答案 1 :(得分:3)

TL;博士

OffsetDateTime.parse ( 
    "30 11 2012 12:08:56.235 +0700" , 
    DateTimeFormatter.ofPattern ( "dd MM uuuu HH:mm:ss.SSS X" , Locale.US )
).toString() 
  

2012-11-30T12:08:56.235 + 07:00

详细

接受的答案是正确的。只要转换为java.util.Date对象,就会丢失时区信息。由于java.util.Date::toString在生成String时容易混淆应用当前默认时区,因此这很复杂。

避免使用像java.util.Date这样的旧日期时间类。它们的设计很差,令人困惑,也很麻烦。现在遗留下来,取而代之的是java.time项目。现在由java.time类取代的Joda-Time项目也是如此。

java.time

将输入字符串解析为OffsetDateTime对象,因为它包含与UTC的偏移但缺少时区。调用DateTimeFormatter.ofPattern指定与输入字符串匹配的自定义格式。将该格式化程序对象传递给OffsetDateTime.parse

String input = "30 11 2012 12:08:56.235 +0700" ;
DateTimeFormatter f = DateTimeFormatter.ofPattern ( "dd MM uuuu HH:mm:ss.SSS X" , Locale.US );
OffsetDateTime odt = OffsetDateTime.parse ( input , f );
  

odt:toString():2012-11-30T12:08:56.235 + 07:00

要查看UTC中的相同时刻,请提取InstantInstant类代表UTC中时间轴上的一个时刻,分辨率为nanoseconds(小数部分最多九(9)位)。

Instant instant = odt.toInstant();
  

instant.toString():2012-11-30T05:08:56.235Z

您可以应用您想要查看同一时刻的任何时区,时间轴上的相同点。

ZonedDateTime zdtKolkata = odt.toInstant ().atZone ( ZoneId.of ( "Asia/Kolkata" ) );
  

zdtKolkata.toString():2012-11-30T10:38:56.235 + 05:30 [亚洲/加尔各答]

根本不需要混合旧的日期时间类。坚持使用java.time。如果必须使用一些尚未更新为java.time类型的旧代码,请查看添加到旧类的新方法以转换为/从java.time。

java.util.Date的等效值为Instant,均为UTC中1970-01-01T00:00:00Z的自上计时码。但要小心数据丢失,因为java.time类支持纳秒分辨率,但旧类限制为毫秒。

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

实时代码

请参阅live working code in IdeOne.com

关于java.time

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

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

要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310

从哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如IntervalYearWeekYearQuartermore

答案 2 :(得分:0)

试试这个。

ISODateTimeFormat.dateTimeParser().parseDateTime(dateString), 

然后将其转换为您想要的格式。

答案 3 :(得分:0)

使用格式     val formatter = DateTimeFormat.forPattern(“ yyyy-MM-dd HH:mm:ss.SSSZZ”)