What does it mean to % 2147483647L on epoch time? - java

I just stepped onto problem with generating id for notification in Android.
I'm going through some notification-handling-sdk and they are using this code to generate notification id:
private int generateTimestampId() {
return (int)(new Date().getTime() % 2147483647L);
}
This is probably good solution, but I can't understand what % 2147483647L stands for in context of epoch time.
(please note: new Date().getTime() returns the number of milliseconds since January 1, 1970, 00:00:00 GMT)

It could be done to prevent negative values which will happen when long is narrowed to int. Consider the following code:
long value = 1L + Integer.MAX_VALUE; // anything past max integer
int i1 = (int) value; // -2147483648
int i2 = (int) (value % 2147483647L); // 1
With the % 2147483647L the code ensures that the number will always be smaller than Integer.MAX_VALUE, which is 2147483647, before it will get narrowed to int.
It more or less makes sense in context of generated ids, we rarely use negative numbers for this use case.

Related

java.time.Duration.dividedBy(long) vs java.time.Duration.dividedBy(Duration)

I am trying to divide two time durations (java.time.Duration) and to perform that there are two methods
java.time.Duration#dividedBy(long) which returns an object of type Duration itself but due to some API restrictions I can not use this version.
java.time.Duraiton#dividedBy(Duration) which returns a long and returns number of times one duration occurs within other duration but here we lose the precision as it does not care about the remainder.
Is there any way out to perform this division and get the result with remainder.
Duration.ofHours(1).dividedBy(7L); //returns 8M34.28571 seconds
Duration.ofHours(1).dividedBy(Duration.ofSeconds(7L)) // returns 514 seconds
I have a restriction not to use the first way. Can I get the results using some other way?
EDIT: (from comments) I am not allowed to convert a Duration instance to an ordinal value, e.g. milliseconds.
Convert to milliseconds/nanoseconds, then divide their ordinal value(s), then create a new Duration object from the result.
When the easy ways are forbidden, you can of course do it the more cumbersome way. The following is similar to how we learned to do division by hand in school.
Duration dividend = Duration.ofHours(1);
long divisor = 7;
long minutes = dividend.toMinutes() / divisor;
dividend = dividend.minusMinutes(minutes * divisor);
long seconds = dividend.toSeconds() / divisor;
dividend = dividend.minusSeconds(seconds * divisor);
long nanoseconds = dividend.toNanos() / divisor;
Duration result = Duration.ofMinutes(minutes)
.plusSeconds(seconds)
.plusNanos(nanoseconds);
System.out.println(result);
Result output:
PT8M34.285714285S
Your requirements are not that precise, so I can’t be sure I haven’t used a forbidden method, though.

How do you represent MS-DTYP `DATETIME` in Java 8 Instant?

In the Microsoft Spec, DATETIME is represented as 2 32-bit integers: low and high
Reference: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/cca27429-5689-4a16-b2b4-9325d93e4ba2
The FILETIME structure is a 64-bit value that represents the number of
100-nanosecond intervals that have elapsed since January 1, 1601,
Coordinated Universal Time (UTC). typedef struct _FILETIME { DWORD
dwLowDateTime; DWORD dwHighDateTime; } FILETIME,
*PFILETIME,
*LPFILETIME; dwLowDateTime: A 32-bit unsigned integer that contains the low-order bits of the file time. dwHighDateTime: A 32-bit unsigned
integer that contains the high-order bits of the file time.
For example, here is the long 130280867040000000
So the the high and low computed with
int high = (int)(fullval >> 32);
int low = (int)fullval;
so high = 30333378
and low = 552794112
How do I compute these to a Java 8 Instant?
Ah I was barking up the wrong tree when I split the bytes in half like that.
Basically it's just saying that the units are in 100ns.
And the Epoch has a different base time too. So you have to add the offset as well.
So it is:
private static final long DATETIME_EPOCH_DIFF_1601;
static {
LocalDateTime time32Epoch1601 = LocalDateTime.of(1601, Month.JANUARY, 1, 0, 0);
Instant instant = time32Epoch1601.atZone(ZoneOffset.UTC).toInstant();
DATETIME_EPOCH_DIFF_1601 = (instant.toEpochMilli() - Instant.EPOCH.toEpochMilli()) / 1000;
}
Instant answer = Instant.ofEpochSecond(fullval / 10000000 + DATETIME_EPOCH_DIFF_1601)
For converting with 1 second precision your own answer is just fine. In case you also need to convert the fraction of second, here’s one way to do that.
Instant msFiletimeEpoch = Instant.parse("1601-01-01T00:00:00Z");
// a tick is 100 nanoseconds
int nanosPerTick = 100;
long ticksPerSecond = TimeUnit.SECONDS.toNanos(1) / nanosPerTick;
long fullval = 130_280_867_040_000_000L;
long seconds = fullval / ticksPerSecond;
long nanos = fullval % ticksPerSecond * nanosPerTick;
Instant answer = msFiletimeEpoch.plusSeconds(seconds).plusNanos(nanos);
System.out.println(answer);
Output is:
2013-11-05T00:58:24Z
Let’s try to put 1 more tick on your oroginal value; it should add 100 nanoseconds.
long fullval = 130_280_867_040_000_001L;
2013-11-05T00:58:24.000000100Z
So so it does.
Caveat for very far future dates: According to your quote the Microsoft integers are both unsigned. A Java long is signed. So some time in year 30828 we will start getting results that are very wrong. Just in case we ought to throw an exception if the long value is negative.

Generating Negative Epoch

I am trying to generate an EPOCH value. I am using the following:
int currentEpoch = ((int) System.currentTimeMillis()) / 1000;
Log.v("EPOCH", String.valueOf(currentEpoch));
Below is my log:
Why is my Epoch value negative?
System.currentTimeMillis() returns a long value. When you cast it to int the value is overflowing and giving you a negative number.
Try
long currentEpoch = System.currentTimeMillis() / 1000;
Log.v("EPOCH", String.valueOf(currentEpoch));`
In short, there's a reason that System.currentTimeMillis() returns a long. An int isn't large enough to store the number of milliseconds since 1970. Because it's trying to represent a value larger than it can hold, the value wraps around and becomes a negative value.

Get a less specific number - from another number

Right, sorry I can't think of the correct words to google this. So I'll have to ask.
I've got a long (System.currentTimeMillis())
Lets say
3453646345345345
I want to remove the last six (or other number of) digits and I think I can do this doing some kind of bit shift?
so I would end up with
3453646345
EDIT
I wanted to get the System.currentTimeMillis() within a time box, so if I ask for the time then ask again 29 seconds later it will return the same number but if I ask 31 seconds later it will return a different number. With the 30 second timebox being configurable.
You have to simply divide it by 1M long shorter = System.currentTimeMillis() / 1000000L;
To build on #Yob's answer, you can make the number of digits to remove configurable by creating a method like this:
public long removeDigits(long number, int digitsToRemove) {
return number / (long)Math.pow(10, digitsToRemove);
}
Depending on what you want to do (in base 10, I assume), you can do this:
int64_t radix = 1000000; // or some other power of 10
x -= x%radix; // last 6 decimal digits are now 0
// e.g: from 3453646345345345 to 3453646345000000
Or this (as in the previous answer):
x /= radix; // last 6 decimal digits are gone, the result rounded down
// e.g: from 3453646345345345 to 3453646345
Response to Edit
For your purposes, you could change radix in the modulus example to 30000:
int64_t timeInterval = 30000;
displayTime = actualTime - (actualTime % timeInterval);
Where displayTime and actualTime are in milliseonds. displayTime will, in this case, have a (rounded-down) granularity of 30 seconds while remaining a unit of milliseconds.
To have a rounded up granularity, you can do the following:
int64_t timeInterval = 30000;
int64_t modulus = actualTime % timeInterval;
displayTime = actualTime - modulus + (modulus?timeInterval:0);
Though, based on what you are asking, it seems you just want to update a display value only every few ticks. The following will work as well:
if((actualTime - displayTime) >= timeInterval){
displayTime = actualTime - (actualTime % timeInterval);
}
Pardon the C integer types, I just prefer to be unambiguous about the width of integer I'm using :P.

How do I fix wrong numbers produced by integer overflow?

I had a bug that caused an integer overflow, resulting in wrong (negative) timestamps being written to the database. The code is fixed already, but I want to fix the wrong data, too.
I thought, I could just take the wrong results and add Integer.MAX_VALUE, but that didn't seem to work, it left me with to high values. I have the offset value in the code snippet below, but the input values are not stored.
The following code reproduces the bug:
#Test
public void testArexxConversion()
{
// The input values represent seconds since midnight, Jan 1, 2000 UTC
final int sample = 361450072; // A sample input value drawn from production
// I use the offset from the UNIX epoch to convert the vakue to UNIX seconds
final int offset = 946684800; // midnight, Jan 01 2000 UTC in UNIX seconds
// This was the buggy line in my code, the assertion will fail
long result = (sample + offset) * 1000;
// Prints 'Result is negative: -1830153280'
Assert.assertTrue(result > 0, String.format("Result is negative: %d", result));
// This is for comparison
Date dt = new Date(offset * 1000);
Assert.assertEquals(dt.getTime() + sample * 1000, result);
}
How to fix the bug in your database
To fix the bug in your database you can do the following addition to all the buggy data:
long new_result = old_buggy_result + 1309965025280L;
The constant number was found like this:
Check the buggy result value
Find what should the correct result value be?
Do an addition to the buggy result value to find the correct `result.
But this is only possible if you have saved sample and offset in your database or somewhere else.
Otherwise, it depends on the number of wraps that occured during the original calculation:
long size_of_int = (long)Math.pow(2, 32);
int number_of_wraps = 305 // Only correct in your example!
// You can't deduct the number of wraps from
// the wrong value alone, because that information
// is lost in the modulo (the "wrap")
long correct_number = wrong_number + size_of_int * number_of_wraps;
If the numbers in your database are close enough to your sample value, this means, you can do the above, using 305 as the number of wraps.
Explanation of the bug (for future readers)
The operation here:
(sample + offset) * 1000;
is computed using int and not long. But the result is "too big" to be saved on an int variable. That's why you have an overflow.
Change it to:
((long) sample + offset) * 1000L;
So now the + and * operations will be done using long values, and the result will be a long value which won't overflow.
That would be like this:
long result = ... ; // bad negative from database
long new_result = (long)((int)result - Integer.MAX_VALUE) + Integer.MAX_VALUE;
Replace this line.
long result = (long)(sample + offset) * 1000L;

Categories

Resources