2014-08-28 70 views
4

什么是适当和更简洁的方式来获取ZonedDateTime(s),它代表在代码运行的系统上设置的时区中当前日期的开始和结束?野田时间 - 带区域的开始/结束日期

是不是下面的代码太复杂了?

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。

+0

您可能想使用'InZoneLeniently'来代替。例如,巴西在其DST前沿转型中,从下午11:59到凌晨1点。没有上午12:00。 'InZoneStrictly'会抛出这种情况。 “InZoneLeniently”将在上午12点转换LocalDateTime时(如果我正确阅读文档),将转到凌晨1点。同样在倒退时间从晚上11:59到晚上11点。有两个11:59 pm。 'InZoneLeniently'选择后面的一个。 – 2014-08-29 00:02:35

+0

@mikez - 关于巴西的好处。这已经被我演示的方法所涵盖。谢谢。 – 2014-08-29 04:58:48

回答

6

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

几个要点:

  • 这是更好地从现在的口号分离时钟实例的习惯来获得。这使得单元测试后更换时钟变得更容易。

  • 您只需要获取当地时区一次。我更喜欢使用Tzdb供应商,但是任何一个供应商都会为此目的而工作。

  • 对于一天结束,最好使用第二天的开始。这可以防止您不必处理粒度问题,例如您是否应该采取23:59,23:59:59,23:59.999,23:59:59.9999999等。此外,它可以更轻松地获取整数做数学的结果。一般而言,日期+时间范围(或时间范围)应视为半开放时间间隔[start,end) - 而仅限日期范围应视为完全关闭时间间隔[start,end]

  • 因此,将开始与<=进行比较,但最后与>进行比较。

  • 如果您确定其他ZonedDateTime值在同一时区并使用相同的日历,则可以省略对ToInstant的调用并直接比较它们。

更新

如乔恩评论所提到的,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类型不是时区知道。

+0

它很好地隐藏在明显的景象;) 感谢您的答案和您指出的最佳做法。 – Jhack 2014-08-29 13:12:37

+1

您可能还想提及'Interval' - 例如,您可以轻松地编写一种方法,在给定时钟和时区(或从2.0开始的ZonedClock)时返回“today”的时间间隔。 – 2014-08-31 16:55:35

相关问题