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

使用java.util.Calendar添加天数会产生奇怪的结果,java,simpledateformat,Java,Simpledateformat,使用java.util.Calendar为日期添加一天,并使用SimpleDateFormat显示结果,通常在3月份,有时似乎会丢失一天,有时会跳过11月份的一天 下面的程序及其输出说明了该问题。请注意,我只是一次添加一天,然后跳过几个月,再添加几天。您将看到2008-03-09打印了两次,但跳过了2008-11-02。同样的事情发生在其他年份,但在不同的日子。我必须进行实验,找出导致问题的时间 如果我没有在SimpleDataFormat中将时区设置为UTC,那么问题就不会发生。我在美国中央时

使用java.util.Calendar为日期添加一天,并使用SimpleDateFormat显示结果,通常在3月份,有时似乎会丢失一天,有时会跳过11月份的一天

下面的程序及其输出说明了该问题。请注意,我只是一次添加一天,然后跳过几个月,再添加几天。您将看到2008-03-09打印了两次,但跳过了2008-11-02。同样的事情发生在其他年份,但在不同的日子。我必须进行实验,找出导致问题的时间

如果我没有在SimpleDataFormat中将时区设置为UTC,那么问题就不会发生。我在美国中央时区的一台机器上运行了这个

这看起来确实像日历或SimpleDataFormat中的一个bug,但我还没有在任何地方找到它的文档。有人对这里发生的事情有解释吗

该方案:

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

它看起来更像是一个节省时间的问题,在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");
您将看到它在时间元素上的差异。

这是由时间元素引起的,完全正确


北半球的时间提前了一个小时,通常是在3月份,11月份又推迟到11月份。

当您遇到此类问题时,您必须打印日历和日期实例以了解发生了什么

以下是:

sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
根据JVM本地时间和时区更改日历的时间。Calendar.getInstance;在执行Calendar.setTimesdf.parse…..时,在JVM本地时间中创建日历。。。。。根据创建sdf!的方式,以UTC为单位设置时间!。取决于年份和年份!这会让你度过午夜,当你用yyyy-MM-dd格式打印日期时,你会看到一天的差异

打印完整的日历和完整的日期,你就会知道发生了什么

夏时制 错误是正确的,问题是DST

当地时间在3月的第二个星期日从当地标准时间02:00更改为当地夏时制03:00,并在11月的第一个星期日从当地夏时制02:00恢复为当地标准时间01:00

参见维基百科

乔达时间 图书馆使这类工作容易得多

一个在乔达时代的人知道自己。要使用/GMT无时间偏移,请传递内置的常量DateTimeZone.UTC

要仅打印日期时间的日期部分,请使用

//©2013巴西尔布尔克。此源代码可以由任何对此承担全部责任的人自由使用。 //导入org.joda.time.*; //导入org.joda.time.format.*; 字符串日期字符串=2008-03-08; //在这个例子中,withTimeAtStartOfDay可能是多余的,但我喜欢它自己记录我们对一天的关注,而不是一天中的某个特定时间。 DateTime DateTime=新的DateTime日期字符串,DateTimeZone.UTC.withTimeAtStartOfDay; DateTime dateTimePlus1=DateTime.plusDays 1.带有时间开始日期; DateTime dateTimePlus2=DateTime.plusDays 2.带时间开始日期; DateTime dateTimePlus3=DateTime.plusDays 3.带时间开始日期; 转储到控制台

System.out.printlndateTime:+dateTime; System.out.printlndateTime日期部分:+ISODateTimeFormat.date.withZone DateTimeZone.UTC.print dateTime; System.out.printlndateTimePlus1:+dateTimePlus1; System.out.printlndateTimePlus2:+dateTimePlus2; System.out.printlndateTimePlus3:+dateTimePlus3; 当运行时

日期时间:2008-03-08T00:00:00.000Z 日期时间日期部分:2008-03-08 dateTimePlus1:2008-03-09T00:00:00.000Z dateTimePlus2:2008-03-10T00:00:00.000Z dateTimePlus3:2008-03-11T00:00:00.000Z
由于时区设置为UTC,夏令时是否重要?根据本节,UTC不知道DST@srikfreak但日历不是UTC格式。发生这种情况时的诀窍是检查您的假设:我刚刚打印了日历本身,没有SDF,问题很明显,因为日历的TZ发生了变化。谢谢。我想你所说的打印日历是指调用字符串。我又做了一些测试。当添加一个日历日时,我的时区CST中的日历将在春季提前日添加23小时,在秋季推迟日添加25小时。如果SimpleDataFormat也是CST,它将正确打印当天,但如果是UTC,则添加的23个小时并不能让您完全进入第二天。到了秋天,25个小时增加了UTC一天内的跳跃。如果日历为UTC,则始终添加24小时。关键的一点是日历和SimpleDateFormat应该在时区上达成一致。但是,只有当你告诉他们使用相同的TZ时,他们才会在时区上达成一致。欢迎来到Java时间/日期处理的世界。吹毛求疵:sdf.setTimeZone不会更改日历的时间,它会更改sdf的时间,wh ich然后用于设置日历的时间。