2013-01-28

.NETte Zeitzonen

Sie sind es ja schon gewohnt, dass ich zu Posts gelegentlich ein Follow-Up mache. Da ich gerne über den Tellerrand schaue, wollte ich wissen, wie man in .NET zwischen Zeitzonen konvertiert.
Wie schon bei meiner Java-Version weise ich auch hier darauf hin, dass das Auswählen einer Zeitzone auf Basis des angezeigten namens nicht ganz sauber ist. Aber stattdessen die Auswahl in eine Klappliste zu verfrachten, ist ja kein großer Akt.
Eine tolle Site zur Online-Konvertierung von C# nach HTML ist übrigens manoli.net.

   1:  using System;
   2:  using System.Collections.ObjectModel;
   3:  using System.Linq;
   4:   
   5:  namespace TimeZoneDemo1
   6:  {
   7:      class Program
   8:      {
   9:          // siehe http://msdn.microsoft.com/en-us/library/bb397769.aspx
  10:   
  11:          static void Main(string[] args)
  12:          {
  13:              ReadOnlyCollection<TimeZoneInfo> timeZones = TimeZoneInfo.GetSystemTimeZones();
  14:              Console.Out.WriteLine("Anzahl: " + timeZones.Count);
  15:   
  16:              TimeZoneInfo berlin = null;
  17:              TimeZoneInfo newYork = null;
  18:   
  19:              for (int i = 0; i < timeZones.Count; i++)
  20:              {
  21:                  TimeZoneInfo tz = timeZones.ElementAt(i);
  22:                  String name = tz.DisplayName;
  23:                  Console.WriteLine("Name: " + name);
  24:   
  25:                  String lc = name.ToLower();
  26:   
  27:                  if (lc.Contains("berlin"))
  28:                  {
  29:                      berlin = tz;
  30:                  }
  31:                  else if (lc.Contains("eastern zeit"))
  32:                  {
  33:                      newYork = tz;
  34:                  }
  35:              }
  36:   
  37:              if ((berlin != null) && (newYork != null))
  38:              {
  39:                  DateTimeOffset dto = new DateTimeOffset(2013, 1, 28, 19, 20, 0,
  40:                      berlin.BaseUtcOffset);
  41:                  DateTime utc = dto.UtcDateTime;
  42:                  Console.WriteLine(berlin.DisplayName + ": " + TimeZoneInfo.ConvertTimeFromUtc(utc, berlin).ToShortTimeString());
  43:                  Console.WriteLine(newYork.DisplayName + ": " + TimeZoneInfo.ConvertTimeFromUtc(utc, newYork).ToShortTimeString());
  44:              }
  45:          }
  46:      }
  47:  }

2013-01-26

Zeitzonen

…hallo, noch jemand da…?

Smiley

Nach einer etwas längeren schöpferischen Pause habe ich ein hoffentlich spannendes Thema für Sie. Mobile Geräte werden weltweit genutzt. Und damit auch deren Apps. Bei der Programmierung ist deshalb darauf zu achten, dass Kalender, Termine oder Erinnerungen überall funktionieren. Das hört sich allerdings einfacher an, als es ist. Nehmen Sie als Beispiel den Geschäftsreisenden, der in München wohnt und zu einem Kunden nach New York muss. Wenn die beiden einen Termin vereinbaren, wählen sie normalerweise eine Zeit bezogen auf den Ort des Treffens, also New York. Tippt der Münchner unbesonnen einfach die beschlossene Uhrzeit in sein teures Smartphone, finden die beiden nicht zueinander. Warum? Unterschiedliche Zeitzonen. Ist es in Deutschland 19:20, zeigt die Uhr an der amerikanischen Ostküste 13:20 (Spezialitäten wie Sommerzeit einmal außen vor gelassen).

Die App zur Erfassung von Terminen wird dem Anwender also die Möglichkeit geben, eine Zeitzone auszuwählen. Gleiches gilt natürlich für klassische Desktop- und Webanwendungen. Der folgende Google Kalender-Screenshot zeigt, wie so etwas aussehen kann.

Anlegen eines Termins mit Zeitzone in Google Kalender

Wie aber kommt man an solche Zeitzonen-Informationen? Tatsächlich kennt Java schon seit Version 1.1 die Klasse java.util.TimeZone. Diese speichert die Abweichung von einer Standardzeit sowie die Information, ob bzw. wann in der Zeitzone die Sommerzeit gilt. Die Standardzeit wird in der Java-Dokumentation als Greenwich Mean Time (GMT) bezeichnet. Allerdings wurde GMT (schon vor geraumer Zeit) durch die Coordinated Universal Time (Achtung, deren Abkürzung lautet UTC!) abgelöst. Bis auf subtile Unterschiede sind GMT und UTC aber synonym. Details hierzu enthält der korrespondierende englischsprachige Wikipedia-Artikel. Fassen wir zusammen: Es gibt also eine weltweit eindeutige Standardzeit (UTC/GMT). Alle Regionen der Erde werden in bestimmte Zeitzonen unterteilt. Lokale Uhrzeiten ergeben sich aus festgelegten Differenzen zu der Standardzeit

Das folgende Quelltextfragment ermittelt alle Zeitzonen, die der Java Runtime bekannt sind bzw. hinterlegt wurden.

    String[] ids = TimeZone.getAvailableIDs();
    System.out.println("Anzahl: " + ids.length);
    for (String id : ids) {
      TimeZone tz = TimeZone.getTimeZone(id);
      System.out.println("Name: " + tz.getDisplayName());
    }

Wenn Sie den Codeschnipsel in eine main-Methode packen und ausführen, sind Sie vielleicht verwundert. Unter Java 7 wandern mehr als 600 Zeilen über den Schirm, von denen viele gleich sind. Dubletten? Fügen Sie der for-Schleife eine Zeile hinzu, die die Variable id ausgibt.

      System.out.println("id: " + id);

Ausgabe von Zeitzonen

Auf den ersten Blick ist es sicher ungewöhnlich, dass eine Id Städtenamen (Honolulu, Johnston) enthält sowie eine hierarchische Struktur (der Slash trennt sehr oft zwei Elemente) aufweist. Würde man diese Informationen nicht eher in der Klasse TimeZone selbst erwarten? Ein Blick in die Dokumentation ernüchtert. Es gibt keine Methoden, die die Gruppierung oder Strukturierung der vielen Daten erleichtern. Das Parsen und Anzeigen der Id würde ich mir wahrscheinlich sehr genau überlegen.

Das folgende Beispiel greift meine Geschichte von weiter oben auf. Es berechnet, wie spät es in New York ist, wenn die Zeiger der Uhr in Berlin (für München existiert leider kein eigener Eintrag in der Java-Zeitzonentabelle) auf 19:20 stehen. Von wegen, in Bayern gehen die Uhren anders.

Smiley

    String[] ids = TimeZone.getAvailableIDs();
    TimeZone berlin = null;
    TimeZone newYork = null;
    for (String id : ids) {
      String lc = id.toLowerCase();
      if (lc.contains("berlin")) {
        berlin = TimeZone.getTimeZone(id);
      } else if (lc.contains("new_york")) {
        newYork = TimeZone.getTimeZone(id);
      }
    }
    if ((berlin != null) && (newYork != null)) {
      Calendar cal = Calendar.getInstance(berlin);
      cal.set(Calendar.HOUR_OF_DAY, 19);
      cal.set(Calendar.MINUTE, 20);
      System.out.println(cal.get(Calendar.HOUR_OF_DAY) + ":"
          + cal.get(Calendar.MINUTE));
      cal.setTimeZone(newYork);
      System.out.println(cal.get(Calendar.HOUR_OF_DAY) + ":"
          + cal.get(Calendar.MINUTE));
    }

Das programmatische Ermitteln von Zeitzonen auf Basis der Ids ist nicht ganz ungefährlich. Ich würde mich eher auf die Auswahl durch den Benutzer verlassen. Ansonsten sollte das kurze Quelltextfragment keine Fragen aufwerfen. Oder haben Sie welche? Falls ja, schreiben Sie mir.