在Java中使用Joda-Time DateTime失败了一个月

时间:2013-11-01 09:05:13

标签: java datetime jodatime

我尝试使用java DateTime和方法plusMonths()从给定的开始日期添加完整的月份。

当我的开始时间是一个月初时,一切都像预期的那样:

DateTime startOfMonth = new DateTime(2013, 1, 1, 00, 00, 00);
    System.out.println(startOfMonth.toString());
    for (int i = 0; i < 12; i++) {
        startOfMonth = startOfMonth.plusMonths(1);
        System.out.println(startOfMonth.toString());
}

输出是每个月的第一天,就像预期的一样,一切都很棒!

2013-01-01T00:00:00.000+01:00
2013-02-01T00:00:00.000+01:00
2013-03-01T00:00:00.000+01:00
2013-04-01T00:00:00.000+02:00
2013-05-01T00:00:00.000+02:00
2013-06-01T00:00:00.000+02:00
2013-07-01T00:00:00.000+02:00
2013-08-01T00:00:00.000+02:00
2013-09-01T00:00:00.000+02:00
2013-10-01T00:00:00.000+02:00
2013-11-01T00:00:00.000+01:00
2013-12-01T00:00:00.000+01:00
2014-01-01T00:00:00.000+01:00

但是,当我将我的例子改为月末时,它并没有返回我想要的东西!

System.out.println("");
DateTime endOfMonth = new DateTime(2012, 12, 31, 23, 59, 59);
System.out.println(endOfMonth.toString());
for (int i = 0; i < 12; i++) {
    endOfMonth = endOfMonth.plusMonths(1);
    System.out.println(endOfMonth.toString());
}

返回:

2012-12-31T23:59:59.000+01:00
2013-01-31T23:59:59.000+01:00
2013-02-28T23:59:59.000+01:00
2013-03-28T23:59:59.000+01:00
2013-04-28T23:59:59.000+02:00
2013-05-28T23:59:59.000+02:00
2013-06-28T23:59:59.000+02:00
2013-07-28T23:59:59.000+02:00
2013-08-28T23:59:59.000+02:00
2013-09-28T23:59:59.000+02:00
2013-10-28T23:59:59.000+01:00
2013-11-28T23:59:59.000+01:00
2013-12-28T23:59:59.000+01:00

那么,为什么"2013-02-28T23:59:59.000+01:00"加上一个月不是"2013-03-31T23:59:59.000+01:00"? 这三天在哪里?

9 个答案:

答案 0 :(得分:8)

日期操作的问题是月份有不同的天数。在1月份,你有31天,2月只有28天。如果你将“一个月”添加到1月31日,软件无法猜出你想要达到的目标,所以它会增加月份的增量,这将给你2月31日 - 无效。接下来的步骤就是调和产生这些奇怪结果的日期。

注意:在原始的Java Date类中,在1月份添加一个月之后,你会得到3月2日或3日,这不是更好: - )

在月末迭代的正确方法是迭代当月的第一天并减去一天(或一毫秒):

DateTime startOfMonth = new DateTime(2013, 1, 1, 00, 00, 00);
System.out.println(startOfMonth.toString());
for (int i = 0; i < 12; i++) {
    startOfMonth = startOfMonth.plusMonths(1);
    DateTime endOfMonth = startOfMonth.minusDays(1); // magic here
    System.out.println(startOfMonth + "-" + endOfMonth);
}

如果您只需要一个日期范围,请使用半开放范围[start,end),其中end始终是一个月中的第一个。

答案 1 :(得分:4)

因为2013年2月仅有28天。因此,当您在一个月后加入时,它将保持在每个月的第28天。

这在doc

中指定
  

计算将尽力只更改月份字段   保留当月的同一天。但是,在某些情况下,它   可能有必要改变较小的领域。例如,2007-03-31加   一个月不能导致2007-04-31,所以每月调整一天   到2007-04-30。

答案 2 :(得分:1)

你无法改变到31年2月,对象无法记住你的意思是指本月的最后一天而不是28日。

相反,我建议您使用每个月的第一天并减去一毫秒。这样计算就可以按照你想要的方式进行计算。

答案 3 :(得分:1)

正如上面的评论所述,获得该月最后一天的Joda方法是获取dayOfMonth属性的最大值。

public static void endOfMonth() {
    DateTime startOfMonth = new DateTime(2013, 1, 1, 00, 00, 00);

    for (int i = 0; i < 12; i++) {
        int lastDay = startOfMonth.dayOfMonth().getMaximumValue();
        System.out.println(startOfMonth.withDayOfMonth(lastDay).toString());

        startOfMonth = startOfMonth.plusMonths(1);
    }
}

这会产生:

2013-01-31T00:00:00.000-06:00
2013-02-28T00:00:00.000-06:00
2013-03-31T00:00:00.000-05:00
2013-04-30T00:00:00.000-05:00
2013-05-31T00:00:00.000-05:00
2013-06-30T00:00:00.000-05:00
2013-07-31T00:00:00.000-05:00
2013-08-31T00:00:00.000-05:00
2013-09-30T00:00:00.000-05:00
2013-10-31T00:00:00.000-05:00
2013-11-30T00:00:00.000-06:00
2013-12-31T00:00:00.000-06:00

答案 4 :(得分:1)

半开

您的问题是一个示例,说明为什么通常使用“半开”方法最好地跟踪时间跨度。在半开放(表示法:[))中,如果时间跨度为包含,则开头为独占。因此,“一个月”意味着该月第一天的第一个时刻,并且在的情况下运行,但不包括下个月第一天的第一个时刻。

Joda-Time库提供了一个代表时间跨度的类,Interval。

DateTimeZone timeZone = DateTimeZone.forID( "Europe/Zurich" );
DateTime firstOfYear = new DateTime( 2013, 1, 1, 0, 0, 0, timeZone ).withTimeAtStartOfDay();
Interval month01_Interval = new Interval( firstOfYear, firstOfYear.plusMonths( 1 ).withTimeAtStartOfDay() );

运行时:

month01_Interval : 2013-01-01T00:00:00.000+01:00/2013-02-01T00:00:00.000+01:00

答案 5 :(得分:0)

如果要使用日期和时间,可以使用Java Calendar对象。 阅读有关Java Calendar对象的内容,您可以使其工作。

例如,在Calendar对象中,您可以执行此操作:

Calendar cal = Calendar.getInstance();
cal.setTime(new Date());

cal.add(Calendar.MONTH, 1);

答案 6 :(得分:0)

这对我有用:

public static void main(String[] args) {
    Calendar endOfMonth = Calendar.getInstance();
    endOfMonth.set(Calendar.DAY_OF_MONTH, 31);
    endOfMonth.set(Calendar.MONTH, 11);
    endOfMonth.set(Calendar.YEAR, 2012);

    endOfMonth.set(Calendar.HOUR_OF_DAY, 23);
    endOfMonth.set(Calendar.MINUTE, 59);
    endOfMonth.set(Calendar.SECOND, 59);
    endOfMonth.set(Calendar.MILLISECOND, 59);

    System.out.println(endOfMonth.getTime());
    for (int i = 0; i < 12; i++) {
        endOfMonth.add(Calendar.MONTH, 1);

        if(i >= 2){
            endOfMonth.add(Calendar.MONTH, 1);
            endOfMonth.set(Calendar.DAY_OF_MONTH, 1);
            endOfMonth.add(Calendar.DAY_OF_MONTH, -1);
        }

        System.out.println(endOfMonth.getTime());
    }
}

结果是:

Mon Dec 31 23:59:59 GMT+04:00 2012
Thu Jan 31 23:59:59 GMT+04:00 2013
Thu Feb 28 23:59:59 GMT+04:00 2013
Sun Mar 31 23:59:59 GMT+04:00 2013
Tue Apr 30 23:59:59 GMT+04:00 2013
Fri May 31 23:59:59 GMT+04:00 2013
Sun Jun 30 23:59:59 GMT+04:00 2013
Wed Jul 31 23:59:59 GMT+04:00 2013
Sat Aug 31 23:59:59 GMT+04:00 2013
Mon Sep 30 23:59:59 GMT+04:00 2013
Thu Oct 31 23:59:59 GMT+04:00 2013
Sat Nov 30 23:59:59 GMT+04:00 2013
Tue Dec 31 23:59:59 GMT+04:00 2013

答案 7 :(得分:0)

我认为这种计算的最佳解决方案是修复原始种子日期,并在循环时增加要添加的月数。这样你就不会继续累积任何调整。试试这个:

    System.out.println("");
    DateTime endOfMonth = new DateTime(2012, 12, 31, 23, 59, 59);
    System.out.println(endOfMonth.toString());
    for (int i = 0; i < 12; i++) {
        DateTime newEndOfMonth = endOfMonth.plusMonths(i+1);
        System.out.println(newEndOfMonth.toString());
    }

答案 8 :(得分:0)

在递增月份之前添加一天,然后减去一天:

new DateTime(date).plusDays(1).plusMonths(months).minusDays(1).toDate()