Reputation: 7256
I have an issue with java DataTimeFormmater
.
I feel that I missed something but cannot figure out what exactly.
String format = "yyyy-MM-dd'T'HH:mm:ss[.S]'T'zxxx";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
String date = "2017-07-05T12:28:36.4TGMT+03:00";
System.out.println(formatter.format(ZonedDateTime.now()));
System.out.println(formatter.parse(date));
The code above produces string of the current ZonedDateTime
and attempts to parse date time string with the same date formatter.
Result that it successfully produces 2017-07-05T06:07:51.0TCDT-05:00
but fails to parse 2017-07-05T12:28:36.4TGMT+03:00
My goal is to parse 2017-07-05T12:28:36.4TGMT+03:00
and come up with appropriate DateTimeFormatter
.
Upvotes: 6
Views: 8776
Reputation:
You must change the format to:
String format = "yyyy-MM-dd'T'HH:mm:ss[.S]'T'[zzz][xxx]";
Both [zzz]
and [xxx]
are in optional sections because zzz
can parse either the whole GMT+03:00
part or just the zone short name (such as CDT
), and xxx
parses only the offset part (such as -05:00
- so it's not needed if a GMT+03:00
is found).
Just reminding that formatter.parse(date)
returns a TemporalAccessor
object. If you want to create a specific type, it's better to use the class's respective parse
method:
System.out.println(ZonedDateTime.parse(date, formatter)); // 2017-07-05T12:28:36.400+03:00[GMT+03:00]
PS: the only problem with this formatter is that, when formatting, it prints all the optional sections. So, if you do something like this:
String date = "2017-07-05T12:28:36.4TGMT+03:00";
ZonedDateTime z = ZonedDateTime.parse(date, formatter);
System.out.println(formatter.format(z));
The output will be:
2017-07-05T12:28:36.4TGMT+03:00+03:00
That's because the GMT+03:00
is the result of zzz
and the second +03:00
is the result of xxx
. If you don't want this, I recomment using 2 different DateTimeFormatter
's (one for parsing, another for formatting).
Or (an "uglier" approach), use 2 different formatters:
DateTimeFormatter noGMT = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.S]'T'zzzxxx");
DateTimeFormatter gmt = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.S]'TGMT'xxx");
Then, you try to parse with the first - if you get an exception, try with the second (or you check if your input contains GMT
to know which one to use).
I personally don't like this because GMT
is part of the zone name and should't be treated as a literal. But in the end, you get a ZonedDateTime
with the correct offset, so I'm not sure of how wrong this approach is.
Please be aware that you should avoid (as possible) using the 3-letter abbreviations (like CDT
or PST
) because they are ambiguous and not standard. CDT
can be both Central Daylight Time
(UTC-05:00), Cuba Daylight Time
(UTC-04:00) or even China Daylight Time
(UTC+09:00).
Prefer, if possible, to use IANA timezones names (always in the format Continent/City
, like America/Sao_Paulo
or Europe/Berlin
). Based on that list, there are more than 40 timezones that uses (or had used somewhere in the past) the CDT
abbreviation.
CDT
works for this case because some abbreviations have default values configured, probably due to retro-compatibility reasons, but you shouldn't rely on them for all cases.
To make sure your timezone abbreviations always work (in case you can't avoid using them), you can create a formatter that uses a set of prefered zones. In this case, I'm using America/Chicago
(so, CST
or CDT
will be parsed as Chicago's timezone):
Set<ZoneId> preferedZones = new HashSet<>();
preferedZones.add(ZoneId.of("America/Chicago"));
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
// append first part of pattern (before timezone)
.appendPattern("yyyy-MM-dd'T'HH:mm:ss[.S]'T'")
// append zone name, use prefered zones (optional)
.optionalStart().appendZoneText(TextStyle.SHORT, preferedZones).optionalEnd()
// offset (optional)
.appendPattern("[xxx]")
// create formatter
.toFormatter();
This formatter works the same way as above, for both your inputs (with and without GMT
), and uses America/Chicago
as default timezone when CDT
is in the input. You can add as many zones you want in the set, according to your use cases.
Just reminding that this formatter has the same issues regarding the output (it prints all optional sections), as already stated above.
Upvotes: 7
Reputation: 340230
OffsetDateTime.parse(
"2017-07-05T12:28:36.4TGMT+03:00".replace( "TGMT" , "" )
)
Your format is bizarre, like a strange misunderstanding or corruption of the standard ISO 8601 formats.
If all your inputs have “TGMT" in the last part, strip that out to comply with ISO 8601.
The java.time classes use standard formats by default when parsing/generating strings. So no need to define a formatting pattern.
OffsetDateTime odt = OffsetDateTime.parse( "2017-07-05T12:28:36.4TGMT+03:00".replace( "TGMT" , "" ) ) ;
And never use the 3-4 letter pseudo-timezones like CMT
, EST
, and IST
. These are not actual time zones, not standardized, and not even unique(!). Real time zone names are in the format of continent/region
such as America/Montreal
or Pacific/Auckland
.
Upvotes: 2