Parse CIM_DateTime with milliseconds to Java Date

断了今生、忘了曾经 提交于 2021-02-05 06:09:54

问题


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

An example that I am trying to parse is 20160513072950.782000-420 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 SSS 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 S for milliseconds but it parsed all three.

I can't find anything in the SimpleDateFormat 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 SSS case?
  2. Why does a single S parse all 3 millisecond digits?
  3. Other than truncating the string like I did, is there any other way of telling the SimpleDateFormat to stop parsing the string at the indicated position?

回答1:


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.

java.time

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 by the ThreeTen-Backport project 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

Convert

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).




回答2:


While I accepted Basil Bourque's answer and have, for the past two years, kept the threetenbp dependency in my project, I managed to throw together a quick parsing using Calendar that does the same thing and works for my needs. Add exception checking, etc., but this gives an identical result using JDK1.1 code, and may be useful for others. Note, however, that this approach is not thread safe, and the accepted answer should be preferred.

    Calendar c = Calendar.getInstance();
    c.set(Calendar.YEAR, Integer.parseInt(cimDate.substring(0, 4)));
    // Calendar uses 0-indexed months
    c.set(Calendar.MONTH, Integer.parseInt(cimDate.substring(4, 6)) - 1);
    c.set(Calendar.DATE, Integer.parseInt(cimDate.substring(6, 8)));
    c.set(Calendar.HOUR, Integer.parseInt(cimDate.substring(8, 10)));
    c.set(Calendar.MINUTE, Integer.parseInt(cimDate.substring(10, 12)));
    c.set(Calendar.SECOND, Integer.parseInt(cimDate.substring(12, 14)));
    c.set(Calendar.MILLISECOND, Integer.parseInt(cimDate.substring(15, 18)));
    c.setTimeZone(TimeZone.getTimeZone("UTC"));
    // Offset from UTC is in minutes
    return c.getTimeInMillis() + Integer.parseInt(cimDate.substring(22)) * 60_000L;*


来源:https://stackoverflow.com/questions/37308672/parse-cim-datetime-with-milliseconds-to-java-date

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!