Encode current date into short unique string - c#

I need to encode current datetime into some unique string to store it in database.
I found this article how to generate a unique token which expires after 24 hours? but for me generated token is to long (34 symbols)
Is there some other similar way to encode shorter string?
Perfect size <= 10 symbols.

I'd ask why? What are you trying to do? If you want to timestamp something like a log entry then just use the datetime value - every database type I know has a built in date/time type.
If you're trying to generate a unique Id for use as something like a primary key then this would be a bad idea - I've yet to find a good case for using a date based unique id.
It would be much better to have an auto-incrementing integer or even GUID value. If you wanted you could then add a timestamp column to the database

You can use a tick (DateTime.Ticks for instance) but don't store the tick as simple string, encode the bits. If you use a long tick (64bit) you should consider ASCII85 encoding of the bytes so it wont exceed 10 symbols.
var tickBytes = BitConverter.GetBytes(DateTime.UtcNow.Ticks);
string encodedTicks = new Ascii85().Encode(tickBytes);
If you chose a 32bit tick, base 64 should be fine.
For a readable tick with precision to second (less precise than the previous solution)
long origin = new DateTime(2014, 7, 24).Ticks / TimeSpan.TicksPerSecond;
long customTicks = (DateTime.UtcNow.Ticks / TimeSpan.TicksPerSecond) - origin;
string readableTicks = customTicks.ToString(CultureInfo.InvariantCulture);
That will stay on 10 chars or less for ~300 years.

Okay, if you want it from "about now" to some point in the future, and you want seconds granularity, and you want ASCII symbols, let's assume base64.
With 8 characters of base64, we can encode 6 bytes of data. That will give us 248 different values, which allows about 9 million years-worth of seconds. Given that range, we might as well use the DateTime.Ticks property and divide by ticks-per-second, not worrying about the epoch. Full code coming later if you want it, but as a list of steps:
Take DateTime.UtcNow.Ticks
Divide by TimeSpan.TicksPerSecond
Convert the result into a byte[], e.g. with BitConverter.GetBytes(long)
Encode the least-significant 6 bits (I'm hopeless with endianness - either the first or last 6 bytes of the byte[] as base64 using Convert.ToBase64String

Related

How does String.Format work in this situation?

I have a website where you can buy stuff, and we want to format the orderID that goes to our portal in certain way. I am using the string.format method to format it like this:
Portal.OrderID = string.Format( "{0}{1:0000000}-{2:000}",
"Z",
this.Order.OrderID,
"000");
So we want it to look like this basically Z0545698-001. My question is, if I am using string.format will it blow up if this.Order.OrderID is greater than 7 characters?
If so, how can I keep the same formatting (i.e. Z 1234567 - 000) but have the first set of numbers (the 1-7) be a minimum of 7 (with any numbers less than 7 in length have leading 0's). And then have anything greater than 7 in length just extend the formatting so I could get an order number like Z12345678-001?
how can I keep the same formatting (i.e. Z 1234567 - 000) but have the first set of numbers (the 1-7) be a minimum of 7 (with any numbers less than 7 in length have leading 0's). And then have anything greater than 7 in length just extend the formatting so I could get an order number like Z12345678-001?
Use exactly the code that you have, because that's what it does.

Generating a unique 15 digite Pin code from a 10digit number

I want to create pin codes and serial numbers for scratch papers , I have already generated unique 10 digit numbers , now I want to turn that 10 digit number to a 16 digit number (with check digit in the end) . The thing is that the function that does this should be reversible so by seeing the 16 digit number I can check whether it is valid or not .(if it is not generated by me it should not be valid) .
this is how I have generated the 10 digit unique random codes :
Guid PinGuid;
byte[] Arr;
UInt32 PINnum = 0;
while (PINnum.ToString().Length != 10)
{
PinGuid = Guid.NewGuid();
Arr = PinGuid.ToByteArray();
PINnum = BitConverter.ToUInt32(Arr, 0);
}
return PINnum.ToString();
I would be grateful if you can give me a hint on how to do it .
First off, I would avoid GUID since some prefixes are reserved for special applications. Which means that these areas of the GUID may not be allocated uniformly on creation, so you may not get exactly 10 digits of randomness like you plan.
Also since your loop waits for the GUID to become the right size you could do it more efficiently.
10 digits = 10**10
Log_2(10) = approx 3322/1000
So you need approx 33 bits for 10 digit number. Since you want your number to be exactly 10 digits, you can either pad numbers less than 10^10 with leading zeroes, or you can generate only numbers between 10^9 and 10^10 - 1.
If you take the latter case you need 9*10^9 numbers in your space -- giving you all numbers from 1 followed by nine zeroes up to 9 followed by 9 9s.
Then you would like to convert this space of numbers into a larger space, to expand it by a factor of 5 and include one more digit as a check digit.
Pick a check digit function as anything you like. You could simply sum (mod 10) the original 10 digits, or choose something more complicated.
Presumably you do not want people to be able to generate valid instances. So if you are really serious about your security, you should modify any suggestions you get from the net before deploying them.
I would do something along the lines of :
Generate a uniform 10digit number with no leading zeroes by
randomTenDigits = 10**9 + rand(9*10**9)
Using an encryption scheme (like AES 256 or even RSA or El-Gamal since their slower speed will no be so important since input length is small ) encrypt this 10 digit number using a secret key only you and others you trust are aware of. Perhaps you can concatenate the 10 digit number 10 times, and then concatenate that result with some other secret that you choose, and then finally encrypt this expanded secret of which the 10 digit number is a part.
Take some choice 5 digits (around 17 bits) of the resulting ciphertext, and append these to your 10 digit number.
Generate 1 digit of check digit by whatever method you desire.
As you will note the real security of this scheme is not from a check digit, it is from the secret key you can use to authenticate the 16 digit number. The test you will use to authenticate it is: does the given 10 digit number when concatenated with other secrets I have, encrypt, using a secret key only I know, to the given 5 digit number presented with it.
Since the difficulty for an attacker of forging one of your numbers depends on the difficulty of
discovering your secret keys and other info
discovering which method of encryption you use
discovering which part of the resulting cipher text you emit for the 5 digit secret, or
simply brute forcing the 5 digits to discover the correct pairing, and since 5 digits is not a big space to search, I would suggest instead generating larger numbers. 10 or 16 digits is not really a huge space to search. So instead of digits I would use upper and lower case letters plus digits plus space and full stop to give you 64 letters in your alphabet. Then if you used 16 you get around 96 bits of security.
However if numbers are non-negotiable and the size of 10 digits for your base space is also non-negotiable, doing it this way is probably the most secure. You may be able to set up your system to deter people from brute forcing it, though you should consider what if someone acquires a piece of your hardware through a vendor. I believe it is easier to design security in rather than design in a mechanism for detecting people trying to brute force query your system.
However if serious dough is on the line ( like millions ) the security you employ should really be first class. Equivalent to the kind of security you would employ to protect a pin number to a million dollar bank account. The more secure you are the longer you can carry on your biz with credibility and trust.
So along these lines I would suggest increasing the size of your secrets to make it infeasible for someone to simply try all combinations and forge a valid one, and in particular thinking about how to design your system to make it difficult to break for people with lots of skills and motivation (money). You really can't be too careful.
I would keep it simple. Put PINnum.ToString() into a buffer. Place a filler digit at 5 intervals. The first four could be random garbage and the last could be a check digit, or you could make each filler a check digit for its section. Here is an example.
buf = PINnum.ToString();
int chkdgit = function to create your checkdigit
Random rnd = new Random();
int i = rnd.Next(1001,9999);
fillbuf = i.toString();
return buf[0] + buf[1] + fillbuf[0] + buf[2] .... chkdgit.toString();
its a rather simple approach, but if your security needs aren't at level 1, it might suffice

what type shall use to store 12 digit value shall I use decimal or nvarchar in SQL DB?

I need to store an CARD ID number in Database. So there is no calculation just a search of the ID and putting the value in Session as property in a class.
The is ID is always numeric and it's 12 positions.
e.g. 123456789012 and I would like to show on the screen in this format. 123.456.789.012 (every 3 digit a dot).
I tried a test and defined Decimal(12,0) in database and I have put this value in database: 555666777888
then I try to display on the screen I used this code (CardID is decimal):
lblCardID.Text = ent.CardID.ToString("0:#,###")
but it shows on the screen like this: 555,666,77:7,888
where is the colon (:) coming from?
question additional:
- What type shall use in MS SQL to store this value in Database. Decimal (12,0) or Nvarchar(12) ?
nvarchar is definitely not needed. if it's always 12 digits, char(12) would be fine, but I think a 64-bit integer would be most appropriate.
Try writing
lblCardID.Text = ent.CardID.ToString("#,###")
You can user the decimal(12,0) or the bigint datatype. bigint requires one byte less (8 bytes total) per stored value.
The colon is coming from the colon in your format string. The "0:" at the beginning of the format string is needed when you are using string.Format(), as a placeholder to identify which of the arguments to format, but not if you are using ToString() (since there's only one value being formatted).
I would use bigint because it needs only 8 bytes per value.
decimal(12,0) needs 9 bytes and varchar or nvarchar even more (12 or 24 bytes respectively in case of storing 12 digits).
Smaller column size makes indexes smaller, which make indexes faster in use.
Formatting numbers can be done in application.
It's also much easier to change formatting in app in case of requirements change.
If you need to store the formatting, and it's just a numeric value, use varchar, don't waste time with nvarchar as it increases your storage size and won't do you any good unless you expect special (international) chars
If it's never going to be calculated on, I would store it as char(12).
Then in your code, split it with something like this and use the replace function to convert commas to dots:
lblCardID.Text = ent.CardID.ToString("#,###").Replace(",", ".")
If it's an ID number store it as a string datatype, you're not going to be doing sums on it, you also won't have problems losing any leading zeros. You could also then store the card id with the embedded dots, sorting out your formatting problems.
Does your identifier's domain have matematical properties, other than being composed of digits? If not, your value is fixed width, so use CHAR(12). Do not forget to add appropriate domain checks (no characters other than digits, no leading zero, etc) e.g.
CREATE TABLE Cards
(
card_ID CHAR(12) NOT NULL
UNIQUE
CONSTRAINT card_ID__all_digits
CHECK (card_ID NOT LIKE '%[^0-9]%'),
CONSTRAINT card_ID__no_leading_zero
CHECK (card_ID NOT LIKE '[1-9]%)')
);

Date Time Encoding

Any ideas or implementations floating about for encoding the current date including the milliseconds into the shortest possible string length?
e.g I want 31/10/2011 10:41:45 in the shortest string possible (ideally 5 characters) - obviously decodable.
If it is impossible to get down to 5 characters, then the year is optional.
edit: it doesn't actually need to be decodable. It just needs to be a unique string.
An time_t is 31 bits. Add 10 bits for up to 1000 milliseconds: That's 41 bits. You want 5 characters: That's 8 bits for the 1st 4 characters + 9 bits for the last one.
Using Chinese ideograms, you should easily be able to find a range of 256 consecutive chars for each of the 1st 4 chars and a range of 512 for the last one.
Needless to say your encoded date will look... chinese! But it should do the trick ;-)
BTW, you don't have to stick to Chinese. You might even want to choose a different Unicode 256 chars range for each character. Of course, you'll want to find sequences of 256/512 printable chars.
Now let's say we skip the year. We're down to 86400 x 366 seconds per year = 31622400 seconds. Including millisecs : 31622400000. That's 35 bits. Great: We're down at 7 bits per character. Easy! :-)
you can use the Ticks:
var ticks = System.DateTime.Now.Ticks;
this is a 64bit number. You get the Time back by calling:
var timeBack = new System.DateTime(ticks);
of course this are 8 bytes but I don't think you can get this more compact (easily).
No can do: The total ms in an year (365 days) is 31,536,000,000 (=365*24*60*60*1000). You need 34.87628063 bits of information to store that value (log2 31,536,000,000). You probably meant "printable characters" BUT you would need 7 bits/character to store 35 bits in 5 characters. As an example base64 is 6 bits/character of information, so 6 characters. Ascii85 would be a little better, but still you would need around 5.5 characters, so 6 characters.
Clearly if you meant 5 BYTES, everything changes. You can store 34.84 years (in ms) in that space.
And if you meant 5 C# PRINTABLE AND UNPRINTABLE CHARACTERS (each C# character is 16 bits), then it's even better. 10 bytes! DateTime in C# is only 8 bytes and it uses ticks (they are a VERY VERY VERY small part of a second)!
BUT if you meant 5 C# PRINTABLE CHARACTERS characters, then use Serge's response. It's very good and show us that the world is a big place (and show us that why good questions are so much important: they let us see the world in new ways).
You can use ASCII characters to represent the numbers and drop the formatting, for example:
31/10/2011 10:41:45
*/*/** *:*:*
*******
That's 7, you can drop 2 if you don't want to include the full year. Obviously the * are actual characters relating to a number, A could be 1 etc, or even use the proper ASCII codes.

18 Digit Unique ID - Code reliability

I want a number that would be unique forever, I came up with the following code,
it generates a number and adds a check digit to the end of it, I would like to know how reliable is this code?
public void GenerateUniqueNumber(out string ValidUniqueNumber) {
string GeneratedUniqueNumber = "";
// Default implementation of UNIX time of the current UTC time
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
string FormatedDateTime = Convert.ToInt64(ts.TotalSeconds).ToString();
string ssUniqueId = DateTime.UtcNow.ToString("fffffff");
//Add Padding to UniqueId
string FormatedUniqueId = ssUniqueId.PadLeft(7, '0');
if (FormatedDateTime.Length == 10 && FormatedUniqueId.Length == 7)
{
// Calculate checksum number using Luhn's algorithm.
int sum = 0;
bool odd = true;
string InputData = FormatedDateTime + FormatedUniqueId;
int CheckSumNumber;
for (int i = InputData.Length - 1; i >= 0; i--)
{
if (odd == true)
{
int tSum = Convert.ToInt32(InputData[i].ToString()) * 2;
if (tSum >= 10)
{
string tData = tSum.ToString();
tSum = Convert.ToInt32(tData[0].ToString()) + Convert.ToInt32(tData[1].ToString());
}
sum += tSum;
}
else
sum += Convert.ToInt32(InputData[i].ToString());
odd = !odd;
}
//CheckSumNumber = (((sum / 10) + 1) * 10) - sum;
CheckSumNumber = (((sum + 9) / 10) * 10) - sum;
// Compute Full length 18 digit UniqueNumber
GeneratedUniqueNumber = FormatedDateTime + FormatedUniqueId + Convert.ToString(CheckSumNumber);
}
else
{
// Error
GeneratedUniqueNumber = Convert.ToString(-1);
}
ValidUniqueNumber = GeneratedUniqueNumber;
}
EDIT: clarification
GUID can not be used, the number will need to be entered into a IVR system via telephone keypad.
You cannot use GUIDs, but you can create your own format of unique number similar to a GUID, that is based on the machine's MAC address (space) and the current time and date (time). This is guaranteed to be unique if the machines all have synchronised clocks.
For more information, please see here
Why don't you just use a Guid?
There are a few problems with this method:
You're basically just counting the number of milliseconds from January 1, 1970. You can get this from ts.TotalSeconds rounded to 0.0000001. All your conversion and millisecond calculation is unnecessary.
10 years is about 3×10¹¹ milliseconds. You are keeping 17 significant digits, so for the next 10 years the first 5 digits will never change and cannot be used to distinguish numbers. They are useless.
Are you generating numbers for milliseconds between 1970 and now? If not, they also cannot be used to distinguish numbers and are useless.
This is totally dependent on what machine is returning the date. Anyone who has access to this machine can generate whatever "unique" numbers they want. Is this is problem?
Anyone who sees one of these numbers can tell when it was generated. Is this a problem?
Anyone can predict what number will be generated when. Is this a problem?
1015 milliseconds is about 30000 years. After then, your algorithm will repeat numbers. Seems like a long time, but you specified "forever" and 30000 years is not "forever". Do you really mean "forever"?
If I understand your implementation correctly, it only uses the current date/time as a basis. That means that if you create two IDs simultaneously, they will not be unique.
Since you mentioned (in comments) that the IDs are stored in a DB, you can generate the IDs either using the method you mentioned or randomly and check for the existence in the DB.
If it already exists, generate a new one, otherwise you're done.
One thing though, I would make sure that checking for the existence of the ID and the actual saving of the record to the DB be done in a transaction, otherwise you run the risk of having another request create that record in between the checking for the ID and the creation of the row.
Also just checking, why wouldn't an auto-increment number generated by the database itself work? The DB would guarantee it's uniqueness (for that table anyway)
You don't say what the numbers are to be used for. Do they have some sort of value associated with them? Will it be a problem if users can figure out the scheme and guess valid ticket numbers?
If it is important for these numbers to be hard to guess, this scheme falls down; something that outputs data that looks really random would be better. You might take a monotonically increasing serial number and encrypt it using a block cipher (with a 64-bit block size); that gives you a 64-bit output or about 20 decimal digits worth, which you could take (say) the last 18 of. (If reversibility is important, i.e. given a ticket number you want to be able to recover the serial number, you need to be a bit more careful here.)
Do you need a cast-iron 100% guarantee that no ticket numbers will ever be the same? If so, you need to keep them in a database and mark them off when used. If you do that, it might be reasonable to just use a good random number generator and check for dupes every time.
Using the system time is a good start, but it gives you collisions if you need to generate two UIDs at the same time. It doesn't help that you're using the "fffffff" format: The Windows clock resolution is only 15-16 ms, so only one or two of those "f"s are doing any good.
Also, your approach tells you exactly when the ID was generated. Depending on your needs, this may be a desirable feature, or it may be a security risk.
You'll need your IDs to include other information instead of or in addition to the time. Some possible choices are:
A random number
A cyclic counter
A hash of the program name (if your need these IDs in multiple programs)
The MAC address or other identifier for the machine (If the IDs need to be unique across multiple computers)
If you want to ensure uniqueness, then store your IDs in a database so you can check for duplicates.
As "Andrew Hare" says, You can use Guid.
About your code the answer is "NO"!
because if client's computer's DateTime was wrong or change result may be couple or more!
No such thing as random anyway. Here's a suggestion.
Create your own "random" 18 digit number
Before sending it to the user, check it against existing ones in DB
If already in DB, rinse and repeat.

Categories