简体   繁体   English

Noda Time - 带区域的开始/结束日期

[英]Noda Time - Start/end of day with zone

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? 在代码运行的系统上设置的时区中,获取ZonedDateTime(表示当前日期的开始和结束)的正确和更简洁的方法是什么?

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. 鉴于这些值,我需要测试另一个ZonedDateTime是否在它们之间。

The AtStartOfDay value on the DateTimeZone object has the magic you're looking for. 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();

A couple of points: 几点:

  • It's better to get in the habit of separating the clock instance from the call to Now. 最好养成将时钟实例与调用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. 我更喜欢使用Tzdb提供程序,但任何一个提供程序都可以用于此目的。

  • 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. 这可以防止您不得不处理粒度问题,例如是否应该采取23:59,23:59:59,23:59.999,23:59:59.9999999等。此外,它使得更容易获得整数做数学的结果。

    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] . 通常,日期+时间范围(或仅时间范围)应视为半开区间[start,end) - 而仅日期范围应视为完全关闭区间[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 to ToInstant and just compare them directly. 如果您确定其他ZonedDateTime值位于同一时区并使用相同的日历,则可以省略对ToInstant的调用,并直接比较它们。

Update 更新

As Jon mentioned in comments, the Interval type may be a useful convenience for this purpose. 正如Jon在评论中提到的, Interval类型可能是一个有用的便利。 It is already set up to work with a half-open range of Instant values. 它已经设置为使用半开放的Instant值。 The following function will get the interval for a the current "day" in a particular 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());
}

Call it like this (using the same values from above): 像这样调用它(使用上面相同的值):

Interval day = GetTodaysInterval(systemClock, tz);

And now comparison can be done with the Contains function: 现在可以使用Contains函数进行比较:

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

Note that you still have to convert to an Instant , as the Interval type is not time zone aware. 请注意,您仍然必须转换为Instant ,因为Interval类型不能Interval时区。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM