4

DateTime我尝试使用 java和 method从给定的开始日期添加整月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"呢?这三天在哪里?

4

9 回答 9

7

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

注意:在原始 Java Date 类中,在将一个月添加到一月后,您会得到三月 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始终是一个月的第一天。

于 2013-11-01T09:11:19.063 回答
4

因为 2013 年 2 月只有 28 天。所以当您在 1 个月后添加时,它将保持为每个月的 28 日。

这是在文档中指定的:

计算将尽最大努力仅更改保留月份同一天的月份字段。但是,在某些情况下,可能需要更改较小的字段。例如,2007-03-31 加一个月不能得出 2007-04-31,因此将月份日期调整为 2007-04-30。

于 2013-11-01T09:07:36.533 回答
1

您不能将 2 月 31 日更改为 2 月 31 日,并且该对象无法记住您指的是该月的最后一天而不是 28 日。

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

于 2013-11-01T09:11:03.717 回答
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
于 2013-11-03T04:38:55.843 回答
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
于 2014-05-06T07:21:57.470 回答
0

如果要使用日期和时间,可以使用 java Calendar 对象。阅读有关 java Calendar 对象的信息,您可以完成这项工作。

例如在 Calendar 对象中,您可以这样做:

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

cal.add(Calendar.MONTH, 1);
于 2013-11-01T09:13:28.887 回答
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
于 2013-11-01T10:00:08.920 回答
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());
    }
于 2014-05-06T07:00:20.650 回答
0

在增加月份前加一天,然后减一天:

new DateTime(date).plusDays(1).plusMonths(months).minusDays(1).toDate()
于 2014-06-18T06:27:11.840 回答