Way to generate a unique number that does not repeat in a reasonable time?
Asked Answered
O

6

18

I'm integrating/testing with a remote web service and even though it's the "QA" endpoint, it still enforces a unique email address on every call.

I can think of DateTime.Now.Ticks (e.g. 634970372342724417) and Guid.NewGuid(), but neither of those can be coalesced into an email with max. 20 chars (or can they?).

I suppose it's not that hard to write out to a file a number that contains the last number used and then use [email protected], [email protected], etc... but if I can avoid persisting state I always do.

Does anyone have a trick or an algorithm that gives something of a short length "guid" that is unique to a reasonably long time period (say a year) that I could use for my email addresses of max length 20 chars with (max length of guid) = 14 = 20 - length of "@x.com"?

Orbicular answered 21/2, 2013 at 18:15 Comment(5)
How often do you send requests?Kyoko
It's not a load test. There's at least a second between callsOrbicular
#4421942 Specifically Jon Skeet's response might be helpful for your caseBlackfish
Is there only one application that has to be unique, or do the emails have to be unique across multiple instances (possibly calling the service at the same time)?Venditti
just me and my dev boxOrbicular
M
26

If you assume that you will not generate two e-mail addresses at the same 'tick', then you can indeed use the ticks to generate an e-mail address.

However, if ticks is a 64-bit number, and you write out that number, you will end up with more than 20 characters.

The trick is to encode your 64-bit number using a different scheme. Assume that you can use the 26 characters from the western alphabet + 10 digits. This makes 36 possible characters. If you take 5 bits, you can represent 32 characters. That should be enough. Take the 64-bits and divide them in groups of 5 bits (64 /5 is about 13 groups). Translate every 5 bits to one character. That way you end up with 13 characters, and you can still add a character in front of it).

long ticks = DateTime.Now.Ticks;
byte[] bytes = BitConverter.GetBytes(ticks);
string id = Convert.ToBase64String(bytes)
                        .Replace('+', '_')
                        .Replace('/', '-')
                        .TrimEnd('=');
Console.WriteLine (id);

Yields:

Gq1rNzbezwg
Masturbate answered 21/2, 2013 at 18:26 Comment(1)
I'm on the verge of getting your answer to work by converting ticks to base64.. just have to figure out what to do with '+' and '/'...Orbicular
C
17

If you get the following digits from your date-time, you should be able to make it work... Soemthing like:

DateTime.Now.ToString("yyMMddHHmmssff");

which is 16 characters, leaving 4 for some other prefix as you need.

So, Feb 21, 2013, at approximately 10:21 would be "130321102142" and the next one would be "130321102169", etc...

Have a look at http://msdn.microsoft.com/en-us/library/zdtaw1bw.aspx for more details on datetime formatting.

Clarisaclarise answered 21/2, 2013 at 18:22 Comment(3)
+1 for using yy and ff. But there is a tiny chance there will be duplicates if you use hh and not HH.Kyoko
Yea, it was a non-tested code sample. When possible I like to throw these kinds of things into my lab project just to make sure they work as advertised. Regardless - updated answer to user HH.Clarisaclarise
Thanks for a quick solution to a common problem.Creak
K
13

Since you specified at least 1 second between each call, this should work :

DateTime.Now.ToString("yyyyMMddHHmmss");

its exactly 14 characters.

Kyoko answered 21/2, 2013 at 18:21 Comment(3)
@VenomFangs yep, see the OP comment on the question. It's not a load test. There's at least a second between callsKyoko
yeah i up voted this answer too - it's a good answer - the one i marked just seems a little bit more robust, but this definitely meets the "spec" from my question :)Orbicular
@AaronAnodide Its your right to choose the answer that works best, thanks for the +1 :)Kyoko
C
1
    public async Task<string> GeneratePatientNumberAsync()
    {
        var random = new Random();
        var chars = DateTime.Now.Ticks + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123456789" + DateTime.Now.Ticks;
        return new string(Enumerable.Repeat(chars, 5)
            .Select(s => s[random.Next(s.Length)]).ToArray());
    }
Cookshop answered 16/8, 2022 at 13:31 Comment(1)
We can control the list of the string, in my case I just need 5 digits.Cookshop
O
0

Just to add... If you want to use number only from ticks, you can by using substring, for example:

int onlyThisAmount = 20;
string ticks = DateTime.Now.Ticks.ToString();
ticks = ticks.Substring(ticks.Length - onlyThisAmount);
Oleaceous answered 26/8, 2016 at 11:53 Comment(0)
D
0
    /// <summary>
    /// Get a unique reference number.
    /// </summary>
    /// <returns></returns>
    public string GetUniqueReferenceNumber(char firstChar)
    {
        var ticks = DateTime.Now.Ticks;
        var ticksString = ticks.ToString();
        var ticksSubString = ticksString.Substring((ticksString.Length - 15 > 0) ? ticksString.Length - 15 : 0); 
        if (this.currentTicks.Equals(ticks))
        {
            this.currentReference++;

            if (this.currentReference >= 9999)
            {
                // Only when there are very fast computers.
                System.Threading.Thread.Sleep(1);
            }

            return (firstChar + ticksSubString + this.currentReference.ToString("D4")).PadRight(20, '9');
        }

        this.currentReference = -1;
        this.currentTicks = ticks;
        return (firstChar + ticksSubString).PadRight(20, '9');
    }

In my case I needed to create a unique reference number with a unique first character and a maximum of 20 characters. Maybe you can use the function below, it allows you to create 9999 unique numbers within one tick. (zero included)

Of course you can create your own implementation without the first character and maximum character count of 20

Derayne answered 17/11, 2016 at 14:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.