Dieser Artikel wurde maschinell übersetzt.

Programmiererpraxis

Zeit des Noda

Ted Neward

 

Ted NewardJemals verbrachte viel Zeit damit, über Zeit?

Sehr früh in meiner Karriere arbeitete ich auf einem System, die Bereitstellung auf mehreren verschiedenen Call-Centern landete. Besonders wichtig war die Verfolgung der "als" ein Ereignis aufgetreten ist (es war ein medizinisch -­ähnliche System für ein Call-Center der Krankenschwestern), und so, ohne darüber nachzudenken zu viel, wir pflichtgemäß schrieb die Zeit des Ereignisses in eine Datenbankzeile und ließ es damals. Außer, wie wir später feststellten, wenn das System in vier verschiedene Call-Center, jeweils in verschiedenen in den USA bereitgestellt wurde Zeitzone, wurden die Protokolle der Zeit alle ein wenig "off" Dank der Tatsache, die wir noch nicht gedacht-Zeitzone Offsets enthalten.

Zeit in einem Softwaresystem ist wie das — alles scheint ziemlich unkompliziert und einfach, bis es plötzlich nicht mehr.

In Uebereinstimmung mit dem Thema meiner letzten zwei Spalten (alle meine Spalten finden Sie unter bit.ly/ghMsco), noch einmal die .NET-Gemeinschaft profitiert von Arbeit, die von der Java -Community; in diesem Fall ist es ein Paket namens "Noda Time" ein Microsoft .NET Framework Port des Projektes "Joda Time" Java , die selbst als Ersatz für die "Date"- Java -Klasse (ein schrecklich gebrochenen Stück Software aus den Tagen der Java 1.0) entworfen. Jon Skeet, der Autor des Noda Zeit, anhand der Algorithmen und Konzepte in Joda Time, sondern aufgebaut es vom Boden als .NET Bibliothek.

Genügend Präambel: Ein "Install-Package-NodaTime" zu tun (Beachten Sie kein Leerzeichen zwischen "Noda" und "Time"), und schauen wir uns einige Code.

'Mir' Zeit

Die erste Sache zu erkennen ist, dass, bei allem Respekt zu Einsteins Theorien, Sie nicht auf Lichtgeschwindigkeit zur Realisierung dieser Zeit nahezu sein ist relativ. Wenn sie 19 ist (das ist 1900 bis Sie Europäische Leute) hier in Seattle, dann ist es 19 für alle von uns in Seattle, aber es ist 20 für meine Leute in Salt Lake City 21 für mein Reisebüro in Dallas und 22 für meine trinken Buddy in Boston. Wir alle erhalten, — das ist die Magie von Zeitzonen. Aber mein Computer nicht wirklich verstehen, Zeitzonen, schlechthin — berichtet die Zeit, die es auf, festgelegt worden ist und die in diesem Fall 19, trotz der Tatsache ist, dass es den exakt gleichen Zeitpunkt für alle von uns auf der ganzen Welt. Mit anderen Worten, ist es nicht, dass die Zeit selbst ist relativ, es ist, dass unsere Darstellung der Zeit relativ ist. Rechtzeitig Noda spiegelt diese Darstellung als "global" Zeit, d. h. einen Moment auf einer universellen Zeitleiste, mit der alle einig. Was wir als "Ortszeit" — d. h. jener Zeit mit einer zugehörigen Zeitzone — Noda Mal ruft "Zonen-Zeit," im Gegensatz zu dem, was Noda Zeit hält "lokalen" Zeit, das ohne jede Zeitzone befestigt (mehr dazu später).

Also, verweist beispielsweise des Noda-Zeit "Instant" auf einen Punkt auf der globalen Zeitachse mit Ursprungspunkt des Mitternacht am Jan. 1, 1970, Koordinierter Weltzeit (UTC). (Es ist nichts Magisches an diesem Datum, außer dass dies gemäß der Konvention "Beginn der Epoche" für Unix-Systeme, die seit diesem Zeitpunkt zählen ist "tickt", und somit als gute Herkunft Bezugspunkt wie jede andere dient.) So gibt Sie uns auf diese Weise die aktuelle Instant (vorausgesetzt, dass der Zeit des Noda-Namespace verwiesen wird — "mit NodaTime" — natürlich):

var now = SystemClock.Instance.Now;

Um eine Seattle-Relative Zeit zu erhalten, wollen wir eine ZonedDateTime. Dies ist im Wesentlichen eine sofortige, aber mit der Zeitzone-Information enthalten, damit es sich identifiziert, als relativ "Seattle, auf Jan. «««9, 2013 "(das Datum als wichtig, weil wir müssen wissen, ob wir in der Sommerzeit [DST] oder nicht sind). Eine ZonedDateTime wird durch einen Konstruktor übergeben Sie einen Moment und ein Datum erreicht­Zeitzone. Wir haben im Augenblick, aber wir brauchen die DateTime-Zone für Seattle im DST. Um die DateTime-Zone zu erhalten, brauchen wir eine IDateTime-ZoneProvider. (Der Grund für diese Dereferenzierung ist subtil, aber mit der Tatsache zu tun, dass das .NET Framework eine Darstellung für Zeitzonen, die anders als jede andere Programmierplattform; verwendet die Internet Assigned Namen Authority oder IANA, verwendet ein Format wie "America/Los_Angeles.") Noda Time bietet zwei integrierte Anbieter, nämlich die IANA-Version und die andere die Norm .NET Basis Klasse Bibliothek (BCL) Version, durch statische Eigenschaften der DateTime-ZoneProviders-Klasse:

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

Die Version die Zeitzone-Datenbank (TZDB) ist die IANA-Version, und erhalten also die Zeitzone, die Seattle darstellt ist eine Frage der Auswahl (die, laut IANA, "America/Los_Angeles," ist oder wenn Sie etwas näher, "America/Vancouver"):

var seattleNow = new ZonedDateTime(now, seattleTZ);

Und wenn wir dieses heraus drucken, erhalten wir eine Darstellung der "lokalen: 09.01.2013 19:54:16 Offset: -08 Zone: America/Vancouver." Beachten Sie, dass in der Darstellung "Offset" Teil? Es ist wichtig, weil daran erinnern, dass auf der Grundlage welcher Tag des Jahres ist (und was du bist im Land und welchen Sie Kalender unter und... arbeiten), ändert sich der Offset von UTC. Für diejenigen von uns in Seattle bedeutet DST Erhalt oder Verlust des eine Stunde vor der lokalen Uhr, daher es wichtig ist zu beachten, was ist der Offset von UTC. In der Tat, Noda Zeit verfolgt die Spuren der, die separat, da beim Analysieren von einem Datum wie "2012-06-26T20:41:00 + 01:00," beispielsweise wissen wir, dass es eine Stunde vor UTC, aber wir wissen nicht, ob das wegen der DST in diesem bestimmten Zeitzone oder nicht.

Ist noch Reaktionszeit einfach?

'Wir' mal

Jetzt nehmen wir an, ich wie lange, bis ein wichtiges Datum in meinem Leben wissen will — wie mein 25. Hochzeitstag, die auf Jan sein wird. 16, 2018. (Verfolgung von solchen Dingen ist wichtig, wie ich glaube, jeder Ehepartner würden Sie sagen, und ich muss wissen, wie lange ich habe, bevor ich ein wirklich teures Geschenk kaufen müssen.) Dies ist, wo Noda Zeit wirklich geht glänzen, denn es zum Nachverfolgen der nagende kleine Details für Sie wird.

Zuerst, ich brauche eine LocalDateTime zu konstruieren (oder, wenn ich nicht, die Zeit, ein LocalDate kümmern; oder, wenn ich das Datum, ein LocalTime interessiert nicht). Ein LocalDateTime (oder LocalDate oder LocalTime) ist eine relative Position auf der Zeitachse, die nicht wissen, genau das, was es ist, bezogen auf – mit anderen Worten, es ist ein Punkt in der Zeit ohne zu wissen, ihre Zeitzone.

(Trotz nicht wissend, die Zeitzone, das ist noch eine nützliche Informationen zu erhalten. Betrachten Sie es wie folgt: Wenn Sie und ich zusammen in einem Büro arbeiten, und wir später heute erfüllen ich sage wollen, "Treffen wir uns um 16" Weil wir beide in der gleichen Zeitzone sind, ist keine zusätzliche Informationen notwendig, diesmal eindeutig zueinander zu qualifizieren.)

Also, da ich den Punkt rechtzeitig, dass Wissen kümmern ich bereits, es ist einfach zu konstruieren:

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

Und da ich nur über den Unterschied zwischen zwei Datumsangaben, die ohne Rücksicht auf die Zeitzonen Fragen bin, ich brauche nur den LocalDate Teil der LocalDateTime von der ZonedDateTime aus meinem früheren Berechnung:

var today = seattleNow.LocalDateTime.Date;

Aber was wir hier fragst ist für eine neue Art von Zeiteinheit: ein "Zeitraum" zwischen zwei Zeitangaben. (In der BCL ist dies eine Dauer). Noda Zeit verwendet ein anderes Konstrukt, um dies darzustellen — einen Zeitraum — und wie alle ordnungsgemäß vertretenen Maßeinheiten, erfordert es eine Einheit mit ihm zu gehen. Beispielsweise ist die Antwort "47" nutzlos ohne eine begleitende Einheit, z. B. "47 Tage", "47 Stunden" oder "47 Jahre." Zeitraum bietet eine praktische Methode, zwischen, um die Anzahl von einigen bestimmten Zeiteinheit zwischen zwei LocalDates zu berechnen:

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

Dies sagt mir genau, wie viele Tage, aber wir zählen nicht in der Regel Tage in große Höhe (1.833, damals schrieb ich in diesem Artikel) wie die. Wir bevorzugen die Zeit sich in mehr überschaubare Einheiten, wie z. B. "Jahre, Monate, Tage," die Fragen wir wieder Noda Zeit verwalten können. Können bitten wir sie uns einen Zeitraum geben, die Jahre/Monate/Tage betrachtet, nach OR-Verknüpfung die PeriodUnits-Flags zusammen enthält:

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

Oder, da dies eine ziemlich häufige Anforderung ist, bitten wir sie uns einen Zeitraum geben, die Jahre/Monate/Tage Aufschlüsselung enthält mit dem Schwedenhaus Flag mit dem gleichen Namen:

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

(Anscheinend habe ich noch ein wenig Zeit noch, was gut ist, denn ich habe keine Ahnung was ihr zu bekommen.)

Prüfzeit

Häufige Leser dieser Kolumne wissen, dass ich Exploration Tests zu schreiben gerne, wenn Sie eine neue Bibliothek zu untersuchen und diese Spalte keine Ausnahme ist. Jedoch ist es unmöglich, das Schreiben von Tests basierend auf Zeit, vor allem, weil Zeit diese lästige Angewohnheit hat weitermachen. Jede Millisekunde, die übergibt wirft, aus welchem das erwartete Ergebnis ist, und das macht es schwer, wenn nicht unmöglich, Tests, die vorhersagbaren Ergebnissen führt zu schreiben, dass wir behaupten können und melden Sie Verstöße.

Aus diesem Grund etwas bereitstellt Zeit (wie Sie wissen, eine Uhr) implementiert die IClock Schnittstelle, einschließlich der SystemClock ich früher habe die Instant für "right now" zu erhalten (die jetzt statische Eigenschaft). Wenn wir beispielsweise eine Implementierung, die die IClock-Schnittstelle implementiert und stellt einen konstanten Wert zurück für die Now-Eigenschaft (das einzige Mitglied von der IClock Schnittstelle benötigt, in der Tat), erstellen, da der Rest des Noda Zeit Bibliothek Basi­cally verwendet Instants zu diesem Moment rechtzeitig erkennen, haben wir im Wesentlichen eine völlig testbare Umgebung, wodurch das ganze getestet werden, behauptet und überprüft. Also, ich kann meinen früheren Code geringfügig ändern und erzeugen eine Reihe von Tests der Exploration, wie in gezeigt Abbildung 1.

Abbildung 1 Erstellen von Exploration Tests

[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);
  }
}

Indem alle zeitbezogenen Code durchlaufen und Noda Zeit anstelle der integrierten .NET-Zeit-Typen, wird Code einfach durch Ersetzen der IClock verwendet, um die Instant für "right now" etwas steuerbar und bekannte erhalten viel mehr getestet werden.

Aber warten...

Es gibt Noda Zeit viel mehr als nur was ich hier gezeigt habe. Zum Beispiel ist es relativ einfach hinzufügen Zeiteinheiten (Tage, Monate usw.) zu einem bestimmten Zeitpunkt mit das "Plus" und "Minus" Methoden (wofür es gibt auch Operatorüberladungen, wenn diejenigen sinnvoller verwenden), sowie eine FakeClock-Klasse, die speziell für die zeitbezogenen Code testen, einschließlich der Möglichkeit, Mal in einige diskrete Mode programmgesteuert zu "fördern", erleichtert damit testen vergangen-zeitkritische Code (z. B. Windows Workflow-Instanzen, z. B., die angeblich um zu handeln, nachdem eine bestimmte Zeit ohne Aktivität abgelaufen ist).

Auf einer tieferen, konzeptionellen Ebene veranschaulicht Noda Zeit auch wie ein Typsystem in einer Programmiersprache kann jedoch leicht verschieden Arten von Werten innerhalb der Problemdomäne unterscheiden: Man trennt die verschiedenen Arten von Zeit (Augenblicken, lokale Zeit, lokale Termine lokale Termine und, und gezonten Termine und Zeiten, zum Beispiel) den in diskreter und miteinander verbundener Typen, hilft den Programmierer werden klar und deutlich über genau was dieser Code soll sein tun oder arbeiten mit. Es kann beispielsweise besonders wichtig sein, ein "Geburtsdatum" von einem "Geburt Tag" im Code zu unterscheiden: Einen Moment in das Universum Zeitleiste reflektiert, wenn eine Person geboren wurde, der andere ist ein wiederkehrendes Datum auf dem feiern wir diesem Moment in das Universum Timeline. (Praktisch, man hat ein Jahr angefügt, die andere nicht)

Skeet hat deutlich gemacht, die er die Bibliothek "fertige" in irgendeiner Weise berücksichtigen nicht, und er hat Pläne zu verbessern und weiter auszubauen. Glücklicherweise Noda Zeit ist heute zur Verfügung, und Entwicklern Schulden es sich selbst NuGet Noda Zeit, schauen Sie und starten Sie herauszufinden mehr wie und wo Sie es in der Problemdomäne verwenden. Denn ist Zeit kostbar.

Viel Spaß beim Programmieren!

Ted Neward*, Geschäftsführer von Neward & Associates LLC, Er hat mehr als 100 Artikel geschrieben und Autor und Mitautor von einem Dutzend Bücher, darunter "professionelle f# 2.0" (Wrox, 2010). Er ist F#-MVP, ein bekannter Experte für Java, und er hält auf Java- und .NET-Konferenzen in der ganzen Welt Vorträge. Er berät und Mentoren regelmäßig – Sie erreichen ihn unter ted@tedneward.com Wenn Sie mit ihm kommen mit Ihrem Team Arbeiten interessiert sind. Unter blogs.tedneward.com können Sie seinen Blog lesen, und Sie können Neward unter twitter.com/tedneward auf Twitter folgen.*

Dank der folgenden technischen Experten für die Überprüfung dieses Artikels: Jon Skeet
Jon Skeet ist senior Software Engineer bei Google, in London arbeitete. Tagsüber, die er in Javacodes, aber seine Leidenschaft ist c#. Sie erreichen Jon auf Twitter (@jonskeet) oder per post einfach eine Frage auf die Stack-Überlauf.