2017-02-22 1 views
1

Я тестирую с "2016-03-28T02:00:00+0200"Получение неверного результата после того, как Вычитание дня с даты Использованием LocalDateTime и ZonedDateTime

После вычитания 1 дня, DST применяется и вывод должен быть:

"2016-03-27T03: 00: 00 + 0200"

Но я получаю это:

2016-03-26T01: 00 + 01: 00 [Европа/Стокгольм]

КОД:

public class DateFormatSampleCode { 
    public static void main(String[] args) 
    { 
     LocalDateTime localDateTime = LocalDateTime.ofEpochSecond(1459123200, 0, ZoneOffset.UTC); 

     System.out.println(localDateTime); 
     localDateTime = localDateTime.minusDays(1); 
     System.out.println(localDateTime); 

     ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, ZoneId.of("Europe/Stockholm")); 

     System.out.println(zonedDateTime); 
    } 
} 

Пожалуйста, проверьте и указать, где я неправильно.

+1

Вы не можете вычесть день из * локального * даты/времени, который не имеет понятия о дневном свете, и ожидать, что он применит правила летнего времени. – RealSkeptic

+0

@RealSkeptic Не могли бы вы предложить, как это сделать. –

+0

Если вы хотите зонированную дату/время, используйте ZonedDateTime. Не используйте 'LocalDateTime'. – RealSkeptic

ответ

1

Я думаю, что могу ответить на мой вышеупомянутый вопрос.

Вот код.

public ZonedDateTime addDays(long myUTCTimeInSeconds, int days) { 
    Instant instant = Instant.ofEpochSecond(myUTCTimeInSeconds); 
    ZonedDateTime dateTimeWithOffSet = ZonedDateTime.ofInstant(instant, ZoneId.systemDefault()); 
    if (localDays >= 0) { 
     dateTimeWithOffSet = dateTimeWithOffSet.plusDays(localDays); 
    } else { 
     dateTimeWithOffSet = dateTimeWithOffSet.minusDays(abs(localDays)); 
    } 
    return dateTimeWithOffSet; 
} 

Если часовой пояс отличается от системы, можно установить по умолчанию TimeZone и после вызова метода выше сброса часового пояса, как:

TimeZone systemDefaultTimeZone = TimeZone.getDefault(); 
TimeZone.setDefault(TimeZone.getTimeZone(timezone)); 

addDays(1459123200, 1); 
TimeZone.setDefault(systemDefaultTimeZone); 
0

Хорошо вы нашли решение, я только как добавить некоторые идеи и предложить небольшое улучшение до your answer.

Установка часового пояса по умолчанию JVM с использованием TimeZone.setDefault - не лучший способ достичь этого. Хотя это может работать в большинстве случаев, это немного рискованно и подвержено ошибкам, если вы считаете, что этот код работает в более сложной среде.

Это потому, что TimeZone.setDefault изменяет часовой пояс по умолчанию для всего JVM. Любое другое приложение, работающее в одной JVM, будет затронуто этим. Другие части одного и того же приложения также будут затронуты, и даже этот же код, работающий в нескольких потоках, может привести к неправильным результатам (и race conditions are hard to debug).

Я заметил, что вы используете TimeZone.setDefault(TimeZone.getTimeZone(timezone));. Это означает, что вы уже работаете с определенным часовым поясом, поэтому нет необходимости полагаться на JVM по умолчанию. Если у вас есть определенное имя часового пояса, просто используйте его вместо стандартного. Поэтому я предлагаю вам, что метод addDays должен быть таким:

public ZonedDateTime addDays(long myUTCTimeInSeconds, int days, String timezone) { 
    // get the instant from the UTC seconds 
    Instant instant = Instant.ofEpochSecond(myUTCTimeInSeconds); 
    // get the instant at the specified timezone 
    ZonedDateTime z = instant.atZone(ZoneId.of(timezone)); 

    // add days 
    return z.plusDays(days); 
} 

Улучшения сделаны:

  • plusDays уже вычитает 1 день, если вы передаете -1 к нему. Нет необходимости проверять значение и использовать метод abs.
  • не использовать JVM часовой пояс по умолчанию: вместо ZoneId.systemDefault(), используйте timezone, что у вас уже есть (тот, который вы использовали, в методе setDefault)
  • instant.atZone эквивалентно ZonedDateTime.ofInstant.IMO, atZone более «читабельна», но в данном случае это вопрос выбора и стиля кода. Это не влияет на конечный результат.

При этом, вы можете сделать:

// call directly, no need to change the default timezone 
System.out.println(addDays(1459123200, -1, "Europe/Stockholm")); 

Это будет печатать:

2016-03-27T03: 00 + 02: 00 [Европа/Стокгольм]

Смежные вопросы