在Java8 Date API中将持续时间转换为年份?

时间:2016-02-04 00:05:13

标签: java date java-8 duration java-time

我有一个远在过去的约会。
我发现了这个日期和现在之间的持续时间。
现在我想知道 - 多年来这有多少?

我使用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) );

3 个答案:

答案 0 :(得分:6)

您是否尝试过使用LocalDateTimeDateTime代替LocalDate?按照设计,后者不支持小时/分钟/秒/等,因此当您尝试向其添加秒时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

答案 1 :(得分:3)

使用a b c d e f 获取两个Period个对象之间的年数:

LocalDate

答案 2 :(得分:3)

虽然@Matt Ball接受的答案试图巧妙地使用Java-8-API,但我会提出以下异议:

您的要求并不准确,因为无法将秒数准确转换为年。

原因是:

  • 最重要的是:月份的长度不同(从28到31)。
  • 有时闰日(2月29日)也会对计算年度增量产生影响。
  • 格列高利的切入:你从1415年开始,这是在第一次格里高利历改革取消整整十天之前,在英格兰甚至11天,在俄罗斯更多。在旧儒略历中的岁月有不同的闰年规则。
  • 历史日期未定义为第二精度。你能举例说明黑斯廷斯战役的瞬间/时刻吗?我们甚至不知道确切的时间,只是一天。假设在一天开始的午夜已经是一个粗略的,可能是错误的假设。
  • 影响一天长度的时区效应(23h,24h,25h或甚至不同的其他长度)。
  • 闰秒(异国情调)

也许是对您的代码最重要的反对意见:

我无法想象1415年的日期供应商有意将这样的日期解释为格里高利日期。

我理解从几秒到几年的转换的愿望,但只能是一个近似值,无论您选择什么作为解决方案。因此,如果您有像1415这样的年份,我建议遵循非常简单的近似值:

Duration d = ...;
int approximateYears = (int) (d.toDays() / 365.2425);

对我来说,只要我们真的想在这样一个用例中使用第二个持续时间,就足以满足历史背景。您似乎无法更改从外部来源获得的输入(否则最好联系持续时间供应商并询问是否可以提供天数)。无论如何,你必须问自己想要应用什么样的年份定义。

旁注:

您的投诉"为什么能够让JAVA做出让我做的事情?"与新的java.time-API的字符不匹配。

您希望API是类型安全的,但 java.time(JSR-310)并非设计为类型安全且严重依赖于运行时异常。编译器不会帮助您使用此API。相反,如果任何给定的时间单位适用于任何给定的时间类型,您必须查阅文档。您可以在Temporal.isSupported(TemporalUnit)的任何具体实现的文档中找到这样的答案。无论如何,编译安全的愿望是可以理解的(我自己也尽力将我自己的时间库Time4J实现为类型安全)但JSR-310的设计已经完成了。

如果您在java.time.DurationLocalDateTime上应用Instant,也存在一个微妙的陷阱,因为结果不完全可比(第一种类型的秒数在本地时间轴上定义,而秒Instant的定义在全球时间轴上定义。因此,即使没有像@Matt Ball所接受的回答中那样的运行时异常,我们也必须仔细考虑这种计算的结果是否合理且值得信赖。