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

Jha*_*ack 10 .net c# timezone nodatime

在代码运行的系统上设置的时区中,获取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());
Run Code Online (Sandbox Code Playgroud)

鉴于这些值,我需要测试另一个ZonedDateTime是否在它们之间.

Mat*_*int 14

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

// 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();
Run Code Online (Sandbox Code Playgroud)

几点:

  • 最好养成将时钟实例与调用Now分开的习惯.这使得在单元测试后更容易更换时钟.

  • 您只需要获取一次本地时区.我更喜欢使用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());
}
Run Code Online (Sandbox Code Playgroud)

像这样调用它(使用上面相同的值):

Interval day = GetTodaysInterval(systemClock, tz);
Run Code Online (Sandbox Code Playgroud)

现在可以使用以下Contains功能进行比较:

bool between = day.Contains(other.ToInstant());
Run Code Online (Sandbox Code Playgroud)

请注意,您仍然必须转换为Instant,因为该Interval类型不能识别时区.