New Date() object gives the same value

Viewed 1005

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?

4 Answers

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