2014-11-23 72 views
2

我遇到了以下需求的有趣問題: 測試過程是否在同一天運行,如果不運行該過程。日期存儲爲DataTimeOffset。DateTimeOffset當天比較

我最初的做法是:

  1. 都轉換值UTC,因爲可能在不同的時區中創建這些日期,並有不同的偏移。
  2. 查看每個值的日期值。這是在轉換爲UTC後完成的,因爲Date方法會忽略偏移量。

大多數情況下,這工作,但我遇到了一種情況下,邏輯會失敗。如果其中一個值的時間接近前一天/第二天,以便轉換爲UTC時它將更改日期。如果其他值沒有時間也轉換爲前一天/下一天,則日期比較失敗。

所以我結束了以下邏輯包括情景:

public static bool SameDate(DateTimeOffset first, DateTimeOffset second) 
{ 
    bool returnValue = false; 
    DateTime firstAdjusted = first.ToUniversalTime().Date; 
    DateTime secondAdjusted = second.ToUniversalTime().Date; 

    // If date is now a day ahead after conversion, than add/deduct a day to other date if that date hasn't advanced 
    if (first.Date < firstAdjusted.Date && second.Date == secondAdjusted.Date) 
     secondAdjusted = secondAdjusted.Date.AddDays(1); 
    if (first.Date > firstAdjusted.Date && second.Date == secondAdjusted.Date) 
     secondAdjusted = secondAdjusted.Date.AddDays(-1); 

    if (second.Date < secondAdjusted.Date && first.Date == firstAdjusted.Date) 
     firstAdjusted = firstAdjusted.Date.AddDays(1); 
    if (second.Date > secondAdjusted.Date && first.Date == firstAdjusted.Date) 
     firstAdjusted = firstAdjusted.Date.AddDays(-1); 

    if (DateTime.Compare(firstAdjusted, secondAdjusted) == 0) 
     returnValue = true; 

    return returnValue; 
} 

這裏是被失敗的,現在通過單元測試:

[TestMethod()] 
public void SameDateTest() 
{ 
DateTimeOffset current = DateTimeOffset.Now; 
DateTimeOffset first = current; 
DateTimeOffset second = current; 

// 23 hours later, next day, with negative offset (EST) -- First rolls over 
first = new DateTimeOffset(2014, 1, 1, 19, 0, 0, new TimeSpan(-5, 0, 0)); 
second = new DateTimeOffset(2014, 1, 2, 18, 0, 0, new TimeSpan(-5, 0, 0)); 
Assert.IsFalse(Common.SameDate(first, second)); 

// 23 hours earlier, next day, with postive offset -- First rollovers 
first = new DateTimeOffset(2014, 1, 1, 4, 0, 0, new TimeSpan(5, 0, 0)); 
second = new DateTimeOffset(2014, 1, 2, 5, 0, 0, new TimeSpan(5, 0, 0)); 
Assert.IsFalse(Common.SameDate(first, second)); 

// 23 hours later, next day, with negative offset (EST) -- Second rolls over 
first = new DateTimeOffset(2014, 1, 2, 18, 0, 0, new TimeSpan(-5, 0, 0)); 
second = new DateTimeOffset(2014, 1, 1, 19, 0, 0, new TimeSpan(-5, 0, 0)); 
Assert.IsFalse(Common.SameDate(first, second)); 

// 23 hours earlier, next day, with postive offset -- Second rolls over 
first = new DateTimeOffset(2014, 1, 2, 5, 0, 0, new TimeSpan(5, 0, 0)); 
second = new DateTimeOffset(2014, 1, 1, 4, 0, 0, new TimeSpan(5, 0, 0)); 
Assert.IsFalse(Common.SameDate(first, second)); 
} 

我的直覺是,有比基於其他值的遞增/遞減更清潔的方法。有更好的方法嗎?

的主要標準:

  1. 調整兩個日期爲具有相同的偏移量。
  2. 只有在第一個和第二個日期都在同一日曆日內發生,而不是在24小時內才返回true。
+3

你所做的是一個肯定的跡象,表明方法很差。根據我的經驗,處理日期/時間的最佳方法是將它們存儲爲UTC或具有特定偏移量。任何「數學」工作都會消失,現在您可以使用標準日期時間API轉換爲UI顯示等。 – 2014-11-23 19:18:27

+0

它們與偏移量一起存儲,但偏移量不總是相同。例如,我們通常存儲東部時區的偏移量,但取決於夏令時,它可以是-4或-5。 – Josh 2014-11-23 19:30:46

+0

在您的示例中,使用帶有和不帶夏令時的東部時區,實際上應該表示爲兩個不同的時區:冬季的EST(UTC-0500)和夏季的EDT(UTC-0400)。 – 2014-11-23 19:42:33

回答

3

調整日期在兩個日期之差的一個:

public static bool SameDate(DateTimeOffset first, DateTimeOffset second) 
{ 
    bool returnValue = false; 
    DateTime firstAdjusted = first.ToUniversalTime().Date; 
    DateTime secondAdjusted = second.ToUniversalTime().Date; 

    // calculate the total diference between the dates 
    int diff = first.Date.CompareTo(firstAdjusted) - second.Date.CompareTo(secondAdjusted); 
    // the firstAdjusted date is corected for the difference in BOTH dates. 
    firstAdjusted = firstAdjusted.AddDays(diff); 

    if (DateTime.Compare(firstAdjusted, secondAdjusted) == 0) 
     returnValue = true; 

    return returnValue; 
} 

在這個函數中,我asuming的偏移量將不會超過24小時。 IE日期和它的調整日期之間的區別不會是兩天或更多天。如果情況並非如此,那麼可以使用time span進行比較。

+0

我認爲你釘了它。我需要通過幾個單元測試來確認,然後我可以接受你的答案。順便說一句,我不認爲偏移量可能會超過24,因爲,因爲距離UTC的偏移量在-12小時到14小時之間(http://en.wikipedia.org/wiki/List_of_UTC_time_offsets) – Josh 2014-12-01 20:54:42

+0

減去'CompareTo'返回值顯然是錯誤的 - [唯一指定的](http://msdn.microsoft.com/zh-cn/library/5ata5aya.aspx)有關返回值是符號(負/零/正),而不是絕對值。 – Mormegil 2014-12-01 21:59:09

+0

確實如此,但這意味着CompareTo函數不會聲明差異的大小,只是存在差異。結果將始終爲-1,0或1. – martijn 2014-12-02 08:20:49

3

您描述的一般方法(轉換爲常見時區,然後比較日期部分)是合理的。這裏的問題實際上是決定參照系的一個問題。你有任意選擇UTC作爲你的參照系。一開始,只要將它們在相同的時區中進行比較,則無關緊要,但正如您發現這可以將它們放在一天的邊界的任一側。

我認爲您需要完善您的規範。問問你自己試圖確定下列哪一項。

  • 該值是否出現在指定時區的同一天日曆天。
  • 值是否不超過12小時(+/- 12小時是24小時)。
  • 這些值是否不超過24小時。

也可能是別的。實施(但被您拒絕)的定義是「值是否出現在同一UTC日曆日」。

+0

我正在尋找測試相同的日曆日與兩個日期具有相同的時區或偏移量。如果轉換爲ET比UTC更容易,那對我很有用。大多數情況下,取決於夏令時,值將爲ET,偏移量爲-4或-5。 – Josh 2014-11-26 00:31:00

0

首先,您在UnitTest中出現錯誤。

[TestMethod()] 
    public void SameDateTest() 
    { 
     DateTimeOffset current = DateTimeOffset.Now; 
     DateTimeOffset first = current; 
     DateTimeOffset second = current; 

     // 23 hours later, next day, with negative offset (EST) -- First rolls over 
     first = new DateTimeOffset(2014, 1, 1, 19, 0, 0, new TimeSpan(-5, 0, 0)); 
     second = new DateTimeOffset(2014, 1, 2, 18, 0, 0, new TimeSpan(-5, 0, 0)); 
     Assert.IsTrue(DateTimeComparison.Program.SameDate(first, second)); 

     // 23 hours earlier, next day, with positive offset -- First rollovers 
     first = new DateTimeOffset(2014, 1, 1, 4, 0, 0, new TimeSpan(5, 0, 0)); 
     second = new DateTimeOffset(2014, 1, 2, 5, 0, 0, new TimeSpan(5, 0, 0)); 
     Assert.IsFalse(DateTimeComparison.Program.SameDate(first, second)); 

     // 23 hours later, next day, with negative offset (EST) -- Second rolls over 
     first = new DateTimeOffset(2014, 1, 2, 18, 0, 0, new TimeSpan(-5, 0, 0)); 
     second = new DateTimeOffset(2014, 1, 1, 19, 0, 0, new TimeSpan(-5, 0, 0)); 
     Assert.IsTrue(DateTimeComparison.Program.SameDate(first, second)); 

     // 23 hours earlier, next day, with positive offset -- Second rolls over 
     first = new DateTimeOffset(2014, 1, 2, 5, 0, 0, new TimeSpan(5, 0, 0)); 
     second = new DateTimeOffset(2014, 1, 1, 4, 0, 0, new TimeSpan(5, 0, 0)); 
     Assert.IsFalse(DateTimeComparison.Program.SameDate(first, second)); 
    } 

這是校正後的測試。你的第一次測試應該返回「真實」,就像你的第三次測試一樣。被比較的那些DateTimeOffset在相同的UTC日期。只有測試用例2和4應該返回「False」,因爲這些DateTimeOffset事實上在2個不同的日期。

其次,您可以簡化SameDate()功能如下:

public static bool SameDate(DateTimeOffset first, DateTimeOffset second) 
    { 
     bool returnValue = false; 
     DateTime firstAdjusted = first.UtcDateTime; 
     DateTime secondAdjusted = second.UtcDateTime; 

     if(firstAdjusted.Date == secondAdjusted.Date) 
      returnValue = true; 

     return returnValue; 
    } 

因爲所有你感興趣的是,如果first.Datesecond.Date實際上是在同一個UTC日期,這將會把工作不需要額外做投射/轉換爲UTC。

第三,你可以用這個完整的程序測試你的測試用例:

using System; 

namespace DateTimeComparison 
{ 
    public class Program 
    { 
     static void Main(string[] args) 
     { 
      DateTimeOffset current = DateTimeOffset.Now; 
      DateTimeOffset first = current; 
      DateTimeOffset second = current; 

      // 23 hours later, next day, with negative offset (EST) -- First rolls over 
      first = new DateTimeOffset(2014, 1, 1, 19, 0, 0, new TimeSpan(-5, 0, 0)); 
      second = new DateTimeOffset(2014, 1, 2, 18, 0, 0, new TimeSpan(-5, 0, 0)); 
      if(false == SameDate(first, second)) { 
       Console.WriteLine("Different day values!"); 
      } else { 
       Console.WriteLine("Same day value!"); 
      } 

      // --Comment is wrong -- 23 hours earlier, next day, with positive offset -- First rollovers 
      first = new DateTimeOffset(2014, 1, 1, 4, 0, 0, new TimeSpan(5, 0, 0)); 
      second = new DateTimeOffset(2014, 1, 2, 5, 0, 0, new TimeSpan(5, 0, 0)); 
      if(false == SameDate(first, second)) { 
       Console.WriteLine("Different day values!"); 
      } else { 
       Console.WriteLine("Same day value!"); 
      } 

      // 23 hours later, next day, with negative offset (EST) -- Second rolls over 
      first = new DateTimeOffset(2014, 1, 2, 18, 0, 0, new TimeSpan(-5, 0, 0)); 
      second = new DateTimeOffset(2014, 1, 1, 19, 0, 0, new TimeSpan(-5, 0, 0)); 
      if(false == SameDate(first, second)) { 
       Console.WriteLine("Different day values!"); 
      } else { 
       Console.WriteLine("Same day value!"); 
      } 


      // --Comment is wrong -- 23 hours earlier, next day, with positive offset -- Second rolls over 
      first = new DateTimeOffset(2014, 1, 2, 5, 0, 0, new TimeSpan(5, 0, 0)); 
      second = new DateTimeOffset(2014, 1, 1, 4, 0, 0, new TimeSpan(5, 0, 0)); 
      if(false == SameDate(first, second)) { 
       Console.WriteLine("Different day values!"); 
      } else { 
       Console.WriteLine("Same day value!"); 
      } 
     } 

     public static bool SameDate(DateTimeOffset first, DateTimeOffset second) 
     { 
      bool returnValue = false; 
      DateTime firstAdjusted = first.UtcDateTime; 
      DateTime secondAdjusted = second.UtcDateTime; 

      if(firstAdjusted.Date == secondAdjusted.Date) 
       returnValue = true; 

      return returnValue; 
     }   
    } 
} 

設置你喜歡的地方並運行調試這套短節目一個破發點。這會告訴你測試用例2和測試用例4實際上是UTC時間相隔2天以上,因此應該是錯誤的。此外,它將顯示測試用例1和測試用例3在相同的UTC日期,並且應該從正常運行的SameDate()期望真實。

如果你想你的第二個和第四個測試用例分開23小時在同一日期,然後測試情況下,兩個,你應該使用:

 // 23 hours earlier, next day, with positive offset -- First rollovers 
     first = new DateTimeOffset(2014, 1, 2, 4, 0, 0, new TimeSpan(5, 0, 0)); 
     second = new DateTimeOffset(2014, 1, 1, 5, 0, 0, new TimeSpan(5, 0, 0)); 
     Assert.IsTrue(DateTimeComparison.Program.SameDate(first, second)); 

而對於測試情況下爲四個,你應該使用:

 // 23 hours earlier, next day, with positive offset -- Second rolls over 
     first = new DateTimeOffset(2014, 1, 2, 5, 0, 0, new TimeSpan(5, 0, 0)); 
     second = new DateTimeOffset(2014, 1, 3, 4, 0, 0, new TimeSpan(5, 0, 0)); 
     Assert.IsTrue(DateTimeComparison.Program.SameDate(first, second)); 
+0

您誤解了標準,所有四個單元測試應該是錯誤的。我只希望該方法只在兩個日期發生在同一日曆日時才返回true,並將兩個日期調整爲使用相同的偏移量。因爲你的簡化代碼不起作用 – Josh 2014-11-28 15:22:31

+0

但是你的測試用例不在同一天。一個簡單的調試運行表明,與您的測試數據一樣。正因爲如此,你的測試用例不能傳遞給定的數據。如果您希望所有四個測試用例都通過'Assert.IsFalse()',您必須相應地調整您的測試用例數據。如果這是你的意圖,你的測試用例數據會被竊聽。修復你的「bug」測試用例數據,你的代碼將按需要傳遞。 – StarPilot 2014-12-01 23:37:27

+0

也就是說,它們在UTC的日期不同。如果你使用它作爲你的參考框架,你的單元測試是錯誤的。如果你想使用不同的參考框架,例如EDT或EST,那麼你應該將所有的datetimeoffsets轉換爲該參考框架,然後進行比較。當你所追求的是兩個日期時間的日期時,引用的框架很重要。由於兩個日期時間/日期時間偏移之間的一個刻度差異很重要,如果它是午夜與午夜1刻度之間的差異。 – StarPilot 2014-12-01 23:54:33

0

怎麼樣這樣的功能:

public static bool SameDate(DateTimeOffset first, DateTimeOffset second) 
{ 
    return Math.Abs((first - second).TotalDays) < 1; 
} 

可以。減去兩個日期(DateTimeOff設置是聰明的,知道時區),它會給你一個範圍,一個時間跨度。然後你可以檢查這個範圍是否爲+ - 1天。

+0

Simon,如果兩個日期(調整到相同的偏移量)發生在同一日曆日,而不是24小時內,該方法應該返回true。 – Josh 2014-11-28 15:21:35

+1

一些絕對的「相同的日曆日」的概念沒有意義。如果您需要12小時的間隔,請將1更改爲0.5。 – 2014-11-28 15:58:31

+0

如果您在Windows任務計劃程序中安排任務以在凌晨2點每天運行一個程序,則它將執行計算以查看它是在相同的日曆日還是在達到凌晨2點的不同日期。不確定「絕對」是什麼意思,但日曆日在這方面有意義。這同樣適用於Outlook或其他日曆程序中的每日日曆約會。 – Josh 2014-11-28 16:11:56

1

首先,你需要清楚一些混淆程序應該做什麼。對於兩個通用時區中的兩個通用時間戳(兩個DateTimeOffset實例沒有具體限制),不存在「日曆日」這樣的概念。每個時區都有其自己的日曆日。例如,我們可以有兩個實例DateTimeOffset,名爲firstsecond,它們有不同的偏移量。讓我們將時間軸可視化,並使用|__|標記DateTimeOffset實例引用的具體時間點與*和相應時區中的日曆日(即特定時區中0:00和23:59之間的時間間隔)。它看起來是這樣的:(2-3時之間)

first: ........|___________________*__|....... 
second: ...|______*_______________|............ 

當在first的時區,在second活動期間日曆天發生的事情。當在second的時區中時,first事件發生在之後日曆日(上午1-2點之間)。

所以很明顯這個問題需要澄清,可能還有一點限制。那些真的是通用時區,還是他們在同一地點的時區,可能只在夏令時有所不同?在那種情況下,你爲什麼不忽略時區?例如。 2014年11月2日00:10至23:50之間,UTC偏移量已經發生變化(EDT-> ET),並且兩個時刻間隔超過24小時:new DateTimeOffset(2014, 11, 02, 00, 10, 00, new TimeSpan(-4, 0, 0)).Date == new DateTimeOffset(2014, 11, 02, 23, 50, 00, new TimeSpan(-5, 0, 0)).Date並不重要。基本上,這是martijn試圖做的,但是以一種非常複雜的方式。當你嘗試只是

public static bool SameDateSimple(DateTimeOffset first, DateTimeOffset second) 
{ 
    return first.Date == second.Date; 
} 

它會適用於所有上述單元測試。而且,這也是大多數人在保證兩個實例在一個地方提及時間的情況下稱爲「同一日曆日」的情況。或者,如果你真的比較兩個「隨機」時區,你必須選擇你的參考時區。最初嘗試時可能是UTC。或者,它可能是從人類的角度來看更符合邏輯使用第一時區爲基準(你也可以選擇第二個,它會得出不同的結果,但兩者的變體「一樣好」):

public static bool SameDateGeneral(DateTimeOffset first, DateTimeOffset second) 
{ 
    DateTime secondAdjusted = second.ToOffset(first.Offset).Date; 
    return first.Date == secondAdjusted.Date; 
} 

這對於上述某些測試不起作用,但在某種意義上對於兩個隨機時區「正確」(在某種意義上)起作用更爲普遍:如果嘗試first = new DateTimeOffset(2014, 1, 2, 0, 30, 0, new TimeSpan(5, 0, 0)), second = new DateTimeOffset(2014, 1, 1, 23, 30, 0, new TimeSpan(4, 0, 0)),簡單SameDateSimple返回false(與martijn's一樣),即使這兩個實例在時間(都是2014-01-01 19:30: 00Z)中提及SameDateGeneral此處正確返回true

+0

目前,我們在系統中看到的兩種情況是兩個DateTimeOffset都是東部時間(-4或-5取決於一年中的時間)。另一個是一個日期是ET,另一個是UTC。當我們有一個不在ET的數據中心時,我想要防範未來的情況,這可能會引入不是ET或UTC的偏移量。 – Josh 2014-12-03 15:08:05