Noda Time – 带区域的开始/结束日期
在代码运行的系统上设置的时区中,获取ZonedDateTime(表示当前日期的开始和结束)的正确和更简洁的方法是什么?
以下代码是不是太复杂了?
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());
鉴于这些值,我需要测试另一个ZonedDateTime是否在它们之间。
DateTimeZone
对象上的AtStartOfDay
值具有您正在寻找的魔力。
// 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();
几点:
-
最好养成将时钟实例与调用Now分开的习惯。 这使得在unit testing后更容易更换时钟。
-
您只需要获取一次本地时区。 我更喜欢使用
Tzdb
提供程序,但任何一个提供程序都可以用于此目的。 -
在一天结束时,最好使用第二天的开始。 这可以防止您不得不处理粒度问题,例如是否应该采取23:59,23:59:59,23:59.999,23:59:59.9999999等。此外,它使得更容易获得整数做数学的结果。
通常,日期+时间范围(或仅时间范围)应视为半开区间
[start,end)
– 而仅日期范围应视为完全关闭区间[start,end]
。 -
因此,将开始与
<=
进行比较,但将结果与>
进行比较。 -
如果您确定其他
ZonedDateTime
值位于同一时区并使用相同的日历,则可以省略对ToInstant
的调用,并直接比较它们。
更新
正如Jon在评论中提到的, Interval
类型可能是一个有用的便利。 它已经设置为使用半开放的Instant
值范围。 以下函数将获取特定时区中当前“日期”的间隔:
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()); }
像这样调用它(使用上面相同的值):
Interval day = GetTodaysInterval(systemClock, tz);
现在可以使用Contains
函数进行比较:
bool between = day.Contains(other.ToInstant());
请注意,您仍然必须转换为Instant
,因为Interval
类型不能Interval
时区。