C# 2.0 specific timezone

可紊 提交于 2019-12-11 06:56:29

问题


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.

I put together this code that I came up with to do the conversion:

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<TIME_ZONE_INFORMATION> GetTimeZones()
  {
   List<TIME_ZONE_INFORMATION> list = new List<TIME_ZONE_INFORMATION>();
   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();
  }
 }
}

but I get this output:

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

So I take local time convert it to brazilian time and back and get an hour less. Any ideas what's wrong?


回答1:


I think you are expecting the first line of your output to match the last line. That is not happening because the first line writes the localtime. You then call GetSystemTime and convert that value to Brazil time and back again. GetSystemTime returns UTC so the value you get back and then output on the last line should be UTC too. In other words you are not comparing like with like.

If you output the value of localtime immediately after the call to GetSystemTime you should see that match the value output after the conversion.

If you want to convert from your local time to Brazil time then you probably need to convert your local time to UTC and then convert UTC to Brazil time using the appropriate time zone info for each step.




回答2:


Remember than DateTime is just a struct in order to store datetime.

You should use everywhere in your app UTC, and just use Locale for output. In order to be clean, i prefer use most of the time utc and doing my convert from it.




回答3:


If it was 3.5...

using System;

// ReSharper disable once CheckNamespace
public static class BrazilTime
{
    public static DateTime Now
    {
        get
        {
            return TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, TimeZoneInfo.FindSystemTimeZoneById("E. South America Standard Time"));
        }
    }
}


来源:https://stackoverflow.com/questions/4244699/c-sharp-2-0-specific-timezone

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!