我正在尝试将ISO 8601格式的字符串转换为java.util.Date。
我发现模式yyyy-MM-dd'T'HH:mm:ssZ是符合iso8601的,如果使用区域设置(比较样本)。
然而,使用java.text。SimpleDateFormat,我无法转换正确格式化的字符串2010-01-01T12:00:00+01:00。我必须首先将其转换为2010-01-01T12:00:00+0100,不带冒号。
目前的解决方案是
SimpleDateFormat ISO8601DATEFORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.GERMANY);
String date = "2010-01-01T12:00:00+01:00".replaceAll("\\+0([0-9]){1}\\:00", "+0$100");
System.out.println(ISO8601DATEFORMAT.parse(date));
这显然不太好。是我错过了什么,还是有更好的解决方案?
回答
感谢JuanZe的评论,我发现了Joda-Time魔法,这里也有描述。
所以解是
DateTimeFormatter parser2 = ISODateTimeFormat.dateTimeNoMillis();
String jtdate = "2010-01-01T12:00:00+01:00";
System.out.println(parser2.parseDateTime(jtdate));
或者更简单地说,通过构造函数使用默认解析器:
DateTime dt = new DateTime( "2010-01-01T12:00:00+01:00" ) ;
对我来说,这很好。
Java 12
从Java 12开始,Instant#parse可以解析包含时区偏移的日期-时间字符串。
Date dt = Date.from(Instant.parse(your-date-time-string));
使用Instant# Parse解析ISO 8601格式的日期-时间字符串,并使用dat# from将结果转换为java.util.Date。在Ideone.com上查看运行的代码。
演示:
import java.time.Instant;
import java.util.Date;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
Stream.of(
"2010-01-01T12:00:00+01:00",
"2010-01-01T12:00:00-01:00",
"2010-01-01T12:00:00Z"
)
.map(Instant::parse)
.map(Date::from)
.forEach(System.out::println);
}
}
在Ideone.com上查看运行的代码。
从Trail: Date Time了解更多关于现代Date-Time API的信息。
一个小测试,展示了如何解析ISO8601中的日期,以及LocalDateTime不能处理DSTs。
@Test
public void shouldHandleDaylightSavingTimes() throws ParseException {
//ISO8601 UTC date format
SimpleDateFormat utcFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
// 1 hour of difference between 2 dates in UTC happening at the Daylight Saving Time
Date d1 = utcFormat.parse("2019-10-27T00:30:00.000Z");
Date d2 = utcFormat.parse("2019-10-27T01:30:00.000Z");
//Date 2 is before date 2
Assert.assertTrue(d1.getTime() < d2.getTime());
// And there is 1 hour difference between the 2 dates
Assert.assertEquals(1000*60*60, d2.getTime() - d1.getTime());
//Print the dates in local time
SimpleDateFormat localFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm z Z", Locale.forLanguageTag("fr_CH"));
localFormat.setTimeZone(TimeZone.getTimeZone("Europe/Zurich"));
//Both dates are at 02h30 local time (because of DST), but one is CEST +0200 and the other CET +0100 (clock goes backwards)
Assert.assertEquals("2019-10-27 02:30 CEST +0200", localFormat.format(d1));
Assert.assertEquals("2019-10-27 02:30 CET +0100", localFormat.format(d2));
//Small test that shows that LocalDateTime does not handle DST (and should not be used for storing timeseries data)
LocalDateTime ld1 = LocalDateTime.ofInstant(d1.toInstant(), ZoneId.of("Europe/Zurich"));
LocalDateTime ld2 = LocalDateTime.ofInstant(d2.toInstant(), ZoneId.of("Europe/Zurich"));
//Note that a localdatetime does not handle DST, therefore the 2 dates are the same
Assert.assertEquals(ld1, ld2);
//They both have the following local values
Assert.assertEquals(2019, ld1.getYear());
Assert.assertEquals(27, ld1.getDayOfMonth());
Assert.assertEquals(10, ld1.getMonthValue());
Assert.assertEquals(2, ld1.getHour());
Assert.assertEquals(30, ld1.getMinute());
Assert.assertEquals(0, ld1.getSecond());
}