NodaTime的Instant.Now

39

我正在尝试使用Jon Skeet(和其他人)开发的Noda Time框架。

我想要存储当前时间(Instant)。Instant是由长整型刻度创建的,但现在的当前刻度数是多少?

是这样吗:

Instant now = new Instant(DateTime.Now.ToUniversalTime().Ticks);

和还是或?

Instant now = Instant.FromDateTimeUtc(DateTime.Now.ToUniversalTime());

它们是否相等,我甚至是否做得对?

附言:如果Jon回答这个问题 - 我想提出一个Instant.Now属性。

附言2:我知道标题包含一个标签,但它不让我使用一个简短的“Instant.Now”标题。


“Instant.Now”作为属性的使用情况不好,因为它会在“幕后”更改值(BCL也存在同样的问题:“DateTime.Now”)。 “Instant.GetCurrent()”会更好,而“new Instant()”可能是最好的选择。免责声明:我对Noda Time没有任何了解。 - Jon
@Jon,Instant.GetCurrent()不存在 - 请查看Instant文档。Instant只有一个构造函数Instant(Int64) - Chuck Savage
但无论如何,这两个定义是等价的。Ticks是一个绝对数量,它不随时区改变。 - Jon
7
等待Jon Skeet出现,回答100%准确,获得所有赞并让ChuckSavage感到开心。 - Aniket Inge
4
"缺少Instant.Now是经过深思熟虑的。它无法进行测试,这就是为什么IClock存在的原因。" - Jon Skeet
2个回答

54

我进行了一些调查,似乎NodaTime的方法是根据时钟获取当前时间。

如果您想使用系统时钟来获取当前时间,只需使用SystemClock.Instance.GetCurrentInstant()

但是,不建议在代码中直接使用SystemClock.Instance,而是建议将IClock依赖项注入您的时间感知类中。

这将允许您:

  • 在运行时向类提供SystemClock.Instance,以便代码将使用正确的时间
  • 在单元测试期间提供虚假的IClock实现,以便您可以根据需要调整时间以测试各种场景(如时间流逝)。有一个名为FakeClock的NodaTime.Testing项目提供了这样的类。

我认为这非常有用。我认为像new Instant()Instant.Now这样的东西返回当前时间会使硬编码使用SystemClock更加容易,从而错过了NodaTime提供的测试优势。

有关使用NodaTime进行单元测试的更多信息,请参见此链接


关于您的代码示例:它们不等同。

  • Instant.FromDateTimeUtc(DateTime.Now.ToUniversalTime())确实会给您提供UTC中的当前瞬间。
  • new Instant(DateTime.Now.ToUniversalTime().Ticks)
  • 会给你一个未来很久的错误日期,因为BCL的DateTime.Ticks表示自1/1/0001起的滴答数,而NodaTime的Instant.Ticks表示自1/1/1970起的滴答数(请参见此处的注释)。

1
我喜欢两个答案,但你也告诉我我做错了什么,以及为什么 - 谢谢。 - Chuck Savage
基本上,将时钟注册为应用程序服务的单例,如下所示: services.AddSingleton <IClock>(SystemClock.Instance);然后在您的控制器中添加一个IClock注入参数,以便于任何人想知道如何间接使用SystemClock.Instance - Alex Hope O'Connor
据我所知,要注入一个实例,需要一个公共构造函数来完成。我收到了错误信息:“未找到类型NodaTime.SystemClock的可访问构造函数”。使用Autofac:containerBuilder.RegisterType<SystemClock>().As<IClock>().SingleInstance(); - Allstar

18

SystemClock.Now方法返回当前时间作为Instant值:

Instant now = SystemClock.Instance.Now;

但你可能需要注意文档中 IClock 接口的说明:

IClock 旨在用于任何需要访问当前时间的地方。虽然直接调用 SystemClock.Instance.Now(就像调用UtcNow一样)并不是严格错误,但在生产代码中作为一种风格强烈不建议这样做。我们建议为需要它的任何内容提供一个 IClock 实例,这使得您可以使用 NodaTime.Testing 组件中的存根时钟(或自己的实现)编写测试。

假设您有一个需要当前时间的 Logger 类。不要直接访问 SystemClock,而是通过构造函数提供一个 IClock 实例:

public class Logger
{
    private readonly IClock clock;

    public Logger(IClock clock)
    {
        this.clock = clock;
    }

    public void Log(string message)
    {
        Instant timestamp = this.clock.Now;
        // Now log the message with the timestamp... 
    }
}

当您在生产代码中实例化Logger时,可以将其赋值为SystemClock.Instance。但是,在Logger类的单元测试中,您可以将其赋值为FakeClock


目前我想要将当前时间作为日志事件的时间戳插入数据库,IClock是否过于复杂? - Chuck Savage
由你决定。如果你没有进行任何单元测试,那么也许是这样的。 - Michael Liu
如果不使用SystemClock,你还能以什么方式获取“生产代码”中的时间呢? - Chuck Savage
1
在生产代码中,当前时间将由SystemClock提供,但这并不意味着您实际上必须在需要当前时间的每个地方引用SystemClock。我已经在我的答案中添加了一个简单的示例。 - Michael Liu
1
好的,现在我明白了。在你解释并提供FakeClock链接之前,我无法理解整个测试概念。 - Chuck Savage

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接