4

So I have been looking up on how to properly parse an incoming datetime, problem is that this string contains the zone as well which apparently can't be parsed for some reason. To give an example of the incoming date time string: 2021-10-05T10:00:00.0000000

Now I tried to the following:

var dateTimeString = "2021-10-05T10:00:00.0000000"
var formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
var date = LocalDate.parse(dateTimeString, formatter)

I tried replacing Z with nothing and with ZZZZ, but that didn't work I assume it doesn't work because of the plus or minus sign not being present. FYI, I receive the date in this format because of the Microsoft Graph API when retrieving the calendar events.

Any idea as to how this date should be formatted properly?

Edit: This comes from Microsoft Graph. Basically they give like a date as an object:

"start": {
    "dateTime": "2021-10-05T10:00:00.0000000",
    "timeZone": "UTC"
}

This is the page of the documentation that explains this date object: dateTimeTimeZone resource type.

Update:

I was finally able to solve this date issue, what I did was the following:

var inputDateTime = "2021-10-05T10:00:00.0000000"
var inputTimeZone = "UTC"
var zonedDateTime = ZonedDateTime.parse(
    inputDateTime,
    DateTimeFormatter.ISO_DATE_TIME.withZone(ZoneId.of(inputTimeZone))
).withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime()

This way, the date would be converted correctly to the right time zone and to the right Date/Time.

Billy Cottrell
  • 443
  • 5
  • 20
  • 2
    What exactly are you trying to parse with `Z`? I cannot see any zone-offset in `"2021-10-05T10:00:00.0000000"`. – deHaar Oct 05 '21 at 10:51
  • timezone is the last 4 digits – Billy Cottrell Oct 05 '21 at 10:53
  • 1
    There's no plus and no minus sign, how would this resolve to a valid time zone? Ok, `0000` would be UTC, but what about other zones? – deHaar Oct 05 '21 at 10:54
  • 1
    I know but this comes from Microsoft Graph it's not something I am making up. Basically they give like a date as an object: `"start": {"dateTime": "2021-10-05T10:00:00.0000000", "timeZone": "UTC"}` – Billy Cottrell Oct 05 '21 at 10:57
  • This is the page of the documentation that explains this date object: https://learn.microsoft.com/en-us/graph/api/resources/datetimetimezone?view=graph-rest-1.0 – Billy Cottrell Oct 05 '21 at 11:00
  • That does not look like a standard representation, I really doubt the last 4 digits represent a zone, you even get the zone separately as `String`. Since it's 0 here, you could just parse it without a zone, but I wouldn't do. – deHaar Oct 05 '21 at 11:00
  • Do you have an example that is not in UTC? Oh, and **where in that documentation** is the talk about the last 4 digits? I cannot find it... – deHaar Oct 05 '21 at 11:01
  • No otherwise I would have been sure if the last 4 digits actually represent time zone offset. – Billy Cottrell Oct 05 '21 at 11:03

4 Answers4

3

As I can see from docs you've provided https://learn.microsoft.com/en-us/graph/api/resources/datetimetimezone?view=graph-rest-1.0

dateTime    String  A single point of time in a combined date and time representation ({date}T{time}; for example, 2017-08-29T04:00:00.0000000).
timeZone    String  Represents a time zone, for example, "Pacific Standard Time". See below for more possible values.

dateTime object has no zone encoded. And all 7 zeroes represent fractions of a second. In such case it's regular ISO_DATE_TIME and you don't need to create your own formatter.

The following code should work

var dateTimeString = "2021-10-05T10:00:00.0000000"
var date = LocalDate.parse(dateTimeString, DateTimeFormatter.ISO_DATE_TIME)
geobreze
  • 2,274
  • 1
  • 10
  • 15
  • Thank you for this, first time I encountered such a format, usually dates only have 3 digits to represent the fractions of seconds, so I assumed the other 4 digits must have been the time zone offset – Billy Cottrell Oct 05 '21 at 11:09
2

For the case that the last 4 digits in your example String are not representing a time zone:

Parse it without a formatter (no need since it would be perfect ISO standard if the last 4 digits are just additional fractions of second), but also consider the time zone you get with the JSON:

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

fun main() {
    // the time String from JSON
    var dateTimeString = "2021-10-05T10:00:00.0000000"
    // the zone from JSON (may not always work, but does with UTC)
    var timeZone = "UTC"
    // create the zone from the timezone String
    var zoneId = ZoneId.of(timeZone)
    // then parse the time String using plain LocalDateTime and add the zone afterwards
    var zdt: ZonedDateTime = LocalDateTime.parse(dateTimeString).atZone(zoneId)
    // print some results
    println("Full ZonedDateTime: ${zdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME)}")
    println("(Local)Date only:   ${zdt.toLocalDate()}")
}
Full ZonedDateTime: 2021-10-05T10:00:00Z[UTC]
(Local)Date only:   2021-10-05

Please note that parsing the time zones currently supported by Windows won't work this easy (except from UTC), but the time zones supported by the calendar API are (mostly) sufficient for the creation of a java.time.ZoneId.

deHaar
  • 17,687
  • 10
  • 38
  • 51
2

Your Date-Time string does not have timezone information

Your Date-Time string, 2021-10-05T10:00:00.0000000 does not have timezone information. The .0000000 represents the fraction of a second and as of now, java.time is capable of handling up to 9 digits of precision (i.e. nanosecond of precision).

Since it does not have timezone information, it represents a local Date-Time and hence should be parsed into LocalDateTime.

You do not need a DateTimeFormatter for your Date-Time string

The modern Date-Time API is based on ISO 8601 and does not require using a DateTimeFormatter object explicitly as long as the Date-Time string conforms to the ISO 8601 standards. Your Date-Time string is already in the ISO 8601 format.

Demo:

import java.time.LocalDateTime;

public class Main {
    public static void main(String args[]) {
        var dateTimeString = "2021-10-05T10:00:00.0000000";
        var ldt = LocalDateTime.parse(dateTimeString);
        System.out.println(ldt);
    }
}

Output:

2021-10-05T10:00

ONLINE DEMO

How to get ZonedDateTime out of the LocalDateTime instance?

You can use LocalDateTime#atZone to convert attach a ZoneId to a LocalDateTime resulting into a ZonedDateTime.

ZonedDateTime zdt = ldt.atZone(ZoneId.of("Etc/UTC"));

Note: If you need an Instant, you can get it from this instant of ZonedDateTime e.g.

Instant instant = zdt.toInstant();

An Instant represents an instantaneous point on the timeline, normally represented in UTC time. The Z in the output is the timezone designator for a zero-timezone offset. It stands for Zulu and specifies the Etc/UTC timezone (which has the timezone offset of +00:00 hours).

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


* If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring. Note that Android 8.0 Oreo already provides support for java.time.

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
1

As a supplement: Your documentation mentions Pacific Standard Time as a time zone string that may come as part of your MS Graph dateTimeTimeZone. I don’t think the other answers can handle that one, so I should like to show how you do handle it in Java.

private static final DateTimeFormatter ZONE_FORMATTER
        = DateTimeFormatter.ofPattern("zzzz", Locale.ENGLISH);

static ZoneId parseZone(String timeZoneString) {
    try {
        return ZoneId.from(ZONE_FORMATTER.parse(timeZoneString));
    } catch (DateTimeParseException dtpe) {
        return ZoneId.of(timeZoneString);
    }
}

I believe that this handles the strings mentioned in the documentation and also UTC from your question. I am demonstrating just three different ones:

    String[] msTimeZoneStrings = { "UTC", "Pacific Standard Time", "Pacific/Honolulu" };
    for (String zoneString : msTimeZoneStrings) {
        ZoneId zid = parseZone(zoneString);
        System.out.format("%-21s parsed to %s%n", zoneString, zid);
    }

Output:

UTC                   parsed to UTC
Pacific Standard Time parsed to America/Los_Angeles
Pacific/Honolulu      parsed to Pacific/Honolulu

In the formatter I use zzzz is for time zone name like Pacific Standard Time. My parse method tries this formatter first. It fails for UTC and Pacific/Honolulu. When the DateTimeParseException is caught, the method next tries the ZoneId.of method also used in the other answers. It handles UTC and all the time zone ID sin region/city format mentioned in the documentation.

Combine the ZoneId obtained from my method with the parsed LocalDateTime to get a ZonedDateTime the way that deHaar is already showing in their answer.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161