DateTimeFormatter does not parse custom date format
I have a problem with java DataTimeFormmater
. I feel like I missed something, but can't 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 above code creates the current string ZonedDateTime
and tries to parse the date time string with the same date formatting. The result it produces successfully 2017-07-05T06:07:51.0TCDT-05:00
, but does not parse2017-07-05T12:28:36.4TGMT+03:00
My goal is to disassemble 2017-07-05T12:28:36.4TGMT+03:00
and create an appropriate one DateTimeFormatter
.
source to share
You have to change the format:
String format = "yyyy-MM-dd'T'HH:mm:ss[.S]'T'[zzz][xxx]";
Both [zzz]
and [xxx]
are non-binding sections, because it zzz
can analyze whole or part GMT+03:00
, or just a short name of the zone (for example CDT
), and xxx
analyzes only the offset (for example, -05:00
- is therefore not required if found GMT+03:00
).
As a reminder, what the formatter.parse(date)
object returns TemporalAccessor
. If you want to create a specific type, it is better to use the appropriate class parse
:
System.out.println(ZonedDateTime.parse(date, formatter)); // 2017-07-05T12:28:36.400+03:00[GMT+03:00]
PS: The only problem with this formatting is that it prints all optional sections when formatted. 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
This is because it GMT+03:00
is the result zzz
and the second +03:00
is the result xxx
. If you don't want that, I repeat using 2 different ones DateTimeFormatter
(one for parsing, the other for formatting).
Or ("ugly" approach), use 2 different formatting:
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 the first - if you get an exception, try with the second (or you check to see if your input contains GMT
to see which one to use).
I personally don't like this because it GMT
is part of the zone name and should not be considered a literal. But you end up ZonedDateTime
with the correct offset, so I'm not sure how wrong this approach is.
Abbreviations of time zones
Remember that you should avoid (as much as possible) the use of three-letter abbreviations (for example, CDT
or PST
) as they are ambiguous and not standard . CDT
can be like Central Daylight Time
(UTC-05: 00), Cuba Daylight Time
(UTC-04: 00) or even China Daylight Time
(UTC + 09: 00).
Prefer to use IANA time zone names if possible (always in the format Continent/City
such as America/Sao_Paulo
or Europe/Berlin
). Based on this list, there are over 40 time zones that use (or have used somewhere in the past) the abbreviation CDT
.
CDT
works for this case because some abbreviations have defaults, probably for retro compatibility reasons, but you shouldn't rely on them for all cases.
To make your timezone abbreviations always work (in case you can't avoid using them), you can create a formatter that uses a set of preferred zones. In this case I am using America/Chicago
(so that CST
or CDT
will be parsed as timezone in Chicago):
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 as above for both inputs (with and without GMT
) and uses the America/Chicago
default timezone as the CDT
input. You can add as many zones as you want to install according to your use cases.
As a reminder, this formatter has the same output issues (it prints all optional sections) as stated above.
TL; DR
OffsetDateTime.parse(
"2017-07-05T12:28:36.4TGMT+03:00".replace( "TGMT" , "" )
)
More details
Your format is strange, like a strange misunderstanding or distortion of the standard ISO 8601 formats .
If all of your inputs have "TGMT" in the last part, align it according to ISO 8601.
The java.time classes use standard default formats when parsing / generating strings. Therefore, there is no need to define a formatting template.
OffsetDateTime odt = OffsetDateTime.parse( "2017-07-05T12:28:36.4TGMT+03:00".replace( "TGMT" , "" ) ) ;
And never use pseudo-random letters 3-4 like CMT
, EST
and IST
. These are not real time zones, not standardized, or even unique (!). Live names are in a format continent/region
such as America/Montreal
or Pacific/Auckland
.
source to share