4.10 Datum und Zeit 

Berechnungen mit Daten und Zeit durchführen zu müssen, ist eine alltägliche Programmieranforderung, die in den meisten Programmiersprachen eine etwas knifflige Angelegenheit ist. Ruby kommt uns etwas entgegen, damit wir einige Probleme mit Datum und Zeit einfacher lösen können.
Die aktuelle Zeit bestimmen 

new, now
Das grundlegendste Problem ist, die aktuelle Zeit und das aktuelle Datum zu ermitteln. In Ruby wird ein neues Time-Objekt, das ohne Parameter erzeugt wird, auf das aktuelle Tagesdatum und die aktuelle Zeit gesetzt:
>> t = Time.new => Mon Jun 11 21:11:40 +0200 2007 >> t = Time.now => Mon Jun 11 21:11:50 +0200 2007
Die Methode now ist ein Alias für die Methode new.
Mit bestimmten Zeiten arbeiten 

Wir müssen öfter mit Daten in der Zukunft oder aus der Vergangenheit arbeiten. Auch dafür können wir die Klasse Time nutzen. Die wichtigsten Klassenmethoden sind mktime, local, gm und utc.
mktime
Die Methode mktime erzeugt ein neues Time-Objekt der lokalen Zeitzone (der Unterschied zur globalen Standardzeit UTC wird in plus oder minus Stunden ausgegeben) basierend auf den ihr übergebenen Parametern: Jahr, Monat, Tag, Stunde, Minute, Sekunde, Millisekunde (die Parameter werden in absteigender Reihenfolge übergeben). Alle Parameter, außer dem Jahr, sind optional und nehmen als Default-Wert den jeweils kleinstmöglichen Wert an. Die Millisekunden werden auf den meisten Systemen ignoriert. Die Stunden werden im 24-Stunden-Format angegeben:
>> t1 = Time.mktime(1968) => Mon Jan 01 00:00:00 +0100 1968 >> t1 = Time.mktime(1968,2) => Thu Feb 01 00:00:00 +0100 1968 >> t1 = Time.mktime(1968,2,6) => Tue Feb 06 00:00:00 +0100 1968 >> t1 = Time.mktime(1968,2,6,17) => Tue Feb 06 17:00:00 +0100 1968 >> t1 = Time.mktime(1968,2,6,17,30) => Tue Feb 06 17:30:00 +0100 1968 >> t1 = Time.mktime(1968,2,6,17,30,15) => Tue Feb 06 17:30:15 +0100 1968
Die Methode local ist ein Synonym für mktime.
utc
Um die Zeit in der Standardzeit UTC (Coordinated Universal Time), die die Greenwich Mean Time (GMT) ersetzt, auszugeben, stehen die beiden Methoden gm und ihr Synonym utc zur Verfügung. Die Methoden erwarten die gleichen Parameter wie mktime:
>> Time.utc(2007,2,6,23,0,0) => Tue Feb 06 23:00:00 UTC 2007 >> Time.gm(2007,2,6,23,0,0) => Tue Feb 06 23:00:00 UTC 2007
All diesen Methoden können Sie auch die jeweiligen Parameter in Form eines Arrays übergeben, das die Werte in folgender Reihenfolge enthält: Sekunden, Minuten, Stunden, Tag, Monat, Jahr, Wochentag (0--6), Tag im Jahr (1--366), Sommerzeit (true oder false) und die Zeitzone als String. Dieses Array können Sie aus jedem Time-Objekt mit Hilfe der Methode to_a erzeugen:
>> Time.local(0,15,3,20,11,1979,2,324,false,"GMT-8:00") => Tue Nov 20 03:15:00 +0100 1979 >> Time.local(0,15,3,20,11,1979,3,324,false,"GMT-8:00") => Tue Nov 20 03:15:00 +0100 1979 >> Time.gm(*Time.now.to_a) => Sat Jun 23 11:43:41 UTC 2007
Das Ergebnis lässt sich nicht alleine dadurch beeinflussen, dass wir nur den Wochentag von 2 (Dienstag) auf 3 (Mittwoch) ändern. Der 20. November 1979 war ein Dienstag, egal, welchen Wert wir für den Wochentag übergeben.
Wenn Sie Parameter übergeben, die wirklich eine falsche Zeit erzeugen würden, wie zum Beispiel einen 13. Monat oder einen 35. Tag eines Monats, wird ein ArgumentError ausgegeben.
Einen Wochentag bestimmen 

Es gibt mehrere Möglichkeiten, den Wochentag zu bestimmen. Sie könnten die eben angesprochene Methode to_a auf ein Time-Objekt anwenden und dann das siebte Element des Arrays auslesen, das eine Zahl zwischen 0 und 6 zurückliefert (0 steht für Sonntag und 6 für Samstag):
>> Time.now => Sun Feb 24 16:43:46 +0100 2008 >> Time.now.to_a => [14, 44, 16, 24, 2, 2008, 0, 55, false, "CET"] >> Time.now.to_a[6] => 0
wday
Time-Objekten steht aber auch die Methode wday zur Verfügung, um den Wochentag zu ermitteln:
>> Time.now.wday => 6
strftime
Auch wenn der Weg über die Methode wday etwas eleganter ist, sind beide Methoden etwas schwerfällig, da sie uns »nur« den Zahlwert des Wochentages zurückliefern. Was wir aber häufiger im Programmieralltag benötigen, ist der Name des Wochentages. Um diesen zu ermitteln, können wir die Methode strftime nutzen, der wir mehr als zwei Dutzend unterschiedliche Möglichkeiten übergeben können, um Daten und Zeit nach unseren Wünschen zu formatieren. Welche das sind, können Sie in der Referenz im Anhang nachlesen.
Den Namen eines Wochentages in Lang- oder Kurzschreibweise können Sie über folgende Aufrufe ausgeben:
>> Time.now.strftime("%A") => "Saturday" >> Time.now.strftime("%a") => "Sat"
Mit der Unix-Zeit arbeiten 

to_i, to_f, at
Die Unix-Zeit gibt die Anzahl der Sekunden seit dem 1. Januar 1970 00:00 Uhr UTC an. Mit der Methode to_i können Sie ein Time-Objekt in die Unix-Zeit umrechnen. Wenn Sie den Wert in Millisekunden benötigen und Ihr System das unterstützt, können Sie auch die Methode to_f nutzen. Die Methode at rechnet die ihr übergebene Unix-Zeit in einen Datums- und Zeitwert um:
>> Time.at(0) => Thu Jan 01 01:00:00 +0100 1970 >> now = Time.now.to_i => 1182595804 >> Time.at(now) => Sat Jun 23 12:50:04 +0200 2007
Einen Tag im Jahr ermitteln 

yday
Um die Nummer eines Tages (1--366) im Jahr zu ermitteln, stellt uns Ruby die Methode yday zur Verfügung:
>> Time.now.yday => 174
Wochenzahlen ermitteln 

Die Nummerierung der Wochen ist nicht eindeutig definiert. Das kommt vor allem daher, dass das Jahr an jedem Tag der Woche beginnen kann. Wir möchten neue Wochen gerne an einem Sonntag oder einem Montag beginnen lassen, wollen aber oft keine Wochenteile akzeptieren.
cweek
Wir werden Ihnen hier drei Möglichkeiten vorstellen, die Wochenzahl zu ermitteln. Die ersten beiden stellt uns die Methode strftime zur Verfügung, der wir entweder den Formatierungsparameter %U oder %W übergeben. Der Unterschied besteht darin, dass der erste Aufruf die Wochen beginnend mit einem Sonntag nummeriert und der zweite Aufruf beginnend mit einem Montag. Die dritte Möglichkeit, die Wochenzahl zu ermitteln, bietet uns die Methode cweek der Klasse Date . Sie liefert die Nummer der Woche basierend auf der ISO-8601-Definition zurück, die besagt, dass die erste Woche im Jahr diejenige ist, die den ersten Donnerstag im Jahr enthält.
Sie können natürlich jederzeit Ihre eigenen Methoden entwickeln, um die Wochenzahlen nach Ihrer Definition zu ermitteln.
require "date" t1 = Time.local(2002,5,1) d1 = Date.new(2002,5,1) puts t1.strftime("%U").to_i # => 17 puts t1.strftime("%W").to_i # => 17 puts d1.cweek # => 18 t2 = Time.local(2005,5,1) d2 = Date.new(2005,5,1) puts t2.strftime("%U").to_i # => 18 puts t2.strftime("%W").to_i # => 17 puts d2.cweek
Schaltjahre bestimmen 

Die Regeln, um ein Schaltjahr zu ermitteln, lauten:
- Ist die Jahreszahl ohne Rest durch 4 teilbar, aber nicht durch 100, dann ist das Jahr ein Schaltjahr.
- Ist die Jahreszahl ohne Rest durch 100 teilbar, aber nicht durch 400, dann ist das Jahr kein Schaltjahr.
- Ist die Jahreszahl ohne Rest durch 400 teilbar, dann ist das Jahr ein Schaltjahr.
julian_leap?, gregorian_leap?
Die Klasse Date hat zwei Klassenmethoden julian_leap? und gregorian_leap?, von denen allerdings nur noch letztere genutzt wird, um ein Schaltjahr zu ermitteln. Die Methode leap? ist ein Alias für gregorian_leap? . Die Methoden erwarten die zu prüfende Jahreszahl als Parameter und liefern true oder false zurück:
>> require "date" => true >> Date.gregorian_leap? 1900 => false >> Date.leap? 1900 => false >> Date.leap? 2000 => true
Die Klasse Time stellt keine Methode zur Verfügung, um ein Schaltjahr zu ermitteln. Diese können Sie aber ganz einfach implementieren, indem Sie entweder den Algorithmus umsetzen oder innerhalb der Methode Time.leap? die Methode Date.leap? aufrufen. Dabei ist es Ihnen überlassen, ob Sie die Methode als Klassenmethode oder als Instanzmethode implementieren.
Uhrzeiten ausgeben 

Sehr oft möchten wir nur die Uhrzeit ausgeben, sei es im 24-Stunden-Format, im 12-Stunden-Format, mit oder ohne Angabe der Sekunden. All das können wir mit der Methode strftime realisieren:
>> t = Time.now => Sun Jun 24 21:53:22 +0200 2007 >> t.strftime("%H:%M:%S") => "21:53:22" >> t.strftime("%H:%M") => "21:53" >> t.strftime("%I:%M %p") => "09:53 PM"
Natürlich gibt es noch viele weitere Möglichkeiten. Lassen Sie Ihrer Phantasie freien Lauf!
Datums- und Zeitwerte vergleichen 

Die Klasse Time beinhaltet das Modul Comparable, so dass Datums- und Zeitwerte direkt miteinander verglichen werden können:
t0 = Time.local(2000,10,5,22,15) t1 = Time.local(2000,10,10,23,45) t2 = Time.local(2000,10,12,8,10) t3 = Time.local(2000,10,11,10,25) if t0 < t1 then puts "t0 < t1" end # t0 < t1 if t1 != t2 then puts "t1 != t2" end # t1 != t2 if t1 <= t2 then puts "t1 <= t2" end # t1 <= t2 if t3.between?(t1,t2) puts "t3 ist zwischen t1 und t2" # t3 ist zwischen t1 und t2 end
Mit Datums- und Zeitwerten rechnen 

Addition und Subtraktion
Wir können einen weiteren Zeitwert zu einem bestehenden Zeitwert addieren oder von ihm subtrahieren. Die Angabe erfolgt in Sekunden:
>> t0 = Time.now => Mon Jun 25 21:35:04 +0200 2007 >> t1 = t0 + 60 => Mon Jun 25 21:36:04 +0200 2007 >> t2 = t0 + 3600 => Mon Jun 25 22:35:04 +0200 2007 >> t3 = t0 + 86400 => Tue Jun 26 21:35:04 +0200 2007
Die Differenz zwischen zwei Datums- oder Zeitwerten können wir ermitteln, indem wir einfach den einen Wert vom anderen subtrahieren. Die Differenz wird in Sekunden zurückgeliefert:
>> today = Time.local(2007,6,25) => Mon Jun 25 00:00:00 +0200 2007 >> yesterday = Time.local(2007,6,24) => Sun Jun 24 00:00:00 +0200 2007 >> diff = today - yesterday => 86400.0
ActiveSupport
Mit Hilfe von ActiveSupport lässt sich jede Zahl ganz einfach in die gewünschten Einheiten Jahre, Monate, Tage, Stunden, Minuten oder Sekunden umrechnen. Mehr dazu erfahren Sie in Kapitel 12.
Datums- und Zeitwerte aus Zeichenketten ermitteln 

parsedate
Es gibt viele unterschiedliche Möglichkeiten, ein Datum innerhalb einer Zeichenkette zu formatieren. Von der unterschiedlichen Reihenfolge von Tag, Monat und Jahr bis hin zu verschiedenen Abkürzungen sind der Phantasie keine Grenzen gesetzt. Etwas zu programmieren, das all diese unterschiedlichen Möglichkeiten in einen Datums- oder Zeitwert umwandelt, löst bei manchem Alpträume aus. Ruby bringt das Modul ParseDate mit, dessen Methode parsedate die meiste Arbeit schon erledigt. Die Methode erwartet den umzuwandelnden String als Parameter und liefert ein Array mit den Werten Jahr, Monat, Tag, Stunde, Minute, Sekunde, Zeitzone und Wochentag. Kann ein Wert nicht gesetzt werden, wird nil zurückgeliefert:
require "parsedate.rb" include ParseDate s1 = "9/13/98 2:15am" s2 = "1961-05-31" s3 = "11 July 1924" s4 = "April 17, 1929" s5 = "20 July 1969 16:17 EDT" s6 = "Mon Nov 13 2000" s7 = "August 24, 79" s8 = "8/24/79" p parsedate(s1) # [98, 9, 13, 2, 15, nil, nil, nil] p parsedate(s2) # [1961, 5, 31, nil, nil, nil, nil, nil] p parsedate(s3) # [1924, 7, 11, nil, nil, nil, nil, nil] p parsedate(s4) # [1929, 4, 17, nil, nil, nil, nil, nil] p parsedate(s5) # [1969, 7, 20, 16, 17, nil, "EDT", nil] p parsedate(s6) # [2000, 11, 13, nil, nil, nil, nil, 1] p parsedate(s7) # [79, 8, 24, nil, nil, nil, nil, nil] p parsedate(s8,true) # [1979, 8, 24, nil, nil, nil, nil, nil]
Wenn Sie als zweiten Parameter true übergeben, wandelt die Methode guess_year eine zweistellige Jahreszahl nach folgenden Regeln in eine vierstellige um:
Wenn die Jahreszahl kleiner als 100, aber größer als 70 ist, wird 1900 addiert, ansonsten 2000.
Keine Prüfung auf Plausibilität
Die Methode parsedate nimmt keine Plausibilitätsprüfung vor. Das heißt, wird eine falsche Datumszeichenkette übergeben, wird diese ignoriert.
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.