【问题标题】:Calculating years between from a leap year计算闰年之间的年份
【发布时间】:2016-01-01 20:55:10
【问题描述】:

在计算两个日期之间的年份时,第二个日期是从第一个日期计算出来的(这是我正在处理的一个简化示例),LocalDate 和 Period 似乎计算年份略有不同。

例如,

LocalDate date = LocalDate.of(1996, 2, 29);
LocalDate plusYear = date.plusYears(1);
System.out.println(Period.between(date, plusYear).getYears());

同时

LocalDate date = LocalDate.of(1996, 3, 29);
LocalDate plusYear = date.plusYears(1);
System.out.println(Period.between(date, plusYear).getYears());

尽管明确添加了年份,但首先Period 将年份返回为0,而第二种情况返回1。

有没有巧妙的方法解决这个问题?

【问题讨论】:

  • @dotvav:给出的例子对我来说似乎很清楚......我看不出有什么问题。
  • 这并不是他们计算一年的方式不同 - 这只是日历算术的一个怪癖。正如你所说,这种行为是合理的。你能准确地描述你想要的行为吗?
  • (虽然说是合理的,但野田时间在这种情况下实际上确实给出了1年的期限:)
  • @MenoHochschild:我强烈怀疑算术的这两种“模式”在某些情况下都是有用的,而在其他情况下则不太有用。我认为在 Noda Time 的操作完全按照文档记录的情况下故意声称存在 错误 是不合理的。 (请注意,这也是 OP 所希望的行为,这表明我认为它在某些情况下很有用是正确的......)
  • 在 1997 年 1 月 1 日午夜和 1998 年 1 月 1 日午夜之间,地球并没有绕太阳公转一周,因为一个太阳年超过 365 天......在这里将天文学视为真理的最终来源。正如 Misha 所说 - 无论哪种方式都是合理的 IMO,只要它有记录即可。

标签: java java-8 java-time leap-year


【解决方案1】:

这个问题具有哲学性质,涉及时间测量和日期格式约定等少数问题。

LocalDate 是ISO 8601 日期交换标准的实现。 Java Doc 明确指出该类不表示时间,而仅提供标准日期表示法。

API 仅提供对符号本身的简单操作,所有计算都是通过递增 Year、Month 或 Day 来完成的给定日期。

换句话说,当调用 LocalDate.plusYears() 时,您添加的概念年份是每年 365 天,而不是一年内的确切时间。

这使得 Day 成为可以添加到由LocalDate 表示的日期的最低时间单位。

在人类的理解中,日期不是一个时刻,而是一个时期。

它以 00h 00m 00s (...) 开始,以 23h 59m 59s (...) 结束。

LocalDate 但是避免了时间测量和人类时间单位(小时、天、月和 >year 都可以有不同的长度)并将日期表示法简单地建模为以下的元组:

(years, months within a year, days within a month )

从纪元开始计算。

在这种解释中,Day 是影响日期的最小单位是有道理的。

如下示例:

LocalDate date = LocalDate.of(1996, 2, 29);
LocalDate plusSecond = date.plus(1, ChronoUnit.SECONDS);

返回

java.time.temporal.UnsupportedTemporalTypeException: Unsupported unit: Seconds

...这表明,使用 LocalDate 并添加秒数(或更小的单位来驱动精度),您无法克服问题中列出的限制。

查看实现,您会发现LocalDate.plusYears() 在添加年份后调用resolvePreviousValid()。然后此方法检查闰年并以下列方式修改 day 字段:

day = Math.min(day, IsoChronology.INSTANCE.isLeapYear((long)year)?29:28);

换句话说,它通过有效地扣除 1 天来纠正它。

您可以使用 Year.length() 返回给定年份的天数,并在闰年返回 366。所以你可以这样做:

LocalDate plusYear = date.plus(Year.of(date.getYear()).length(), ChronoUnit.DAYS);

您仍会遇到以下奇怪情况(为简洁起见,请致电 Year.length() 替换为日期):

LocalDate date = LocalDate.of(1996, 2, 29); 
LocalDate plusYear = date.plus(365, ChronoUnit.DAYS);
System.out.println(plusYear);
Period between = Period.between(date, plusYear);
System.out.println( between.getYears() + "y " + 
                    between.getMonths() + "m " + 
                    between.getDays() + "d");

返回

1997-02-28
0y 11m 30d

然后

LocalDate date = LocalDate.of(1996, 3, 29);
LocalDate plusYear = date.plus(365, ChronoUnit.DAYS);
System.out.println(plusYear);
Period between = Period.between(date, plusYear);
System.out.println( between.getYears() + "y " +
                    between.getMonths() + "m " +
                    between.getDays() + "d");

返回

1997-03-29
1y 0m 0d

最后:

LocalDate date = LocalDate.of(1996, 2, 29);
LocalDate plusYear = date.plus(366, ChronoUnit.DAYS);
System.out.println(plusYear);
Period between = Period.between(date, plusYear);
System.out.println( between.getYears() + "y " +
                    between.getMonths() + "m " +
                    between.getDays() + "d");

返回:

1997-03-01
1y 0m 1d

请注意,将日期移动 366 而不是 365 天会将时间段从 11 个月零 30 天 增加到 1一年零 1 天(增加 2 天!)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多