I have some code that utilizes the Date object as the name of a file in order to have distinct file names every time but the strange thing is that a new Date object gives off the same toString() for each loop iteration. I mean, the following:
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss");
String fileName = sdf.format((d = new Date())) + ".jpg";
Log.d("Date", d.toString());
is executed in loops.
Side note: Since this is related to Java API, I haven't tagged the question as that of Android but the OS that executes this code is Android.
AFAIK, new Date() uses System.currentTimeMilis() as the init value, what might be reason for this unusual behavior?
You format your time as yyyyMMdd_HHmmss, but the run of a loop needs just milliseconds, so use yyyyMMdd_HHmmssSSS to get a more acurate time.
As Jon Skeet mentions in his comment, the run of a loop could even take less than a millisecond (depending on the tasks you perform), so you could run into issues with this solution as well!
java.time
The modern approach uses java.time classes.
The Instant class represents a point on the timeline with a resolution of nanoseconds. The Instant.now command captures the current moment in milliseconds in Java 8, and in microseconds in Java 9 (or perhaps finer).
Replace colon characters for macOS compatibility.
String filename = Instant.now().toString().replace( ":" , "-" ) + ".jpeg" ;
Note that even with milliseconds or microseconds resolution, you may still run into collisions if running brief code on fast cores. I suggest using UUID to eliminate such risk.
UUID
If you simply want unique file names without really needing to represent the current moment in the name, then use a UUID value rather a date-time.
The UUID class can generate some versions such as Version 4 (mostly random):
String filename = UUID.randomUUID() + ".jpeg" ;
Java in loop is faster than one second it will stay same
to make sure its always unique especially in multi threaded function.
Use somthing like this
SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss:SSSSSSS");
Some hints so you wont go into much debug trouble
printDate("dd.MM.yyyy HH:mm:ss.SSS");//02.05.2010 21:45:58.073
printDate("dd.MM.yyyy HH:mm:ss.SSSSSS");//02.05.2010 21:45:58.000073
printDate("dd.MM.yyyy HH:mm:ss.SSS'000'");//02.05.2010 21:45:58.073000
printDate("dd.MM.yyyy HH:mm:ss.'000000'");//02.05.2010 21:45:58.000000
tryToParseDate("dd.MM.yyyy HH:mm:ss.SSS");//good
tryToParseDate("dd.MM.yyyy HH:mm:ss.SSSSSS");//good
tryToParseDate("dd.MM.yyyy HH:mm:ss.SSS'000'");//bad
tryToParseDate("dd.MM.yyyy HH:mm:ss.'000000'");//good
Reference:
#slartidan Answer
String-Date conversion with nanoseconds
As Recommendation when i faced this situation :
1) If am calling from S3 AWS
files it should be named as unique at start of the file name it will make hashing and searching pretty fast. as from AWS S3 best practices to optimize.
public static String genarateFileName(String name) {
StringBuilder sb = new StringBuilder(name);
sb.insert(0, IdUtil.getUniqueUuid());in short to increase performance of S3 put and get etc..)
if (sb.lastIndexOf(".") != -1) {
sb.insert(sb.lastIndexOf("."), "_" + System.nanoTime());
} else {
sb.append("_").append(System.nanoTime());
}
return sb.toString();
}
2) To generate random nano
public static String getUniqueUuid() {
int rand = (int) (Math.random() * 100);
return Integer.toHexString(rand) + Long.toHexString(java.lang.System.nanoTime());
}
3) I do use both random nano with random string check below generates
random string of some length
/**
* Generate a random uuid of the specified length. Example: uuid(15) returns
* "VcydxgltxrVZSTV"
*
* #param len the desired number of characters
* #return
*/
public static String uuid(int len) {
return uuid(len, CHARS.length);
}
/**
* Generate a random uuid of the specified length, and radix. Examples: <ul>
* <li>uuid(8, 2) returns "01001010" (8 character ID, base=2) <li>uuid(8,
* 10) returns "47473046" (8 character ID, base=10) <li>uuid(8, 16) returns
* "098F4D35" (8 character ID, base=16) </ul>
*
* #param len the desired number of characters
* #param radix the number of allowable values for each character (must be
* <= 62)
* #return
*/
public static String uuid(int len, int radix) {
if (radix > CHARS.length) {
throw new IllegalArgumentException();
}
char[] uuid = new char[len];
// Compact form
for (int i = 0; i < len; i++) {
uuid[i] = CHARS[(int) (Math.random() * radix)];
}
return new String(uuid);
}
I see many answers pointing to use the classes from java.time package instead of java.util.Date. But, to precisely answer the question, I would say that moving to a different package will not automatically solve the problem.
In fact, the loop is running faster than the rate at which the value of new Date() can change. In order to see different values for invocation of new Date(), put a Thread.sleep(1*60*1000) between each iteration (The sleep value can be as per your requirement).
Related
I encountered a troublesome issue and I can't really explain to myself why it is appearing.
Basically I want to add time to a timestamp (a simple long).
I understand it the following. If I add time to a timestamp I end in the future. If I subtract time to the timestamp I end in the past.
In my example it is the other way around. If I add something to my timestamp it is reduced and if I subtract something is added.
public class MyClass {
public static void main(String args[]) {
static final int MONTH_IN_SECONDS = 2629743;
final long current = System.currentTimeMillis();
System.out.println("Current: " + current);
final long future = System.currentTimeMillis() + (MONTH_IN_SECONDS * 1000 * 3);
System.out.println("Addition: " + future);
final long past = System.currentTimeMillis() - (MONTH_IN_SECONDS * 1000 * 3);
System.out.println("Subtraction: " + past);
}
}
Result (compare the first 5 chars):
Current: 1582275101365
Addition: 1581574395774 // smaller than current even though it should be greater
Subtraction: 1582975806958 // great than current even though it should be smaller
Why does this happend? Does the term (MONTH_IN_SECONDS * 1000 * 3) overflow because it is only an Integer and thus the calculation does not work (or ends in a negative value)?
If I change the term to (MONTH_IN_SECONDS * 1000L * 3) it seems to work correctly. Is it because the complete term is casted to a long?
The problem is here:
(MONTH_IN_SECONDS * 1000 * 3)
That's integer multiplication that's overflowing, and resulting in a negative number:
System.out.println((MONTH_IN_SECONDS * 1000 * 3));
That outputs -700705592. You'd have to declare MONTH_IN_SECONDS as long, or otherwise change the expression so that the result is long-typed.
Does the term (MONTH_IN_SECONDS * 1000 * 3) overflow because it is
only an Integer and thus the calculation does not work (or ends in a
negative value)?
Month in seconds? Google says 2,630,000. (Though I see you have 2629743.)
2,630,000 * 1000 * 3 = 7,890,000,000
Integer.MAX_VALUE = 2^31 = 2,147,483,648
So yeah, it's an integer overflow.
I have java program which convert String to int, but the rang of String is 190520141618013381(above the rang of int) when i convert this int the java.lang.NumberFormatException: is thrown
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(format.format(date));
stringBuffer.append(demandCount);
int test_int = Integer.parseInt(stringBuffer.toString()); // exception has been fixed by putting
//long abc_i = Long.parseLong(abc);
log.info("test_int: "+test_int);
my question is that compiler should throw NumberOutOfRangException(if this Exception is available in API) instead java.lang.NumberFormatException:, the format of number(190520141618013381) is right.
The String 190520141618013381 is outside the range of an int but it also doesn't match the accepted format of an int because it is too long.
The compiler doesn't throw this error, it is thrown at runtime.
I believe it is correct to comply with the documentation for this method.
BTW Don't use StringBuffer, it was replaced by StringBuilder ten years ago.
IMHO storing a date as an integer isn't a good idea in general.
A more efficient to get a unique id which contains the time in millis is to do something like this.
private static final AtomicLong TS_COUNTER = new AtomicLong();
public static long nextTimeStamp() {
long time = System.currentTimeMillis() * 1000;
long curr = TS_COUNTER.get();
if (curr < time && TS_COUNTER.compareAndSet(curr, time))
return time;
return TS_COUNTER.incrementAndGet();
}
This will have the time-in-millis * 1000 plus a unique id. This works fine if you average less than one million ids per second.
I am trying to generate a unique identifier of a fixed length such as the IDs that are generated by Megaupload for the uploaded files.
For example:
ALGYTAB5
BCLD23A6
In this example using from A-Z and 0-9 and with a fixed length of 8 the total different combinations are 2,821,109,907,456.
What if one of the generated id is already taken. Those ids are going to be stored in a database and it shouldn't be used more than once.
How can I achieve that in Java?
Thank you.
Hmm... You could imitate a smaller GUID the following way. Let first 4 bytes of your string be the encoded current time - seconds passed after Unix. And the last 4 just a random combination. In this case the only way two ID's would coincide is that they were built at the same second. And the chances of that would be very veeery low because of the other 4 random characters.
Pseudocode:
get current time (4 byte integer
id[0] = 1st byte of current time (encoded to be a digit or a letter)
id[1] = 2nd
id[2] = 3rd
id[3] = 4th
id[4] = random character
id[5] = random character
id[6] = random character
id[7] = random character
I have tried #Armen's solution however I would like to give another solution
UUID idOne = UUID.randomUUID();
UUID idTwo = UUID.randomUUID();
UUID idThree = UUID.randomUUID();
UUID idFour = UUID.randomUUID();
String time = idOne.toString().replace("-", "");
String time2 = idTwo.toString().replace("-", "");
String time3 = idThree.toString().replace("-", "");
String time4 = idFour.toString().replace("-", "");
StringBuffer data = new StringBuffer();
data.append(time);
data.append(time2);
data.append(time3);
data.append(time4);
SecureRandom random = new SecureRandom();
int beginIndex = random.nextInt(100); //Begin index + length of your string < data length
int endIndex = beginIndex + 10; //Length of string which you want
String yourID = data.substring(beginIndex, endIndex);
Hope this help!
We're using the database to check whether they already exist. If the number of IDs is low compared to the possible number you should be relatively safe.
You might also have a look at the UUID class (although it's 16-byte UUIDs).
Sounds like a job for a hash function. You're not 100% guaranteed that a hash function will return a unique identifier, but it works most of the time. Hash collisions must be dealt with separately, but there are many standard techniques for you to look into.
Specifically how you deal with collisions depends on what you're using this unique identifier for. If it's a simple one-way identifier where you give your program the ID and it returns the data, then you can simply use the next available ID in the case of a collision.
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;
How to convert a long number in base 10 to base 9 without converting to string ?
FWIW, all values are actually in base 2 inside your machine (I bet you already knew that). It only shows up as base 10 because string conversion creates string representations in base 10 (e.g. when you print), because methods like parseLong assumes the input string is in base 10 and because the compiler expects all literals to be in base 10 when you actually write code. In other words, everything is in binary, the computer only converts stuff into and from base 10 for the convenience of us humans.
It follows that we should be easily able to change the output base to be something other than 10, and hence get string representations for the same value in base 9. In Java this is done by passing an optional extra base parameter into the Long.toString method.
long x=10;
System.out.println(Long.toString(x,9));
Long base10 = 10;
Long.valueOf(base10.toString(), 9);
What does "convert to base 9 without converting to string" actually mean?
Base-9, base-10, base-2 (binary), base-16 (hexadecimal), are just ways to represent numbers. The value itself does not depend on how you represent it. int x = 256 is exactly the same as int x = 0xff as far as the compiler is concerned.
If you don't want to "convert to string" (I read this as meaning you are not concerned with the representation of the value), then what do you want to do exactly?
You can't convert to base 9 without converting to string.
When you write
Long a = 123;
you're making the implicit assumption that it's in base 10. If you want to interpret that as a base 9 number that's fine, but there's no way Java (or any other language I know of) is suddenly going to see it that way and so 8+1 will return 9 and not 10. There's native support for base 2, 8, 16 and 10 but for any other base you'll have to treat it as a string. (And then, if you're sure you want this, convert it back to a long)
You have to apply the algorithm that converts number from one base to another by applying repeated modulo operations. Look here for a Java implementation. I report here the code found on that site. The variable M must contain the number to be converted, and N is the new base.
Caveat: for the snippet to work properly, N>=1 && N<=10 must be true. The extension with N>10 is left to the interested reader (you have to use letters instead of digits).
String Conversion(int M, int N) // return string, accept two integers
{
Stack stack = new Stack(); // create a stack
while (M >= N) // now the repetitive loop is clearly seen
{
stack.push(M mod N); // store a digit
M = M/N; // find new M
}
// now it's time to collect the digits together
String str = new String(""+M); // create a string with a single digit M
while (stack.NotEmpty())
str = str+stack.pop() // get from the stack next digit
return str;
}
If you LITERALLY can do anything but convert to string do the following:
public static long toBase(long num, int base) {
long result;
StringBuilder buffer = new StringBuilder();
buffer.append(Long.toString(num, base));
return Long.parseLong(buffer.toString());
}