Java 8 DateTime: Part 2 The Parsing

Sai Peddy
5 min readFeb 18, 2018

--

It’s time to parse — source

Perfect you are just in time for the code samples. I will assume that you came here from part 1, Java 8 DateTime, and that you know all the information written in the previous post 😛. This post will include code samples on how to parse and use timestamps!

Timestamp Use Cases

Parsing Timestamps:

In order to parse timestamps, you will need to create an instance of a DateTimeFormatter object and specify the pattern of your timestamp. Details in the docs.

Let’s go through this pattern — yyyy-MM-dd'T'HH:mm:ss.SSSVV — as we will use it in the following code sample. I will break down each segment of the pattern and what that segment represents in a timestamp — once again some segments may be straight forward.

-:. all represent the characters themselves within the timestamp
'T' The T is surrounded by single quotes because it is just a character and does not mean anything to the timestamp. Regular strings/characters need to be surrounded by single quotes to successfully ignore them.
yyyy represents the 4-digit year — beware YYYY represents something slightly different and may only cause a problem in rare circumstances.
MM represents the month — this can represent a number or text version of the month, depending on the number of M’s you use. e.g 7 vs. 07 vs. Jul vs. July
dd represents 2-digit days — days can also be single digits, refer to the code below to see how to handle both single digit and double digit days
HH represents “happy hour” — jk this represents just the regular hours in timestamp
mm represents the minutes in the timestamp
ss represents the seconds in the timestamp
SSS in the documentation this is referred to as fractions of seconds — but also microseconds. Depending on your granularity add or delete S’s
VV represents the zone-id — This can be as simple as the Z at the end of timestamps to represent zulu timezone or can also include offsets and timezone information.

Straight forward parsing — you know the format of your timestamp, and the zone is provided:

String timestampStringToParse = “2018–01–051T10:23:12.123Z”/*****OPTION 1:******///Get Your mind out of the gutter
DateTimeFormatter dtf= new DateTimeFormatterBuilder().appendPattern(“yyyy-MM-dd’T’HH:mm:ss.SSSVV”).toFormatter();
Instant timestamp = Instant.from(dtf.parse(timestampStringToParse));/*****OPTION 2:******/Instant timestamp = Instant.from(DateTimeFormatter.ISO_INSTANT.parse(timestampStringToParse));/*****OPTION 3:******/Instant timestamp = Instant.parse(timestampStringToParse);

As you can see in the above code blocks, there are 3 potential methods to parse the timestamp with. The last one being the simplest, requires you to already have a valid Instant formatted timestamp. The second method can be used with a variety of existing formats, which can be found in the DateTimeFormatter documentation above. The first is the most flexible — allowing you to define your own pattern. Documentation on what each character in the pattern means is also available in the link above.

In the case you are missing Zone information in your timestamps, you must provide a default zone into the datetime formatter as done below:

String timestamp_Sample_3 = “1970–01–01T00:00:00.123”;DateTimeFormatter dtf = new DateTimeFormatterBuilder().appendPattern(“yyyy-MM-dd’T’HH:mm:ss.SSS”).toFormatter().withZone(ZoneOffset.UTC);

In the case you are missing a year:

String timestamp = “Oct 13 09:32:28”private static DateTimeFormatter dtf = new DateTimeFormatterBuilder().appendPattern(“MMM dd HH:mm:ss”).parseDefaulting(ChronoField.YEAR, OffsetDateTime.now().with(ZoneOffset.UTC).getYear()).toFormatter().withZone(ZoneOffset.UTC);

Quite often, timestamps may not contain a year — at which point, one option is to default to the current year at the time of parsing.

It is not uncommon to have to handle varying styles of timestamps. The difference in styles may be as simple as sending both single and double digit days vs. just double digit days. Varying styles will require you to use optionals in your formatter.

String timestamp = “Oct 13 09:32:28”String timestamp2 = “Oct 3 09:32:28”Private static DateTimeFormatter datetimeFormatter=new DateTimeFormatterBuilder().appendPattern(“MMM [ d][dd] HH:mm:ss”).parseDefaulting(ChronoField.YEAR, OffsetDateTime.now().with(ZoneOffset.UTC).getYear()).toFormatter().withZone(ZoneOffset.UTC);

Notice the square brackets in the appendPattern section? It essentially allows the format inside the brackets to be optional. Therefore here you will successfully match a date with just one digit days (and an extra space) as well as two digit days. This can be expanded to more complex formats. Often times, being able to support multiple formats using just one formatter.

Changing the timestamp

I’m sure you’ve realized that in the previous example if that code was to run on the day of writing this (Jan X 2018), the date would be October 2018…which probably is not right considering that date is in the future. If your data or system allows you to determine the right year, then it may be useful to change the year of the timestamp after parsing. This can be expanded to other aspects of the timestamp as well.

String timestamp_Sample_3 = “2017–01–01T00:00:00”;DateTimeFormatter dtf = new DateTimeFormatterBuilder().appendPattern(“yyyy-MM-dd’T’HH:mm:ss”).toFormatter().withZone(ZoneOffset.UTC);ZonedDateTime timestampWithRightYear = ZonedDateTime.parse(timestamp_Sample_3, dtf)Instant timestampFinal = ZonedDateTime.parse(timestamp, dtf).with(ChronoField.YEAR, timestampWithRightYear.getYear()).toInstant());

The above code changes the year of timestamp from current year to 2017. Other fields can also be modified similar to the example shown above. The fields that are supported by the .with() method can vary among the 3 timestamp classes. The following documentation should clarify what isSupported(): ZoneDateTime, OffsetDateTime, and Instant. But timestamps can easily be converted to Instant from either of the other two formats.

Closing notes for data producers

Best format to produce is usually the Instant format above — having the timestamp pre-converted to Zulu time. This provides a standard way for consumers to use the data. Providing local time is also usually sufficient, but zone information must be made clear to consumers. Quite often, it is not provided in the timestamp format nor verbally, leading many consumers to just guess the zone — this can lead to some interesting results, depending on the data. Lastly, it is always recommended to provide the year in all timestamps in any type of log message, etc. This allows developers to not have to guess the year, which would be correct 9/10 times (or more) excluding the edge of time (aka the new year — 12/31–1/1). It also prevents problems inherent in parsing multi-year data. Overall, the best data sources we have had do not require us to assume anything in the data and this allows us to preserve the raw logs as they are for future consumption.

--

--

Sai Peddy

Data Engineer | Love to Learn | Interested in…too many things