野田时间 - 一天区的开始/结束结束、时间

2023-09-04 00:49:10 作者:孤单的城

什么是正确的,更简洁的方式来获得ZonedDateTime(县),其中重present开始和当前日期的时区的系统上设置的结尾上的code运行?

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?

不是以下code太复杂?

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());

鉴于这些价值观,我需要测试,如果另一个ZonedDateTime他们之间。

Given those values, I need to test if another ZonedDateTime is between them.

推荐答案

DateTimeZone 对象上的 AtStartOfDay 价值有你要找的魔力。

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

这几点:

这是更好地得到由调用现在分离时钟实例的习惯。这使得更容易更换时钟后,当单元测试

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.

您只需要获取本地时区一次。我preFER使用 TZDB 供应商,但无论是供应商将努力达到这一目的。

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.

有关一天结束的时候,最好是使用第二天的开始。这prevents你不必处理粒度问题,如是否应采取23:59,23:59:59,23:59.999,23:59:59.9999999等。此外,它可以更容易获得全数字结果做数学的时候。

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].

这一点,因为,开始时相比&LT; = 但最终与&GT相比;

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

如果您肯定知道对方 ZonedDateTime 值在同一时区,并使用相同的日历,你可以省略调用 ToInstant 键,只需直接进行比较。

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.

更新

随着乔恩在评论中提到的,间隔类型可以是用于此目的的实用方便。它已经建立了一个半开区间的即时值工作。下面的函数会在特定的时间段间隔为现在的天:

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 of Instant values. 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:

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.