Instant.Now for NodaTime

2019-03-14 10:42发布

问题:

I'm trying to get a handle on using the Noda Time framework by Jon Skeet (and others).

I'm trying to store the current now(Instant). Instant is created from a long ticks, but what is the current now count of Ticks?

Is it:

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

And or?

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

Are they equivalent, am I even doing this right?

PS, if Jon answer's this - I'd like to propose an Instant.Now property.

PS2 I know the title contains a tag, but it wouldn't let me have a short "Instant.Now" title.

回答1:

I did a bit of research and it seems that the NodaTime way is to get the now moment according to a clock.

If you want to get the current time using the system clock, just use SystemClock.Instance.GetCurrentInstant().

However, instead of using the SystemClock.Instance directly in your code, it's preferable that you inject an IClock dependency in your time-aware classes.

This will allow you to:

  • provide the class with SystemClock.Instance at runtime, so the code will use the correct time
  • supply a fake implementation of IClock during unit testing to allow you to tweak the time as needed in order to test various scenarios (like the passing of time). There's a NodaTime.Testing project that offers such a class, called FakeClock.

I find this very useful. I think having something like new Instant() or Instant.Now return the current time would make it easier to hardcode usages of SystemClock under the covers, therefore missing the testing advantage that NodaTime offers.

For more info on unit testing with NodaTime, see this link.


Regarding your code examples: they are not equivalent.

  • Instant.FromDateTimeUtc(DateTime.Now.ToUniversalTime()) will indeed give you the current instant in UTC.
  • new Instant(DateTime.Now.ToUniversalTime().Ticks) will give you a wrong date far in the future, because the BCL's DateTime.Ticks represents the number of ticks since 1/1/0001, and NodaTime's Instant.Ticks represents the number of ticks since 1/1/1970 (see the remark here).


回答2:

SystemClock.Now returns the current time as an Instant value:

Instant now = SystemClock.Instance.Now;

But you may want to heed the remarks in the documentation for the IClock interface:

IClock is intended for use anywhere you need to have access to the current time. Although it's not strictly incorrect to call SystemClock.Instance.Now directly, in the same way as you might call UtcNow, it's strongly discouraged as a matter of style for production code. We recommend providing an instance of IClock to anything that needs it, which allows you to write tests using the stub clock in the NodaTime.Testing assembly (or your own implementation).

As a simple example, suppose you have a Logger class that needs the current time. Instead of accessing SystemClock directly, use an IClock instance that's supplied via its constructor:

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... 
    }
}

When you instantiate a Logger in your production code, you can give it SystemClock.Instance. But in a unit test for the Logger class, you can give it a FakeClock.



标签: c# nodatime