简体   繁体   中英

Joda Time parse a date with timezone and retain that timezone

I want to parse a date, which was created with a specific timezone, convert it to a format and return it. The conversion works but the timezone offset is always set to +0000 with the time difference being added/subtracted as necessary. How can I get it to format and keep the offset correct?

I expect this: 2012-11-30T12:08:56.23+07:00

But get this: 2012-11-30T05:08:56.23+00:00

Implementation:

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);
}

Test class:

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; }

Basically, once you parse the date string [in your createDate() method] you've lost the original zone. Joda-Time will allow you to format the date using any zone, but you'll need to retain the original zone.

In your createDate() method, the DateTimeFormatter "df" can return the zone that was on the string. You'll need to use the withOffsetParsed() method. Then, when you have your DateTime , call getZone() . If you save this zone somewhere or somehow pass it to your formatting routine, then you can use it there by creating a DateTimeFormatter "withZone" and specifying that zone as the one you want on the format.

As a demo, here's some sample code in a single method. Hopefully, it'll help change your code the way you want it to run.

  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));

  }

tl;dr

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

Details

The accepted Answer is correct. As soon as you convert to a java.util.Date object, you lose time zone information. This is complicated by the fact that java.util.Date::toString confusingly applies a current default time zone when generating the String.

Avoid using these old date-time classes like java.util.Date . They are poorly-designed, confusing, and troublesome. Now legacy, supplanted by the java.time project. So too is the Joda-Time project now supplanted by the java.time classes.

java.time

Parse that input string as a OffsetDateTime object as it includes an offset-from-UTC but lacks a time zone. Call DateTimeFormatter.ofPattern to specify a custom format matching your input string. Pass that formatter object to 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

To see the same moment in UTC, extract an Instant . The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).

Instant instant = odt.toInstant();

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

You can apply any time zone through which you want to view the same moment, the same point on the timeline.

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

zdtKolkata.toString(): 2012-11-30T10:38:56.235+05:30[Asia/Kolkata]

No need to mix in the old date-time classes at all. Stick with java.time. If you must use some old code not yet updated to java.time types, look to new methods added to the old classes to convert to/from java.time.

The equivalent of java.util.Date is Instant , both being a count-since-epoch of 1970-01-01T00:00:00Z in UTC. But beware of data-loss as the java.time classes support nanosecond resolution but the old classes are limited to milliseconds.

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

Live code

See live working code in IdeOne.com .


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date , .Calendar , & java.text.SimpleDateFormat .

The Joda-Time project, now in maintenance mode , advises migration to java.time.

To learn more, see the Oracle Tutorial . And search Stack Overflow for many examples and explanations. Specification is JSR 310 .

Where to obtain the java.time classes?

  • Java SE 8 and SE 9 and later
    • Built-in.
    • Part of the standard Java API with a bundled implementation.
    • Java 9 adds some minor features and fixes.
  • Java SE 6 and SE 7
    • Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport .
  • Android

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval , YearWeek , YearQuarter , and more .

Try this.

ISODateTimeFormat.dateTimeParser().parseDateTime(dateString), 

then convert that to the format you desire.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM