【问题标题】:Instant.Now for NodaTimeNodaTime 的 Instant.Now
【发布时间】:2013-01-25 22:55:06
【问题描述】:

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

我正在尝试存储当前(即时)。 Instant 是根据长刻度创建的,但当前刻度的当前计数是多少?

是吗:

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

和或?

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

它们是等价的吗?我这样做对吗?

PS,如果 Jon 回答这个问题 - 我想提议一个 Instant.Now 属性。

PS2 我知道标题包含一个标签,但它不会让我有一个简短的“Instant.Now”标题。

【问题讨论】:

  • Instant.Now 对属性来说是一个糟糕的用例,因为它会在“幕后”改变值(BCL 遇到同样的问题:DateTime.Now)。 Instant.GetCurrent() 会更好,new Instant() 可能是最好的。免责声明:我对 Noda Time 的曝光率为零。
  • @Jon 没有Instant.GetCurrent() - 请参阅Instant docs. Instant 也只有一个构造函数Instant(Int64)
  • 但无论如何,这两个定义是等价的。 Ticks 是一个绝对量,不随时区变化。
  • 等待 Jon Skeet 出现,100% 准确回答,获得所有支持,让快乐的露营者离开 ChuckSavage
  • @ChuckSavage:Instant.Now 的缺失是非常刻意的。它是不可测试的。这就是 IClock 存在的原因。

标签: c# nodatime


【解决方案1】:

我做了一些研究,似乎 NodaTime 的方法是根据clock 获取 now 时刻。

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

但是,与其在代码中直接使用SystemClock.Instance,不如在时间感知类中注入IClock 依赖项。

这将允许您:

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

我觉得这非常有用。我认为让new Instant()Instant.Now 之类的东西返回当前时间会更容易在幕后硬编码SystemClock 的用法,因此会失去NodaTime 提供的测试优势。

有关使用 NodaTime 进行单元测试的更多信息,请参阅this link


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

  • Instant.FromDateTimeUtc(DateTime.Now.ToUniversalTime()) 确实会为您提供 UTC 的当前时刻。
  • new Instant(DateTime.Now.ToUniversalTime().Ticks) 会给你一个错误的未来日期,因为 BCL 的 DateTime.Ticks 代表自 1/1/0001 以来的滴答数,而 NodaTime 的 Instant.Ticks 代表自 1/1/1970 以来的滴答数(见备注@987654325 @)。

【讨论】:

  • 我喜欢这两个答案,但你告诉我我做错了什么,以及为什么 - 谢谢。
  • 基本上将时钟注册为像这样的单例应用程序服务:services.AddSingleton<IClock>(SystemClock.Instance); 然后在您的控制器中添加一个 IClock 注入参数,以便任何想知道如何间接使用 SystemClock.Instance 的人。跨度>
  • 据我了解,要注入一个实例,它需要一个公共构造函数吗?我收到错误消息:“没有为类型 NodaTime.SystemClock 找到可访问的构造函数”。使用 Autofac:containerBuilder.RegisterType<SystemClock>().As<IClock>().SingleInstance();
【解决方案2】:

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 是否矫枉过正?
  • 这取决于你。如果您不进行任何单元测试,那么也许是。
  • 如果不是SystemClock,你怎么能在“生产代码”中获得时间?
  • 在生产代码中,当前时间将由SystemClock 提供,但这并不意味着您实际上必须在需要当前时间的任何地方引用SystemClock。我在答案中添加了一个简单的示例。
  • 好的,现在我明白了。在您解释之前,我无法理解整个测试概念,FakeClock 的链接帮助澄清了这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-16
  • 1970-01-01
  • 2019-05-19
  • 1970-01-01
  • 1970-01-01
  • 2015-09-14
相关资源
最近更新 更多