6

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?

Mitul Maheshwari
  • 2,647
  • 4
  • 24
  • 38
Manish Kumar Sharma
  • 12,982
  • 9
  • 58
  • 105
  • 3
    Java 8 introduced new APIs for Date and Time. You can have a quick look here: http://www.baeldung.com/java-8-date-time-intro – Touniouk Nov 21 '17 at 07:05
  • 1
    you must use milli second , and if you are using threads use nanosecond – shareef Nov 21 '17 at 07:07
  • 2
    You have a precission of seconds. Are you sure the code is not executed in same second with milliseconds of difference?. I would recomend to use System.currentTimeMillis() to a unique part of the file name. – Francisco Valle Nov 21 '17 at 07:07
  • 1
    If you want to try `java.time` already recommended by @Touniouk, to use it on Android you need [ThreeTenABP](https://github.com/JakeWharton/ThreeTenABP), see [this question: How to use ThreeTenABP in Android Project](https://stackoverflow.com/questions/38922754/how-to-use-threetenabp-in-android-project). I am not sure what accuracy it will give you, it could be finer than milliseconds. – Ole V.V. Nov 21 '17 at 07:33

4 Answers4

5

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!

Sergej
  • 1,082
  • 11
  • 27
3

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" ;
Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
0

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);
    }
shareef
  • 9,255
  • 13
  • 58
  • 89
  • 1
    Using `SimpleDateFormat` you won't be able to format to more precision than a millisecond anyway, as that's the precision of `java.util.Date`. Beyond that, you've then got the system clock precision to consider, which may return the same value for chunks of 15ms in some case. Fundamentally, if this loop is running quickly, you shouldn't rely on the system clock for uniqueness. – Jon Skeet Nov 21 '17 at 07:24
  • @JonSkeet correct thats why i use both check my recommended practice, 3) I do use both random nano with random string check below generates random string of some length and give me your feed back i would appreciate it. – shareef Nov 21 '17 at 07:27
  • 1
    Apart from what @JonSkeet already said, using 6 or 7 capital `S` in the format pattern string just leads to incorrect rendering of the milliseconds. Your example is printed as both `58.073` and `58.000073`, this is not the same number. The former is correct, the latter incorrect. For uniqueness it doesn’t matter, but likely someone will someday understand this as a timestamp. – Ole V.V. Nov 21 '17 at 07:27
  • As i said its my recommendation based on my experience and so far on my production it gave me no trouble, any way you are most than welcome to improve the answer as you see fits for any one want to check this question Thanks All. – shareef Nov 21 '17 at 07:32
0

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).

Binita Bharati
  • 5,239
  • 1
  • 43
  • 24