使用java.util.Calendar添加天数会产生奇怪的结果

时间:2011-10-07 19:14:55

标签: java simpledateformat

使用java.util.Calendar将一天添加到Date,并使用SimpleDateFormat显示结果,有时似乎会丢失一天(通常在三月),有时会跳过一天(十一月)。

下面的程序带有输出,说明了这个问题。请注意,我只是一次添加一天,然后跳过几个月并再添加几天。您会看到2008-03-09会被打印两次,但会跳过2008-11-02。同样的事情发生在其他年份,但在不同的日子。我不得不试验找出引起问题的日子。

如果我没有在SimpleDateFormat中将时区设置为UTC,则不会出现问题。我在美国中部时区的一台机器上运行它。

这当然看起来像是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 个答案:

答案 0 :(得分:3)

这是由daylight saving time引起的,并且完全正确。

时间(在北半球)通常在三月提前一小时,并在十一月后移回。

答案 1 :(得分:2)

它看起来更像是一个节日时间问题,在3月和11月发生变化。您可以尝试将时间元素设置为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");

你会看到它在时间元素中的区别。

答案 2 :(得分:0)

当您遇到这些问题时,您必须打印日历和日期实例以确定发生了什么。

以下内容:

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

根据您的JVM本地时间(和时区)更改日历的时间。 Calendar.getInstance();在您的JVM本地时间创建一个日历,当您执行calendar.setTime(sdf.parse("....."))设置UTC时间时(给定您创建sdf的方式!)。根据DoY(也是年份!)可以让你通过午夜,当你用yyyy-MM-dd格式打印日期时,你会看到一天的差异。

打印完整的日历和完整日期,您将了解正在发生的事情!

答案 3 :(得分:0)

夏令时

answer by Tomasz Nurkiewicz是正确的,问题是Daylight Saving Time(DST)。

  

当地时间在当地标准时间02:00变为3月第二个星期日的当地白天时间03:00,并在当地白天时间02:00返回到11月第一个星期日的当地标准时间01:00。

请参阅Central Time Zone

上的维基百科

约达时间

Joda-Time库使这种工作变得更加容易。

Joda-Time中的DateTime知道自己的time zone。要使用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