title: C# 2.0 specific timezone
body: <p>I am working on a legacy application in .NET 2.0 and I want to convert time from local time (which happens to be UTC+1) to the time in Brazil (that is "E. South America Standard Time" as Windows calls it) and back.</p>
  
  <p>I put together this code that I came up with to do the conversion:</p>
  
  <pre><code>using System;
  using System.Collections.Generic;
  using System.Text;
  using System.Runtime.InteropServices;
  using Microsoft.Win32;
  
  namespace timezone
  {
   class Program
   {
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = false)]
    private static extern int SystemTimeToTzSpecificLocalTime(ref
     TIME_ZONE_INFORMATION lpTimeZone, ref SYSTEMTIME lpUniversalTIme, out
     SYSTEMTIME lpLocalTime);
  
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = false)]
    private static extern int TzSpecificLocalTimeToSystemTime(ref
     TIME_ZONE_INFORMATION lpTimeZone, ref SYSTEMTIME lpLocalTime, out SYSTEMTIME
     lpUniversalTIme);
  
    [DllImport("kernel32.dll")]
    private static extern void GetSystemTime(out SYSTEMTIME lpSystemTime);
  
    [StructLayout(LayoutKind.Sequential)]
    private struct SYSTEMTIME
    {
     public ushort wYear;
     public ushort wMonth;
     public ushort wDayOfWeek;
     public ushort wDay;
     public ushort wHour;
     public ushort wMinute;
     public ushort wSecond;
     public ushort wMilliseconds;
    }
  
    //Registry time zone format. See KB article Q115231
    [StructLayout(LayoutKind.Sequential)]
    private struct REG_TIME_ZONE_INFORMATION
    {
     public int Bias;
     public int StandardBias;
     public int DaylightBias;
     public SYSTEMTIME StandardDate;
     public SYSTEMTIME DaylightDate;
    }
  
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private struct TIME_ZONE_INFORMATION
    {
     public int Bias;
     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
     public string StandardName;
     public SYSTEMTIME StandardDate;
     public int StandardBias;
     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
     public string DaylightName;
     public SYSTEMTIME DaylightDate;
     public int DaylightBias;
    }
  
    private static List&lt;TIME_ZONE_INFORMATION&gt; GetTimeZones()
    {
     List&lt;TIME_ZONE_INFORMATION&gt; list = new List&lt;TIME_ZONE_INFORMATION&gt;();
     RegistryKey key =
     Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones");
     if (key == null)
      return list;
  
     string[] subKeyNames = key.GetSubKeyNames();
     foreach (string subKeyName in subKeyNames)
     {
      RegistryKey subKey = key.OpenSubKey(subKeyName);
      if (subKey != null)
      {
       object value = subKey.GetValue("TZI");
       if (value != null)
       {
        int length =
        Marshal.SizeOf(typeof(REG_TIME_ZONE_INFORMATION));
        IntPtr p = Marshal.AllocHGlobal(length);
        Marshal.Copy((byte[])value, 0, p, length);
        REG_TIME_ZONE_INFORMATION rtzi =
        (REG_TIME_ZONE_INFORMATION)Marshal.PtrToStructure(p,
        typeof(REG_TIME_ZONE_INFORMATION));
        Marshal.FreeHGlobal(p);
  
        TIME_ZONE_INFORMATION tzi = new TIME_ZONE_INFORMATION();
        tzi.Bias = rtzi.Bias;
        tzi.DaylightBias = rtzi.DaylightBias;
        tzi.StandardBias = rtzi.StandardBias;
        tzi.DaylightDate = rtzi.DaylightDate;
        tzi.StandardDate = rtzi.StandardDate;
        tzi.DaylightName = (string)subKey.GetValue("Dlt", "");
        tzi.StandardName = (string)subKey.GetValue("Std", "");
        list.Add(tzi);
       }
       subKey.Close();
      }
     }
     key.Close();
     return list;
    }
  
    static void Main(string[] args)
    {
     foreach (TIME_ZONE_INFORMATION tzi in GetTimeZones())
     {
      if ("E. South America Standard Time" == tzi.StandardName)
      {
       Console.WriteLine("local time: " + DateTime.Now);
       Console.WriteLine("name: {0} st bias:{1} daylight date:{2}-{3}-{4}, bias:{5}", tzi.StandardName, tzi.DaylightBias, tzi.DaylightDate.wDay, tzi.DaylightDate.wMonth, tzi.DaylightDate.wYear, tzi.Bias);
       TIME_ZONE_INFORMATION tzi2 = tzi; //local copy so that i can pass it as a ref
       SYSTEMTIME braziltime = new SYSTEMTIME();
       SYSTEMTIME localtime = new SYSTEMTIME();
       GetSystemTime(out localtime);
       SystemTimeToTzSpecificLocalTime(ref tzi2, ref localtime, out braziltime);
       Console.WriteLine("{0}:{1}", braziltime.wHour, braziltime.wMinute);
       Console.WriteLine("{0}-{1}-{2} {3}:{4}:{5}", braziltime.wYear, braziltime.wMonth, braziltime.wDay, braziltime.wHour, braziltime.wMinute, braziltime.wSecond);
       DateTime dt = DateTime.Now;
       braziltime.wYear = (ushort)dt.Year;
       braziltime.wMonth = (ushort)dt.Month;
       braziltime.wDay = (ushort)dt.Day;
       braziltime.wHour = (ushort)(dt.Hour - 3); //today the timezone difference is 3 hours
       braziltime.wMinute = (ushort)dt.Minute;
       braziltime.wSecond = (ushort)dt.Second;
       TzSpecificLocalTimeToSystemTime(ref tzi2, ref braziltime, out localtime);
       Console.WriteLine("{0}-{1}-{2} {3}:{4}:{5}", localtime.wYear, localtime.wMonth, localtime.wDay, localtime.wHour, localtime.wMinute, localtime.wSecond);
       break;
      }
     }
     Console.ReadLine();
    }
   }
  }
  </code></pre>
  
  <p>but I get this output:</p>
  
  <pre><code>local time: 11/22/2010 11:55:15 AM
  name: E. South America Standard Time st bias:-60 daylight date:3-10-0, bias:180 8:55
  2010-11-22 8:55:15
  2010-11-22 10:55:15
  </code></pre>
  
  <p>So I take local time convert it to brazilian time and back and get an hour less. 
  Any ideas what's wrong?</p>