13

I've switched to threeten for date times but I've still got a 3rd party tool that uses joda to write timestamp with timezone to the database and I need to convert from one to the other. What's the best way? As a workaround I tried DateTime.parse(zdt.toString) but it falls over because joda doesn't like the zone format

Invalid format: "2015-01-25T23:35:07.684Z[Europe/London]" is malformed at "[Europe/London]"

leppie
  • 115,091
  • 17
  • 196
  • 297
Tim Pigden
  • 895
  • 2
  • 9
  • 18

3 Answers3

22

Please notice that using DateTimeZone.forID(...) is not safe, which might throw DateTimeParseException as usually ZoneOffset.UTC has a ID "Z" which cannot be recognized by DateTimeZone.

What I would recommend in order to convert ZonedDateTime to DateTime is:

return new DateTime(
    zonedDateTime.toInstant().toEpochMilli(),
    DateTimeZone.forTimeZone(TimeZone.getTimeZone(zonedDateTime.getZone())));
James Ding
  • 313
  • 5
  • 7
  • This should be the accepted answer. We ran into this exact issue! – ChrisDekker Dec 05 '18 at 12:26
  • @ChrisDekker There is no perfect transformation of zone identifiers in every case. While the expression `TimeZone.getTimeZone(zoneId)` might better work for Z-literals (representing UTC) than Joda-Time, such an expression can silently fail for other ids, but not with an exception, instead with arbitrary fallback to UTC (which is even worse), see the API-doc: "Returns:the specified TimeZone, or the GMT zone if the given ID cannot be understood", my answer just addresses the special example the OP presented in the question, but I don't pretend it to work for every zone id. – Meno Hochschild Dec 05 '18 at 18:22
  • The main problem of this suggested answer is that it pretends to work for all identifiers although unrecognized ids can just be switched to UTC by the expression `TimeZone.getTimeZone(zoneId)` - without any exception. The underlying timezone repositories of Joda-Time and JDK know different sets of valid ids, and this fact cannot be cured by any "genious" or "smart" transformation code. – Meno Hochschild Dec 05 '18 at 18:28
9
ZonedDateTime zdt = 
  ZonedDateTime.of(
    2015, 1, 25, 23, 35, 7, 684000000, 
    ZoneId.of("Europe/London"));

System.out.println(zdt); // 2015-01-25T23:35:07.684Z[Europe/London]
System.out.println(zdt.getZone().getId()); // Europe/London
System.out.println(zdt.toInstant().toEpochMilli()); // 1422228907684

DateTimeZone london = DateTimeZone.forID(zdt.getZone().getId());
DateTime dt = new DateTime(zdt.toInstant().toEpochMilli(), london);
System.out.println(dt); // 2015-01-25T23:35:07.684Z

In case the zone id transformation might crash for any unsupported or unrecognized id, I recommend to

  • catch and log it,
  • do updates of tz-repositories (for Joda: update to latest version, for JDK: use tz-updater-tool)

That is usually the better strategy than to just silently fall back to any arbitrary tz-offset like UTC.

Meno Hochschild
  • 42,708
  • 7
  • 104
  • 126
  • To the downvoters, think twice before you are going to use `TimeZone.getTimeZone(...)` as suggested in the answer of James Ding. Switching back to UTC for unsupported ids is probably worse than a crash. – Meno Hochschild Dec 05 '18 at 18:30
2

here's a kotlin extension to do the same (in case you code that way)

fun ZonedDateTime.toDateTime(): DateTime =
    DateTime(this.toInstant().toEpochMilli(), 
        DateTimeZone.forTimeZone(TimeZone.getTimeZone(this.zone)))
mixel
  • 25,177
  • 13
  • 126
  • 165
pabl0rg
  • 171
  • 8