Vitalii Isaenko
Vitalii Isaenko

Reputation: 989

How can I check whether "now" is in between two OffsetDateTime objects with NodaTime?

I need to set IsCurrent field of such a view model:

public class SessionVm {
    public OffsetDateTime StartTime { get; set; }
    public OffsetDateTime EndTime { get; set; }
    public string TimezoneId { get; set; }
    public bool IsCurrent {get;}
}

I thought I can write such a getter for the IsCurrent field:

public bool IsCurrent
{
    get
    {
        var currentTimeWithOffset = new OffsetDateTime(LocalDateTime.FromDateTime(DateTime.UtcNow), StartTime.Offset);
        return StartTime < currentTimeWithOffset && currentTimeWithOffset < EndTime;
    }
}

However, it looks like OffsetDateTime doesn't implement IComparable. Then I found that LocalDateTime does and tried this:

public bool IsCurrent
{
    get
    {
        var currentLocalDateTime = SystemClock.Instance.InUtc().GetCurrentLocalDateTime();
        var currentOffsetDateTime = new OffsetDateTime(currentLocalDateTime, StartTime.Offset);
        var currentLocalDateTimeExpectedWithOffsetAdded = currentOffsetDateTime.LocalDateTime;

        var startLocalDateTime = StartTime.LocalDateTime;
        var endLocalDateTime = StartTime.LocalDateTime;
        return startLocalDateTime < currentLocalDateTimeExpectedWithOffsetAdded && currentLocalDateTimeExpectedWithOffsetAdded < endLocalDateTime;
    }
}

However, currentLocalDateTimeExpectedWithOffsetAdded doesn't really represent what I need. From the OffsetDateTime class documentation above the LocalDateTime field:

/// <summary>
/// Returns the local date and time represented within this offset date and time.
/// </summary>
/// <value>The local date and time represented within this offset date and time.</value>
public LocalDateTime LocalDateTime => new LocalDateTime(Date, TimeOfDay);

Apparently, I misunderstood the sentence and thought that it would give me the datetime with an offset.
Could you help me to figure out how can I get the datetime object that would be UTC with added offset? Or maybe there is a better way to implement IsCurrent field? Thank you in advance :)

Upvotes: 0

Views: 463

Answers (2)

Sweeper
Sweeper

Reputation: 271735

OffsetDateTimes unambiguously represent an instant in time, so you can ToInstant and compare the start and end instants with the current instant. Instants are comparable.

Instant now = SystemClock.Instance.GetCurrentInstant();
Instant startInstant = StartTime.ToInstant();
Instant endInstant = EndTime.ToInstant();
return startInstant < now && now < endInstant;

Upvotes: 1

Jon Skeet
Jon Skeet

Reputation: 1500785

Firstly, I'd strongly suggest removing the use of DateTime.UtcNow entirely. Instead, use the IClock interface that's part of NodaTime. Inject that as you would any other dependency, using SystemClock.Instance as the production implementation. For tests, you can use a FakeClock as provided by the NodaTime.Testing package.

Once you've got a clock, call IClock.GetCurrentInstant() to find out the current instant. If you really, really want to use DateTime.UtcNow, you can call Instant.FromDateTimeUtc(DateTime.UtcNow). But be aware that this is a much less testable, and less NodaTime-idiomatic approach.

I'd convert your current OffsetDateTime values to a instants, then you can perform the comparison:

public bool IsCurrent
{
    get
    {
        var startInstant = StartTime.ToInstant();
        var endInstant = EndTime.ToInstant();
        var now = clock.GetCurrentInstant();
        return startInstant <= now && now < endInstant;
    }
}

(Alternatively, create an Interval from the two instants, and use Interval.Contains(Instant).)

Upvotes: 2

Related Questions