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