Home > other >  NodaTime - Calculate duration between two LocalTime variables that span midnight
NodaTime - Calculate duration between two LocalTime variables that span midnight

Time:11-20

I have a requirement to calculate the duration between two times that may span midnight.

The use case is to allow the user to set up shift plans e.g. “09:00 to 17:00” or “22:00 to 06:00” and calculate the contracted time

For “09:00 to 17:00” I can use:

LocalTime inAt = new LocalTime(9, 0);
LocalTime outAt = new LocalTime(17, 0);

var period = Period.Between(inAt, outAt);

Which results in 8 hours, the answer I am looking for.

For “22:00 to 06:00” the period returns 16 hours (regardless of the order of the parameters).

LocalTime inAt = new LocalTime(22, 0);
LocalTime outAt = new LocalTime(6, 0);

var period = Period.Between(inAt, outAt);
var period2 = Period.Between(outAt, inAt);

I am guessing that this is related to daylight saving time, unless you know the dates you cannot be sure that the answer will always the 8 hours. If the clocks go forward it would be 7, backwards would be 9.

How can I ensure that no matter what LocalTime values are used the period would disregard any daylight savings? Should I use LocalDateTime with an arbitrary date such as 2021-01-01?

Also, am I correct in using Period or should I be using Duration?

Update

This seems to work however I am still wondering if there is an eaiser way of doing it?

LocalTime inAt = new LocalTime(22, 0);
LocalTime outAt = new LocalTime(6, 0);

var period = Period.Between(inAt, outAt, PeriodUnits.Ticks);
LocalTime? midnightAdjustedTime = null;

if (period.Ticks < 0)
{
  midnightAdjustedTime = LocalTime.Midnight   period;  
}

var duration = Duration.FromTicks(midnightAdjustedTime?.TickOfDay ?? period.Ticks);

CodePudding user response:

This has nothing to do with daylight savings - it can't do, given that everything is in terms of LocalTime. It's about negative periods.

For “22:00 to 06:00” the period returns 16 hours (regardless of the order of the parameters)

No, it doesn't. One returns 16 hours, the other returns -16 hours:

using NodaTime;

var start = new LocalTime(22, 0);
var end = new LocalTime(6, 0);

var startToEnd = Period.Between(start, end);
var endToStart = Period.Between(end, start);

Console.WriteLine(startToEnd);
Console.WriteLine(endToStart);

Output:

PT-16H
PT16H

It's not clear to me how you came to the conclusion that both returned the same value, but the fact that they don't return the same value is crucial to fixing the problem. The simple approach is just to add 1 day (24 hours) if the period is negative. The code you've got is almost right, but it can be done much simpler - and without using the Ticks unit at all:

// Note: Period.ToDuration() isn't "generally safe" due to variable lengths
// of months etc, but is okay here.
var duration = startToEnd.ToDuration();
if (duration < Duration.Zero)
{
    duration  = Duration.FromDays(1);
}

Console.WriteLine(duration);
  • Related