Joda DateTime 到 BigDecimal 分数计算

Posted

技术标签:

【中文标题】Joda DateTime 到 BigDecimal 分数计算【英文标题】:JodaDateTime To BigDecimal Fraction Calculation 【发布时间】:2021-10-25 15:03:27 【问题描述】:

我需要将两个日期之间的差异计算为一个分数,该分数可用于使用compareTo(....) 进行后续 BigDecimal 比较。

输入日期是java.util.Date,所以我尝试将它们转换为joda.time.DateTime,以获取两个日期之间的numberOfDays,并将结果转换为BigDecimal,以便以后进行比较。

如果我以 2019 年 3 月 30 日的 startServiceDate 和 2021 年 1 月 3 日的 endServiceDate 运行以下命令,我得到的结果为 1。

import java.util.Date;
import org.joda.time.DateTime;
import org.joda.time.Days;
        
private BigDecimal getNumberOfYearsService(Date startServiceDate, Date endServiceDate) 
    DateTime startServiceDateTime = new DateTime(startServiceDate);
    DateTime endServiceDateTime = new DateTime(endServiceDate);
    int numberOfDays = Days.daysBetween(startServiceDateTime, endServiceDateTime).getDays();
    return new BigDecimal(numberOfDays/365);

将代码更新为return new BigDecimal(numberOfDays/365).setScale(2,RoundingMode.HALF_UP); 后,我得到1.00 的结果,但如果numberOfDays702,我期待1.92 的结果

【问题讨论】:

想知道你为什么不使用java.time...? 好的,这意味着您可以使用ThreeTen Backport 使java.time 的功能在Java 6 和7 中可用。 我想你可以,但 JodaTime 也是一个库,它不是最新的。 一个提示:你得到的结果可能是你使用 JodaTime 的正确方式,但不是你使用 BigDecimal 的方式,因为你没有 @987654341 @,但实际上你创建了一个new BigDecimal(1),因为702 / 365 导致1。这就是为什么您会收到 1.00 而不是 1.92 Joda-Time 确实得到了维护。制作Frequent releases,主要是更新嵌套的tzdata跟踪时区规则。如果您愿意,您可以继续使用 Joda-Time,甚至可以purchase a support plan。但要使用与 java.time 几乎相同的 API 为未来做好准备,请在 Java 6 中使用 ThreeTen-Backport 【参考方案1】:

BigDecimal.divide() 和 java.time 通过 ThreeTen Backport

我建议您将 java.time 用于您的日期和时间工作。我的建议是:

private static final BigDecimal MILLISEONDS_PER_YEAR_AVERAGE
        = new BigDecimal(ChronoUnit.YEARS.getDuration().toMillis());

private static BigDecimal getNumberOfYearsService(Date startServiceDate, Date endServiceDate) 
    long millisBetween = ChronoUnit.MILLIS.between(startServiceDate.toInstant(),
                                                   endServiceDate.toInstant());
    return new BigDecimal(millisBetween)
            .divide(MILLISEONDS_PER_YEAR_AVERAGE, 7, RoundingMode.HALF_UP);

让我们用你的示例日期来试试吧:

    ZoneId zone = ZoneId.of("America/Scoresbysund");
    Date start = Date.from(LocalDate.of(2019, Month.MARCH, 30).atStartOfDay(zone).toInstant());
    Date end = Date.from(LocalDate.of(2021, Month.MARCH, 1).atStartOfDay(zone).toInstant());
    BigDecimal yearsWithFraction = getNumberOfYearsService(start, end);
    System.out.println(yearsWithFraction);

输出是:

1.9220107

如您所知,岁月的长度并不相同。 ChronoUnit.YEARS.getDuration() 为我们提供了估计的年长度(365 天 5 小时 49 分 12 秒)。

在我的代码中,我考虑到老式的Date 类具有毫秒精度。如果您只想考虑一整天,您可以相应地修改代码。为了计算的精确性,您仍然希望将估计的一年长度转换为毫秒,然后转换为秒。

BigDecimal.divide() 的中间参数 7 指定我希望将结果四舍五入到小数点后 7 位。指定您想要的任何其他数字。

我正在使用 java.time,现代 Java 日期和时间 API。如果您更喜欢使用 Joda-Time,您可能可以编写一个非常相似的解决方案。如果 Joda-Time 没有提供估计的年份长度,请窃取我刚刚提供的金额并将其硬编码到您的代码中。

你的代码出了什么问题?

在 cmets 中已经说过:numberOfDays/365 在整数除法中并给出整数结果。 702 除以 365 得到 1,余数为 337。余数被丢弃。因此,您将 1 转换为 BigDecimal,其值为 if 1。您可以设置其比例并获得例如 1.00,但您无法取回已丢弃的分数。相反,您需要以支持分数的类型执行除法; floatdoubleBigDecimal

链接

相关问题(骗子?):Int division: Why is the result of 1/3 == 0? Oracle tutorial: Date Time 解释如何使用 java.time。

【讨论】:

这个答案不仅正确地解决了问题,而且还以简单的方式描述了解决方案。此外,毫无疑问,使用java.time,modern Date-Time API 肯定是前进的方向。

以上是关于Joda DateTime 到 BigDecimal 分数计算的主要内容,如果未能解决你的问题,请参考以下文章

Java中从LocalDateTime到joda DateTime的不正确日期转换

将带有joda.DateTime的案例类的DStream转换为Spark DataFrame

使用 Jackson 进行 DateTime 反序列化的默认时区(Joda-Time 模块)

Joda time DateTime 错误地存储在数据库中

如何从 org.joda.time.DateTime 转换为 java.time.ZonedDateTime

org.joda.time.DateTime 日期格式