TimeZoneInformation.cs

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Xml.Linq;
using System.Linq;

namespace VB.Mobile.TimeZones
{
    /// <summary>
    /// Date: 02/18/2008
    /// Created By: Vladimir Burmistrovich
    /// Used to convert to/from utc times and local times and respect DST.
    /// Can also covert between two local time zones.
    /// Information about a time zone containing methods to convert local times to and from UTC and
    /// between local time zones.
    /// 
    /// In order to use it, you must have an XML file with all the time zone information that was created
    /// with my export utility on a Windows PC (as Windows Mobile does not seem to contain the time zone
    /// information in the registry like regular Windows does).
    /// 
    /// Thanks goes out to http://www.codeproject.com/KB/dotnet/WorldClock.aspx
    /// and http://staceyw.spaces.live.com/blog/cns!F4A38E96E598161E!931.entry
    /// for their work on the Windows version of this. I would have no idea where to
    /// even start without their code.
    /// </summary>
    [Serializable]
    public class TimeZoneInformation
    {
        #region Fields
        private TZI tzi; // Current time zone information.
        private string displayName; // Current time zone display name.
        private string standardName; // Current time zone standard name (non-DST).
        private string daylightName; // Current time zone daylight name (DST).
        private static readonly List<TimeZoneInformation> timeZones; // static list of all time zones on machine.

        private static string xmlFile = "timezones.xml"; // the XML file that was created with my exporter.
                                                         // Make sure it's in the root folder of the PPC
        #endregion

        #region Constructors
        private TimeZoneInformation()
        {
        }

        static TimeZoneInformation()
        {
            timeZones = GetListFromXml();
        }
        #endregion

        #region Public Properties
        /// <summary>
        /// Gets list of all time zones defined on the current computer system.
        /// </summary>
        public static List<TimeZoneInformation> TimeZones
        {
            get
            {
                // Return a copy of the list.
                List<TimeZoneInformation> nList = new List<TimeZoneInformation>(timeZones);
                return nList;
            }
        }

        /// <summary>
        /// Gets an string array of standard time zone names supported on the current system.
        /// </summary>
        public static string[] TimeZoneNames
        {
            get
            {
                List<String> list = new List<String>();
                foreach (TimeZoneInformation tzi in timeZones)
                {
                    list.Add(tzi.StandardName);
                }
                return list.ToArray();
            }
        }

        /// <summary>
        /// Gets the time zone information of the current computer system.
        /// </summary>
        public static TimeZoneInformation CurrentTimeZone
        {
            get
            {
                string tzn = TimeZone.CurrentTimeZone.StandardName;
                TimeZoneInformation tzi = TimeZoneInformation.GetTimeZone(tzn);
                return tzi;
            }
        }

        /// <summary>
        /// The time zone's name during 'standard' time (i.e. not daylight savings).
        /// </summary>
        public string StandardName
        {
            get
            {
                return standardName;
            }
        }

        /// <summary>
        /// The time zone's name during daylight savings time (DST).
        /// </summary>
        public string DaylightName
        {
            get
            {
                return daylightName;
            }
        }

        /// <summary>
        /// The time zone's display name (e.g. "(GMT-05:00) Eastern Time (US and Canada)").
        /// </summary>
        public string DisplayName
        {
            get
            {
                return displayName;
            }
        }

        /// <summary>
        /// Gets the standard offset from UTC as a TimeSpan.
        /// </summary>
        public TimeSpan StandardOffset
        {
            get
            {
                return TimeSpan.FromMinutes(StandardBias);
            }
        }

        /// <summary>
        /// Gets the daylight offset from UTC as a TimeSpan.
        /// </summary>
        public TimeSpan DaylightOffset
        {
            get
            {
                return TimeSpan.FromMinutes(DaylightBias);
            }
        }

        /// <summary>
        /// Gets the difference, in minutes, between UTC and local time.
        /// UTC = local time + bias.
        /// </summary>
        public int StandardBias
        {
            get
            {
                return -(tzi.bias + tzi.standardBias);
            }
        }

        /// <summary>
        /// Gets the difference, in minutes, between UTC and local time (in daylight savings time).
        /// UTC = local time + bias.
        /// </summary>
        public int DaylightBias
        {
            get
            {
                return -(tzi.bias + tzi.daylightBias);
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Returns display name of this time zone instance.
        /// </summary>
        /// <returns>Time zone display name.</returns>
        public override string ToString()
        {
            return this.displayName;
        }

        /// <summary>
        /// Returns a TimeZoneInformation instance for the time zone with supplied standard name.
        /// </summary>
        /// <param name="standardTimeZoneName">Standard name of the time zone.</param>
        /// <returns>TimeZoneInformation instance.</returns>
        /// <exception cref="ArgumentException">Thrown if name not found.</exception>
        public static TimeZoneInformation GetTimeZone(string standardTimeZoneName)
        {
            if (standardTimeZoneName == null)
                standardTimeZoneName = ".";
            if (standardTimeZoneName == ".")
                standardTimeZoneName = TimeZone.CurrentTimeZone.StandardName;

            foreach (TimeZoneInformation tzi in TimeZoneInformation.TimeZones)
            {
                if (tzi.StandardName.Equals(standardTimeZoneName, StringComparison.OrdinalIgnoreCase))
                    return tzi;
            }
            throw new ArgumentException("standardTimeZoneName not found.");
        }

        /// <summary>
        /// Converts the value of the utc time to a local time in this time zone.
        /// </summary>
        /// <param name="utc">The UTC time to convert.</param>
        /// <returns>The local time.</returns>
        public DateTime ToLocalTime(DateTime utc)
        {
            // Convert to FILETIME
            FILETIME ftUTC = DateTimeToFileTime(utc);

            // Save current TIME_ZONE_INFORMATION
            TIME_ZONE_INFORMATION tziCurrent;
            NativeMethods.GetTimeZoneInformation(out tziCurrent);

            // Set up the TIME_ZONE_INFORMATION and set the current timezone to be it
            TIME_ZONE_INFORMATION tziNative = TziNative();
            NativeMethods.SetTimeZoneInformation(ref tziNative);

            // Convert FILETIME to the local FILETIME
            FILETIME ftLocal;
            NativeMethods.FileTimeToLocalFileTime(ref ftUTC, out ftLocal);

            // Set time zone back to saved one
            NativeMethods.SetTimeZoneInformation(ref tziCurrent);

            // Convert back to DateTime
            return FileTimeToDateTime(ftLocal);
        }

        /// <summary>
        /// Converts the value of the utc time to local time in supplied time zone.
        /// </summary>
        /// <param name="utc">The time to convert.</param>
        /// <param name="targetTimeZoneName">The standard name of the time zone.</param>
        /// <returns>The local time.</returns>
        /// <exception cref="ArgumentException">Thrown if time zone not found.</exception>
        public static DateTime ToLocalTime(DateTime utc, string targetTimeZoneName)
        {
            TimeZoneInformation tzi = TimeZoneInformation.GetTimeZone(targetTimeZoneName);
            return tzi.ToLocalTime(utc);
        }

        /// <summary>
        /// Converts a localTime from a source time zone to a target time zone, adjusting for DST as needed.
        /// The localTime must be a local time in the sourceTimeZoneName time zone.
        /// </summary>
        /// <param name="sourceTimeZoneName">Time zone name which represents localTime.</param>
        /// <param name="localTime">The source local time.</param>
        /// <param name="targetTimeZoneName">The time zone name which to convert the localTime.</param>
        /// <returns>The local time for targetTimeZoneName.</returns>
        public static DateTime ToLocalTime(string sourceTimeZoneName, DateTime localTime, string targetTimeZoneName)
        {
            DateTime utc = TimeZoneInformation.ToUniversalTime(sourceTimeZoneName, localTime);
            DateTime lt = TimeZoneInformation.ToLocalTime(utc, targetTimeZoneName);
            return lt;
        }

        /// <summary>
        /// Converts the value of the local time to UTC time.
        /// Note that there may be different possible interpretations at the daylight time boundaries.
        /// </summary>
        /// <param name="local">The local time to convert.</param>
        /// <returns>The UTC DateTime.</returns>
        public DateTime ToUniversalTime(DateTime local)
        {
            // Convert to FILETIME
            FILETIME ftLocal = DateTimeToFileTime(local);

            // Save current TIME_ZONE_INFORMATION
            TIME_ZONE_INFORMATION tziCurrent;
            NativeMethods.GetTimeZoneInformation(out tziCurrent);

            // Set up the TIME_ZONE_INFORMATION
            TIME_ZONE_INFORMATION tziNative = TziNative();
            NativeMethods.SetTimeZoneInformation(ref tziNative);

            // Convert local FILETIME to UTC FILETIME
            FILETIME ftUTC;
            NativeMethods.LocalFileTimeToFileTime(ref ftLocal, out ftUTC);

            // Set time zone back to saved one
            NativeMethods.SetTimeZoneInformation(ref tziCurrent);

            // Convert back to DateTime
            return FileTimeToDateTime(ftUTC);
        }

        /// <summary>
        /// Converts a local time in specified time zone to UTC time.
        /// </summary>
        /// <param name="standardTimeZoneName">The standard time zone name.</param>
        /// <param name="local">The local time to convert.</param>
        /// <returns>The UTC time.</returns>
        /// <exception cref="ArgumentException">Thrown if time zone name not found.</exception>
        public static DateTime ToUniversalTime(string standardTimeZoneName, DateTime local)
        {
            TimeZoneInformation tzi = TimeZoneInformation.GetTimeZone(standardTimeZoneName);
            return tzi.ToUniversalTime(local);
        }
        #endregion

        #region Private Methods

        private static List<TimeZoneInformation> GetListFromXml()
        {
            List<TimeZoneInformation> tzs = new List<TimeZoneInformation>();

            XDocument xmldoc = XDocument.Load(xmlFile);
            var timezones = from tz in xmldoc.Descendants("TimeZone") select tz;
            foreach (var timezone in timezones)
            {
                string display = (from tz in timezone.Descendants("Display") select tz.Value).Single();
                string dlt = (from tz in timezone.Descendants("Dlt") select tz.Value).Single();
                string std = (from tz in timezone.Descendants("Std") select tz.Value).Single();
                var tzibytescol = from tz in timezone.Descendants("TziByte") select tz;
                List<byte> tziByteList = new List<byte>();

                foreach (var byteVar in tzibytescol)
                {
                    string valStr = byteVar.Value;
                    int valInt = int.Parse(valStr);
                    byte valByte = (byte)valInt;
                    tziByteList.Add(valByte);
                }

                byte[] tzibytes = tziByteList.ToArray();

                TimeZoneInformation tzi = new TimeZoneInformation();
                tzi.displayName = display;
                tzi.standardName = std;
                tzi.daylightName = dlt;
                tzi.InitTzi(tzibytes);
                tzs.Add(tzi);
            }

            var orderedtzs = from tz in tzs orderby tz.StandardBias, tz.DisplayName select tz;
            return orderedtzs.ToList();
        }

        /// <summary>
        /// Converts a specified DateTime to a FILETIME structure
        /// </summary>
        /// <param name="dt">The DateTime to convert</param>
        /// <returns>A FILETIME structure that represents the input DateTime</returns>
        private static FILETIME DateTimeToFileTime(DateTime dt)
        {
            FILETIME ft = new FILETIME();
            ft.dwHighDateTime = (int)(dt.Ticks >> 32);
            ft.dwLowDateTime = (int)(dt.Ticks & 0xFFFFFFFFL);
            return ft;
        }

        /// <summary>
        /// Converts a specified FILETIME structure to a DateTime object
        /// </summary>
        /// <param name="ft">The FILETIME structure to convert</param>
        /// <returns>A DateTime object that represents the input FILETIME structure</returns>
        private static DateTime FileTimeToDateTime(FILETIME ft)
        {
            DateTime dt = new DateTime((((long)ft.dwHighDateTime) << 32) | (uint)ft.dwLowDateTime);
            return dt;
        }
        
        /// <summary>
        /// Gets a TIME_ZONE_INFORMATION structure for the current timezone
        /// </summary>
        /// <returns>The TIME_ZONE_INFORMATION structure for the current timezone</returns>
        private TIME_ZONE_INFORMATION TziNative()
        {
            TIME_ZONE_INFORMATION tziNative = new TIME_ZONE_INFORMATION();
            tziNative.Bias = tzi.bias;
            tziNative.StandardDate = tzi.standardDate;
            tziNative.StandardBias = tzi.standardBias;
            tziNative.DaylightDate = tzi.daylightDate;
            tziNative.DaylightBias = tzi.daylightBias;
            return tziNative;
        }

        /// <summary>
        /// The standard Windows SYSTEMTIME structure.
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        private struct SYSTEMTIME
        {
            public UInt16 wYear;
            public UInt16 wMonth;
            public UInt16 wDayOfWeek;
            public UInt16 wDay;
            public UInt16 wHour;
            public UInt16 wMinute;
            public UInt16 wSecond;
            public UInt16 wMilliseconds;
        }

        /// <summary>
        /// The standard Windows FILETIME structure.
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        private struct FILETIME
        {
            public int dwLowDateTime;
            public int dwHighDateTime;
        }

        /// <summary>
        /// The layout of the Tzi value in the Windows registry.
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        private struct TZI
        {
            public int bias;
            public int standardBias;
            public int daylightBias;
            public SYSTEMTIME standardDate;
            public SYSTEMTIME daylightDate;
        }

        /// <summary>
        /// The standard Win32 TIME_ZONE_INFORMATION structure.
        /// </summary>
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        private struct TIME_ZONE_INFORMATION
        {
            [MarshalAs(UnmanagedType.I4)]
            public Int32 Bias;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
            public string StandardName;
            public SYSTEMTIME StandardDate;
            [MarshalAs(UnmanagedType.I4)]
            public Int32 StandardBias;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
            public string DaylightName;
            public SYSTEMTIME DaylightDate;
            [MarshalAs(UnmanagedType.I4)]
            public Int32 DaylightBias;
        }

        /// <summary>
        /// A container for P/Invoke declarations.
        /// </summary>
        private struct NativeMethods
        {
            // These are contained in kernel32.dll on Windows, but in coredll.dll on Windows Mobile
            private const string KERNEL32 = "coredll.dll";

            [DllImport(KERNEL32)]
            public static extern uint GetTimeZoneInformation(out TIME_ZONE_INFORMATION lpTimeZoneInformation);

            [DllImport(KERNEL32)]
            public static extern bool SetTimeZoneInformation([In] ref TIME_ZONE_INFORMATION lpTimeZoneInformation);

            [DllImport(KERNEL32)]
            public static extern bool FileTimeToLocalFileTime([In] ref FILETIME lpFileTime, out FILETIME lpLocalFileTime);

            [DllImport(KERNEL32)]
            public static extern bool LocalFileTimeToFileTime([In] ref FILETIME lpLocalFileTime, out FILETIME lpFileTime);
        }

        /// <summary>
        /// Initialise the m_tzi member.
        /// </summary>
        /// <param name="info">The Tzi data from the registry.</param>
        private void InitTzi(byte[] info)
        {
            if (info.Length != Marshal.SizeOf(tzi))
                throw new ArgumentException("Information size is incorrect", "info");

            // Could have sworn there's a Marshal operation to pack bytes into
            // a structure, but I can't see it. Do it manually.
            GCHandle h = GCHandle.Alloc(info, GCHandleType.Pinned);
            try
            {
                tzi = (TZI)Marshal.PtrToStructure(h.AddrOfPinnedObject(), typeof(TZI));
            }
            finally
            {
                h.Free();
            }
        }
        #endregion
    }
}


Recent comments

Comment RSS

Disclaimer

The opinions expressed herein are my own personal opinions. Any code is here does not come with any warraties whatsoever. Use it at your own risk.

© Copyright Vladimir Burmistrovich 2008

Month List

Calendar

<<  January 2009  >>
MoTuWeThFrSaSu
2930311234
567891011
12131415161718
19202122232425
2627282930311
2345678

View posts in large calendar

Sign in