java - 从闰年计算年数

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

当计算两个日期之间的年数时,第二个日期是从第一个日期开始计算的(这是我正在处理的一个简化示例),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

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

最佳答案

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

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

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

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

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

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

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

LocalDate 然而避免了时间测量和人类时间单位模糊的问题(小时 , 和 year 都可以有不同的长度)和模型日期符号简单地作为一个元组:

(年,一年中的几个月,一个月中的几天)

从纪元开始计算。

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

例如:

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 天!)。

关于java - 从闰年计算年数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32944569/

相关文章:

java - Android 应用程序未将详细信息提交到外部数据库

java - 将 JSON 映射到现有的 pojo 对象

java - 将键绑定(bind)添加到 JButton 以从操作命令获取它们的操作?

java - 为什么 JVM HeapMemoryUsage init 值大于 commited/max 值?

maven - 找不到类 AWS lambda 异常

java - 在 Java 中将浮点值四舍五入为最接近的整数的最有效方法是什么?

java - 如何实现用于流式传输斐波那契数的 Spliterator?

java - 将 java.time.LocalDate 转换为 java.util.Date 类型

Scala 使用外部 val 生成时间序列数据

Java.time - 解析月份的名称不起作用