【问题标题】:Parse CIM_DateTime with milliseconds to Java Date以毫秒为单位将 CIM_DateTime 解析为 Java 日期
【发布时间】:2016-05-18 19:36:36
【问题描述】:

我正在尝试将从 Windows 管理界面检索到的 DateTime 值转换为 Java (1.7) 日期;最终到自纪元以来的毫秒数。 format is specified here

我尝试解析的一个示例是 20160513072950.782000-420,即 2016 年 5 月 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 中找不到任何可以清楚地解释解析结束时发生的情况的内容。具体问题:

  1. 为什么在SSS 的情况下,它总是解析超过指定的位数?
  2. 为什么单个 S 会解析所有 3 毫秒数字?
  3. 除了像我一样截断字符串之外,还有其他方法可以告诉SimpleDateFormat 在指定位置停止解析字符串吗?

【问题讨论】:

  • 解析时,模式字母的数量会被忽略,除非需要分隔两个相邻的字段
  • @SotiriosDelimanolis 谢谢——回答了 1 和 2。现在对于 3,我可以在 SSS 之后添加一个“相邻字段”,上面写着“多余的字符,忽略”吗?
  • 不忽略,不。这6个字符让我感到困惑。它们是小数秒吗?看起来不像,但为什么会有 6 位数字呢?为什么不崩溃到其他领域呢? SimpleDateFormat 将该字段视为毫秒,因此像 782000 这样的值会将这么多毫秒添加到总日期中。您必须处理您的字符串以使其与SimpleDateFormat 的模式兼容。
  • 基于很多样本数据点,6个字符代表微秒;但是,Windows 仅以 100 微秒的增量记录时间,因此这 6 个字符中只有前 4 个字符中有数据。我忽略了第 4 个字符,只想将前 3 位数字解析为毫秒。感谢有关预处理字符串的更详细的响应(例如,将 -420 分钟更改为正确的 -0700 时区);如果您将其发布为答案,我会接受。

标签: java parsing datetime wmi


【解决方案1】:

修改输入

据我所知,三个常见的 Java 日期时间框架都没有(旧的捆绑 java.util.Date/.Calendar/java.text.SimpleDateFormat 类、Joda-Time 框架或 java.util.Date/.Calendar/java.text.SimpleDateFormat 类)。 Java 8 及更高版本中内置的时间框架)允许从 UTC 偏移总分钟数。

按照Sotirios Delimanolis 的建议,您必须修改offset-from-UTC 以将总分钟数转换为标准小时数和分钟数(以及秒数——这种奇怪的Microsoft 格式忽略了这种可能性)。所以-420 应该变成-07:00-07:00:00

java.time

您正在使用与最早版本的 Java 捆绑在一起的麻烦的旧日期时间类。旧的类现在是遗留的,并已被 Java 8 及更高版本中内置的 java.time 框架所取代,主要是 back-ported to Java 6 & 7 by the ThreeTen-Backport 项目和进一步的 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,通过它将该字符串解析为一个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 );

输入:20160513072950.782000-420 |输入修改:20160513072950.782000-07:00:00 |时间: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 );

有关转换的更多信息,包括漂亮的图表,请参阅my Answer 到另一个问题。请记住,我们在输入字符串和OffsetDateTime 中仅使用offset-from-UTC,而不是完整的时区。 time zone 是用于处理夏令时 (DST) 等异常情况的偏移 规则。 Instantjava.util.Date 均采用 UTC(偏移量为零)。

【讨论】:

  • 感谢您的详细解答!我正在为一个需要 Java 1.7 的项目开发,所以我不能使用新的时间,因为我主要对“自纪元以来的毫秒数”感兴趣(对应于 System.currentTimeMillis()——我正在解析的值是进程开始时间;通过从当前时间减去我得到正常运行时间)日期是一个中间步骤,为此添加一个 Joda Time 依赖项似乎有点过头了。
  • @DanielWiddis 请注意指向ThreeTen-Backport 项目的链接,该项目的大部分功能已被引入 Java 6 和 7。只需将 jar 放入您的项目中即可。
【解决方案2】:

虽然我接受了 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;*

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-05-04
    • 1970-01-01
    • 2018-01-03
    • 2022-11-27
    • 2015-04-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多