Dealing with dates and times across geographies is an ongoing challenge for many people.  One of the aspects of that challenge is that in .NET, it is not easily possible to format a time for display, using an arbitrary timezone.  It is possible of course, but it requires too much work. 

Now comes word of the PublicDomain project on CodePlex.  This project is completely public domain, and includes a variety of classes that address different topics.  One of those topics is the issue of time zones.

The TzTimeZone class included in that project is a good one. It lets your app instantiate any arbitrary timezone (US Pacific, or the timezone in Paris, or the timezone in Shanghai) and then use that thing.  One use case is to format a time value (DateTime) into a format which makes sense in the specified timezone.  So if I have a time of 12noon in New York, I can format that same DateTime value for the timezone in Shanghai.  This addresses a narrow but very common use case in distributed applications.   This is commonly needed in heterogeneous distributed apps, for example where a Java-based service is running in NYC and is connected to a .NET service that is running in Shanghai.  This class makes it really simple. 

Just by reading the description of what the class does, you might be tempted to conclude that this is a very simple class.  Actually it is pretty simple in concept but in execution it gets very complex, because there are differences with timezone shifts and "Daylight savings time" type issues, both currently and also going back in time.  The DST shifts for various timezones did not happen at the same time in 2007 as they did in 2004.  All of this intellligence is encapsulated into the TzTimeZone class. 

Here's a quick sample that illustrates the new capability provided by the TzTimeZone class (I've also attached this source code to this post):  

    1 // DifferentTimeZones.cs

    2 //

    3 // This sample illustrates the use of the TzTimeZone class that is included in the

    4 // PublicDomain project on Codeplex: http://www.codeplex.com/publicdomain.

    5 //

    6 // The System.TimeZone as included in .NET 2.0 does not permit an

    7 // application to instantiate a TimeZone object given a shorthand name of

    8 // the timezone, or a UTC offset.  Instead, the System.TimeZone class

    9 // "knows" about the local timezone but doesn't do much else.  This makes

   10 // it difficult to take an arbitrary time (say, 3:13pm, October 4th 2006,

   11 // in Los Angeles) and format it for display in an arbitrary other timezone

   12 // (say, Paris).

   13 //

   14 // The TzTimeZone class provides a model for a number of different

   15 // timezones, including their offsets from UTC and the rules they apply for

   16 // daylight savings time.

   17 //

   18 // With this new set of classes, it is possible to instantiate a timezone

   19 // from a well-known set of named instances.  It is also easy to format a

   20 // time value w.r.t. any arbitrary (named) timezone. In particular, it is

   21 // easy to take a time like (3:13pm October 4th 2006, in Los Angeles) and

   22 // format it for display in Paris.

   23 //

   24 // Mon, 08 Oct 2007  13:37

   25 

   26 

   27 using System;

   28 using PublicDomain; // for TzTmeZone, TzDateTime, etc

   29 

   30 

   31 public class ShowDifferentTimeZones

   32 {

   33 

   34   public static DateTime GenerateTime()

   35   {

   36     System.Random rnd= new System.Random();

   37     System.DateTime value;

   38     int[] data= new int[] {

   39       2005+rnd.Next(4), // year

   40       rnd.Next(12)+1,  // month

   41       rnd.Next(28)+1,  // dayOfMonth

   42       rnd.Next(24),  //  hourOfDay

   43       rnd.Next(60),  // minute

   44       rnd.Next(60)   // second

   45     };

   46 

   47     value= new System.DateTime(data[0],

   48                    data[1],

   49                    data[2],

   50                    data[3],

   51                    data[4],

   52                    data[5]);

   53     return value;

   54   } 

   55 

   56 

   57   int _count= 0;

   58   System.DateTime _time;

   59 

   60   public void SetTime(System.DateTime time)

   61   {

   62     _time= time.ToUniversalTime();

   63     _count = 0;

   64   }

   65 

   66   public void ShowZones()

   67   {

   68     // Local timezone

   69     ShowOneZone(TzTimeZone.CurrentTimeZone);

   70 

   71     // other cities

   72     ShowOneZone(TzTimeZone.GetTimeZone(TzConstants.TimezoneAmericaNewYork));

   73     ShowOneZone(TzTimeZone.GetTimeZone(TzConstants.TimezoneAmericaLosAngeles));

   74     ShowOneZone(TzTimeZone.GetTimeZone(TzConstants.TimezoneAmericaArgentinaBuenosAires));

   75     ShowOneZone(TzTimeZone.GetTimeZone(TzConstants.TimezoneEuropeParis));

   76     ShowOneZone(TzTimeZone.GetTimeZone(TzConstants.TimezoneAsiaTokyo));

   77     ShowOneZone(TzTimeZone.GetTimeZone(TzConstants.TimezoneAsiaShanghai));

   78   }

   79 

   80 

   81   private void ShowOneZone(TzTimeZone zone)

   82   {

   83     string formatString= "{0,-32} {1,-8} {2,-10}   {3,10}  {4,-32}";

   84     if (_count == 0)

   85     {

   86       Console.WriteLine();

   87       Console.WriteLine(formatString, "standard name",

   88             "Is DST?",

   89             "short name",

   90             "UTC offset",

   91             "the time");

   92       Console.WriteLine("--------------------------------------------------------------------------------------------");

   93       _count++;

   94     }

   95 

   96     //TzDateTime dt1 =  TzDateTime.Now(zone1);

   97 

   98     // Note: be careful using the TzDateTime constructor!

   99     // --------------------------------------------------

  100     // the following form says:  create a new TzDateTime, and override any timezone

  101     // information and set it to the given. If I am in Chicago and I pass in a time of 8:45am

  102     // and specify the zone as "paris" , then I will get a TzDateTime of 8:45am in Paris. 

  103     //TzDateTime dt1 =  new TzDateTime(_time, zone);

  104 

  105     // These two statements say - give me a new TzDateTime, and set the preferred "attached"

  106     // timezone for this datetime be as specified.  If I am in Chicago and pass in a time of 8:45am,

  107     // and a zone of "paris", then I will get a TzDateTime that is 5:45pm in Paris. 

  108     TzDateTime dt1 =  new TzDateTime(_time);

  109     dt1.TimeZone= zone;

  110 

  111     string TimeStringRelativeToSpecifiedZone=

  112       dt1.DateTimeLocal.ToString("G") + " " + zone.GetAbbreviation();

  113 

  114     Console.WriteLine(formatString,

  115               zone.ToString(),

  116               zone.IsDaylightSavingTime(dt1.DateTimeUtc),

  117               zone.GetAbbreviation(),

  118               zone.GetUtcOffset(dt1.DateTimeUtc),

  119               TimeStringRelativeToSpecifiedZone);

  120 

  121     // key                                             

  122   }

  123 

  124 

  125 

  126 

  127   public void Run()

  128   {

  129     Console.WriteLine("\nHello from the TimeZones demonstration app.");

  130 

  131     Console.WriteLine("\nIn the simplest case, we want to display a time, "+

  132               "or more accurately, format a time value for display,"+

  133               "with respect to a given timezone. Example:");

  134 

  135     System.DateTime theTime = System.DateTime.Now;

  136     TzDateTime dt1 =  new TzDateTime(theTime, TzTimeZone.CurrentTimeZone);

  137 

  138     TzDateTime dt2 =  new TzDateTime(theTime.ToUniversalTime());

  139     // here we explicitly apply a timezone

  140     dt2.TimeZone= TzTimeZone.ZoneUsEastern;

  141 

  142     Console.WriteLine("In '{0}' the time is: {1}\n"+

  143               "This timezone is also known as '{2}' and the time might be written as {3}\n"+

  144               "In '{4}' the same time is written as: {5}",

  145               TimeZone.CurrentTimeZone.StandardName,

  146               theTime.ToString("G"), // + " " + dt1.TimeZone.GetAbbreviation(),

  147               dt1.TimeZone.ToString(),

  148               dt1.DateTimeLocal.ToString("G") + " " + dt1.TimeZone.GetAbbreviation(),

  149               dt2.TimeZone.ToString(),

  150               dt2.DateTimeLocal.ToString("G") + " " + dt2.TimeZone.GetAbbreviation());

  151 

  152 

  153 

  154     Console.WriteLine("\nNow a more general illustration.  In each table, the times are all the same:");

  155 

  156 

  157     SetTime(System.DateTime.Now);

  158     ShowZones();

  159 

  160     SetTime(GenerateTime());

  161     ShowZones();

  162   }

  163 

  164 

  165 

  166 

  167 

  168   static void Main(String[] args)

  169   {

  170     try

  171     {

  172       ShowDifferentTimeZones me= new ShowDifferentTimeZones();

  173       me.Run();

  174     }

  175     catch (Exception ex1)

  176     {

  177       Console.WriteLine("Exception:  {0}", ex1);

  178     }

  179   }

  180 }

  181