Reputation: 29999
I have a javax.xml.datatype.Duration
that contains a fraction of millisecond, e.g. a duration of 1.5ms (1500 microseconds):
Duration duration = DatatypeFactory.newInstance().newDuration("PT0.0015S");
I need to get this value in nanoseconds but the smallest unit of time that Duration
is providing seems to be getTimeInMillis
, which returns a long
and cuts of anything smaller than a millisecond.
How do I get the duration in nanoseconds?
Upvotes: 1
Views: 1043
Reputation: 2183
You can parse the duration to java.time.Duration
. Then call toNanos()
method.
Duration duration = DatatypeFactory.newInstance().newDuration("PT0.0015S");
java.time.Duration.parse(duration.toString()).toNanos();
Upvotes: 3
Reputation: 29999
The Duration API was slightly surprising to me here because you have to get the seconds part of the duration with Duration.getField(Field). That method returns a Number
, which is either a BigInteger
(for days, hours, minutes. etc) or a BigDecimal
(only for seconds).
So to get fractions of a second you can use getField(DatatypeConstants.SECONDS)
and then convert the value:
Duration duration = DatatypeFactory.newInstance().newDuration("PT0.0015S");
BigDecimal seconds = (BigDecimal) duration.getField(DatatypeConstants.SECONDS);
// Note that `getField` will return `null` if the field is not defined.
if(seconds != null) {
System.out.println(seconds + "s"); // 0.0015s
System.out.println(seconds.movePointRight(9) + "ns"); // 1500000ns
}
But these are not the total seconds of the duration. This is only the value of the seconds field and other fields are ignored. For a duration like P1M0.0015
(1 minute and 1.5 milliseconds) this would ignore the minute and would just return 1.5ms.
Converting all other fields of the duration to seconds and summing them up would work. Or by using getTimeInMillis
, which returns the total milliseconds of the duration:
Duration duration = DatatypeFactory.newInstance().newDuration("PT1M0.0015S");
BigDecimal seconds = (BigDecimal) duration.getField(DatatypeConstants.SECONDS);
// only keep the fractional part
BigDecimal fractionalSeconds = seconds.remainder(BigDecimal.ONE);
long totalMillis = duration.getTimeInMillis(new Date(0));
// convert total millis to whole seconds (removes fractional part)
BigDecimal totalIntegerSeconds =
new BigDecimal(totalMillis).movePointLeft(3).setScale(0, RoundingMode.FLOOR);
// add both to get total seconds
BigDecimal totalSeconds = totalIntegerSeconds.add(fractionalSeconds);
System.out.println(totalSeconds + "s"); // 60.0015s
System.out.println(totalSeconds.movePointRight(9) + "ns"); // 60001500000ns
This seems to work but is way too complicated. There must be an easier way to do this.
@hasnae's answer solution looks much better if you are using Java 1.8. I wonder what the API designer's intended way to get nanosecond precision was though, since java.time.Duration
was introduced years later than javax.xml.datatype.Duration
.
Upvotes: 1