Zone parsing for GMT0

Zone parsing for GMT0



I have GMT0 as the default timezone in a system and it causes problem when I'm serializing it and deserializing it just after that.


System.setProperty("user.timezone","GMT0");
DateTimeFormatter zoneFormatter = new DateTimeFormatterBuilder()
.appendZoneOrOffsetId()
.toFormatter();
String formatted = zoneFormatter.format(ZonedDateTime.now());
System.out.println(formatted);
System.out.println(zoneFormatter.parse(formatted));



The first System.out.println prints GMT0 while the second throws the following problem.


System.out.println


GMT0


Exception in thread "main" java.time.format.DateTimeParseException: Text 'GMT0' could not be parsed, unparsed text found at index 3
at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1952)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1777)



is it an expected behavior? Is there a way to do that in a safe manner?





thanks @OleV.V. However I would prefer to avoid changing that since it has impacts on other applications.
– Nicolas Henneaux
Aug 23 at 9:51





The funny thing is I cannot reproduce your problem (only noticed now). On my Java 10.0.1 your code prints GMT0 and then ,ISO,GMT0.
– Ole V.V.
Aug 23 at 9:58


GMT0


,ISO,GMT0





I have reproduced. On Java 1.8.0_131 I get the same exception as you report. On Java 9 I don’t.
– Ole V.V.
Aug 23 at 10:20






Indeed it's a bug in the JVM bugs.java.com/bugdatabase/view_bug.do?bug_id=8138664, seems not fixed in 8
– Nicolas Henneaux
Aug 23 at 10:21






Workarounds include: (1) Move to Java 9 or later (well, that’s not always a walk in the park, though). (2) Set default to GMT or UTC (without the 0). There are probably more…
– Ole V.V.
Aug 23 at 10:28



GMT


UTC


0




2 Answers
2



As you noticed in the comments, that's a bug in JDK 8, fixed only in versions >= 9.



If you're using JDK 8 and can't/won't upgrade it, there's a workaround. You can treat the "GMT" part as a literal (the text "GMT" itself) and consider the 0 as the offset seconds, using the respective ChronoField:


"GMT"


ChronoField


DateTimeFormatter zoneParser = new DateTimeFormatterBuilder()
// text "GMT"
.appendLiteral("GMT")
// offset seconds
.appendValue(ChronoField.OFFSET_SECONDS)
.toFormatter();
System.out.println(zoneParser.parse("GMT0"));



Keep in mind that this works only for offset zero. For any other values (such as "GMT2" or "GMT-2") this won't work, because it'll consider the values "2" and "-2" as seconds, but they actually mean "hours".



Well, JDK 8 also can't handle one-digit offsets, and it always requires a signal, either + or -. So "GMT2" and "GMT-2" won't work with the current API.


+


-



There's a harder alternative, though: create your own TemporalField, representing "offset hours". All the details about how to do it are in the documentation, and I'm not sure if all methods are correctly implemented - I'm just sure about isSupportedBy, getFrom and adjustInto, the others maybe need some improvement/adjustment:


TemporalField


isSupportedBy


getFrom


adjustInto


public class OffsetHours implements TemporalField

@Override
public TemporalUnit getBaseUnit()
return ChronoUnit.HOURS;


@Override
public TemporalUnit getRangeUnit()
return ChronoUnit.FOREVER;


@Override
public ValueRange range()
return ValueRange.of(ZoneOffset.MIN.getTotalSeconds() / 3600, ZoneOffset.MAX.getTotalSeconds() / 3600);


@Override
public boolean isDateBased()
return false;


@Override
public boolean isTimeBased()
return true;


@Override
public boolean isSupportedBy(TemporalAccessor temporal)
return temporal.isSupported(ChronoField.OFFSET_SECONDS);


@Override
public ValueRange rangeRefinedBy(TemporalAccessor temporal)
ValueRange rangeInSecs = temporal.range(ChronoField.OFFSET_SECONDS);
return ValueRange.of(rangeInSecs.getMinimum() / 3600, rangeInSecs.getMaximum() / 3600);


@Override
public long getFrom(TemporalAccessor temporal)
return temporal.getLong(ChronoField.OFFSET_SECONDS) / 3600;


@Override
public <R extends Temporal> R adjustInto(R temporal, long newValue)
return (R) temporal.with(ChronoField.OFFSET_SECONDS, newValue * 3600);




Now you create an instance of this field and use it in your parser:


// the new field
OffsetHours offsetHoursField = new OffsetHours();
DateTimeFormatter zoneParser = new DateTimeFormatterBuilder()
// text "GMT"
.appendLiteral("GMT")
// offset hours
.appendValue(offsetHoursField)
.toFormatter();



I also recommend creating a TemporalQuery to convert the parsed result to a ZoneOffset:


TemporalQuery


ZoneOffset


// get hours and create offset from hours value
TemporalQuery<ZoneOffset> getOffsetFromHours = temporal ->
return ZoneOffset.ofHours((int) temporal.getLong(offsetHoursField));
;



Now you can parse it:


ZoneOffset offsetZero = zoneParser.parse("GMT0", getOffsetFromHours);
ZoneOffset offsetTwo = zoneParser.parse("GMT2", getOffsetFromHours);
ZoneOffset offsetMinusTwo = zoneParser.parse("GMT-2", getOffsetFromHours);



You can improve it letting the OffsetHours field to be a static instance (or maybe an enum), so you don't need to create it all the time.


OffsetHours


enum





I believe that the 0 in GMT0 refers to an offset in hours, not seconds. As long as we’re sure it’s 0, it doesn’t matter, of course, but then we absolutely need to check this assumption explicitly.
– Ole V.V.
Aug 24 at 6:40


0


GMT0



Since Java 8, the ZoneId is responsible for handling timezone names and aliases according to IANA Time Zone Database (often referred as TZ DB, zdata). In particular, ZoneId.html#of is used for that conversion.



Different TZ DB versions are shipped by Oracle with different JREs: see tzdata versions and overview of how to update tzdata



The list of TZ DB identifiers is available on wikipedia (TZ column), as for release release 2017c, GMT0 is already listed as deprecated in there, and canonical name for this time zone is Etc/GMT


GMT0


Etc/GMT



Despite there is no explicit instructions to use only canonical TZ names, it can be a best practice however, as deprecated aliases may be removed from further tzdata distributions (and thus from Zone.of support) without any special notice






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

𛂒𛀶,𛀽𛀑𛂀𛃧𛂓𛀙𛃆𛃑𛃷𛂟𛁡𛀢𛀟𛁤𛂽𛁕𛁪𛂟𛂯,𛁞𛂧𛀴𛁄𛁠𛁼𛂿𛀤 𛂘,𛁺𛂾𛃭𛃭𛃵𛀺,𛂣𛃍𛂖𛃶 𛀸𛃀𛂖𛁶𛁏𛁚 𛂢𛂞 𛁰𛂆𛀔,𛁸𛀽𛁓𛃋𛂇𛃧𛀧𛃣𛂐𛃇,𛂂𛃻𛃲𛁬𛃞𛀧𛃃𛀅 𛂭𛁠𛁡𛃇𛀷𛃓𛁥,𛁙𛁘𛁞𛃸𛁸𛃣𛁜,𛂛,𛃿,𛁯𛂘𛂌𛃛𛁱𛃌𛂈𛂇 𛁊𛃲,𛀕𛃴𛀜 𛀶𛂆𛀶𛃟𛂉𛀣,𛂐𛁞𛁾 𛁷𛂑𛁳𛂯𛀬𛃅,𛃶𛁼

Edmonton

Crossroads (UK TV series)