I'm working with LocalDateTime objects and would like to store these as Long with nano second precision.
I've tried converting between Instant representation but so far failing.
For example:
localDateTime.toInstant(ZoneOffset.UTC).getNano
Returns only the nano seconds segment of localDateTime - is there a way to return the complete value in nanoseconds?
Similar to toEpochSecond but rather than in seconds, nanoseconds?
2262-04-11T23:47:16.854775807Z = Long.MAX_VALUE of nanos
Building on the correct Answers by Jean-Baptiste Yunès and by assylias explaining that a 64-bit long integer cannot represent all Instant values as a count of nanoseconds since 1970-01-01T00:00Z…
You could use a long/Long for a count of nanoseconds to represent moments for the next two centuries, up to 2262-04-11T23:47:16.854775807Z, if my math is correct:
Instant.ofEpochSecond( ( Long.MAX_VALUE / 1_000_000_000L ) , ( Long.MAX_VALUE % 1_000_000_000L ) )
FYI, Long.MAX_VALUE = 9223372036854775807.
I'm not saying it is a good idea to do so. I am simply showing the possibility to demonstrate the issues at hand.
See code run live at IdeOne.com.
long seconds = ( Long.MAX_VALUE / 1_000_000_000L ) ;
long fraction = ( Long.MAX_VALUE % 1_000_000_000L ) ;
long total = ( ( seconds * 1_000_000_000L ) + fraction ) ;
Instant instant = Instant.ofEpochSecond( seconds , fraction ) ;
System.out.println( "Long.MAX_VALUE: " + Long.MAX_VALUE ) ;
System.out.println( "seconds: " + seconds ) ;
System.out.println( "fraction: " + fraction ) ;
System.out.println( "total: " + total ) ;
System.out.println( "instant: " + instant ) ;
Long.MAX_VALUE: 9223372036854775807
seconds: 9223372036
fraction: 854775807
total: 9223372036854775807
instant: 2262-04-11T23:47:16.854775807Z
There is no such method because you can't store all instants in a long number of nanoseconds.
If you are happy that the range of dates you are interested in can all fit in a long as a number of nanoseconds since the epoch, you can calculate it yourself with:
long nanos = ( instant.getEpochSecond * 1_000_000_000L ) + instant.getNano
Note that this will overflow if the date is too far in the past or the future.
From java API documentation:
The range of an instant requires the storage of a number larger than a
long. To achieve this, the class stores a long representing
epoch-seconds and an int representing nanosecond-of-second, which will
always be between 0 and 999,999,999.
Then getting total of nanos from Instant can't be retrieved in a single primitive type as long.
The correct way to get the total number of nanoseconds since EPOCH from a LocalDateTime object is:
Create an Instant object from your LocalDateTime object
Sum up the result of these 2 methods:
getEpochSecond() - documentation
Gets the number of seconds from the Java epoch of 1970-01-01T00:00:00Z.
getNano() - documentation
Gets the number of nanoseconds, later along the time-line, from the start of the second.
Sample Code
public static void main(String args[]) {
Instant instant = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant();
long epochNanos = TimeUnit.NANOSECONDS.convert(instant.getEpochSecond(), TimeUnit.SECONDS);
epochNanos += instant.getNano();
System.out.print("Total nanos since EPOCH: " + epochNanos);
}
Related
I want to generate intervals between two given date/time.
For instance, say for 24 hour format (HH:MM), I have these two endpoints, 00:00 and 11:51, and suppose I want to partition it in 24 pieces. So my calculation is like this:
(hour * 3600 + min * 60) / 24
If I use calendar.add(Calendar.SECOND, (hour * 3600 + min * 60) / 24), I am getting wrong dates/time. My calculation is double and I think calendar.add() does not support double. Like it is taking 28.883 as 29.
In essence I want something like this:
now : 15:57
today start : 00:00 (24hh)
output : 00:00, 00:47.85, …, 15:57
The actual problem with your code is that you are performing integer division. I assume both hour and min are defined as integer types. The formula (hour * 3600 + min * 60) / 24 always yields an integer type. If you change the code to (hour * 3600 + min * 60) / 24d the expression yields a floating point value at least.
The next problem is indeed that Calendar.add(int field, int amount) accepts only an integer as second argument. Of course, if you are passing Calendar.SECOND as first argument, then your precision is not higher than seconds. You can use Calendar.MILLISECOND to get a higher precision.
However, I suggest using the new Java Date and Time API, instead of the troublesome old API:
LocalTime startTime = LocalTime.of(0, 0);
LocalTime endTime = LocalTime.of(11, 51);
long span = Duration.between(startTime, endTime).toNanos();
final int n = 23; // Number of pieces
LongStream.rangeClosed(0, n)
.map(i -> i * span / n)
.mapToObj(i -> startTime.plusNanos(i))
.forEach(System.out::println);
You need to save your start date in a calendar object and then when you generate each division use the formula:
startCalendar.add(Calendar.Second, count * (hour * 3600 + min * 60) / 24))
That way the arithmetic errors that you get by dividing by 24 (or whatever) are not accumulated.
Here’s a variant of MC Emperor’s fine code. I wanted to leave the math to the library class. Duration has methods dividedBy and multipliedBy that we can use to our advantage.
LocalTime startTime = LocalTime.of(0, 0);
LocalTime endTime = LocalTime.of(11, 51);
final int n = 24; // Number of pieces
Duration piece = Duration.between(startTime, endTime).dividedBy(n);
LocalTime[] partitionTimes = IntStream.rangeClosed(0, n)
.mapToObj(i -> startTime.plus(piece.multipliedBy(i)))
.toArray(LocalTime[]::new);
System.out.println(Arrays.toString(partitionTimes));
Output:
[00:00, 00:29:37.500, 00:59:15, 01:28:52.500, 01:58:30, 02:28:07.500,
02:57:45, 03:27:22.500, 03:57, 04:26:37.500, 04:56:15, 05:25:52.500,
05:55:30, 06:25:07.500, 06:54:45, 07:24:22.500, 07:54, 08:23:37.500,
08:53:15, 09:22:52.500, 09:52:30, 10:22:07.500, 10:51:45,
11:21:22.500, 11:51]
Is there a rounding problem? With a start time in whole minutes and 24 pieces there won’t be since 24 divides evenly into the number of nanoseconds in a minute. With another number of pieces you may decide whether the slight inaccuracy is worth worrying about. If it is, for each partitioning time multiply before you divide.
How does long value fit in float data type in Java without loss of precision. Because if there is loss of precision then the following snippet should have produced
long lMax = Long.MAX_VALUE;
float f = lMax;
System.out.println(lMax == f);
Output
true
The problem is, that for comparison of long and float the long value is being cast to float, and the same loss of precision occurred as in the assignment...
If you want to "detect" loss of precision, try using BigDecimals, something like this:
System.out.println(new BigDecimal(lMax).compareTo(new Bigdecimal(f)));
I know that the constructor BigDecimal(double) will get the float cast to double but there is no precision lost...
Edit
For most people long seems to have more "precision" than float. However float has a range of +/-10^38, which is much larger than long's...
Tests
long lMax = Long.MAX_VALUE;
float f = lMax;
System.out.println( lMax == f );
System.out.println( new BigDecimal( lMax ).compareTo( new BigDecimal( f ) ) );
System.out.println( ( (Float) f ).longValue() == lMax );
System.out.println( ( (Long) lMax ).floatValue() == f );
System.out.printf( "%d, %d%n", ( (Float) f ).longValue(), lMax );
f = --lMax;
System.out.println( lMax == f );
System.out.println( new BigDecimal( lMax ).compareTo( new BigDecimal( f ) ) );
System.out.println( ( (Float) f ).longValue() == lMax );
System.out.println( ( (Long) lMax ).floatValue() == f );
System.out.printf( "%d, %d%n", ( (Float) f ).longValue(), lMax );
Outputs:
true
-1
true
true
9223372036854775807, 9223372036854775807
true
-1
false
true
9223372036854775807, 9223372036854775806
The Binary Numeric Promotion rules in the Java language Specification are key to understanding the behavior of the code snippet in the question. In particular "Otherwise, if either operand is of type float, the other is converted to float."
In the lMax == f comparison, lMax is converted to float exactly as was done by the float f = lMax assignment. If you take the same input value, do the same transformation on it twice, and compare the results they will be equal regardless of whether the transformation changed the value or not.
If you want a good demonstration of the precision loss, try this little script when getting system time in milliseconds and converting from long to float:
class Scratch {
public static void main(String[] args) throws InterruptedException {
float currentF;
long currentL;
float lastNewF = 0;
long lastL = System.currentTimeMillis();
long diff;
while (true) {
currentL = System.currentTimeMillis();
currentF = (float) currentL;
if (currentF != lastNewF) {
// we finally got a new "different" float value
lastNewF = currentF;
diff = currentL - lastL;
lastL = currentL;
System.out.println(diff + " milliseconds since we got a new time as float");
}
Thread.sleep(250);
}
}
}
You can see from the output that you are sometimes losing up to 2 minutes of precision:
0 milliseconds since we got a new time as float
46964 milliseconds since we got a new time as float
131054 milliseconds since we got a new time as float
131093 milliseconds since we got a new time as float
131077 milliseconds since we got a new time as float
lMax is a long number. When u assign it to a float it will lose the precision.
float f = lMax;
When you compare a long to a float, internally either long is converted to a float or float is converted to a long. your code
lMax == f
will be similar to
((Float)f).longValue() == lMax
which is 9.223372E18 == 9.223372E18 true
or
((Long)lMax).floatValue() == f
which is 9223372036854775807 == 9223372036854775807 true
in either way the answer is true.
The value of lMax =9223372036854775807; // It might be different in your JVM.
Value of f = 9.223372E18
Now when you compare lMax == f, first f is converted to long and then comparison happens. After converting into long, f value is same as lMax.
Even same apply for following code.
long lMax = Long.MAX_VALUE; // value is 9223372036854775807
float f =lMax;
float f2 = 9223372036854775807.5f;
System.out.println(lMax == f);
System.out.println(lMax == f2);
Output
true
true
Here f2 is losing decimal value when compared to long. And long value of f2 is same as lMax.
I'm really puzzled by this. I'm dividing two positive numbers and getting a negative result (I'm using Java).
long hour = 92233720368L / (3600 * 1000000 );
I got as result -132.
But if I divide them as two long numbers, I get the right result:
long hour1 = 92233720368L / (3600000000L );
Then I get as result: 25
I'm wondering why it occurs...
Thank you in advance! :)
You must add L at the end of 3600 or 1000000:
Example:
long hour = 92233720368L / (3600 * 1000000L );
Here's what's hapenning:
System.out.println(3600 * 1000000); // Gives -694967296 because it exceeds the max limit of an integer size. So 92233720368L / -694967296 = -132
That's exactly what's happening in your division, the dominator is an integer and is considered as negative number for the reason I stated above. So in order to consider the multiplication result of type long you should add L after 3600 or after 1000000
It interprets 3600 and 10000000 as type int which cannot hold enough information to represent their product, and so you get a different number. You'd have to declare them both as type long to get the correct result.
I have a problem regarding really big numbers in Java.
I'm currently trying to convert a double consisting of a large number of seconds into years, days, hours, minutes and the seconds that are left.
My code looks like this: `
import java.util.Scanner;
public class timer {
public static void main(String[] args){
double sum = 1.1078087563769418E16;
double sec=0;
double min=0;
double hou=0;
double da=0;
double yea=0;
yea=sum/31536000;
int year = (int) yea;
sum=sum-year*31536000;
da=sum/86400;
int day = (int) da;
sum=sum-day*86400;
hou=sum/3600;
int hour = (int) hou;
sum=sum-hour*3600;
min=sum/60;
int minute = (int) min;
sum=sum-minute*60;
sec=sum;
int second = (int) sec;
sum=sum-second*60;
System.out.println(year+" "+day+" "+hour+" "+minute+" "+second);
}
}
`
When the double "sum" is as large as it is, the output gets weird. For example, the double "day" becomes larger than 365, which should be impossible.
Any solutions?
You are facing with a very common problem that most developers should know about: Integer overflow.
Your year variable is an int, the constant for second->year conversion is constant int, so int*int is... int. Except if your year is larger than approx 68, the result cannot fit into an int. Hence - overflow and silly results.
You have a couple of options available to you:
Do your timestamp arithmetic in longs. Really, that's the industry standard anyway.
If you insist on precision of over microseconds (or nano if you're concerned with last century only) - BigDecimal or BigInteger are your friends.
Use the TimeUnit class from concurrent package. It doesn't have years, but does have days and does conversion nicely (JodaTime would be even nicer though)
Overflow on the calculation sum=sum-year*31536000;
year and 31536000 are both integers, and their product is ~1E16, which is larger than java's max integer size. Use sum = sum-year*31536000.0 instead.
As a demonstration, add this code after you compute year:
System.out.println(year * 31536000.0); // 1.1078087556672E16
System.out.println(year * 31536000); // 1100687872
You can use java.math.BigDecimal class if you need to represent numbers with great precision.
If you need huge integers you can use java.math.BigInteger.
First use long not double then try this logic
long min = sum / (60) % 60;
long hou = sum / (60 * 60 )% 24;
long da = sum/(60*60*24)%30;
long yea = sum/(60*60*24*30)%365;
If that number is beyond the limit of long then use BigInteger but then this calculation will not work.
I want to round a time up or down with Joda using the roundCeilingCopy or roundFloorCopy methods.
Rounding down seems to work properly, but when I try to round up, it doesn't work properly.
Below is my current code with the output and the desired output. But maybe I just don't understand how those joda methods work?
I'm fairly new to java and joda and got my original code from here
Code:
public static long RoundTime(long time, int amount) {
DateTime dt = new DateTime(time);
System.out.println("Rounding time: " + dt.toString());
DateTime up = dt.withMinuteOfHour((dt.getMinuteOfHour() / amount) * amount)
.minuteOfDay().roundCeilingCopy();
System.out.println("Finished Rounding Time Up (" + amount + "): "
+ up.toString());
DateTime down = dt.withMinuteOfHour((dt.getMinuteOfHour() / amount) * amount)
.minuteOfDay().roundFloorCopy();
System.out.println("Finished Rounding Time Down (" + amount + "): "
+ down.toString());
}
Output: (with amount=5)
Rounding time: 2014-01-21T13:12:00.000-07:00
Finished Rounding Time Up (5): 2014-01-21T13:10:00.000-07:00
Finished Rounding Time Down (5): 2014-01-21T13:10:00.000-07:00
Desired Output: (with amount=5)
Rounding time: 2014-01-21T13:12:00.000-07:00
Finished Rounding Time Up (5): 2014-01-21T13:15:00.000-07:00
Finished Rounding Time Down (5): 2014-01-21T13:10:00.000-07:00
(dt.getMinuteOfHour() / amount) * amount already rounds the minutes towards zero because it uses integer division. You'll have to modify that to get the ceiled value: ((int) Math.ceil(1. * dt.getMinuteOfHour() / amount) * amount (note the 1. * to force the expression to use double precision - you could alternatively cast any value to double as well).