Shift date an hour when in dst

爱⌒轻易说出口 提交于 2019-12-11 05:38:22

问题


I am processing a file line by line.

Each line has a date in the following format: YYMMDD HHMM

The file is basically recording a reading every 15 minutes. The recording is using day light savings for the time zone it is in. The issue I am having is during spring ahead and fall back. The recording is duplicating dates when fall back occurs and a gap when fall back occurs.

Fallback example:

141102 0100

141102 0115

141102 0130

141102 0145

141102 0200

141102 0115 - duplicate

141102 0130 - duplicate

141102 0145 - duplicate

141102 0200 - duplicate

Spring forward example:

150308 0200

150308 0315

What I need to be able to do, is shift all the dates forward an hour when in daylight savings time.

The program I am working on is using java.util.Calendar currently. I have tried doing the shift using the java Calendar and am running into lots of issues with adding the dates. I assume the problem to be that it is trying to correct DST issues itself, when all I want to do is shift the date by an hour. It also has an issue with detecting that gap. For example it thinks that the first 1:15 - 2:00 is in daylight time, when this is not the case for it.

Basically what I want is to change the fallback example to everything that falls in this date range to shift back an hour:

Fallback example:

141102 0100 no change

141102 0115 no change

141102 0130 no change

141102 0145 no change

141102 0200 no change

141102 0115 to 141102 0215

141102 0130 to 141102 0230

141102 0145 to 141102 0245

141102 0200 to 141102 0300

Keeps on changing date until it hits the spring forward

150308 0200 to 150308 0300

150308 0315 no change.

I have tried lots of ways, and can't seem to come to a solution. I am not opposed to using Joda Time as I've been looking into that a bit as well. Any help would be appreciated, thanks.


回答1:


UTC

Always use UTC for data exchange and this kind of readings log. Always, always, always use UTC. Think of UTC as the One True Time and other time zones as deviations from UTC.

By the way, Daylight Saving Time (DST) is not the only problem to worry about with zoned date-time values. Other anomalies occur in various time zones resulting in wall-clock time shifting forward or backward. The solution is easy: Avoid all time zones – Use UTC.

Instant

The Instant class captures a moment on the timeline in UTC with a resolution of nanoseconds. This should be you go-to class for date-time work.

Instant instant = Instant.now();

Strings

For logging, generate a string in standard ISO 8601 format. Stick with the proven ISO 8601 formats rather than invent your own.

The java.time classes including Instant use ISO 8601 formats by default when parsing/generating strings. So no need to specify formatting patterns.

String output = instant.toString();

2016-11-02T21:10:05.321Z

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

And always include the century 20 in your dates. Omitting just creates so much opportunity for errors in misinterpreting. Storage and memory really is cheap enough that we can afford the two extra digits.

Time zone indicator

Always include a time zone or offset-from-UTC indicator with your serialized date-time values.

In the Instant::toString result seen above, the Z stands for Zulu which means UTC.

Parsing strings

Here is code to parse your date-time strings. We parse as LocalDateTime first, without any time zone or offset, because your input lacks any indicator of time zone or offset. Then we assign a time zone to get a ZonedDateTime, to see how it handles the moment of DST cut-over.

I assume your time zone in question uses a DST fall-back cutover of 2 AM. I used America/Montreal as an example of such.

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("");
}

input: 141102 0115

ldt: 2014-11-02T01:15

zdt: 2014-11-02T01:15-04:00[America/Montreal]

instant: 2014-11-02T05:15:00Z

…and…

input: 141102 0215

ldt: 2014-11-02T02:15

zdt: 2014-11-02T02:15-05:00[America/Montreal]

instant: 2014-11-02T07:15:00Z

You can see this code live in IdeOne.com. Note the two hour difference in UTC values (Z).

The behavior we see is documented.

In most cases, there is only one valid offset for a local date-time. In the case of an overlap, where clocks are set back, there are two valid offsets. This method uses the earlier offset typically corresponding to "summer".

Where the 1:15 AM is ambiguous, could be either the first or second occurrence, java.time goes with the first.

You could play a guessing-game to try to adjust these poorly-designed strings. If you are sure you have at least one sample during every clock hour, you could keep track of previous samples and see if the sample being processed has a clock-time earlier than the previous. If so you could assume this is a DST fall-back cutover, and add an hour with plusHours( 1 ).

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

But this is a risky hack of a solution. I'm not sure this work, as I've not thought it through. If desperate, perhaps you can make it work.

The much wise approach is prevention: Use UTC.



来源:https://stackoverflow.com/questions/40388834/shift-date-an-hour-when-in-dst

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!