前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >.NET 9 中基于时间的 Guid

.NET 9 中基于时间的 Guid

作者头像
JusterZhu
发布2025-01-23 20:40:02
发布2025-01-23 20:40:02
5600
代码可运行
举报
文章被收录于专栏:JusterZhuJusterZhu
运行总次数:0
代码可运行

.NET 9 基于时间创建 Guid

Intro

.NET 9 中引入了基于时间来生成 Guid, 因为实现的 RFC 文档里的第七个版本, 所以 API 名称为 Guid.CreateVersion7()

New API

新增的 API 如下:

代码语言:javascript
代码运行次数:0
复制
namespace System;

public partial struct Guid
{
    public static Guid AllBitsSet { get; }

    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

AllBitsSet

VersionVariant 代表了当前 Guid 值的实现细节, 可以参考 RFC 文档说明 https://www.rfc-editor.org/rfc/rfc9562.html#name-variant-field

Guid.CreateVersion7()/Guid.CreateVersion7(DateTimeOffset timestamp) 用于创建基于时间的 Guid, 如果没有参数就会使用当前时间

代码语言:javascript
代码运行次数:0
复制
public static Guid CreateVersion7() => CreateVersion7(DateTimeOffset.UtcNow);

Sample

来看一个使用示例吧,

代码语言:javascript
代码运行次数:0
复制
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 是一样的呢?

我们来测试一下, 接着前面的示例,

代码语言:javascript
代码运行次数:0
复制
guid = Guid.CreateVersion7(timestamp);
Console.WriteLine(guid);

输出结果如下:

代码语言:javascript
代码运行次数:0
复制
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 会是一样的, 他们对应了时间信息, 我们也可以从源码里找到一些细节

这里的 _a, _b 对应的就是前面的两段, 也可以从源码的注释里获取更多说明

最后我们可以从 byte 里获取到时间的信息, 实现如下:

代码语言:javascript
代码运行次数:0
复制
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}");
}

接着前面的示例试一下

代码语言:javascript
代码运行次数:0
复制
Thread.Sleep(2000);
Console.WriteLine(Guid.CreateVersion7());
PrintDateTime(guid);

输出结果如下:

可以看到两个时间比较接近但还是会有一些误差,不过误差会比较小,可以看到只有一分钟多一点的误差

More

Github 上有一个根据 Guid 获取时间的 issue, 不过因为时间并不准确, 可能大概率不会支持, 感兴趣的朋友可以关注 https://github.com/dotnet/runtime/issues/107136

Github issue 上还有作者对于 Guid 实现的一些总结, 感觉可以了解一下, 也分享一下

v1 被广泛认为已过时,应该尽可能用 v7 替代 v2 用于 DCE 安全目的,超出了正常规范 v3 被广泛认为已过时,应该尽可能用 v5 替代 v4 用于创建随机 UUID,目前已经通过 Guid.NewGuid 支持 v5 用于从字符串输入创建 UUID,但由于使用 SHA-1,因此也被广泛认为已过时,因为存在潜在的安全攻击风险 v6 是简单的 v1,并对位进行了替代排序,同样被广泛认为已过时,应该尽可能用 v7 替代 v7 是本提案通过新的 CreateVersion7 API 所支持的版本 有一些可选的扩展功能尚不支持,但我们可以在未来扩展以支持这些功能 v8 明确用于实验性和特定供应商使用,其包含的位没有定义,仅限于版本和变体字段 这间接地通过普通的新 Guid(...) API 得到支持,这些 API 允许您指定所有底层位的值

v1 被广泛认为已过时,应该尽可能用 v7 替代 v2 用于 DCE 安全目的,超出了正常规范 v3 被广泛认为已过时,应该尽可能用 v5 替代 v4 用于创建随机 UUID,目前已经通过 Guid.NewGuid 支持 v5 用于从字符串输入创建 UUID,但由于使用 SHA-1,因此也被广泛认为已过时,因为存在潜在的安全攻击风险 v6 是简单的 v1,并对位进行了替代排序,同样被广泛认为已过时,应该尽可能用 v7 替代 v7 是本提案通过新的 CreateVersion7 API 所支持的版本 有一些可选的扩展功能尚不支持,但我们可以在未来扩展以支持这些功能 v8 明确用于实验性和特定供应商使用,其包含的位没有定义,仅限于版本和变体字段 这间接地通过普通的新 Guid(...) API 得到支持,这些 API 允许您指定所有底层位的值

https://github.com/dotnet/runtime/issues/103658#issuecomment-2226246739

References

  • https://datatracker.ietf.org/doc/rfc9562/
  • https://www.rfc-editor.org/rfc/rfc9562.html
  • https://github.com/dotnet/runtime/issues/103658
  • https://github.com/dotnet/runtime/pull/104124
  • https://github.com/dotnet/runtime/issues/103658#issuecomment-2226246739
  • https://github.com/dotnet/runtime/issues/107136
  • https://github.com/WeihanLi/SamplesInPractice/blob/main/net9sample/Net9Samples/GuidSample.cs
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-09-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 JusterZhu 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • .NET 9 基于时间创建 Guid
    • Intro
    • New API
    • Sample
    • More
    • References
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档