Share via


C# generate a 15 digit always distinct numeric value

Question

Monday, August 20, 2012 1:19 PM

Hi all,

I have a piece of C# code that must generate an always new 15 digit value that must be numeric and unique and must be generated "on demand" which means code only (no database support for example to generate "identity" values).

I'm thinking of using the current time, however the 15 digit only allows me to go until the first digit of the milisecond, so I'm thinking on using the DateTime.Now.ToString("yyyyMMddhhmmss") and addind a Random number after that.

I was wondering if anyone knows a better os more reliable idea or there is any limitation on my idea. I also was wondering if the random assures that I won't be generating duplicated values if this piece of code is called from two sources at the same exact time!

All replies (37)

Monday, August 20, 2012 1:28 PM ✅Answered

Hi,

If I were you, I wouldn't use the DateTime.Now.ToString() strategy.

Two people may click within the same second and get the same random digit (a 10% chance

if both have the same timestamp).

Random doesn't guarantee uniqueness, you can throw dices and have 5s appear two or three

times in a row.

If I were you, I'd obtain an unique value via Guid.NewGuid(), which can easily be converted to decimal.

Sebastian Sajaroff Senior DBA Pharmacies Jean Coutu


Monday, August 20, 2012 1:33 PM ✅Answered | 2 votes

Lock to ensure that no two threads run your code at the same time. Thread.Sleep to ensure that you get two distinct times at the tenth of second.

static object locker = new object();static string Generate15UniqueDigits(){    lock (locker)    {        Thread.Sleep(100);        return DateTime.Now.ToString("yyyyMMddHHmmssf");    }}

Monday, August 20, 2012 1:47 PM ✅Answered

Hi,

did you check the Guid structure? http://msdn.microsoft.com/en-us/library/system.guid.aspx

It is a 16 bytes long unique identity generated with some special algorithm that should produce unique ids that are even unique if generated at the same time on different computers.

Maybe this could be used for your problem, too. Or if you need an algorithm: Maybe you want to check how such a guid could be calculated (e.g. http://en.wikipedia.org/wiki/Globally_unique_identifier).

With kind regards,

Konrad


Monday, August 20, 2012 1:53 PM ✅Answered | 1 vote

Use the ToFileTime which is always unique:

long l = DateTime.Now.ToFileTime();

 

Noam B.

Do not Forget to Vote as Answer/Helpful, please. It encourages us to help you...


Monday, August 20, 2012 1:35 PM

Hi,

Be aware that your strategy is right as long as code executes on a single machine.

Sebastian Sajaroff Senior DBA Pharmacies Jean Coutu


Monday, August 20, 2012 1:39 PM

Or someone's using a Flux Compensator, e.g. NNTP correcting RTC skews..


Monday, August 20, 2012 1:39 PM

You can try experimenting with the least significant 15 digits of the clock ticks as follows:

string ticks = DateTime.Now.Ticks.ToString();
ticks = ticks.Substring(ticks.Length - 15);

Note that this will only guarantee uniqueness for just over 3 years.  To increase the amount of time, you can eliminate several least significant digits, keeping in mind that doing so opens up a window of possible duplicates.  One possible way of eliminating this would be to maintain a list or queue of unique numbers that are generated by a separate thread and will only generate a number and add it to the queue (or list) within a guaranteed timespan long enough to quarantee uniqueness.

David Downing... If this answers your question, please Mark as the Answer. If this post is helpful, please vote as helpful.


Monday, August 20, 2012 1:40 PM

It doesn't work either if executed in two distinct processes. As far as the specifications were, it seems to fit well.


Monday, August 20, 2012 1:50 PM

Without a single instance controlling this, it is possible to generate duplicates.

This does not depend on the algorithm in almost any case. So using a database to ensure uniqueness is the best solution. Using any other form of cache, e.g. a file or memory can also work. The file solution requires concurrency handling, the memory solution requires some considerations about a application/machine restarts.

On thing you may consider, is using a UUID generated by Windows itself. The probability of a collusion is approximately 1:2^122. This better than when a simple algorithm is run at the same time in two processes with Louis solution (1:1) or when the NNTP scenario happens with a single process (>> 1 << 2^122).


Monday, August 20, 2012 2:01 PM

Well, with a proability of maybe > 0.9.

But I've seen real implementations of such kind failing cause the RTC was not stable. Either due to an excessive skew or a sync with an incorrect reference time.


Monday, August 20, 2012 2:06 PM

It's not:

DateTime current = DateTime.Now;
long ft1 = current.ToFileTime();
long ft2 = current.ToFileTime();
Console.WriteLine("{0} == {1} ? {2}", ft1, ft2, ft1.Equals(ft2)); 

Monday, August 20, 2012 2:13 PM | 2 votes

Oh dear, you've stirred all the mathematicians.  But really, what a strange requirement this is!  Why do you have no identity generation capability?  It would be nice if you said what you are trying to do.

Why not just increment a counter every time you need to generate a number?  They're guaranteed to be unique that way and you can generate them at any rate.

Can the counter be shared between your sources?  And I'd like you to clarify: What do you mean by called from two sources?

  • Two threads?
  • Two processes?
  • Two different runs of the same process?
  • Two different machines?
  • Two different algorithms?
  • Two different virtual machines?
  • Two different universes?

Monday, August 20, 2012 2:21 PM

Now.Ticks followed by Sleep(1) will work.


Monday, August 20, 2012 2:48 PM

Hi,

If I were you, I wouldn't use the DateTime.Now.ToString() strategy.

Two people may click within the same second and get the same random digit (a 10% chance

if both have the same timestamp).

Random doesn't guarantee uniqueness, you can throw dices and have 5s appear two or three

times in a row.

If I were you, I'd obtain an unique value via Guid.NewGuid(), which can easily be converted to decimal.

I find this kinda funny.  You say that random doesn't guarantee uniqueness (which is true) and then suggest using a GUID, which is little more than a very big random number.

Anyway, there are a few basic options.  

  1. You can either do what you've said you can and keep a repository of all of the existing IDs (or some place to have a shared counter) so that you can just have IDs increasing by one
  2. You can use a timestamp, but you will need to ensure none are generated with X unit of time of each other, and after Y unit of time you will have run out of all possible values, starting over again and possibly having duplicates.  X and Y are dependent on which digits of the timestamp you take.
  3. You can generate a random number.  The probability of collisions is dependent on the range of the numbers generated.  15 [base 10] digits (~50 base 2 digits) will have a reasonably low collision rate for 2^25 (~34 million) values (Reference to this metric).  This may or may not be good enough for your purposes.  GUIDs would be much, much better simply because of their very large range of random numbers.  If you can up the number of digits (or just use a GUID) you can effectively have unique values.

Monday, August 20, 2012 2:54 PM

Strictly speaking, you're right.

GUID doesn't guarantee uniqueness, but the probability of having a coincidence with GUIDs is far

lower than adding a random digit to the current timestamp in seconds.

Once again, as others said, it all depends on your business requirements and current infrastructure.

Sebastian Sajaroff Senior DBA Pharmacies Jean Coutu


Monday, August 20, 2012 3:01 PM

"GUID doesn't guarantee uniqueness, but the probability of having a coincidence with GUIDs is far lower than adding a random digit to the current timestamp in seconds."

Do you have proof or is that a WAG?

My WAG is that a random digit added to Now.Ticks (internet time) has a lower probability of a duplicate than a GUID.


Monday, August 20, 2012 3:04 PM

Strictly speaking, you're right.

GUID doesn't guarantee uniqueness, but the probability of having a coincidence with GUIDs is far

lower than adding a random digit to the current timestamp in seconds.

Once again, as others said, it all depends on your business requirements and current infrastructure.

No, it's not.  It's entirely dependant on the range of the random number generated.  If you generated a random number in the range of 0 to 2^1024 you'd have a *much* lower probability than a GUID.  The reason it may not be applicable here is that there may not be enough digits to have a sufficiently low collision rate.


Monday, August 20, 2012 3:09 PM

Now.Ticks() returns a 64-bits long

GUIDs are 128-bits long.

So, it's easier to have a coincidence with Now.Ticks() than with GUIDs.

If you write your own alogirthm to generate random numbers with a range wider than 2^128, 

that will be better than GUIDs.

Sebastian Sajaroff Senior DBA Pharmacies Jean Coutu


Monday, August 20, 2012 3:22 PM | 1 vote

"So, it's easier to have a coincidence with Now.Ticks() than with GUIDs."

How is that possible?  Now.Ticks are sequential.

The OP needs uniqueness, not randomness.


Monday, August 20, 2012 3:36 PM

Hi,

I am still missing what the op really wants to do.

You directly run into problems if:

  • you use the Ticks of multiple systems.
  • you also take care of the fact that you have adjustments of the local time (e.g. through AD memberships methods).

The Guid / UUID approach is what other people (who spend much more time on this topic) came to when they tried to solve this problem).

So in the first way I would try to evaluate if some existing methods could be used instead of reinventing the wheel. So maybe switching to a uniqueidentifier / GUID could solve the issues that the op has. (Which could only be answered by him!)

With kind regards,

Konrad


Monday, August 20, 2012 3:43 PM

"You directly run into problems if:

  • you use the Ticks of multiple systems.
  • you also take care of the fact that you have adjustments of the local time (e.g. through AD memberships methods)."

How is this possible?  Internet UTC time is indeterminate only by the difference in transit time of different locations.

Now.Ticks provides additional timing information that might be useful.


Monday, August 20, 2012 3:56 PM

"You directly run into problems if:

  • you use the Ticks of multiple systems.
  • you also take care of the fact that you have adjustments of the local time (e.g. through AD memberships methods)."

How is this possible?  Internet UTC time is indeterminate only by the difference in transit time of different locations.

Now.Ticks provides additional timing information that might be useful.

It is within the realm of possibilities for two computers to ask for a unique ID at the exact same tick. It's also possible for people to mess with their system time, setting it back and resulting in collisions, for the system times to be out of sync resulting in two IDs generated at different times to still think they were generated on the same tick, etc.  Using Ticks isn't really a bad idea, all of these problems have a minuscule probability of coming up in most practical cases, but it's still possible unless you ensure that only one machine is ever generating the IDs.


Monday, August 20, 2012 4:02 PM

"It is within the realm of possibilities for two computers to ask for a unique ID at the exact same tick."

This is possible, but highly improbable.  Not sure how it compares with probability of same GUID.

"It's also possible for people to mess with their system time, setting it back and resulting in collisions, for the system times to be out of sync resulting in two IDs generated at different times to still think they were generated on the same tick, etc"

I don't understand what this has to do with internet time.  If someone messes with the atomic clocks or the software that accesses them, we have more serious problems that our numbers being the same.


Monday, August 20, 2012 4:31 PM

"This is possible, but highly improbable.  Not sure how it compares with probability of same GUID."

Yes, I did say it was highly improbable, assuming there was no malicious attempts at creating collisions.  (All of these techniques are vulnerable to malicious attempts at generating collisions.)

"I don't understand what this has to do with internet time.  If someone messes with the atomic clocks or the software that accesses them, we have more serious problems that our numbers being the same."

You assume all of the machines have access to the same time source.  That may not be a true assumption.  The machine may not have internet access, it could have more network latency than some other machine, it will only check it every so often and have a different "drift" than another machine, it's checks to the standard time source could be disabled, or never set to take place in the first place, etc.


Monday, August 20, 2012 4:42 PM

The requirement states a 15 digit unique numeric value... a GUID does not meet this requirement.

David Downing... If this answers your question, please Mark as the Answer. If this post is helpful, please vote as helpful.


Monday, August 20, 2012 8:11 PM

I think that you can also create an array of all of 15-bit numbers using for example ‘Enumerable.Range(0, 0x7FFF + 1).ToArray()’. It will not take too much memory. Then shuffle it (e.g. exchange pairs using random indices many times). Then your generator will simply return elements sequentially. Use ‘lock’ statement in case of multithreading.


Monday, August 20, 2012 8:38 PM

I think that you can also create an array of all of 15-bit numbers using for example ‘Enumerable.Range(0, 0x7FFF + 1).ToArray()’. It will not take too much memory. Then shuffle it (e.g. exchange pairs using random indices many times). Then your generator will simply return elements sequentially. Use ‘lock’ statement in case of multithreading.

That only works if there is a single source generating the unique IDs for everyone, which is specifically excluded as a possibility in the OP.  It needs to support separate machines not communicating with each other.

If you are going to have a single source generating values there's no need to go through all that hassle though.  You can just keep a counter and increment it each time.  Also note that the OP says 15 digits, not 15 bits, which would imply a base 10 digits.  That's ~50 bits.  That'd be a lot of memory to take up, as opposed to a single 64 bit (long) counter.


Monday, August 20, 2012 9:48 PM

Using one of the numeric techniques above, you can dedicate the high order digit as a source digit (0, 1, ...), the remaining 14 digits can be randomly generated.

// Example Source "0"
string ticks = DateTime.Now.Ticks.ToString();
randomNumberQueue.Enqueue("0" + ticks.Substring(ticks.Length - 18, 14));

David Downing... If this answers your question, please Mark as the Answer. If this post is helpful, please vote as helpful.


Tuesday, August 21, 2012 11:23 AM

Hi, thank you all in first place for your help.

I was reading carefuly your suggestions, however my requirement is that the resunting value must be numeric, so Guid is excluded. It seemed to me that the solution that Louis.fr pointed is the most suitable, however, some of the questions raised in this thread may limit this choise:

This code may run in the same process yet on different threads, however, it will surely run in different machines.


Tuesday, August 21, 2012 12:10 PM

That only works if there is a single source generating the unique IDs for everyone, which is specifically excluded as a possibility in the OP.  It needs to support separate machines not communicating with each other.

Where did you read that? I see "called from two sources" which does not imply more than one generator. In fact, that same sentence makes me think there is one generator serving multiple clients.

Only the last reply from the OP mentions the existence of several machines, though it's not clear what "will surely run on different machines". I guess it's the client code, since it's mentioned "the code may run in the same process".


Tuesday, August 21, 2012 12:13 PM

This code may run in the same process yet on different threads, however, it will surely run in different machines.

What runs in different machines? The ID generator? Or the client code asking for IDs?


Tuesday, August 21, 2012 1:31 PM

The following code (modification of Louis's code) will also allow a unique generator prefix using a setting in a configuration file.

static object locker = new object();
static string Generate15UniqueDigits()
{
    string localPrefix = System.Configuration.ConfigurationSettings.AppSettings["IdentifierPrefix"]; // Single Digit
    lock (locker)
    {
        Thread.Sleep(100);
        return localPrefix + DateTime.Now.ToString("yyyyMMddHHmmss");
    }
}

David Downing... If this answers your question, please Mark as the Answer. If this post is helpful, please vote as helpful.


Tuesday, August 21, 2012 1:35 PM

Hi, thank you all in first place for your help.

I was reading carefuly your suggestions, however my requirement is that the resunting value must be numeric, so Guid is excluded.

You can turn a GUID into a number easily enough; you just need to have 128 bits of data, which your OP apparently states you don't. If you can increase the size of what you're storing you could use a GUID.

It seemed to me that the solution that Louis.fr pointed is the most suitable, however, some of the questions raised in this thread may limit this choise:

This code may run in the same process yet on different threads, however, it will surely run in different machines.

If there will be more than one instance of the program, and it will be running on different machines (unless they can communicate through a database, a webserver, or something along those lines) then Louis' solution won't work. If it will just be several threads running within the scope of a single instance of the program then his code will work (although it will be the least efficient).


Tuesday, August 21, 2012 1:45 PM | 1 vote

Lock to ensure that no two threads run your code at the same time. Thread.Sleep to ensure that you get two distinct times at the tenth of second.

static object locker = new object();
static string Generate15UniqueDigits()
{
    lock (locker)
    {
        Thread.Sleep(100);
        return DateTime.Now.ToString("yyyyMMddHHmmssf");
    }
}

With this technique, I strongly suggest that you use the domain of potential conflict to define what your algorithm should be.  For example, if a conflict means duplicate file names on disk, then futhermore assert that there is no duplicate by checking the file system, and if there is, then try again.  If a conflict means duplicate entries in a database then check for the existence of the new id before using it.

At the very least, please use UTC rather than local time.  

DateTime.Now.ToUniversalTime().ToString( "yyyyMMddHHmmssf" );

Otherwise it could fail when the clocks roll back as we leave daylight savings time and switch to standard time.

It is limited by the thread.Sleep call.  So if the intended application is to generate unique ids for objects, you are limited to doing one of these operations every 100ms.  This may not be so bad depending on the application, but if, for example, the idea was to generate a unique ID for each picture I took off my camera, then to generate the 50 or so IDs for all the pictures I took would take 5 seconds.  If I had taken 300-400 pictures (as I sometimes do) then this takes 30-40 seconds.  In my opinion, that would be unacceptably slow.  But it depends on the application. 

It also could fail if there are multiple processes running this code (because the lock is local to a process.)  So if this code appears in a component that is loaded into a process (like a plug-in, or even a custom control) then it is bogus.

I really don't like the idea of being constrained to 15 digits for a unique ID.  I'd strongly prefer to use a Guid.  It's easy to create one:  Guid.NewGuid()


Tuesday, August 21, 2012 3:54 PM

Lock to ensure that no two threads run your code at the same time. Thread.Sleep to ensure that you get two distinct times at the tenth of second.

static object locker = new object();static string Generate15UniqueDigits(){    lock (locker)    {        Thread.Sleep(100);        return DateTime.Now.ToString("yyyyMMddHHmmssf");    }}

I don't understand why you would use the time rather than a counter.  You just need to load/save the counter at startup/shutdown.  That would eliminate your need to Sleep().  Surely the OP can find < 64-bits of NV storage somewhere.

To run on multiple machines, either implement client/server or reserve N digits of the result for a machine-id (or program-id or combination).


Tuesday, August 21, 2012 4:16 PM

I don't understand why you would use the time rather than a counter.

I read "no database support" and erroneously expanded that to "no storage".


Tuesday, May 30, 2017 4:42 PM

I know this is an old thread but you could convert the DateTime.Now.Ticks to HEX and bring the length from 18 characters to 15 characters.

   public string GetUniqueID() {
        string s = "";
        s = Convert.ToString(DateTime.Now.Ticks, 16).ToUpper;
        return s;
    }