【问题标题】:Java Calendar inconsistent resultsJava Calendar 不一致的结果
【发布时间】:2012-10-23 19:20:28
【问题描述】:

我已经搜索过这个问题的答案,但似乎找不到。有人能解释一下为什么下面的代码没有给我每个月的 1,而是跳到 31 几个月吗?用于需要确定下个月日期的报表事件。

DateFormat formatter = new SimpleDateFormat("dd-MMM-yy"); 
Calendar cal = Calendar.getInstance( TimeZone
    .getTimeZone( "America/Los_Angeles" ) );
DateFormat formatter = new SimpleDateFormat("dd-MMM-yy"); 
Date date;
try
{
  date = (Date)formatter.parse("01-JUN-12");
  cal.setTime(date);
  //cal.set(2012, 05, 01);

  long now = new Date().getTime();
  int frequency = 1;

  System.out.println("Current calendar time=" + cal.getTime().toString()) ;

  while (cal.getTime().getTime() < now)
  {
      cal.add( Calendar.MONTH, frequency );
      System.out.println("In loop - current calendar time=" + cal.getTime().toString()) ;
  }
}
catch (ParseException e)
{
  // TODO Auto-generated catch block
  e.printStackTrace();
} 

输出是:

Current calendar time=Fri Jun 01 00:00:00 EDT 2012

In loop - current calendar time=Sun Jul 01 00:00:00 EDT 2012

In loop - current calendar time=Tue Jul 31 00:00:00 EDT 2012

In loop - current calendar time=Fri Aug 31 00:00:00 EDT 2012

In loop - current calendar time=Mon Oct 01 00:00:00 EDT 2012

In loop - current calendar time=Wed Oct 31 00:00:00 EDT 2012

注意它是如何跳到 31,然后又回到 1。如果我使用 Calendar.set() 代替,输出是正确的:

Current calendar time=Fri Jun 01 15:14:26 EDT 2012

In loop - current calendar time=Sun Jul 01 15:14:26 EDT 2012

In loop - current calendar time=Wed Aug 01 15:14:26 EDT 2012

In loop - current calendar time=Sat Sep 01 15:14:26 EDT 2012

In loop - current calendar time=Mon Oct 01 15:14:26 EDT 2012

In loop - current calendar time=Thu Nov 01 15:14:26 EDT 2012

这似乎是 1) 日历 API 的错误,或 2) 对日历 API 的工作原理缺乏了解。无论哪种情况,我只想要下个月(同一天),除非某些月份和日期当然有问题。但是上面的场景让我很困惑。一个月中的任何其他日子都不会发生这种情况,只会发生在第一天。

【问题讨论】:

  • 在 joda 时间更改库的日期和时间,在这个库中你找不到任何错误
  • 不幸的是,我必须使用日历 API,所以切换不是这个项目的选项。
  • Calendar 可以说是 JDK 中最糟糕的类:它几乎无法使用。在内部,它看起来像是由(糟糕的)初级程序员编写的。使用乔达时间。就是这么简单。你应该告诉谁让你使用Calendar,他们应该尝试在与编程无关的领域工作。

标签: java date calendar


【解决方案1】:

这是一个时区问题。将您现有的设置行替换为以下内容,以便日历和格式化程序都使用相同的时区:

    TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
    formatter.setTimeZone(tz);
    Calendar cal = Calendar.getInstance(tz);

一切正常:

Current calendar time=Fri Jun 01 08:00:00 BST 2012
In loop - current calendar time=Sun Jul 01 08:00:00 BST 2012
In loop - current calendar time=Wed Aug 01 08:00:00 BST 2012
In loop - current calendar time=Sat Sep 01 08:00:00 BST 2012
In loop - current calendar time=Mon Oct 01 08:00:00 BST 2012
In loop - current calendar time=Thu Nov 01 07:00:00 GMT 2012

您可能希望将 toString() 调用替换为 formatter.format() 调用,以便输出也位于正确的时区(如果您在 EDT,它可能看起来不错,但我在英国时区,如您所见)。

【讨论】:

  • 太好了,成功了!忘了我们是从一家初创公司获得这个软件的,而且他们位于加利福尼亚!
【解决方案2】:

不仅仅是cal.add( Calendar.MONTH, frequency );,试试这个:

cal.add(Calendar.MONTH, frequency);
cal.set(Calendar.DAY_OF_MONTH, cal.getActualMinimum(Calendar.DAY_OF_MONTH));

【讨论】:

  • 这提供了更一致的结果,但它现在显示每个月的 2 日:当前日历时间=Fri Jun 01 00:00:00 EDT 2012 In loop - current calendar time=Sat Jun 02 00: 00:00 EDT 2012 循环中 - 当前日历时间 = Mon Jul 02 00:00:00 EDT 循环中 - 当前日历时间 = Thu Aug 02 00:00:00 EDT 2012 循环中 - 当前日历时间 = Sun Sep 02 00 :00:00 EDT 2012 In loop - 当前日历时间 = Tue Oct 02 00:00:00 EDT 2012 In loop - 当前日历时间 = Fri Nov 02 00:00:00 EDT 2012
  • 在这一点上,我想这与时区有关。
【解决方案3】:

您正在以系统默认时区打印时间。使用格式化程序来显示它。

为格式化程序设置相同的时区:

DateFormat formatter = new SimpleDateFormat("dd-MMM-yy");
formatter.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));

并用它来显示时间:

while (cal.getTime().getTime() < now) { 
    cal.add(Calendar.MONTH, frequency);
    System.out.println("In loop - current calendar time=" + formatter.format(cal.getTime()));
}

【讨论】:

  • 谢谢!我应该意识到这很简单!
【解决方案4】:

来自 Javadoc Calendar

add(f, delta) 将 delta 添加到字段 f。这相当于调用 set(f, get(f) + delta) 进行了两次调整:

  • 添加规则 1. 调用后字段 f 的值减去 调用之前的字段 f 是 delta,以任何溢出为模 发生在 f 场。当字段值超过其值时发生溢出 范围,因此,下一个更大的字段增加或 递减且字段值调整回其范围内。

  • 添加规则 2。如果期望较小的字段是不变的,但它是 由于变化,它不可能等于它的先前值 在字段 f 更改或其他 约束,比如时区偏移量变化,那么它的值为 调整到尽可能接近其预期值。一个更小的 字段表示更小的时间单位。 HOUR 是一个比 DAY_OF_MONTH。不调整较小的字段 期望是不变的。日历系统确定哪些字段 预计是不变的。

此外,与 set() 不同,add() 强制立即重新计算日历的毫秒数和所有字段。

示例:考虑一个最初设置为 1999 年 8 月 31 日的 GregorianCalendar。调用 add(Calendar.MONTH, 13) 将日历设置为 2000 年 9 月 30 日。添加规则 1 将 MONTH 字段设置为九月,因为在八月添加了 13 个月给出次年的九月。由于 DAY_OF_MONTH 在 GregorianCalendar 中不能为 9 月的 31,因此添加规则 2 将 DAY_OF_MONTH 设置为 30,这是最接近的可能值。虽然它是一个较小的字段,但 DAY_OF_WEEK 不会被规则 2 调整,因为它预计会随着公历日历中的月份变化而变化。

因此,考虑到您设置的时间,如果日历 API 将月份添加到 Jul,那么它可能会溢出一个多月,因此将其调整回 Jul Midnight

【讨论】:

    猜你喜欢
    • 2011-08-03
    • 1970-01-01
    • 2014-09-21
    • 2021-03-27
    • 2021-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多