Добавление дней с помощью java.util.Calendar дает странные результаты. - PullRequest
2 голосов
/ 07 октября 2011

Использование java.util.Calendar для добавления одного дня к дате и SimpleDateFormat для отображения результата, иногда кажется, что теряет день (обычно в марте), а иногда пропускает день (в ноябре).

Программа ниже с выводом иллюстрирует проблему.Обратите внимание, что я просто добавляю один день за раз, затем пропускаю несколько месяцев и добавляю еще несколько дней.Вы увидите, что 2008-03-09 печатается дважды, но 2008-11-02 пропускается.То же самое происходит в другие годы, но в разные дни.Мне пришлось экспериментировать, чтобы найти дни, которые вызывают проблему.

Если я не установил часовой пояс в формате UTC в SimpleDateFormat, то проблема не возникает.Я запустил это на машине в центральном часовом поясе США.

Это, конечно, похоже на ошибку в Calendar или SimpleDateFormat, но я не смог найти ее нигде в документации.У кого-нибудь есть объяснение, что здесь происходит?

Программа:

package mab;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

public class CalendarHiccup2 {

    public static void main(String[] args) {
        addDays("2008-03-08");
        addDays("2009-03-07");
        addDays("2010-03-13");
    }

    public static void addDays(String dateString) {
        System.out.println("Got dateString: " + dateString);

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));

        Calendar calendar = Calendar.getInstance();
        try {
            calendar.setTime(sdf.parse(dateString));
            Date day1 = calendar.getTime();
            System.out.println("  day1 = " + sdf.format(day1));

            calendar.add(java.util.Calendar.DAY_OF_MONTH, 1);
            Date day2 = calendar.getTime();
            System.out.println("  day2 = " + sdf.format(day2));

            calendar.add(java.util.Calendar.DAY_OF_MONTH, 1);
            Date day3 = calendar.getTime();
            System.out.println("  day3 = " + sdf.format(day3));

            calendar.add(java.util.Calendar.DAY_OF_MONTH, 1);
            Date day4 = calendar.getTime();
            System.out.println("  day4 = " + sdf.format(day4));

            // Skipping a few days ahead:
            calendar.add(java.util.Calendar.DAY_OF_MONTH, 235);
            Date day5 = calendar.getTime();
            System.out.println("  day5 = " + sdf.format(day5));

            calendar.add(java.util.Calendar.DAY_OF_MONTH, 1);
            Date day6 = calendar.getTime();
            System.out.println("  day6 = " + sdf.format(day6));

            calendar.add(java.util.Calendar.DAY_OF_MONTH, 1);
            Date day7 = calendar.getTime();
            System.out.println("  day7 = " + sdf.format(day7));

            calendar.add(java.util.Calendar.DAY_OF_MONTH, 1);
            Date day8 = calendar.getTime();
            System.out.println("  day8 = " + sdf.format(day8));

        } catch (Exception e) {
        }
    }

}

Вывод:

Got dateString: 2008-03-08
  day1 = 2008-03-08
  day2 = 2008-03-09
  day3 = 2008-03-09
  day4 = 2008-03-10
  day5 = 2008-10-31
  day6 = 2008-11-01
  day7 = 2008-11-03
  day8 = 2008-11-04
Got dateString: 2009-03-07
  day1 = 2009-03-07
  day2 = 2009-03-08
  day3 = 2009-03-08
  day4 = 2009-03-09
  day5 = 2009-10-30
  day6 = 2009-10-31
  day7 = 2009-11-02
  day8 = 2009-11-03
Got dateString: 2010-03-13
  day1 = 2010-03-13
  day2 = 2010-03-14
  day3 = 2010-03-14
  day4 = 2010-03-15
  day5 = 2010-11-05
  day6 = 2010-11-06
  day7 = 2010-11-08
  day8 = 2010-11-09

Ответы [ 4 ]

3 голосов
/ 07 октября 2011

Это вызвано летним временем и является полностью правильным.

Время (в северном полушарии) продвигается на один час обычно в марте и переносится в ноябре.

2 голосов
/ 07 октября 2011

Это больше похоже на проблему перехода на летнее время, которая меняется в марте и ноябре. Можете ли вы попробовать установить элемент времени на 00:00:00? Если вы делаете,

addDays("2008-03-08 00:00:00");
addDays("2009-03-07 00:00:00");
addDays("2010-03-13 00:00:00");

и измените формат на,

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

вы увидите разницу, которую он имеет в элементах времени.

0 голосов
/ 30 декабря 2013

Летнее время

Ответ Томаша Нуркевича правильный, проблема Летнее время (летнее время).

местное время меняется в 02:00 по местному стандартному времени на 03:00 по местному летнему времени во второе воскресенье марта и возвращается в 02:00 по местному летнему времени на 01:00 по местному стандартному времени в первое воскресенье ноября.

См. Википедию по Центральный часовой пояс

Joda-Time

Библиотека Joda-Time делает такую ​​работунамного проще.

A DateTime в Joda-Time знает свой собственный часовой пояс .Чтобы использовать UTC / GMT (без смещения по времени), передайте встроенную константу DateTimeZone.UTC.

Чтобы распечатать только часть даты даты-времени, используйте DateTimeFormatter .

// © 2013 Basil Bourque. This source code may be used freely forever by anyone taking full responsibility for doing so.
// import org.joda.time.*;
// import org.joda.time.format.*;

String dateString = "2008-03-08";

// withTimeAtStartOfDay() is probably superfluous in this example, but I like that it self-documents our focus on the day rather than a particular time of day.
DateTime dateTime = new DateTime( dateString, DateTimeZone.UTC ).withTimeAtStartOfDay();
DateTime dateTimePlus1 = dateTime.plusDays( 1 ).withTimeAtStartOfDay();
DateTime dateTimePlus2 = dateTime.plusDays( 2 ).withTimeAtStartOfDay();
DateTime dateTimePlus3 = dateTime.plusDays( 3 ).withTimeAtStartOfDay();

Дамп в консоль ...

System.out.println("dateTime: " + dateTime );
System.out.println("dateTime (date portion): " + ISODateTimeFormat.date().withZone( DateTimeZone.UTC ).print( dateTime ) );
System.out.println("dateTimePlus1: " + dateTimePlus1 );
System.out.println("dateTimePlus2: " + dateTimePlus2 );
System.out.println("dateTimePlus3: " + dateTimePlus3 );

При запуске ...

dateTime: 2008-03-08T00:00:00.000Z
dateTime (date portion): 2008-03-08
dateTimePlus1: 2008-03-09T00:00:00.000Z
dateTimePlus2: 2008-03-10T00:00:00.000Z
dateTimePlus3: 2008-03-11T00:00:00.000Z
0 голосов
/ 07 октября 2011

При возникновении подобных проблем вы должны распечатать экземпляры календаря и даты, чтобы выяснить, что происходит.

Следующее:

sdf.setTimeZone(TimeZone.getTimeZone("UTC"));

меняет время вашего календаряв зависимости от вашего местного времени JVM (и часового пояса).Calendar.getInstance(); создает календарь в вашем местном времени JVM, когда вы делаете calendar.setTime(sdf.parse(".....")), который устанавливает время в UTC (учитывая, как вы создали sdf!).В зависимости от DoY (а также года!), Который может заставить вас пройти полночь, и когда вы печатаете свою дату в формате yyyy-MM-dd, вы видите разницу в один день.

Печать полного календаряи полная дата, и вы поймете, что происходит!

...