Apologies if this seems a simple question, but I mostly just want to check that the solution I have is the most sensible/holds for all cases.
The pre-existing SQL Server database we work with stores a 'period' as inclusive start -> inclusive end UTC DateTime values. (Both start & end columns are datetime2(7)
, automatically-converted to System.DateTime
instances of DateTimeKind.UTC
before we start working with them).
So, if I need to store the "whole day/month/year, given the user's time-zone" I need to find "The last possible instant of a specified LocalDate, in a particular DateTimeZone".
The methods I have are as follows:
public static LocalDateTime AtEndOfDay(this LocalDate localDate)
{
return localDate
.PlusDays(1)
.AtMidnight()
.PlusTicks(-1);
}
public static ZonedDateTime AtEndOfDay(this DateTimeZone zone, LocalDate localDate)
{
return zone
.AtStartOfDay(localDate.PlusDays(1))
.Plus(Duration.FromTicks(-1));
}
I think I also need to avoid (anywhere else) mapping a "end of date" LocalDateTime
using .AtLeniently(..)
since if 23:59:59.9999999 is "skipped" and does not exist in the target DateTimeZone
, then it would be mapped to the next available instant on the 'outer' side of the gap 00:00:00.000000, which would give me an exclusive ZonedDateTime
value.
Amended Methods:
public static LocalDateTime AtEndOfDay(this LocalDate localDate)
{
// TODO: Replace with localDate.At(LocalTime.MaxValue) when NodaTime 2.0 is released.
return localDate
.PlusDays(1)
.AtMidnight()
.PlusTicks(-1);
}
public static ZonedDateTime AtEndOfDay(this DateTimeZone zone, LocalDate localDate)
{
return zone
.AtStartOfDay(localDate.PlusDays(1))
.Plus(-Duration.Epsilon);
}
public static ZonedDateTime AtEndOfDayInZone(this LocalDate localDate, DateTimeZone zone)
{
return zone.AtEndOfDay(localDate);
}
Firstly, as noted in comments, any time you can work with an exclusive upper bound, that would be a good idea :)
Your AtEndOfDay
method looks reasonable to me, except that I'd use Duration.Epsilon
instead of Duration.FromTicks
. In particular, in Noda Time 2.0 we're going to move to a precision of nanoseconds instead of ticks; Duration.Epsilon
will do what you want in both cases.
For your LocalDate
solution, it seems to me that we're missing a value of LocalTime.MaxValue
(or EndOfDay
), the largest representable LocalTime
. If that were available, you could just write:
return date.At(LocalTime.MaxValue);
which would remove the same "ticks" problem as before. I'll try to remember to add that for 2.0 - although it will be documented with a comment to the effect of "Only use this if you're forced to by legacy systems" :)
One downside of adding a day and then subtracting a tick (or a nanosecond) is that it will fail on LocalDate.MaxValue
. This probably isn't a practical issue, but for code within Noda Time itself, we try to avoid things like that. I wouldn't try to avoid it for the ZonedDateTime
version though, as it's a rather more complicated scenario. (There are probably ways of doing it, but it wouldn't be worth it.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With