【问题标题】:Adding one month to date fails using Joda-Time DateTime in Java在 Java 中使用 Joda-Time DateTime 添加一个月至今失败
【发布时间】:2015-01-22 06:43:35
【问题描述】:

我尝试使用 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"? 这三天在哪里?

【问题讨论】:

  • 可能你需要专门处理2月份的情况。正如下面的答案所暗示的,这就是 DateTime 的行为方式。
  • @mawia 很多月份都少于 31 天。
  • 这里是获取当月最后一天的正确方法:stackoverflow.com/questions/9711454/…
  • @Gatschet 你想要什么行为?您看到的行为是文档所承诺的方法 plusMonths()
  • 现在我看到了我的问题。如果我在 2 月 28 日与某人交谈,我们将在一个月后见到我们,我指的是 3 月 28 日!不是 3 月 31 日....

标签: java datetime jodatime


【解决方案1】:

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

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

【讨论】:

  • 或减去一秒/毫秒
【解决方案2】:

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

这是在doc 中指定的:

计算将尽最大努力只更改月份字段 保留一个月的同一天。但是,在某些情况下,它 可能需要更改较小的字段。例如,2007-03-31 加 一个月不能导致2007-04-31,所以调整了月份的日期 至 2007 年 4 月 30 日。

【讨论】:

  • 我的问题是,我有一个应用程序,它有一个开始和结束时间,以及一些 bin 的分辨率来显示一些数据。例如,我可以选择 1.1.2013 00:00:00 到 1.1.2014 00:00:00,分辨率为月份。现在我可以在 bin 中看到每个月的一些数据。此设置将存储在带有本地时间的模板中。当有人在不同的时区使用相同的模板时,就会出现问题。那么开始时间和结束时间例如是 31.12.2012 23:00:00 到 31.12.2013 23:00:00(-1 小时)......
  • ... 1 月和 2 月没有问题。但是三月只有 28 天,因为 28.2.2013 加上一个月的结果是 28.3.2013!这样客户就没有满月了……
【解决方案3】:

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

相反,我建议您使用每个月的第一天并减去一毫秒。这样一来,计算结果就会如你所愿。

【讨论】:

    【解决方案4】:

    正如上面评论中提到的,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
    

    【讨论】:

      【解决方案5】:

      半开

      您的问题是一个示例,说明为什么通常最好使用“半开”方法来跟踪时间跨度。在半开(符号:[))中,如果时间跨度包含,则开始,而结束不包含。所以“一个月”是指当月第一天的第一刻,一直到但不包括下个月第一天的第一刻。

      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
      

      【讨论】:

        【解决方案6】:

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

        例如在日历对象中你可以这样做:

        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date());
        
        cal.add(Calendar.MONTH, 1);
        

        【讨论】:

        • 我在示例中将 DateTime 替换为 java.util.Calendar 但结果是一样的! cal.add(Calendar.MONTH, 1) from "2013-02-28T23:59:59.000+01:00" 仍然是 "2013-03-28T23:59:59.000+01:00"
        【解决方案7】:

        这对我有用:

        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
        

        【讨论】:

          【解决方案8】:

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

              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());
              }
          

          【讨论】:

            【解决方案9】:

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

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

            【讨论】:

              猜你喜欢
              • 2013-05-03
              • 1970-01-01
              • 1970-01-01
              • 2022-10-21
              • 2015-05-22
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-11-23
              相关资源
              最近更新 更多