public static void Main()
var id = CombGuid.Create();
Console.WriteLine(CombGuid.GetTimestamp(id).ToString("o"));
var rtComb = RT.Comb.Provider.Sql.Create();
Console.WriteLine(rtComb);
Console.WriteLine(RT.Comb.Provider.Sql.GetTimestamp(rtComb).ToString("o"));
public static class CombGuid
private const int EMBED_AT_INDEX = 10;
private const long INCREMENT_TICKS = 1 * DateTimeExtensions.TICKS_PER_MILLISECOND;
private static long lastValue = DateTime.UtcNow.Ticks;
public static Guid Create() => Create(Guid.NewGuid(), GetTimestamp());
public static Guid Create(DateTime timestamp)
var guidBytes = Guid.NewGuid().ToByteArray();
var timeBytes = timestamp.ToBytes();
Array.Copy(timeBytes, 0, guidBytes, EMBED_AT_INDEX, DateTimeExtensions.DATE_BYTES);
return new Guid(guidBytes);
public static Guid Create(Guid regularGuid, DateTime timestamp)
var guidBytes = regularGuid.ToByteArray();
var timeBytes = timestamp.ToBytes();
Array.Copy(timeBytes, 0, guidBytes, EMBED_AT_INDEX, DateTimeExtensions.DATE_BYTES);
return new Guid(guidBytes);
public static DateTime GetTimestamp(Guid comb)
var gbytes = comb.ToByteArray();
var dbytes = new byte[DateTimeExtensions.DATE_BYTES];
Array.Copy(gbytes, EMBED_AT_INDEX, dbytes, 0, DateTimeExtensions.DATE_BYTES);
return dbytes.ToDateTime();
private static readonly object lockObj = new object ();
private static DateTime GetTimestamp()
if (Monitor.TryEnter(lockObj, 1))
var now = DateTime.UtcNow.Ticks;
var elapsed = now - lastValue;
if (elapsed < INCREMENT_TICKS)
now = Interlocked.Add(ref lastValue, INCREMENT_TICKS);
now = Interlocked.Add(ref lastValue, elapsed);
return new DateTime(now);
return new DateTime(Interlocked.Add(ref lastValue, INCREMENT_TICKS));
internal static class DateTimeExtensions
public const int DATE_BYTES = 6;
public const long TICKS_PER_MILLISECOND = 10000;
private static readonly DateTime MIN_DATE = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
public static byte[] ToBytes(this DateTime dt)
var ms = ToUnixTimeMilliseconds(dt);
var msBytes = BitConverter.GetBytes(ms);
if (BitConverter.IsLittleEndian)
var result = new byte[DATE_BYTES];
var index = msBytes.GetUpperBound(0) + 1 - DATE_BYTES;
Array.Copy(msBytes, index, result, 0, DATE_BYTES);
public static DateTime ToDateTime(this byte[] combGuid)
var msBytes = new byte[8];
var index = 8 - DATE_BYTES;
Array.Copy(combGuid, 0, msBytes, index, DATE_BYTES);
if (BitConverter.IsLittleEndian)
var ms = BitConverter.ToInt64(msBytes, 0);
return FromUnixTimeMilliseconds(ms);
throw new ArgumentException($"Cannot convert {string.Join(" ", msBytes)} to DateTime", ex);
private static long ToUnixTimeMilliseconds(DateTime dt) => (dt.Ticks - MIN_DATE.Ticks) / TICKS_PER_MILLISECOND;
private static DateTime FromUnixTimeMilliseconds(long ms) => MIN_DATE.AddTicks(ms * TICKS_PER_MILLISECOND);