Are time zone designators- "T" and "Z" case sensitive in ISO8601 format?

1.3k views Asked by At

I am using following ISO8601 format:

YYYY-MM-DDThh:mm:ssZ

And I used OffsetDateTime.parse() to parse this format. I was able to parse date-time by passing t (instead of T) and z (instead of Z) here.

So can anyone tell if it is allowed in ISO8601 or is it missed only in parsing logic?

4

There are 4 answers

2
Masklinn On BEST ANSWER

So can anyone tell if it is allowed in ISO8601 or is it missed only in parsing logic?

I don't think it's valid to produce them though I guess it's fine (though not great) that the parser allows it.

The normative EBNF I have access to (8601-1 DIS Annex A) uses only uppercase latin letter for all designators, whether they be Z, T, W, R, P, Y, M, D, H, M, or S, and unlike (non-A) BNF, as far as I know EBNF terminals are case sensitive.

0
Nicholas Carey On

Just for the record, ISO 8601:2004 Data elements and interchange formats — Information interchange —Representation of dates and times, page 11 says (and I quote):

NOTE 1 In date and time representations lower case characters may be used when upper case letters are not available.

The designators to which this would apply are enumerated on page 11, ibid:

  • P: the duration designator, the use of P being based on the "historical" use of the term "period" for duration)
  • R: the recurring time interval designator
  • T: the time designator
  • W: the week designator
  • Z: the UTC designator (the use of Z being based on the common military, marine, and aviation usage of calling UTC "Zulu time").
0
Anonymous On

TL;DR

While I don’t know about ISO 8601 on this point, it is documented that Java’s one-arg OffsetDateTime.parse(CharSequence) allows both upper and lower case T and Z.

Tracing through the docs

The documentation says:

The string must represent a valid date-time and is parsed using DateTimeFormatter.ISO_OFFSET_DATE_TIME.

The documentation of DateTimeFormatter.ISO_OFFSET_DATE_TIME says that the format consists of:

  • The ISO_LOCAL_DATE_TIME
  • The offset ID. … Parsing is case insensitive.

The last sentence allows upper case Z and lower case z for offset. The documentation of ISO_LOCAL_DATE_TIME says about the T:

  • The letter 'T'. Parsing is case insensitive.

So this allows upper case T and lower case t.

We should not trust Java to tell us the truth about the standard. It seems that the ISO 8601 standard is a secret unless you pay for a copy (a funny way to persuade people to follow a standard IMHO). There’s a fine Wikipedia article on the standard. It gives the letters in upper case and does not mention whether they are allowed in lower case too.

Links

0
Arvind Kumar Avinash On

Z is not the same as z.

DateTimeFormatter evaluates the former as zone-offset while the later as time-zone name.

Another example can be M which it uses for month-of-year, and m which it uses for minute-of-hour.

The symbol for the components of date, time, timezone etc. are case-sensitive. Check DateTimeFormatter to learn more about these symbols.

A quick demo:

import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

public class Main {
    public static void main(String[] args) {
        ZonedDateTime odt = ZonedDateTime.now(ZoneId.of("Asia/Calcutta"));
        System.out.println(DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ssZ").format(odt));
        System.out.println(DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ssz").format(odt));
    }
}

Output:

2020-12-22T00:14:44+0530
2020-12-22T00:14:44IST

A zone offset is not the same as timezone. A timezone has an ID in the form, Continent/City e.g. Asia/Calcutta whereas a zone offset is represented in hours and minutes which tells how many hours and minutes the date & time of a place is offset from UTC date & time. Thus, many timezone IDs can have the same zone offset. In other words, a zone offset can be derived from the timezone ID but the converse is not possible e.g. in the following demo, OffsetDateTime will be able to determine the zone offset from the timezone ID of Asia/Calcutta but trying to get the timezone name (as in the above example) using z will fail.

import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;

public class Main {
    public static void main(String[] args) {
        OffsetDateTime odt = OffsetDateTime.now(ZoneId.of("Asia/Calcutta"));
        System.out.println(DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ssZ").format(odt));
        System.out.println(DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ssz").format(odt));
    }
}

Output:

2020-12-22T00:30:40+0530
Exception in thread "main" java.time.DateTimeException: Unable to extract ZoneId from temporal 2020-12-22T00:30:40.865087+05:30
    at java.base/java.time.format.DateTimePrintContext.getValue(DateTimePrintContext.java:289)
    at java.base/java.time.format.DateTimeFormatterBuilder$ZoneTextPrinterParser.format(DateTimeFormatterBuilder.java:4072)
    at java.base/java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.format(DateTimeFormatterBuilder.java:2341)
    at java.base/java.time.format.DateTimeFormatter.formatTo(DateTimeFormatter.java:1843)
    at java.base/java.time.format.DateTimeFormatter.format(DateTimeFormatter.java:1817)
    at Main.main(Main.java:9)

And I used "OffsetDateTime.parse()" (in java) to parse this format. I was able to parse date time by passing "t" (instead of "T") and "z" (instead of "Z") here.

I have already explained about Z and z. Let's focus on T and t. If you observe carefully, you will find that I've used single quotes around T i.e. 'T' which makes it a string literal to be used inside the date-time string. It means that it can be anything e.g. 't' or 'Foo' or 'Bar'. As long as the literals in the DateTimeFormatter matches in the same case with that in the date-time string, it works without any problem. I've shown it in the following demo:

import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;

public class Main {
    public static void main(String[] args) {
        String strDateTime = "2020-12-22T00:45:50+05:30";

        // The given string is already in the format which is use by OffsetDateTime for
        // parsing without a DateTimeFormatter
        OffsetDateTime odt = OffsetDateTime.parse(strDateTime);

        // Let's try to parse it using different types of DateTimeFormatter instances
        System.out.println(OffsetDateTime.parse(strDateTime, DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ssXXX")));
        
        //The following line will fail as the literal does not match case-wise
        //System.out.println(OffsetDateTime.parse(strDateTime, DateTimeFormatter.ofPattern("uuuu-MM-dd't'HH:mm:ssXXX")));
        
        strDateTime = "2020-12-22t00:45:50+05:30";// Now, DateTimeFormatter with 't' will work successfully
        System.out.println(OffsetDateTime.parse(strDateTime, DateTimeFormatter.ofPattern("uuuu-MM-dd't'HH:mm:ssXXX")));
    }
}

Output:

2020-12-22T00:45:50+05:30
2020-12-22T00:45:50+05:30

Learn more about the modern date-time API from Trail: Date Time.