Noda time - start / end of the day with zone

What is the correct and more concise way to get ZonedDateTime (s) that represent the beginning and end of the current day in the time zone set on the system on which the code is running?

Is the code too 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 these values, I need to check if another ZonedDateTime is between them.

+5
source share
1 answer

The AtStartOfDay value of a AtStartOfDay object has the magic you are looking for.

 // Get the current time IClock systemClock = SystemClock.Instance; Instant now = systemClock.Now; // Get the local time zone, and the current date DateTimeZone tz = DateTimeZoneProviders.Tzdb.GetSystemDefault(); LocalDate today = now.InZone(tz).Date; // Get the start of the day, and the start of the next day as the end date ZonedDateTime dayStart = tz.AtStartOfDay(today); ZonedDateTime dayEnd = tz.AtStartOfDay(today.PlusDays(1)); // Compare instants using inclusive start and exclusive end ZonedDateTime other = new ZonedDateTime(); // some other value bool between = dayStart.ToInstant() <= other.ToInstant() && dayEnd.ToInstant() > other.ToInstant(); 

A few points:

  • It’s better to get used to separating the clock instance from the Now call. This makes it easier to replace the watch later when testing devices.

  • You only need to get the local time zone once. I prefer to use the Tzdb provider, but any provider will work for this purpose.

  • At the end of the day, it is better to use the beginning of the next day. This prevents you from dealing with detailing issues, for example, whether you should take 23:59, 23:59:59, 23: 59.999, 23: 59: 59.9999999, etc. In addition, it is simplified to obtain an integer number of results when performing math.

    In general, date + time ranges (or time-only ranges) should be considered as half-open intervals [start,end) - while dates-only ranges should be considered as completely closed [start,end] intervals.

  • Because of this, the beginning is compared with <= , but the end is compared with > .

  • If you know for sure that a different ZonedDateTime value is in the same time zone and uses the same calendar, you can omit calls to ToInstant and simply compare them directly.

Update

As John mentioned in the comments, the Interval type may be useful for this purpose. It is already configured to work with a semi-open range of Instant values. The following function will receive the interval for the current "day" in a specific time zone:

 public Interval GetTodaysInterval(IClock clock, DateTimeZone timeZone) { LocalDate today = clock.Now.InZone(timeZone).Date; ZonedDateTime dayStart = timeZone.AtStartOfDay(today); ZonedDateTime dayEnd = timeZone.AtStartOfDay(today.PlusDays(1)); return new Interval(dayStart.ToInstant(), dayEnd.ToInstant()); } 

Name it like this (using the same values ​​above):

 Interval day = GetTodaysInterval(systemClock, tz); 

And now the comparison can be done using the Contains function:

 bool between = day.Contains(other.ToInstant()); 

Note that you still need to convert to Instant , since the Interval type is not a time zone.

+7
source

Source: https://habr.com/ru/post/1201373/


All Articles