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
的结果,但如果numberOfDays
是702
,我期待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,但您无法取回已丢弃的分数。相反,您需要以支持分数的类型执行除法; float
、double
或 BigDecimal
。
链接
相关问题(骗子?):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 模块)