I have a JodaTime Period that I've created from two DateTime instants. Is there a good way to convert that Period into a decimal number of hours?
For instance, I have a Period that goes from 1pm to 1:30pm on Jan 1, 2010. How can I get that Period as 1.5 hours.
In the past I've manually converted using seconds and BigDecimals such as this:
int seconds = myPeriod.toStandardSeconds().getSeconds();
BigDecimal d = new BigDecimal((double) seconds / 3600);
// Round to two decimals
BigDecimal correctResult = d.setScale(2, RoundingMode.HALF_DOWN);
This kind of feels like a hack, not to mention awkward when I start adding Periods together. It seems like there should be a better way.
Any ideas? Thanks
A bit old but I was looking at this recently and you can use the DateTimeFormatterBuilder
to do this. It has a series of methods called appendFraction...
which will append a fraction of an hour, minute, second etc.
This is not exactly what you were asking as you were using a Period
but you can work around this by making a new DateTime
at midnight.
This formatter should parse your DateTime:
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendHourOfDay(1)
.appendLiteral(".")
.appendFractionOfHour(1, 2).toFormatter();
You could then do something like this to convert your period:
DateMidnight midnight = new DateTime("2012-12-13T21:39:45.618").toDateMidnight();
LocalTime localTime = new LocalTime(midnight);
localTime.plus(period);
formatter.print(localTime);
Using the Jon Skeet example, you have a Non-terminating decimal expansion Exception
from divide BigDecimal divide(MILLIS_PER_HOUR)
, for fix error alter code for this:
private static final BigDecimal MILLIS_PER_HOUR =
BigDecimal.valueOf(DateTimeConstants.MILLIS_PER_HOUR);
DateTime t = new DateTime();
DateTime t1 = new DateTime();
DateTime dt1 = t;
DateTime dt2 = t1.plusMinutes(46);
Duration duration = new Duration(dt1, dt2);
BigDecimal result = BigDecimal.valueOf(duration.getMillis())
.divide(MILLIS_PER_HOUR, 2, RoundingMode.CEILING) // <--- Add scale and RoundingMode.CEILING
.setScale(2);
If you've got two DateTimes, I'd expect you to have a Duration
between them rather than a Period
... but other than that and Mike's comments, that looks correct:
private static final BigDecimal SECONDS_PER_HOUR =
BigDecimal.valueOf(DateTimeConstants.SECONDS_PER_HOUR);
...
DateTime dt1 = ...;
DateTime dt2 = ...;
Duration duration = new Duration(dt1, dt2);
BigDecimal result = BigDecimal.valueOf(duration.toStandardSeconds().getSeconds())
.divide(SECONDS_PER_HOUR)
.setScale(2, RoundingMode.HALF_DOWN);
Note that you can avoid the toStandardSeconds().getSeconds()
using Duration.getMillis()
instead:
private static final BigDecimal MILLIS_PER_HOUR =
BigDecimal.valueOf(DateTimeConstants.MILLIS_PER_HOUR);
...
DateTime dt1 = ...;
DateTime dt2 = ...;
Duration duration = new Duration(dt1, dt2);
BigDecimal result = BigDecimal.valueOf(duration.getMillis())
.divide(MILLIS_PER_HOUR)
.setScale(2, RoundingMode.HALF_DOWN);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With