28

I'm trying to build a DateTimeFormatter that can accepts offset with colon or offset without colon.

Is there a way to pass this test :

DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[X]");
dateTimeFormatter.parse("2015-01-28T10:21:44+0100"); // OK
dateTimeFormatter.parse("2015-01-28T10:21:44+01:00"); // KO
Tunaki
  • 132,869
  • 46
  • 340
  • 423
Nelson G.
  • 5,145
  • 4
  • 43
  • 54
  • 1
    The colon you consider to be optional is fixed part of the zone offset part. You can only choose between pattern letters X (without colon) or XXX (with colon). That means the whole zone offset is optional, not just the colon inside. – Meno Hochschild Jan 06 '16 at 16:23
  • I agree, it should work. But when I use [X] it expects an offset without a colon and when I use [XXX] it expects an offset with a colon. I can't use both together. – Nelson G. Jan 06 '16 at 16:23
  • According the Javadoc: with symbol X theses examples are given : Z; -08; -0830; -08:30; -083015; -08:30:15; – Nelson G. Jan 06 '16 at 16:28
  • 3
    Please read the javadoc-section "Offset X and x". It precisely describes which count of letters X,x is associated with which format representation. So we have: XX (without colon) and XXX (with colon). X (without colon) makes the minute part optional. – Meno Hochschild Jan 06 '16 at 16:49
  • i'm getting java.time.format.DateTimeParseException: Text '2015-01-28T10:21:44+01:00' could not be parsed, unparsed text found at index 22 under openjdk version "1.8.0_312" – Simon Logic Oct 19 '22 at 08:02

2 Answers2

39

This: yyyy-MM-dd'T'HH:mm:ss[XXX][X] seems to work.

DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[XXX][X]");
dateTimeFormatter.parse("2015-01-28T10:21:44+0100");
dateTimeFormatter.parse("2015-01-28T10:21:44+01:00");
Cœur
  • 37,241
  • 25
  • 195
  • 267
Titus
  • 22,031
  • 1
  • 23
  • 33
35

Here is the ultimate pattern matching for pretty everything looking like an ISO string date !

"[yyyyMMdd][yyyy-MM-dd][yyyy-DDD]['T'[HHmmss][HHmm][HH:mm:ss][HH:mm][.SSSSSSSSS][.SSSSSS][.SSS][.SS][.S]][OOOO][O][z][XXXXX][XXXX]['['VV']']"

It works for the following list of tests except for the two in comments but I still don't know why...

    ZonedDateTime dt = DateTimeUtils.parse("2016-10-27T16:36:08.993+02:00:00[Europe/Paris]");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08.993+02:00[Europe/Paris]");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08.993+020000[Europe/Paris]");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08.993+0200[Europe/Paris]");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08.993+0000[Europe/Paris]");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08.993Z[Europe/Paris]");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08.993GMT+1[Europe/Paris]");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08.993UTC[Europe/Paris]");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08.993PST[Europe/Paris]");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08.993[Europe/Paris]");

    dt = DateTimeUtils.parse("2016-10-27T16:36:08+02:00[Europe/Paris]");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08+020000[Europe/Paris]");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08+0200[Europe/Paris]");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08+0000[Europe/Paris]");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08Z[Europe/Paris]");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08GMT+1[Europe/Paris]");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08UTC[Europe/Paris]");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08PST[Europe/Paris]");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08[Europe/Paris]");

    dt = DateTimeUtils.parse("2016-10-27T16:36:08.993+02:00:00");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08.993+02:00");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08.993+020000");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08.993+0200");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08.993+0000");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08.993Z");
    //dt = DateTimeUtils.parse("2016-10-27T16:36:08.993GMT+1");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08.993UTC");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08.993PST");

    dt = DateTimeUtils.parse("2016-10-27T16:36:08.993");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08.000993");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08.000000993");

    dt = DateTimeUtils.parse("2016-10-27T16:36:08+02:00:00");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08+02:00");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08+020000");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08+0200");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08+0000");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08Z");
    //dt = DateTimeUtils.parse("2016-10-27T16:36:08GMT+1");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08UTC");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08PST");
    dt = DateTimeUtils.parse("2016-10-27T16:36:08");

    dt = DateTimeUtils.parse("2016-100T16:36:08Z");
    dt = DateTimeUtils.parse("2016-100T16:36.1Z");
    dt = DateTimeUtils.parse("2016-10-27");

    dt = DateTimeUtils.parse("20161223T163608");
    dt = DateTimeUtils.parse("20161223T1636");

And the parse method itself :

public static ZonedDateTime parse(CharSequence text) {
    TemporalAccessor temporalAccessor = LOOSE_ISO_DATE_TIME_ZONE_PARSER.parseBest(text, ZonedDateTime::from, LocalDateTime::from, LocalDate::from);
    if (temporalAccessor instanceof ZonedDateTime) {
        return ((ZonedDateTime) temporalAccessor);
    }
    if (temporalAccessor instanceof LocalDateTime) {
        return ((LocalDateTime) temporalAccessor).atZone(ZoneId.systemDefault());
    }
    return ((LocalDate) temporalAccessor).atStartOfDay(ZoneId.systemDefault());
}
Philippe B.
  • 351
  • 3
  • 3
  • I'm assuming it doesn't work for the ones in comment because you haven't catered for the "+1" after GMT. Granted, I don't recognize those expressions, but it seems there is no catering for the "+"-sign anywhere? – junkfoodjunkie Oct 31 '16 at 12:06
  • It is supposed to recognize the GMT+1 as a whole, as it does for 2016-10-27T16:36:08.993GMT+1[Europe/Paris] though. – Philippe B. Oct 31 '16 at 13:14
  • Okay - Europe/Paris ends with text/the caret, though, while the ones not working ends with "+1" - I might be something there :) – junkfoodjunkie Oct 31 '16 at 13:54
  • 2
    ALMOST PERFECT, BUT, when offset comes without a colon, the parse does not end with error, but the offset is not recognized, here the output for "2016-10-27T16:36:08.993+020000" ==> 2016-10-27T16:36:08.993Z, I guess this is a parser problem, investigating a bit more. – tonio Feb 13 '17 at 18:09
  • Here is [DateTimeFormatterBuilder](https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatterBuilder.html#appendPattern-java.lang.String-) document. You can get that what's the mean of `XXXX` or `VV`, and so on. – Cnfn Oct 29 '18 at 12:51
  • Thanks @PhilippeB. – Nelson G. Nov 08 '18 at 21:04
  • 2016-10-27T16:36:08.993Z , unable to parse this format. Any solutions?@PhilippeB. @tonio – sSD Nov 06 '19 at 06:21