What's the proper and more concise way to get the ZonedDateTime(s) which represent the start and the end of the current day in the timezone set on the system on which the code runs?
Isn't the following code too much complicated?
ZonedDateTime nowInZone = SystemClock.Instance.Now.InZone(DateTimeZoneProviders.Bcl.GetSystemDefault());
ZonedDateTime start = new LocalDateTime(nowInZone.Year, nowInZone.Month, nowInZone.Day, 0, 0, 0).InZoneStrictly(DateTimeZoneProviders.Bcl.GetSystemDefault());
ZonedDateTime end = new LocalDateTime(nowInZone.Year, nowInZone.Month, nowInZone.Day, 23, 59, 59).InZoneStrictly(DateTimeZoneProviders.Bcl.GetSystemDefault());
Given those values, I need to test if another ZonedDateTime is between them.
The
AtStartOfDay
value on theDateTimeZone
object has the magic you're looking for.A couple of points:
It's better to get in the habit of separating the clock instance from the call to Now. This makes it easier to replace the clock later when unit testing.
You only need to get the local time zone once. I prefer to use the
Tzdb
provider, but either provider will work for this purpose.For the end of day, it's better to use the start of the next day. This prevents you from having to deal with granularity issues, such as whether you should take 23:59, 23:59:59, 23:59.999, 23:59:59.9999999, etc. Also, it makes it easier to get whole-number results when doing math.
In general, date+time ranges (or time-only ranges) should be treated as half-open intervals
[start,end)
- while date-only ranges should be treated as fully-closed intervals[start,end]
.Because of this, the start is compared with
<=
but the end is compared with>
.If you know for certain that the other
ZonedDateTime
value is in the same time zone and uses the same calendar, you can omit the calls toToInstant
and just compare them directly.Update
As Jon mentioned in comments, the
Interval
type may be a useful convenience for this purpose. It is already set up to work with a half-open range ofInstant
values. The following function will get the interval for a the current "day" in a particular time zone:Call it like this (using the same values from above):
And now comparison can be done with the
Contains
function:Note that you still have to convert to an
Instant
, as theInterval
type is not time zone aware.