2014-12-04 86 views
7

如何从不包含zone和其他字段的字符串中解析ZoneDateTime如何解析ZonedDateTime与默认区域?

下面是测试中斯波克重现:

import spock.lang.Specification 
import spock.lang.Unroll 

import java.time.ZoneId 
import java.time.ZoneOffset 
import java.time.ZonedDateTime 
import java.time.format.DateTimeFormatter 

@Unroll 
class ZonedDateTimeParsingSpec extends Specification { 
    def "DateTimeFormatter.ISO_ZONED_DATE_TIME parsing incomplete date: #value #expected"() { 
     expect: 
     ZonedDateTime.parse(value, DateTimeFormatter.ISO_ZONED_DATE_TIME) == expected 
     where: 
     value       | expected 
     '2014-04-23T04:30:45.123Z'  | ZonedDateTime.of(2014, 4, 23, 4, 30, 45, 123_000_000, ZoneOffset.UTC) 
     '2014-04-23T04:30:45.123+01:00' | ZonedDateTime.of(2014, 4, 23, 4, 30, 45, 123_000_000, ZoneOffset.ofHours(1)) 
     '2014-04-23T04:30:45.123'  | ZonedDateTime.of(2014, 4, 23, 4, 30, 45, 123_000_000, ZoneId.systemDefault()) 
     '2014-04-23T04:30'    | ZonedDateTime.of(2014, 4, 23, 4, 30, 0, 0, ZoneId.systemDefault()) 
     '2014-04-23'     | ZonedDateTime.of(2014, 4, 23, 0, 0, 0, 0, ZoneId.systemDefault()) 
    } 
} 

前两个测试通过,所有其他失败,DateTimeParseException:

  • '2014-04-23T04:30:45.123' 不能“2014-04-23T04:30”在索引23
  • 解析不能在索引被解析16
  • “2014年4月23日”无法在索引10
  • 被解析

如何解析时间和区域设置为默认的未完成日期?

回答

11

由于ISO_ZONED_DATE_TIME格式化程序需要区域或偏移量信息,因此解析失败。 您必须制作一个DateTimeFormatter,其中包含区域信息和时间部分的可选部分。 这不是很难逆向工程ZonedDateTimeFormatter并添加可选标签。

然后使用格式化程序的parseBest()方法解析String。然后,对于不理想的解析结果,您可以使用任何默认值创建ZonedDateTime

DateTimeFormatter formatter = new DateTimeFormatterBuilder() 
     .parseCaseInsensitive() 
     .append(ISO_LOCAL_DATE) 
     .optionalStart()   // time made optional 
     .appendLiteral('T') 
     .append(ISO_LOCAL_TIME) 
     .optionalStart()   // zone and offset made optional 
     .appendOffsetId() 
     .optionalStart() 
     .appendLiteral('[') 
     .parseCaseSensitive() 
     .appendZoneRegionId() 
     .appendLiteral(']') 
     .optionalEnd() 
     .optionalEnd() 
     .optionalEnd() 
     .toFormatter(); 

TemporalAccessor temporalAccessor = formatter.parseBest(value, ZonedDateTime::from, LocalDateTime::from, LocalDate::from); 
if (temporalAccessor instanceof ZonedDateTime) { 
    return ((ZonedDateTime) temporalAccessor); 
} 
if (temporalAccessor instanceof LocalDateTime) { 
    return ((LocalDateTime) temporalAccessor).atZone(ZoneId.systemDefault()); 
} 
return ((LocalDate) temporalAccessor).atStartOfDay(ZoneId.systemDefault()); 
+0

谢谢,它看起来更好,但并没有解决一个问题:

 Caused by: java.time.DateTimeException: Unable to obtain ZoneId from TemporalAccessor: {},ISO resolved to 2014-04-23T04:30:45.123 of type java.time.format.Parsed 
所以,再次失败,因为我无法设置解析器的默认时区。 其他部件相同的东西,用于检查日期。 例如,如何使用当前日期和系统时区将字符串'23:45'解析为ZonedDateTime? – stokito 2014-12-07 14:09:55

+0

对于问题中的所有5个示例字符串,它都使用java 8u25运行正常。所以,不知道你的问题是什么。 – bowmore 2014-12-07 16:24:07

+1

问题是我想获得LocalDateTime的ZonedDateTime insted LocalDateTime – stokito 2014-12-08 07:54:05

8

该格式化程序有一个withZone()method可以调用以提供缺少的时区。

ZonedDateTime.parse(
    value, 
    DateTimeFormatter.ISO_ZONED_DATE_TIME.withZone(ZoneId.systemDefault())) 

请记住,有一个错误,所以你需要8u20或更高版本才能完全工作。

+0

似乎不工作(与8u25试过) – bowmore 2014-12-04 14:55:52

+0

方法'withZone()'总是覆盖区,即使它包含字符串 – stokito 2014-12-04 17:02:25

+0

@stokito:对没有区信息串出现问题被解析。 – bowmore 2014-12-04 17:35:59