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

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

例如,

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】:

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

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

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

换句话说,当调用 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
    相关资源
    最近更新 更多