2

I am trying to convert a valid ISO 8601 string to a consistent format so that sort and search using simple lexicographical order is possible.

My application could receive a date/time in any of the following formats, for example:

2015-02-05T02:05:17.000+00:00
2015-02-05T02:05:17+00:00
2015-02-05T02:05:17Z

These all represent the same date/time and I would like to convert them all to a canonical form for storage, say:

2015-02-05T02:05:17.000Z

My first thought was to just parse them using a technique from Converting ISO 8601-compliant String to java.util.Date, and then convert back to the desired string, but this breaks down when dealing with less precise date/times, for example:

2015-02-05T02:05:17Z
2015-02-05T02:05Z
2015-02-05Z
2015-02Z
2015Z

The imprecision of these times should be preserved. They should not be converted to:

2015-02-05T00:00:00.000Z

I've looked Java 8 and Joda-Time, but they seem to want to treat everything as specific points in time, and can't model the imprecise nature or partial dates/times.

UPDATE:

Using Java 8, I can do:

OffsetDateTime dateTime = OffsetDateTime.parse("2015-02-05T02:05:17+00:00");
System.out.println(dateTime.toString());

which gives me:

2015-02-05T02:05:17Z

which is what I want, but:

OffsetDateTime dateTime = OffsetDateTime.parse("2015-02-05T02:05:17.000+00:00");
System.out.println(dateTime.toString());

also gives me:

2015-02-05T02:05:17Z

Notice that java has thrown away the millisecond precision. Specifying 000 is treated the same as not specifying anything, which doesn't seem quite right.

Community
  • 1
  • 1
James Scriven
  • 7,784
  • 1
  • 32
  • 36
  • `2015-02-05T02:05:17+0000` is not a valid ISO date. – Michael-O Feb 05 '15 at 19:41
  • 1
    Wikipedia suggests that +0000 is a valid offset. Do you have a better reference? – James Scriven Feb 05 '15 at 20:00
  • I dont think there is a single object which can hold a date with the restrictions you have defined (i.e. partial precision). So your best bet is to use your own representation. – eckes Feb 05 '15 at 20:13
  • @JamesScriven, if WP says that then it is wrong. You have either provide all parts in extended format or in standard format but not in a mixed fashion. See my [comment](https://github.com/jersey/jersey/pull/94#issuecomment-70377338) on Jersey about this. I can cite the chapter of ISO 8601 if you like. – Michael-O Feb 05 '15 at 20:27
  • @Michael-O The distinction between basic and extended notation was not clear in my head. I've updated the question to remove the bad examples. Thanks. – James Scriven Feb 05 '15 at 21:08
  • You can store the LocalDateTime and the precision as two fields. This will keep all the information you need. There is not a single built in type which will do what you want. – Peter Lawrey Feb 05 '15 at 21:40

2 Answers2

7

In Java 8, you can use LocalDate.parse() or LocalDateTime.parse() on a String without providing it with a pattern, if the String is in ISO 8601 format.

parse(), Obtains an instance of LocalDate from a text string such as 2007-12-03. The string must represent a valid date and is parsed using DateTimeFormatter.ISO_LOCAL_DATE.

and,

DateTimeFormatter.ISO_LOCAL_DATE, This returns an immutable formatter capable of formatting and parsing the ISO 8601

for example,

String strDate = "2015-08-04";
LocalDate aLD = LocalDate.parse(strDate);
System.out.println("Date: " + aLD);

String strDatewithTime = "2015-08-04T10:11:30";
LocalDateTime aLDT = LocalDateTime.parse(strDatewithTime);
System.out.println("Date with Time: " + aLDT);

gives,

Date: 2015-08-04
Date with Time: 2015-08-04T10:11:30

Update:

your date, i.e "2015-02-05T02:05:17.000+00:00" would only throw zeros away when they are all zeros, if the value of nano seconds is other than zeros then it will display just fine, however, if you want to display zeros too then you can simply add if/else block and append zeros to your date if your nano-of-seconds are zeros (yourdate.getNano()==0), else print it as it is,

String dateTimestr = "2015-02-05T02:05:17.000+00:00";

OffsetDateTime dateTime = OffsetDateTime.parse(dateTimestr);

if ((dateTime.getNano() == 0) && (dateTimestr.length() > 25 ))
    System.out.println(dateTime.toLocalDateTime() + ".000Z");

else
    System.out.println(dateTime.toString());

will give,

2015-02-05T02:05:17.000Z

changing your date to,

String dateTimestr = "2015-02-05T02:05:17+00:00";

gives,

2015-02-05T02:05:17Z

changing date to,

String dateTimestr = "2015-02-05T02:05:17.100+00:00";

gives,

2015-02-05T02:05:17.100Z

changing it to,

String dateTimestr = "2015-02-05T02:05:17Z";

gives,

2015-02-05T02:05:17Z
Sufiyan Ghori
  • 18,164
  • 14
  • 82
  • 110
1

You have to understand that the preservation of precision can only happen in textual space but not in value space. So a different degree of precision can be expressed in a textual way (see your input), but not in the value object (here of type OffsetDateTime). Latter only stores the date-time-value but not any extra precision information. If you really want to achieve this goal then you have no other choice than to write your own class as wrapper around an OffsetDateTime and the precision (expressed as ChronoUnit-enum).

Otherwise, if you always want to preserve the precision in milliseconds for your output then you should not use the toString()-method but a dedicated formatter. In Java-8 this would be like:

OffsetDateTime odt = OffsetDateTime.parse("2015-02-05T02:05:17+00:00");
String dateTimeWithMilliSecondPrecision = 
  odt.format(DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSxxx"));
System.out.println(dateTimeWithMilliSecondPrecision);
// output: 2015-02-05T02:05:17.000+00:00
kapex
  • 28,903
  • 6
  • 107
  • 121
Meno Hochschild
  • 42,708
  • 7
  • 104
  • 126