Daniel Widdis Daniel Widdis - 1 year ago 172
Java Question

Parse CIM_DateTime with milliseconds to Java Date

I am trying to convert a DateTime value retrieved from Windows Management Interface into a Java (1.7) Date. The format is specified here.

An example that I am trying to parse is

which is 2016-05-13 at 07:29:50 plus 782 milliseconds, in my local timezone (-420 minutes = UTC-7 hours). The digits after the decimal are fractional seconds; in theory up to 6 digits of microseconds, but in practice only the first 4 digits are nonzero.

I initially attempted to parse using a SimpleDateFormat specifying the three digits of milliseconds that I wanted to parse:

SimpleDateFormat cimDateFormat = new SimpleDateFormat("yyyyMMddHHmmss.SSS");
Date date = cimDateFormat.parse(s, new ParsePosition(0));

My reasoning was that specifying the three digits of milliseconds with
would stop the parsing. Unfortunately, this didn't work; many more than 782 milliseconds in the above example were added.

I eventually got it to work as desired by trimming the string to the required characters:

SimpleDateFormat cimDateFormat = new SimpleDateFormat("yyyyMMddHHmmss.S");
Date date = cimDateFormat.parse(s.substring(0, 18), new ParsePosition(0));

In this case, I only included one
for milliseconds but it parsed all three.

I can't find anything in the
javadoc that clearly explains what's going on at the end of this parsing. Specific questions:

  1. Why does it keep parsing past the specified number of digits in the

  2. Why does a single
    parse all 3 millisecond digits?

  3. Other than truncating the string like I did, is there any other way of telling the
    to stop parsing the string at the indicated position?

Answer Source

Modify the input

As far as I know, none of the three common date-time frameworks for Java (the old bundled java.util.Date/.Calendar/java.text.SimpleDateFormat classes, the Joda-Time framework, or the java.time framework built into Java 8 and later) allow for an offset-from-UTC as a total number of minutes.

As suggested by Sotirios Delimanolis, you must modify the offset-from-UTC to convert from a number of total minutes to the standard number of hours and minutes (and seconds – a possibility ignored by that odd Microsoft format). So -420 should become -07:00 or -07:00:00.


You are using the troublesome old date-time classes bundled with the earliest versions of Java. The old classes are now legacy, and have been supplanted by the java.time framework built into Java 8 and later, and largely back-ported to Java 6 & 7 and further adapted to Android.

The java.time classes have a resolution of nanoseconds, for up to nine digits of a decimal fraction of second. So no problem handling your inputs 4-6 digits of fractional second.

Our strategy has two parts: (a) Modify the input to convert that offset-from-UTC, and (b) Parse the modified input string as a date-time object.

Modify input

First we change the input from 20160513072950.782000-420 to 20160513072950.782000-07:00:00. We do this by extracting the characters trailing after the + or -, the 420 in this case.

// Modify the input to replace offset as a number of minutes to the standard format, a number of hours, minutes, and seconds.
String input = "20160513072950.782000-420";
String offsetInMinutesAsString = input.substring ( 22 );

Convert that to a long, and create a LocalTime object so that we can generate a string in the format of HH:mm:ss.

long offsetInMinutes = Long.parseLong ( offsetInMinutesAsString );
LocalTime offsetAsLocalTime = LocalTime.MIN.plusMinutes ( offsetInMinutes );
String offsetAsString = offsetAsLocalTime.format ( DateTimeFormatter.ISO_LOCAL_TIME );

Replace those trailing characters with our generated string.

String inputModified = ( input.substring ( 0 , 22 ) + offsetAsString );

Parse string to date-time object

Define a custom formatting pattern by which to parse that string into a OffsetDateTime object.

// Parse the modified input as an OffsetDateTime.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern ( "yyyyMMddHHmmss.SSSSSSZZZZZ" , Locale.US );
OffsetDateTime odt = OffsetDateTime.parse ( inputModified , formatter );

Dump to console.

System.out.println ( "input: " + input + " | inputModified: " + inputModified + " | odt: " + odt );

input: 20160513072950.782000-420 | inputModified: 20160513072950.782000-07:00:00 | odt: 2016-05-13T07:29:50.782-07:00


I strongly suggest avoiding the old date-time classes. But if you must use a java.util.Date object to interoperate with old date-time code, you can convert.

Look for new methods added to the old classes for conversion. For this conversion we use java.util.Date.from. We need to feed that conversion method a Instant object, a moment on the timeline in UTC with a resolution of nanoseconds. We can extract one from our OffsetDateTime.

Instant instant = odt.toInstant();
java.util.Date utilDate = java.util.Date.from( instant );

For more info about converting, including a nifty diagram, see my Answer to another Question. Keep in mind that we are working with only a mere offset-from-UTC in our input strings and our OffsetDateTime, not a full time zone. A time zone is an offset plus rules for handling anomalies such as Daylight Saving Time (DST). Both the Instant and the java.util.Date are in UTC (an offset of zero).