我正在逐行处理文件。

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

该文件基本上每15分钟记录一次读数。录制使用的是所在时区的日光节约。我遇到的问题是春季提前和后退。发生回退时,记录将复制日期,发生回退时,记录将间隔。

后备示例:

141102 0100

141102 0115

141102 0130

141102 0145

141102 0200

141102 0115-重复

141102 0130-重复

141102 0145-重复

141102 0200-重复

春季向前的例子:

150308 0200

150308 0315

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

我正在使用的程序当前正在使用java.util.Calendar。我尝试使用Java日历进行轮班,添加日期时遇到很多问题。我想的问题是,当我只想将日期移动一个小时时,它会尝试自行纠正DST问题。在检测该差距方面也存在问题。例如,它认为第一个1:15-2:00是夏令时,但事实并非如此。

基本上,我想要的是将后备示例更改为该日期范围内的所有内容,以后退一小时:

后备示例:

141102 0100不变

141102 0115没有变化

141102 0130不变

141102 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不变。

我尝试了很多方法,但似乎无法解决。我也不反对使用Joda Time,因为我也一直在研究这一点。任何帮助,将不胜感激,谢谢。

最佳答案

世界标准时间

始终使用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转换的时刻。

我假设您所讨论的时区使用DST后备切换时间凌晨2点。我以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 AM不明确的地方,可能是第一次出现,也可能是第二次出现,java.time与第一次出现一起出现。

您可以玩猜谜游戏来尝试调整这些设计欠佳的琴弦。如果确定每个时钟小时内至少有一个样本,则可以跟踪先前的样本,并查看所处理的样本的时钟时间是否早于先前的时间。如果是这样,您可以假定这是DST后备转换,并使用plusHours( 1 )添加一个小时。

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


但这是解决方案的一个冒险技巧。我不确定这项工作,因为我还没有仔细考虑。如果不顾一切,也许您可​​以使其工作。

明智的方法是预防:使用UTC。

08-04 14:47