用毫秒解析 CIM_DateTime 到 Java 日期
Parse CIM_DateTime with milliseconds to Java Date
我正在尝试将从 Windows 管理界面检索到的 DateTime 值转换为 Java (1.7) 日期;最终到纪元以来的毫秒数。 format is specified here。
我尝试解析的一个示例是 20160513072950.782000-420
,即 2016-05-13 07:29:50 加 782 毫秒,在我的本地时区(-420 分钟 = UTC-7 小时) .小数点后的数字是小数秒;理论上最多 6 位微秒,但实际上只有前 4 位非零。
我最初尝试使用 SimpleDateFormat 指定我想要解析的三位毫秒数进行解析:
SimpleDateFormat cimDateFormat = new SimpleDateFormat("yyyyMMddHHmmss.SSS");
Date date = cimDateFormat.parse(s, new ParsePosition(0));
我的理由是用 SSS
指定毫秒的三位数会停止解析。不幸的是,这没有用;在上面的示例中添加了超过 782 毫秒。
我最终通过将字符串修剪为所需字符使其按预期工作:
SimpleDateFormat cimDateFormat = new SimpleDateFormat("yyyyMMddHHmmss.S");
Date date = cimDateFormat.parse(s.substring(0, 18), new ParsePosition(0));
在这种情况下,我只包含一个 S
毫秒,但它解析了所有三个。
我在 SimpleDateFormat
javadoc 中找不到任何内容可以清楚地解释此解析结束时发生的情况。具体问题:
- 为什么在
SSS
的情况下,它一直解析超过指定的位数?
- 为什么单个
S
解析所有 3 毫秒数字?
- 除了像我那样截断字符串之外,还有其他方法可以让
SimpleDateFormat
停止在指定位置解析字符串吗?
修改输入
据我所知,Java 的三种常见日期时间框架中的 none(旧的捆绑 java.util.Date/.Calendar/java.text.SimpleDateFormat 类、Joda-Time 框架或 Java 8 及更高版本中内置的 java.time 框架)允许从 UTC 偏移量作为总分钟数。
按照 Sotirios Delimanolis, you must modify the offset-from-UTC 的建议,将总分钟数转换为标准的小时数和分钟数(和秒数——这种可能性被奇怪的 Microsoft 格式忽略了)。所以-420
应该变成-07:00
或者-07:00:00
。
java.time
您正在使用与 Java 的最早版本捆绑在一起的麻烦的旧日期时间 类。旧的 类 现在是遗留的,并已被 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.
取代
java.time 类 的分辨率为 nanoseconds,最多九位小数秒。因此,处理您的输入 4-6 位小数秒没有问题。
我们的策略分为两部分:(a) 修改输入以转换该偏移量与 UTC,以及 (b) 将修改后的输入字符串解析为日期时间对象。
修改输入
首先我们将输入从 20160513072950.782000-420
更改为 20160513072950.782000-07:00:00
。我们通过提取 +
或 -
之后的字符来完成此操作,在本例中为 420
。
// 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 );
将其转换为 long
,并创建一个 LocalTime
对象,以便我们可以生成 HH:mm:ss
格式的字符串。
long offsetInMinutes = Long.parseLong ( offsetInMinutesAsString );
LocalTime offsetAsLocalTime = LocalTime.MIN.plusMinutes ( offsetInMinutes );
String offsetAsString = offsetAsLocalTime.format ( DateTimeFormatter.ISO_LOCAL_TIME );
用我们生成的字符串替换那些尾随字符。
String inputModified = ( input.substring ( 0 , 22 ) + offsetAsString );
将字符串解析为日期时间对象
定义一个 custom formatting pattern by which to parse that string into a OffsetDateTime
对象。
// Parse the modified input as an OffsetDateTime.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern ( "yyyyMMddHHmmss.SSSSSSZZZZZ" , Locale.US );
OffsetDateTime odt = OffsetDateTime.parse ( inputModified , formatter );
转储到控制台。
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
转换
我强烈建议避免使用旧的日期时间 类。但是,如果您必须使用 java.util.Date
对象与旧日期时间代码进行互操作,则可以进行转换。
寻找添加到旧 类 的新方法进行转换。对于此转换,我们使用 java.util.Date.from
。我们需要为该转换方法提供一个 Instant
对象,UTC 时间轴上的一个时刻,分辨率为 nanoseconds。我们可以从 OffsetDateTime
.
中提取一个
Instant instant = odt.toInstant();
java.util.Date utilDate = java.util.Date.from( instant );
有关转换的更多信息(包括漂亮的图表),请参阅 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 是一个偏移量 加上 用于处理夏令时 (DST) 等异常的规则。 Instant
和 java.util.Date
均为 UTC(偏移量为零)。
虽然我接受了 Basil Bourque 的回答,并且在过去两年中一直在我的项目中保留 threetenbp
依赖项,但我设法使用 Calendar
进行了快速解析,结果相同东西,可以满足我的需要。添加异常检查等,但这使用 JDK1.1 代码给出了相同的结果,并且可能对其他人有用。但是请注意,这种方法不是线程安全的,应该首选已接受的答案。
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;*
我正在尝试将从 Windows 管理界面检索到的 DateTime 值转换为 Java (1.7) 日期;最终到纪元以来的毫秒数。 format is specified here。
我尝试解析的一个示例是 20160513072950.782000-420
,即 2016-05-13 07:29:50 加 782 毫秒,在我的本地时区(-420 分钟 = UTC-7 小时) .小数点后的数字是小数秒;理论上最多 6 位微秒,但实际上只有前 4 位非零。
我最初尝试使用 SimpleDateFormat 指定我想要解析的三位毫秒数进行解析:
SimpleDateFormat cimDateFormat = new SimpleDateFormat("yyyyMMddHHmmss.SSS");
Date date = cimDateFormat.parse(s, new ParsePosition(0));
我的理由是用 SSS
指定毫秒的三位数会停止解析。不幸的是,这没有用;在上面的示例中添加了超过 782 毫秒。
我最终通过将字符串修剪为所需字符使其按预期工作:
SimpleDateFormat cimDateFormat = new SimpleDateFormat("yyyyMMddHHmmss.S");
Date date = cimDateFormat.parse(s.substring(0, 18), new ParsePosition(0));
在这种情况下,我只包含一个 S
毫秒,但它解析了所有三个。
我在 SimpleDateFormat
javadoc 中找不到任何内容可以清楚地解释此解析结束时发生的情况。具体问题:
- 为什么在
SSS
的情况下,它一直解析超过指定的位数? - 为什么单个
S
解析所有 3 毫秒数字? - 除了像我那样截断字符串之外,还有其他方法可以让
SimpleDateFormat
停止在指定位置解析字符串吗?
修改输入
据我所知,Java 的三种常见日期时间框架中的 none(旧的捆绑 java.util.Date/.Calendar/java.text.SimpleDateFormat 类、Joda-Time 框架或 Java 8 及更高版本中内置的 java.time 框架)允许从 UTC 偏移量作为总分钟数。
按照 Sotirios Delimanolis, you must modify the offset-from-UTC 的建议,将总分钟数转换为标准的小时数和分钟数(和秒数——这种可能性被奇怪的 Microsoft 格式忽略了)。所以-420
应该变成-07:00
或者-07:00:00
。
java.time
您正在使用与 Java 的最早版本捆绑在一起的麻烦的旧日期时间 类。旧的 类 现在是遗留的,并已被 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.
取代java.time 类 的分辨率为 nanoseconds,最多九位小数秒。因此,处理您的输入 4-6 位小数秒没有问题。
我们的策略分为两部分:(a) 修改输入以转换该偏移量与 UTC,以及 (b) 将修改后的输入字符串解析为日期时间对象。
修改输入
首先我们将输入从 20160513072950.782000-420
更改为 20160513072950.782000-07:00:00
。我们通过提取 +
或 -
之后的字符来完成此操作,在本例中为 420
。
// 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 );
将其转换为 long
,并创建一个 LocalTime
对象,以便我们可以生成 HH:mm:ss
格式的字符串。
long offsetInMinutes = Long.parseLong ( offsetInMinutesAsString );
LocalTime offsetAsLocalTime = LocalTime.MIN.plusMinutes ( offsetInMinutes );
String offsetAsString = offsetAsLocalTime.format ( DateTimeFormatter.ISO_LOCAL_TIME );
用我们生成的字符串替换那些尾随字符。
String inputModified = ( input.substring ( 0 , 22 ) + offsetAsString );
将字符串解析为日期时间对象
定义一个 custom formatting pattern by which to parse that string into a OffsetDateTime
对象。
// Parse the modified input as an OffsetDateTime.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern ( "yyyyMMddHHmmss.SSSSSSZZZZZ" , Locale.US );
OffsetDateTime odt = OffsetDateTime.parse ( inputModified , formatter );
转储到控制台。
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
转换
我强烈建议避免使用旧的日期时间 类。但是,如果您必须使用 java.util.Date
对象与旧日期时间代码进行互操作,则可以进行转换。
寻找添加到旧 类 的新方法进行转换。对于此转换,我们使用 java.util.Date.from
。我们需要为该转换方法提供一个 Instant
对象,UTC 时间轴上的一个时刻,分辨率为 nanoseconds。我们可以从 OffsetDateTime
.
Instant instant = odt.toInstant();
java.util.Date utilDate = java.util.Date.from( instant );
有关转换的更多信息(包括漂亮的图表),请参阅 OffsetDateTime
, not a full time zone. A time zone 是一个偏移量 加上 用于处理夏令时 (DST) 等异常的规则。 Instant
和 java.util.Date
均为 UTC(偏移量为零)。
虽然我接受了 Basil Bourque 的回答,并且在过去两年中一直在我的项目中保留 threetenbp
依赖项,但我设法使用 Calendar
进行了快速解析,结果相同东西,可以满足我的需要。添加异常检查等,但这使用 JDK1.1 代码给出了相同的结果,并且可能对其他人有用。但是请注意,这种方法不是线程安全的,应该首选已接受的答案。
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;*