本文章是由機器翻譯。

程式設計師雜談

野田佳彥時間

Ted Neward

 

Ted Neward曾經花了很多時間來思考的時間嗎?

很早在我的職業生涯,我正在工作的系統上部署到幾個不同的話務中心告終。特別重要的是"何時"發生的事件跟蹤的 (它是醫療-­相關護士的話務中心系統),所以,沒有太多想這件事,我們和盡職盡責地寫到資料庫的行的事件的時間離開它當時。除外,因為我們發現了以後,當系統被部署到四個不同的電話中心,每個不同的美國時區,時間日誌都有點"off",由於這一事實,我們本來沒想到要包括時區偏移量。

在軟體系統中的時間是這樣 — — 這一切似乎非常簡單明瞭,直到它突然再也不會。

根據我過去的兩個列的主題 (可以在找到我的所有列 bit.ly/ghMsco),再一次.NET 社區受益于JAVA社區 ; 所做的工作 在這種情況下,它是一個叫"野田佳彥時間,"JAVA"焦達時間"專案,其本身的埠被設計為JAVA"日期"類 (軟體追溯到JAVA1.0 的天的可怕破片) 更換 Microsoft.NET 框架的包。Jon 飛碟,野田佳彥的時間,作者基於演算法和中焦達時間的概念,但建立它從地面作為.NET 庫。

足夠的序言:做"安裝套裝軟體 NodaTime"(注意"野田佳彥"和"時間"之間沒有空格),並讓我們看看一些代碼。

'我' 的時間

首先要知道的是直言對愛因斯坦的理論,你不必接近光的速度來實現該時間是相對的。如果它是 7 下午(這就是歐洲的朋友流覽 1900年) 在這裡在西雅圖,然後是 7 下午對於我們所有人在西雅圖,但它是 8 下午為我的父母在鹽湖城,9 下午在達拉斯和 10 下午我旅行代理為在波士頓我喝好友。我們所有獲得的 — — 這就是神奇的時區。但我的電腦並不真的理解時區,本身 — — 它會報告時,它被設置為,在這種情況下即 7 下午,儘管它是確切的同一時間在時間中為我們所有人在世界各地。換句話說,它不是時間的時間本身是時間的相對的它是時間的我們的表示形式是時間的相對的。野田佳彥的時間,在此表示形式反映為"全球化"的時間,意味著每個人都同意的通用時間表上的時刻。我們認為是 「 本地人 」 的時間 — — 也就是說,這次與相關聯的時區 — — 野田佳彥次調用"劃的時間,"而不是野田佳彥時間認為 「 本地人 」 的時間,這是沒有任何附加的時間區域 (更多在後面介紹)。

所以,例如,野田佳彥時間"即時"指揚午夜原點與全球時間軸上上的一個點。1、 1970 年,協調通用時間 (UTC)。(沒有什麼神奇,大概是這一天,只是這是由公約 》"時代的開始"Unix 系統,從那時起,計數"滴答聲",從而充當好參考原點作為任何其他。)所以,這樣做使我們當前的即時 (假定的野田佳彥時間命名空間引用 — —"使用 NodaTime"— — 當然):

var now = SystemClock.Instance.Now;

若要獲取相對於西雅圖的時間,我們想要 ZonedDateTime。 這是本質上是即時的但在內的時區資訊,這樣,它將自身標識為相對於"西雅圖,對揚。 9、 2013"(日期不重要,因為我們需要知道是否我們在夏令時 [DST] 或不)。 ZonedDateTime 通過建構函式,並傳遞即時和日期­時區的時間。 我們已即時,但我們需要 DateTime 區 DST 在西雅圖。 若要獲取日期時間區域,我們需要 IDateTime zoneProvider。 (這種間接方式的原因是微妙的但就是要與.NET 框架的時區不同于任何其他程式設計的平臺 ; 使用一種表示形式這一事實 互聯網分配名稱管理局或 IANA,使用像"美國 Los_Angeles"的格式)。野田佳彥時間提供了兩個內置提供程式,一種是 IANA 版本和其他標準的.NET 基礎類 (BCL) 庫版本,通過對 DateTime zoneProviders 類的靜態屬性:

var seattleTZ = dtzi["America/Vancouver"];
var dtzi = DateTime zoneProviders.Tzdb;

時區資料庫 (TZDB) 版本是 IANA 的版本,並因此獲得時區表示西雅圖是選取它的問題,(,根據 IANA,是"美國/Los_Angeles,"如果你想要的東西更接近,"美國/溫哥華"或):

var seattleNow = new ZonedDateTime(now, seattleTZ);

如果我們列印出來,我們得到的一種表示"地方:2013/1/9 7:54:16 下午偏移量:-08 區:美國/溫哥華"。注意到"偏移量"部分的表示形式中嗎? 它是重要的因為記得哪年的一天 (和什麼國家中,你和什麼日曆您正在操作下,和......) 的基礎,從 UTC 的偏移量將會改變。 對於那些我們在西雅圖,DST 意味著獲得或失去一個小時關閉本地時鐘,因此很重要的是要注意從 UTC 的偏移量是什麼。 事實上,野田佳彥時間跟蹤的另外,因為如分析日期時"2012年-06-26T20:41:00 + 1:00,"例如,我們知道這是早于 utc 時間,一小時,但我們不知道是否這是由於 DST 的特定時區中的時間或不。

仍考慮時間是很容易嗎?

'我們' 的時間

現在讓我們假設我想知道多長時間才在我生命中一個重要的日子 — — 如我 25 的結婚紀念日,這將會揚。 16, 2018. (跟蹤的這種事情是有點重要,如認為任何配偶就會告訴你,我需要知道我多久之前,我需要買一件非常昂貴的禮物。這是野田佳彥時間的真正去向出盡風頭,因為它會跟蹤您的所有隱隱的小細節。

首先,我需要構建檢索到的 LocalDateTime (或者,如果我沒有在意時間,LocalDate ; 或者,如果我不關心的日期,本地時間)。 檢索到的 LocalDateTime (或 LocalDate 或本地時間) 是不知道它到底是什麼相對於的時間表上的相對位置 — — 換句話說,它是不知道其時區的時間點。

(儘管不知道時區的時間,這是仍然有用的資訊。 它認為,像這樣:如果你和我在同一個辦公室,在一起工作,並且我們想要滿足今天晚些時候,我會說,"我們在 4 下午見面"因為我們倆都在同一時區中,沒有附加資訊有必要限定這次毫不含糊地向彼此。)

所以,因為我知道點時間,我關心已經,它易於構建:

var twentyFifth = new LocalDate(2018, 1, 16);

因為我只詢問有關的兩個日期,而不必關心的時區的差異,我只需要檢索到的 LocalDateTime 部分的 ZonedDateTime 的 LocalDate 部分從我較早前的計算:

var today = seattleNow.LocalDateTime.Date;

但我們的要求是一種新的時間單位:"期"兩個時間之間。 (在 BCL,這是持續時間。野田佳彥時間使用不同的構造來表示這 — — 一段時間 — — 像所有正確表示度量單位的它需要一個單位跟它去。 例如,"47"答案是徒勞無隨附的單位,如"47 天,""47 小時"或"47 年"。期之間,提供一個方便的方法,來計算兩個 LocalDates 之間某些特定時間單位的數目:

var period = Period.Between(today, twentyFifth, PeriodUnits.Days);
testContextInstance.WriteLine("Only {0} more days to shop!", period.Days);

這會告訴我到底是多少天,但我們通常不像這樣計數天在大金額 (在我寫這篇文章的時候,1,833)。 我們更喜歡時間,將更易於管理的資料塊,如"年、 月、 幾天,"我們可以再次詢問野田佳彥管理時間。 我們可以問它給我們一段時間,其中包含 or 按年/月/天劃分、 PeriodUnits 標誌一起:

Period.Between(today, twentyFifth,
  PeriodUnits.Years | PeriodUnits.Months | PeriodUnits.Days)

或者,因為這是一個很普通的要求,我們可以問它給我們一段時間年/月/日擊穿包含通過使用相同的名稱的標誌:

Period.Between(today, twentyFifth, PeriodUnits.YearMonthDay)

(顯然,我仍有一點點時間,這很好,因為並不知道給她買什麼。

測試時間

經常閱讀此專欄的讀者會知道我喜歡寫勘探測試新的庫,在調查時,此列也不例外。 然而,它不可能寫測試基於時間,尤其是因為時間上繼續這惱人的習慣。 關閉任何預期的結果是,每個經過的毫秒引發和使它硬,如果不是不可能的更要編寫測試,將生成可預測的結果,我們可以斷言,侵犯行為報告。

出於此原因,任何東西,提供時間 (如,你知道,一個時鐘) 實現 IClock 介面,包括我較早前用於獲取即時的"現在"SystemClock (現在靜態屬性)。 如果我們,例如,創建一個實現,實現 IClock 介面,並提供一個常量值回 (唯一成員必需由 IClock 介面,事實上) 現在屬性,因為其餘的野田佳彥時間庫尋­我們效法使用瞬間識別時間在那一刻,我們基本上是創立了完全可測試的環境,使整件事要測試斷言和驗證。 因此,我可以稍有更改我較早前的代碼,並創建一組探勘測試,如中所示圖 1

圖 1 創建探勘測試

[TestClass]
public class UnitTest1
{
  // SystemClock.Instance.Now was at 13578106905161124 when I
  // ran it, so let's mock up a clock that returns that moment
  // in time as "Now"
  public class MockClock : IClock
  {
    public Instant Now
    {
      get { return new Instant(13578106905161124); }
    }
  }
  [TestMethod]
  public void TestMethod1()
  {
    IClock clock = new MockClock(); // was SystemClock.Instance;
    var now = clock.Now;
    Assert.AreEqual(13578106905161124, now.Ticks);
    var dtzi = DateTime zoneProviders.Tzdb;
    var seattleTZ = dtzi["America/Vancouver"];
    Assert.AreEqual("America/Vancouver", seattleTZ.Id);
    var seattleNow = new ZonedDateTime(now, seattleTZ);
    Assert.AreEqual(1, seattleNow.Hour);
    Assert.AreEqual(38, seattleNow.Minute);
    var today = seattleNow.LocalDateTime.Date;
    var twentyFifth = new LocalDate(2018, 1, 16);
    var period = Period.Between(today, twentyFifth, PeriodUnits.Days);
    Assert.AreEqual(1832, period.Days);
  }
}

通過所有與時間相關代碼並使用野田佳彥的時間,而不內置的.NET 時間類型,代碼將成為更多可測試只需通過更換 IClock 用於獲得即時的"現在"可控和已知的東西。

但等待......

有更多的野田佳彥時間比只是什麼我在這裡。 為例,它是相對容易地添加時間單位 (天、 月等) 到給定時間的工作都是通過使用"加號"和"減去"(為其有還運算子多載,如果那些使更多的意義上使用) 的方法,以及一個 FakeClock 類,專門用於測試與時間相關的代碼,包括在一些離散的時尚時間以程式設計方式的"提前"的能力使它更易於測試經過時間敏感的代碼 (如 Windows 工作流實例,例如,應該採取行動經過一段時間後沒有活動)。

在更深入、 更概念層面上,野田佳彥時間還演示如何在程式設計語言中的類型系統可以説明區分略有不同種類的問題域中的值:通過分離出的不同種類的時間 (瞬間、 當地時間、 本地日期、 本地日期和時間,和劃的日期和時間,例如) 到離散的、 相互關聯的類型,它可以協助程式員需要清楚明確正是此代碼是什麼要做或與工作有關。 它可以是特別重要,例如,從某些代碼"誕生日"區分"出生日期":一個反映了宇宙的時間軸中的時刻,當一個人出生時,另一種是反復出現的日期,依據我們慶祝的宇宙線中的那一刻。 (實際來說,之一已附加到它上面的一年,其他不會。

飛碟已清楚表明,他並不認為圖書館"完成"以任何方式,和他有計劃,以增強和進一步擴展它。 幸運的是,今天,野田佳彥時間是可供使用和開發人員自己欠 NuGet 野田佳彥時間、 看一看,並開始找出如何以及在何處的問題域中使用它。 畢竟,時間很寶貴。

編碼愉快 !

Ted Neward 是一個主體與 Neward & 同夥 LLC。他已寫一百多篇和創作和合著了十幾本書,其中包括"專業 F # 2.0"(Wrox,2010年)。他是 F # MVP 和注意到JAVA專家,並在世界各地的JAVA和.NET 會議講話。他徵求意見,並定期指導 — — 與他聯繫。 ted@tedneward.com 如果你感興趣讓他來與您的團隊一起工作。在他的博客 blogs.tedneward.com 和可以在 Twitter 上遵循 twitter.com/tedneward

感謝以下技術專家對本文的審閱: Jon 飛碟
Jon 飛碟是谷歌,在倫敦工作的高級軟體工程師。 白天他代碼在JAVA中,但他的激情是 C#。 你還可以在 Twitter 上的 Jon (@jonskeet) 或只是將問題張貼在堆疊溢位。