我正在尝试将从Windows管理界面检索到的DateTime值转换为Java(1.7)日期;最终到了纪元以来的毫秒。 format is specified here。SimpleDateFormat。
我尝试解析的一个例子是20160513072950.782000-420
,这是2016-05-13在07:29:50加上782毫秒,在我当地的时区(-420分钟= UTC-7小时)。小数点后的数字是小数秒;理论上最多6位数微秒,但实际上只有前4位是非零的。
我最初尝试使用https://bugs.chromium.org/p/chromium/issues/detail?id=422883解析,指定我要解析的三位毫秒数:
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
停止在指定位置解析字符串吗?答案 0 :(得分:1)
据我所知,Java的三个常见日期时间框架(旧的捆绑的java.util.Date/.Calendar/java.text.SimpleDateFormat类,Joda-Time框架或java)都没有。 Java 8及更高版本中内置的时间框架允许从UTC的偏移量作为总分钟数。
根据Sotirios Delimanolis的建议,您必须修改offset-from-UTC以将总分钟数转换为标准小时数和分钟数(以及秒数 - 这种奇怪的Microsoft格式会忽略这种可能性) 。因此-420
应该变为-07:00
或-07:00:00
。
您正在使用与最早版本的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 | 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 );
有关转换的详细信息,包括漂亮的图表,请参阅my Answer到另一个问题。请注意,我们的输入字符串和OffsetDateTime
只有offset-from-UTC,而不是全时区。 time zone是用于处理夏令时(DST)等异常的偏移加上规则。 Instant
和java.util.Date
都是UTC(零偏移)。
答案 1 :(得分:0)
虽然我接受了Basil Bourque的回答,并且在过去的两年中一直将threetenbp
依赖项保留在我的项目中,但最近我消除了对Java 8日期的另一种需求,而这种特殊的解析方法是我拥有的唯一原因依赖性。我设法使用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;*