将持续时间转换为 Java8 日期 API 中的年数?
Converting duration to years in Java8 Date API?
我在很久以前有个约会。
我发现了这个日期和现在之间的持续时间。
现在我想知道 - 这是多少年?
我使用 Java8 API 想出了这个解决方案。
这是一个可怕的解决方案,因为我必须首先手动将持续时间转换为天数,因为否则会有 UnsupportedTemporalTypeException
- LocalDate.plus(SECONDS)
出于任何原因不受支持。
即使编译器允许这个调用。
是否可以更简洁地将 Duration
转换为年?
LocalDate dateOne = LocalDate.of(1415, Month.JULY, 6);
Duration durationSinceGuss1 = Duration.between(LocalDateTime.of(dateOne, LocalTime.MIDNIGHT),LocalDateTime.now());
long yearsSinceGuss = ChronoUnit.YEARS.between(LocalDate.now(),
LocalDate.now().plus(
TimeUnit.SECONDS.toDays(
durationSinceGuss1.getSeconds()),
ChronoUnit.DAYS) );
/*
* ERROR -
* LocalDate.now().plus(durationSinceGuss1) causes an Exception.
* Seconds are not Supported for LocalDate.plus()!!!
* WHY OR WHY CAN'T JAVA DO WHAT COMPILER ALLOWS ME TO DO?
*/
//long yearsSinceGuss = ChronoUnit.YEARS.between(LocalDate.now(), LocalDate.now().plus(durationSinceGuss) );
/*
* ERROR -
* Still an exception!
* Even on explicitly converting duration to seconds.
* Everything like above. Seconds are just not allowed. Have to convert them manually first e.g. to Days?!
* WHY OR WHY CAN'T YOU CONVERT SECONDS TO DAYS OR SOMETHING AUTOMATICALLY, JAVA?
*/
//long yearsSinceGuss = ChronoUnit.YEARS.between(LocalDate.now(), LocalDate.now().plus(durationSinceGuss.getSeconds(), ChronoUnit.SECONDS) );
您是否尝试过使用 LocalDateTime
或 DateTime
而不是 LocalDate
?根据设计,后者不支持 hours/minutes/seconds/etc,因此当您尝试向其添加秒数时 UnsupportedTemporalTypeException
。
例如,这个有效:
LocalDateTime dateOne = LocalDateTime.of(1415, Month.JULY, 6, 0, 0);
Duration durationSinceGuss1 = Duration.between(dateOne, LocalDateTime.now());
long yearsSinceGuss = ChronoUnit.YEARS.between(LocalDateTime.now(), LocalDateTime.now().plus(durationSinceGuss1) );
System.out.println(yearsSinceGuss); // prints 600
使用Period
获取两个LocalDate
对象之间的年数:
LocalDate before = LocalDate.of(1415, Month.JULY, 6);
LocalDate now = LocalDate.now();
Period period = Period.between(before, now);
int yearsPassed = period.getYears();
System.out.println(yearsPassed);
虽然@Matt Ball 接受的答案试图巧妙地使用 Java-8-API,但我会提出以下异议:
您的要求不准确,因为无法将秒准确转换为年。
原因是:
- 最重要的是:月份的天数不同(从 28 天到 31 天)。
- 年份有时会有闰日(2 月 29 日),这也会影响计算年份增量。
- 公历转换:您从 1415 年开始,这远远早于第一次公历改革,该改革取消了整整十天,在英国甚至是 11 天,在俄罗斯更多。旧儒略历的年份有不同的闰年规则。
- 历史日期未定义为精确到秒。例如,你能描述一下黑斯廷斯战役的 instant/moment 吗?我们甚至不知道确切的时间,只知道那天。假设午夜在一天的开始已经是一个粗略的并且可能是错误的假设。
- 影响一天长度(23 小时、24 小时、25 小时或什至其他不同长度)的时区效应。
- 闰秒(异国情调)
也许对您的代码最重要的反对意见:
我无法想象1415年日期的供应商有意将这样的日期解释为公历
我理解从秒到年的愿望,但它只能是一个近似值,无论您选择什么作为解决方案。因此,如果您有像 1415 这样的年份,我建议您遵循非常简单的近似值:
Duration d = ...;
int approximateYears = (int) (d.toDays() / 365.2425);
对我来说,只要我们真的想为这样的用例使用基于秒的持续时间,在历史背景下就足够了。似乎您无法更改从外部来源获得的输入(否则最好联系持续时间供应商并询问是否可以提供天数)。不管怎样,你得问问自己你想应用什么样的年份定义。
旁注:
您的投诉"WHY OR WHY CAN'T JAVA DO WHAT COMPILER ALLOWS ME TO DO?"与新java.time-API.
的字符不匹配
您希望 API 是类型安全的,但 java.time
(JSR-310) 并未设计为类型安全的并且严重依赖运行时异常。编译器不会帮助你 API。相反,如果任何给定的时间单位是否适用于任何给定的时间类型,您必须在有疑问时查阅文档。您可以在 Temporal.isSupported(TemporalUnit) 的任何具体实现的文档中找到这样的答案。无论如何,编译安全的愿望是可以理解的(我自己已经尽最大努力将我自己的时间库 Time4J 实现为类型安全的)但是 JSR-310 的设计已经是一成不变的。
如果在 LocalDateTime
或 Instant
上应用 java.time.Duration
也会有一个微妙的陷阱,因为结果不完全可比(第一种类型的秒数在本地时间轴上定义而 Instant
的秒数在全球时间轴上定义)。所以即使没有像@Matt Ball 接受的答案那样的运行时异常,我们也必须仔细考虑这样的计算结果是否合理可信。
我在很久以前有个约会。
我发现了这个日期和现在之间的持续时间。
现在我想知道 - 这是多少年?
我使用 Java8 API 想出了这个解决方案。
这是一个可怕的解决方案,因为我必须首先手动将持续时间转换为天数,因为否则会有 UnsupportedTemporalTypeException
- LocalDate.plus(SECONDS)
出于任何原因不受支持。
即使编译器允许这个调用。
是否可以更简洁地将 Duration
转换为年?
LocalDate dateOne = LocalDate.of(1415, Month.JULY, 6);
Duration durationSinceGuss1 = Duration.between(LocalDateTime.of(dateOne, LocalTime.MIDNIGHT),LocalDateTime.now());
long yearsSinceGuss = ChronoUnit.YEARS.between(LocalDate.now(),
LocalDate.now().plus(
TimeUnit.SECONDS.toDays(
durationSinceGuss1.getSeconds()),
ChronoUnit.DAYS) );
/*
* ERROR -
* LocalDate.now().plus(durationSinceGuss1) causes an Exception.
* Seconds are not Supported for LocalDate.plus()!!!
* WHY OR WHY CAN'T JAVA DO WHAT COMPILER ALLOWS ME TO DO?
*/
//long yearsSinceGuss = ChronoUnit.YEARS.between(LocalDate.now(), LocalDate.now().plus(durationSinceGuss) );
/*
* ERROR -
* Still an exception!
* Even on explicitly converting duration to seconds.
* Everything like above. Seconds are just not allowed. Have to convert them manually first e.g. to Days?!
* WHY OR WHY CAN'T YOU CONVERT SECONDS TO DAYS OR SOMETHING AUTOMATICALLY, JAVA?
*/
//long yearsSinceGuss = ChronoUnit.YEARS.between(LocalDate.now(), LocalDate.now().plus(durationSinceGuss.getSeconds(), ChronoUnit.SECONDS) );
您是否尝试过使用 LocalDateTime
或 DateTime
而不是 LocalDate
?根据设计,后者不支持 hours/minutes/seconds/etc,因此当您尝试向其添加秒数时 UnsupportedTemporalTypeException
。
例如,这个有效:
LocalDateTime dateOne = LocalDateTime.of(1415, Month.JULY, 6, 0, 0);
Duration durationSinceGuss1 = Duration.between(dateOne, LocalDateTime.now());
long yearsSinceGuss = ChronoUnit.YEARS.between(LocalDateTime.now(), LocalDateTime.now().plus(durationSinceGuss1) );
System.out.println(yearsSinceGuss); // prints 600
使用Period
获取两个LocalDate
对象之间的年数:
LocalDate before = LocalDate.of(1415, Month.JULY, 6);
LocalDate now = LocalDate.now();
Period period = Period.between(before, now);
int yearsPassed = period.getYears();
System.out.println(yearsPassed);
虽然@Matt Ball 接受的答案试图巧妙地使用 Java-8-API,但我会提出以下异议:
您的要求不准确,因为无法将秒准确转换为年。
原因是:
- 最重要的是:月份的天数不同(从 28 天到 31 天)。
- 年份有时会有闰日(2 月 29 日),这也会影响计算年份增量。
- 公历转换:您从 1415 年开始,这远远早于第一次公历改革,该改革取消了整整十天,在英国甚至是 11 天,在俄罗斯更多。旧儒略历的年份有不同的闰年规则。
- 历史日期未定义为精确到秒。例如,你能描述一下黑斯廷斯战役的 instant/moment 吗?我们甚至不知道确切的时间,只知道那天。假设午夜在一天的开始已经是一个粗略的并且可能是错误的假设。
- 影响一天长度(23 小时、24 小时、25 小时或什至其他不同长度)的时区效应。
- 闰秒(异国情调)
也许对您的代码最重要的反对意见:
我无法想象1415年日期的供应商有意将这样的日期解释为公历
我理解从秒到年的愿望,但它只能是一个近似值,无论您选择什么作为解决方案。因此,如果您有像 1415 这样的年份,我建议您遵循非常简单的近似值:
Duration d = ...;
int approximateYears = (int) (d.toDays() / 365.2425);
对我来说,只要我们真的想为这样的用例使用基于秒的持续时间,在历史背景下就足够了。似乎您无法更改从外部来源获得的输入(否则最好联系持续时间供应商并询问是否可以提供天数)。不管怎样,你得问问自己你想应用什么样的年份定义。
旁注:
您的投诉"WHY OR WHY CAN'T JAVA DO WHAT COMPILER ALLOWS ME TO DO?"与新java.time-API.
的字符不匹配您希望 API 是类型安全的,但 java.time
(JSR-310) 并未设计为类型安全的并且严重依赖运行时异常。编译器不会帮助你 API。相反,如果任何给定的时间单位是否适用于任何给定的时间类型,您必须在有疑问时查阅文档。您可以在 Temporal.isSupported(TemporalUnit) 的任何具体实现的文档中找到这样的答案。无论如何,编译安全的愿望是可以理解的(我自己已经尽最大努力将我自己的时间库 Time4J 实现为类型安全的)但是 JSR-310 的设计已经是一成不变的。
如果在 LocalDateTime
或 Instant
上应用 java.time.Duration
也会有一个微妙的陷阱,因为结果不完全可比(第一种类型的秒数在本地时间轴上定义而 Instant
的秒数在全球时间轴上定义)。所以即使没有像@Matt Ball 接受的答案那样的运行时异常,我们也必须仔细考虑这样的计算结果是否合理可信。