2016-11-02 79 views
1

我正在逐行处理文件。转换日期在dst中一小时

的每一行都具有以下列格式的日期:YYMMDD HHMM

该文件被基本上记录读取每隔15分钟。该录制使用的是节省时间的日光。我遇到的问题是在春季前进和后退。记录是复制发生回退的日期和发生回退时的间隔。

后备例如:

141102 0 115 - 复制

141102 0130 - 复制

141102 0145 - 复制

141102 0200 - 复制

春天正向例如:

我需要做的是在夏令时期间将所有日期向前移动一个小时。

我正在使用的程序目前正在使用java.util.Calendar。我已经尝试使用java日历进行转换,并且遇到了很多添加日期的问题。我认为问题在于它正在尝试纠正DST问题本身,当我想要做的就是将日期转换一小时。它还有一个检测差距的问题。例如,它认为,第一个1:15 - 2:00是在白天,当这不是情况。

基本上我想要的是后备例如更改为在此日期范围内下降到移回一小时的一切:

后备例如:

141102 0100没有变化

141102 0115无变化

141102 0130无变化

1 41102 0145没有变化

141102 0200没有变化

141102 0115至141102 0215

141102 0130至141102 0230

141102 0145至141102 0245

141102 0200至141102 0300

保持更改日期,直到它弹出前进

150308 0200至150308 0300

150308 0315没有变化。

我尝试了很多方法,似乎无法找到解决方案。我不反对使用乔达时代,因为我一直在研究这一点。任何帮助将不胜感激,谢谢。

+0

所以你真正想要做的是将日期转换为一个文件名,使用的时区在夏天不会改变它的偏移量,对吧? –

+0

它不是文件名,而是文件中有'timestamp'的一行。我想在dst的一个小时内转换该时间戳,以便我没有缺口或重复的条目。 – DHines

+2

好的,但不会只使用日期格式没有夏时制的时区达到那个目的?或者我错过了什么?标准做法是在日志文件中使用UTC作为时间戳,但大概您更希望在冬天您的时区与您的时区保持一致。 –

回答

2

UTC

始终使用UTC进行数据交换和这种读数记录。始终,始终使用UTC。将UTC视为一个真实时间和其他时区与UTC的偏差。

顺便说一句,Daylight Saving Time (DST)不是唯一的问题是担心区划日期时间值。其他异常发生在各个时区,导致wall-clock time向前或向后移动。解决方案很简单:避免使用所有时区 - 使用UTC

Instant

Instant类捕获UTC时间线与纳秒的分辨率上一会儿。这应该是你去上课的日期时间工作。

Instant instant = Instant.now(); 

字符串

用于记录,生成标准ISO 8601格式的字符串。坚持采用经过验证的ISO 8601格式,而不是发明自己的产品。

包括Instant的java.time类在解析/生成字符串时默认使用ISO 8601格式。所以不需要指定格式化模式。

String output = instant.toString(); 

2016-11-02T21:10:05.321Z

Instant instant = Instant.parse("2016-11-02T21:10:05.321Z"); 

而且始终在您的日期世纪20。忽略只会为错误解释中的错误创造很多机会。存储和内存真的很便宜,我们可以负担两个额外的数字。

时区指针

始终包括时区或偏移从-UTC指标与序列化日期时间值。

在上面看到的Instant::toString结果中,Z代表Zulu,表示UTC。

解析字符串

下面是代码来解析您的日期时间字符串。我们首先解析为LocalDateTime,没有任何时区或偏移量,因为您的输入缺少任何时区或偏移量的指示符。然后我们分配一个时区来获得ZonedDateTime,以了解它如何处理DST切换的时刻。

我认为问题所在时区使用凌晨2点的DST回退割接。作为这样的例子,我使用America/Montreal

List<String> inputs = new ArrayList<>(2); 
inputs.add("141102 0115"); // 1 AM 
inputs.add("141102 0215"); // 2 AM 

for(String input : inputs) { 
    DateTimeFormatter f = DateTimeFormatter.ofPattern("uuMMdd HHmm"); 
    LocalDateTime ldt = LocalDateTime.parse(input , f); 

    // At 2 AM in this zone, the clock falls back one hour for DST cutover. The 1 AM hour repeats. 
    ZoneId z = ZoneId.of("America/Montreal"); 
    ZonedDateTime zdt = ldt.atZone(z); 

    System.out.println("input: " + input); 
    System.out.println("ldt: " + ldt); 
    System.out.println("zdt: " + zdt); 
    System.out.println("instant: " + zdt.toInstant()); 
    System.out.println(""); 
} 

输入:141102 0115

LDT:2014-11-02T01:15

ZDT:2014-11-02T01:15-04:00 [美国/蒙特利尔]

瞬间:2014-11-02T05:15:00Z

......还有......

输入:141102 0215

LDT:2014-11-02T02:15

ZDT:2014-11-02T02:15-05:00 [美国/蒙特利尔]

瞬间: 2014-11-02T07:15:00Z

您可以see this code live in IdeOne.com。请注意UTC值的两小时差异(Z)。

我们看到的行为被记录在案。

在大多数情况下,本地日期时间只有一个有效偏移量。在时钟重新设置的情况下,有两个有效的偏移量。此方法使用通常对应于“夏季”的较早的偏移量。

上午1点15分含糊不清,可能是第一或第二次出现java。时间与第一次一样。

你可以玩猜谜游戏来尝试调整这些糟糕设计的字符串。如果您确定在每个时钟小时内至少有一个样本,则可以跟踪以前的样本并查看正在处理的样本是否比以前的时钟早。如果是这样,你可以认为这是一个DST后退切换,并添加一小时plusHours(1)

if(processingZdt.toLocalDateTime().isBefore(previousZdt.toLocalDateTime()) { 
    processingZdt.plusHours(1) ; // A hack; I am *not* recommending this. 
    … 

但是,这是一个解决方案的冒险攻击。我不确定这项工作,因为我没有想到它通过。如果绝望,也许你可以让它工作。

明智的做法是预防:使用UTC

+0

谢谢,这是对我的回答很好的回应,并且对未来如何做到这一点非常有帮助。问题是我正在做一个15年前创建的程序,现在他们想要添加它,并且我在java.util.Calendar类中绑定了它们,因为它们在整个程序中都使用它。 – DHines

+0

@DHines你可以[转换](http://docs.oracle.com/javase/8/docs/api/java/util/GregorianCalendar.html#toZonedDateTime--)一个'GregorianCalendar'到'ZonedDateTime'并提取UTC的“即时”。 'ZonedDateTime zdt = myGregCal.toZonedDateTime();'查看[这个问题](http://stackoverflow.com/q/36639154/642706)获取更多关于转换的信息。遗留的日期 - 时间类真的很麻烦,应该避免。这就是Sun和Oracle同意发明java.time的原因。 –

+0

感谢您提供该问题的链接。这是有见地的,我认为会有所帮助。我不确定它100%是否解决了我的问题。文件进入的方式有两个输入141102 0115时会有重复的日期,并且当它们有弹簧前进时(即输入150308 0200和150308 0315)会有重复的日期。我不能改变文件的方式,我必须处理它。所以我想我正试图完成的是当我在第一时间需要将所有数据转移一小时时。 – DHines