Home All Groups Group Topic Archive Search About

Dealing with Timezones

Author
25 Aug 2006 2:38 AM
Chris Mullins
In .Net 2.0, I can get the standard name of the current timezone by:
System.TimeZone.CurrentTimeZone.StandardName

The problem I'm having is that I have datetimes that are serialized as a UTC
time, with a named timezone. I keep looking for a way to say:

System.TimeZone tz = new TimeZone("PST");
tz.ConvertToLocalTime(utcTime);

Problem is there doesn't seem to way to do this. Anyone got any suggesting
for dealing with TimeZones?



--
Chris Mullins  MCSD.Net, MCPD Enterprise
http://www.coversant.net/blogs/cmullins

Author
25 Aug 2006 5:56 AM
Pål
Does this solve your porblem:

System.TimeZone tz = new TimeZone("PST");
tz.ToLocalTime(utcTime);
Author
25 Aug 2006 2:31 PM
Carl Daniel [VC++ MVP]
Pål wrote:
> Does this solve your porblem:
>
> System.TimeZone tz = new TimeZone("PST");
> tz.ToLocalTime(utcTime);

This won't work:

1. There's no TimeZone constructor that takes a timezone identifier.
2. System.TimeZone is abstract.

Interestingly, in the java support libraries for J#, there's another
TimeZone class which does have a constructor taking a time zone identifier.
It doesn't provide a toLocalTime() function, but it does expose the
timezone's offset from GMT as a property.

-cd
Author
25 Aug 2006 5:23 PM
Chris Mullins
"Pål" <pal.***@home.no> wrote:
> Does this solve your porblem:
>
> System.TimeZone tz = new TimeZone("PST");
> tz.ToLocalTime(utcTime);

That sure does solve my problem.

.... only problem is that it doesn't work. TimeZone is an abstract class.

--
Chris Mullins, MCSC.NET, MCPD:Enterprise
http://www.coversant.net/blogs/cmullins
Author
25 Aug 2006 4:27 PM
William Stacey [MVP]
Never could figure out why they did not include such a needed thing. Until
then, here is a class I did that will convert between timezones, etc.
It uses standard names (long) because that is how windows indexes them.  If
you can figure out the "standard" 3 char abbrev for each timezone and post
here or send to me, I can update to also take abbreviations.  But the
standard names are pretty easy to work with also as shown below.

Sample
--------------------------
        private void button4_Click(object sender, EventArgs e)
        {
            // List all supported timezones (w/ standard names).
            foreach (TimeZoneInformation tz in
TimeZoneInformation.TimeZones)
            {
                Console.WriteLine(tz.ToString());
            }
            Console.WriteLine();

            // Convert a utc to a local time in the supplied time zone
respecting daylight savings of target timezone.
            Console.WriteLine("EST:{0}", DateTime.Now);
            DateTime pst = TimeZoneInformation.ToLocalTime(DateTime.UtcNow,
"pacific standard time");
            Console.WriteLine("PST:{0}", pst.ToString());

            Console.WriteLine("\nCurrent TimeZone:");
            TimeZoneInformation tzi = TimeZoneInformation.CurrentTimeZone;
            Console.WriteLine("DisName:{0}\nStdName:{1}\nStdOffset:{2}\nDayLightName:{3}",
tzi.DisplayName, tzi.StandardName, tzi.StandardOffset, tzi.DaylightName);

            // Simply convert a local time to another local time in the
target timezone.
            DateTime dt = TimeZoneInformation.ToLocalTime("eastern standard
time", DateTime.Now, "central standard time");
            Console.WriteLine("EST:{0}", DateTime.Now);
            Console.WriteLine("CST:{0}", dt.ToString());
        }

The Class
--------------------------
    /// <summary>
    /// Date: 12/4/2005
    /// Created By: William Stacey, MVP
    /// 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.
    /// </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; // List
of all time zones on machine.
        #endregion

        #region Constructors
        private TimeZoneInformation()
        {
        }

        static TimeZoneInformation()
        {
            timeZones = new List<TimeZoneInformation>();

            using (RegistryKey key =
Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows
NT\CurrentVersion\Time Zones"))
            {
                string[] zoneNames = key.GetSubKeyNames();

                foreach (string zoneName in zoneNames)
                {
                    using (RegistryKey subKey = key.OpenSubKey(zoneName))
                    {
                        TimeZoneInformation tzi = new TimeZoneInformation();
                        tzi.displayName =
(string)subKey.GetValue("Display");
                        tzi.standardName = (string)subKey.GetValue("Std");
                        tzi.daylightName = (string)subKey.GetValue("Dlt");
                        tzi.InitTzi((byte[])subKey.GetValue("Tzi"));
                        timeZones.Add(tzi);
                    }
                }
            }
        }
        #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 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 standard name of this time zone instance.
        /// </summary>
        /// <returns>Time zone standard name.</returns>
        public override string ToString()
        {
            return this.standardName;
        }

        /// <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 SYSTEMTIME
            SYSTEMTIME stUTC = DateTimeToSystemTime(utc);

            // Set up the TIME_ZONE_INFORMATION
            TIME_ZONE_INFORMATION tziNative = TziNative();
            SYSTEMTIME stLocal;
            NativeMethods.SystemTimeToTzSpecificLocalTime(ref tziNative, ref
stUTC, out stLocal);

            // Convert back to DateTime
            return SystemTimeToDateTime(ref stLocal);
        }

        /// <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>
        /// <exception cref="NotSupportedException">Thrown if the method
failed due to missing platform support.</exception>
        public DateTime ToUniversalTime(DateTime local)
        {
            SYSTEMTIME stLocal = DateTimeToSystemTime(local);
            TIME_ZONE_INFORMATION tziNative = TziNative();
            SYSTEMTIME stUTC;

            try
            {
                NativeMethods.TzSpecificLocalTimeToSystemTime(ref tziNative,
ref stLocal, out stUTC);
                return SystemTimeToDateTime(ref stUTC);
            }
            catch (EntryPointNotFoundException e)
            {
                throw new NotSupportedException("This method is not
supported on this operating system", e);
            }
        }

        /// <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>
        /// <exception cref="NotSupportedException">Thrown if the method
failed due to missing platform support.</exception>
        public static DateTime ToUniversalTime(string standardTimeZoneName,
DateTime local)
        {
            TimeZoneInformation tzi =
TimeZoneInformation.GetTimeZone(standardTimeZoneName);
            return tzi.ToUniversalTime(local);
        }
        #endregion

        #region Private Methods
        private static SYSTEMTIME DateTimeToSystemTime(DateTime dt)
        {
            SYSTEMTIME st;
            FILETIME ft = new FILETIME();
            ft.dwHighDateTime = (int)(dt.Ticks >> 32);
            ft.dwLowDateTime = (int)(dt.Ticks & 0xFFFFFFFFL);
            NativeMethods.FileTimeToSystemTime(ref ft, out st);
            return st;
        }

        private static DateTime SystemTimeToDateTime(ref SYSTEMTIME st)
        {
            FILETIME ft = new FILETIME();
            NativeMethods.SystemTimeToFileTime(ref st, out ft);
            DateTime dt = new DateTime((((long)ft.dwHighDateTime) << 32) |
(uint)ft.dwLowDateTime);
            return dt;
        }

        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;
        }

        // FILETIME is already declared in System.Runtime.InteropServices.

        /// <summary>
        /// The layout of the Tzi value in the 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.
        /// Thanks to www.pinvoke.net.
        /// </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
        {
            private const string KERNEL32 = "kernel32.dll";

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

            [DllImport(KERNEL32)]
            public static extern bool SystemTimeToTzSpecificLocalTime(
                [In] ref TIME_ZONE_INFORMATION lpTimeZone,
                [In] ref SYSTEMTIME lpUniversalTime,
                out SYSTEMTIME lpLocalTime);

            [DllImport(KERNEL32)]
            public static extern bool SystemTimeToFileTime(
                [In] ref SYSTEMTIME lpSystemTime,
                out FILETIME lpFileTime);

            [DllImport(KERNEL32)]
            public static extern bool FileTimeToSystemTime(
                [In] ref FILETIME lpFileTime,
                out SYSTEMTIME lpSystemTime);

            /// <summary>
            /// Convert a local time to UTC, using the supplied time zone
information.
            /// Windows XP and Server 2003 and later only.
            /// </summary>
            /// <param name="lpTimeZone">The time zone to use.</param>
            /// <param name="lpLocalTime">The local time to convert.</param>
            /// <param name="lpUniversalTime">The resultant time in
UTC.</param>
            /// <returns>true if successful, false otherwise.</returns>
            [DllImport(KERNEL32)]
            public static extern bool TzSpecificLocalTimeToSystemTime(
                [In] ref TIME_ZONE_INFORMATION lpTimeZone,
                [In] ref SYSTEMTIME lpLocalTime,
                out SYSTEMTIME lpUniversalTime);
        }

        /// <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
    }

--
William Stacey [MVP]

Show quote
"Chris Mullins" <cmull***@yahoo.com> wrote in message
news:uNlyJ%23%23xGHA.1268@TK2MSFTNGP02.phx.gbl...
| In .Net 2.0, I can get the standard name of the current timezone by:
| System.TimeZone.CurrentTimeZone.StandardName
|
| The problem I'm having is that I have datetimes that are serialized as a
UTC
| time, with a named timezone. I keep looking for a way to say:
|
| System.TimeZone tz = new TimeZone("PST");
| tz.ConvertToLocalTime(utcTime);
|
| Problem is there doesn't seem to way to do this. Anyone got any suggesting
| for dealing with TimeZones?
|
|
|
| --
| Chris Mullins  MCSD.Net, MCPD Enterprise
| http://www.coversant.net/blogs/cmullins
|
|
Author
25 Aug 2006 5:40 PM
Chris Mullins
"William Stacey [MVP]" <william.sta***@gmail.com> wrote:

[Lack of TimeZone Classes in .Net 1.1 and 2.0]
> Never could figure out why they did not include such a needed thing.

That makes two of us.

> Until then, here is a class I did that will convert between
> timezones, etc.

[Snip Class]

Thanks. That'll do the trick.

The only problem now is that I'm working with an ancient extension to the
XMPP Instant Messaging protocol (RFC 3920/3921), specifically JEP 0090,
http://www.jabber.org/jeps/jep-0090.html. That JEP defines a mechanism by
which two clients can exchange time information in a request/response
format.

That JEP defines a TimeZone string, which may be coming from other operating
systems.
<xs:sequence minOccurs='0'>
    <xs:element name='utc' type='xs:string' minOccurs='1'/>
    <xs:element name='tz' type='xs:string' minOccurs='0'/>
    <xs:element name='display' type='xs:string' minOccurs='0'/>
</xs:sequence>I think I'm going to recommend this old format (it's probably
from 2000) be replaced with a more current one that includes a standard XML
datetime format.

--
Chris Mullins
Author
28 Aug 2006 12:34 AM
William Stacey [MVP]
Yes.  I would transfer *all dates in UTC with the Z format and put the
string result to your class before xml serializer.  IIRC, xml serializer has
some issue with dates, so I just do them myself in utc.

--
William Stacey [MVP]

Show quote
"Chris Mullins" <cmull***@yahoo.com> wrote in message
news:O1WTO2GyGHA.4976@TK2MSFTNGP04.phx.gbl...
| "William Stacey [MVP]" <william.sta***@gmail.com> wrote:
|
| [Lack of TimeZone Classes in .Net 1.1 and 2.0]
| > Never could figure out why they did not include such a needed thing.
|
| That makes two of us.
|
| > Until then, here is a class I did that will convert between
| > timezones, etc.
|
| [Snip Class]
|
| Thanks. That'll do the trick.
|
| The only problem now is that I'm working with an ancient extension to the
| XMPP Instant Messaging protocol (RFC 3920/3921), specifically JEP 0090,
| http://www.jabber.org/jeps/jep-0090.html. That JEP defines a mechanism by
| which two clients can exchange time information in a request/response
| format.
|
| That JEP defines a TimeZone string, which may be coming from other
operating
| systems.
| <xs:sequence minOccurs='0'>
|    <xs:element name='utc' type='xs:string' minOccurs='1'/>
|    <xs:element name='tz' type='xs:string' minOccurs='0'/>
|    <xs:element name='display' type='xs:string' minOccurs='0'/>
| </xs:sequence>I think I'm going to recommend this old format (it's
probably
| from 2000) be replaced with a more current one that includes a standard
XML
| datetime format.
|
| --
| Chris Mullins
|
|
Author
28 Aug 2006 5:11 AM
Marc Scheuner
>In .Net 2.0, I can get the standard name of the current timezone by:
>System.TimeZone.CurrentTimeZone.StandardName
>The problem I'm having is that I have datetimes that are serialized as a UTC
>time, with a named timezone. I keep looking for a way to say:

CodeProject's your friend! :-)

World Clock and the TimeZoneInformation class
http://www.codeproject.com/dotnet/WorldClock.asp

This should handle just about anything you need to do with timezones
in .NET - also, check out more Timezone-related stuff on CodeProject:

Convert between UTC (Universal Co-ordinated Time) and local time
http://www.codeproject.com/csharp/timezoneconversions.asp

Display the current time across any time zone via custom clocks on
your desktop
http://www.codeproject.com/smartclient/TimePiece.asp

I'm sure you'll find more if you do a thorough search!

HTH
Marc

AddThis Social Bookmark Button