namespace System; public partial struct Guid { public static Guid AllBitsSet { get; } // 堆代码 duidaima.com public int Variant { get; } public int Version { get; } public static Guid CreateVersion7(); public static Guid CreateVersion7(DateTimeOffset timestamp); }Guid.AllBitsSet 对应着 Guid.Empty, Empty 的所有比特位都是 0, AllBitsSet 则都是 1
Version 和 Variant 代表了当前 Guid 值的实现细节, 可以参考 RFC 文档说明 https://www.rfc-editor.org/rfc/rfc9562.html#name-variant-field
public static Guid CreateVersion7() => CreateVersion7(DateTimeOffset.UtcNow);
var guid = Guid.CreateVersion7(); Console.WriteLine(guid.ToString()); Console.WriteLine($"{nameof(guid.Version)}: {guid.Version}"); Console.WriteLine($"{nameof(guid.Variant)}: {guid.Variant}"); var timestamp = DateTimeOffset.UtcNow; Console.WriteLine($"Timestamp: {timestamp} {timestamp.ToUnixTimeMilliseconds()}"); Console.WriteLine(Guid.CreateVersion7(timestamp));用起来是不是还挺简单的, 有一个问题, 既然是基于时间的,同一个时间戳会不会生成的 Guid 是一样的呢?
guid = Guid.CreateVersion7(timestamp); Console.WriteLine(guid);输出结果如下:
0191fa19-7082-7541-ae8e-befcfffe79cb Version: 7 Variant: 10 Timestamp: 9/16/2024 09:10:56 +00:00 1726477856901 0191fa19-7085-7e0b-ae72-aa63b4585467 0191fa19-7085-782f-a30b-3a0223ba3a31可以看到两次生成的 guid 并不相同, 这从 rfc 文档或者实现细节中可以了解到, 这是因为除了时间参数之外还会有随机参数,导致即使时间一样生成的 guid 还是会不一样。那我们能否从 Guid 中获取到时间呢? 答案是肯定的, 不过获取到的时间不会完全准确有一定的误差, 因为可能会引入随机参数, 从上面的输出也可以看得出来, 两个 guid 的前面十二个字符是完全一样的, 前面 6 个 byte 会是一样的, 他们对应了时间信息, 我们也可以从源码里找到一些细节
private static void PrintDateTime(Guid guid) { if (guid.Version is not 7) { throw new InvalidOperationException("Guid.Version is not 7"); } var bytes = guid.ToByteArray(); var a = BitConverter.ToInt32(bytes.AsSpan(0, 4)); var b = BitConverter.ToInt16(bytes.AsSpan(4, 2)); var timestamp = (((long)a) << 16) + b; var dateTime = DateTimeOffset.FromUnixTimeMilliseconds(timestamp); Console.WriteLine($"DateTime: {dateTime.UtcDateTime} {timestamp}"); }接着前面的示例试一下
Thread.Sleep(2000); Console.WriteLine(Guid.CreateVersion7()); PrintDateTime(guid);输出结果如下:
可以看到两个时间比较接近但还是会有一些误差,不过误差会比较小,可以看到只有一分钟多一点的误差。