namespace eCRF.Utility {
///
/// 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.
///
[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 timeZones; // static list of all time zones on machine.
#endregion#region Constructors
public TimeZoneInformation() {}
static TimeZoneInformation() {
timeZones = new List();
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
///
/// Gets list of all time zones defined on the current computer system.
///
public static List TimeZones {
get {
// Return a copy of the list.
List nList = new List(timeZones);
return nList;
}
}
///
/// Gets an string array of standard time zone names supported on the current system.
///
public static string[] TimeZoneNames {
get {
List list = new List();
foreach(TimeZoneInformation tzi in timeZones) {
list.Add(tzi.StandardName);
}
return list.ToArray();
}
}
///
/// Gets the time zone information of the current computer system.
///
public static TimeZoneInformation CurrentTimeZone {
get {
string tzn = TimeZone.CurrentTimeZone.StandardName;
TimeZoneInformation tzi = TimeZoneInformation.GetTimeZone(tzn);
return tzi;
}
}
///
/// The time zone's name during 'standard' time (i.e. not daylight savings).
///
public string StandardName {
get {
return standardName;
}
}
///
/// The time zone's name during daylight savings time (DST).
///
public string DaylightName {
get {
return daylightName;
}
}
///
/// The time zone's display name (e.g. "(GMT-05:00) Eastern Time (US and Canada)").
///
public string DisplayName {
get {
return displayName;
}
}
///
/// Gets the standard offset from UTC as a TimeSpan.
///
public TimeSpan StandardOffset {
get {
return TimeSpan.FromMinutes(StandardBias);
}
}
///
/// Gets the daylight offset from UTC as a TimeSpan.
///
public TimeSpan DaylightOffset {
get {
return TimeSpan.FromMinutes(DaylightBias);
}
}
///
/// Gets the difference, in minutes, between UTC and local time.
/// UTC = local time + bias.
///
public int StandardBias {
get {
return - (tzi.bias + tzi.standardBias);
}
}
///
/// Gets the difference, in minutes, between UTC and local time (in daylight savings time).
/// UTC = local time + bias.
///
public int DaylightBias {
get {
return - (tzi.bias + tzi.daylightBias);
}
}#endregion#region Public Methods
///
/// Returns standard name of this time zone instance.
///
/// Time zone standard name.
public override string ToString() {
return this.standardName;
}
///
/// Returns a TimeZoneInformation instance for the time zone with supplied standard name.
///
/// Standard name of the time zone.
/// TimeZoneInformation instance.
/// Thrown if name not found.
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.");
}
///
/// Converts the value of the utc time to a local time in this time zone.
///
/// The UTC time to convert.
/// The local time.
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);
}
///
/// Converts the value of the utc time to local time in supplied time zone.
///
/// The time to convert.
/// The standard name of the time zone.
/// The local time.
/// Thrown if time zone not found.
public static DateTime ToLocalTime(DateTime utc, string targetTimeZoneName) {
TimeZoneInformation tzi = TimeZoneInformation.GetTimeZone(targetTimeZoneName);
return tzi.ToLocalTime(utc);
}
///
/// 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.
///
/// Time zone name which represents localTime.
/// The source local time.
/// The time zone name which to convert the localTime.
/// The local time for targetTimeZoneName.
public static DateTime ToLocalTime(string sourceTimeZoneName, DateTime localTime, string targetTimeZoneName) {
DateTime utc = TimeZoneInformation.ToUniversalTime(sourceTimeZoneName, localTime);
DateTime lt = TimeZoneInformation.ToLocalTime(utc, targetTimeZoneName);
return lt;
}
///
/// Converts the value of the local time to UTC time.
/// Note that there may be different possible interpretations at the daylight time boundaries.
///
/// The local time to convert.
/// The UTC DateTime.
/// Thrown if the method failed due to missing platform support.
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);
}
}
///
/// Converts a local time in specified time zone to UTC time.
///
/// The standard time zone name.
/// The local time to convert.
/// The UTC time.
/// Thrown if time zone name not found.
/// Thrown if the method failed due to missing platform support.
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) << tzinative = " new"
bias = " tzi.bias;"
standarddate = " tzi.standardDate;"
standardbias = " tzi.standardBias;"
daylightdate = " tzi.daylightDate;"
daylightbias = " tzi.daylightBias;" >
/// The standard Windows SYSTEMTIME structure.
///
[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.
///
/// The layout of the Tzi value in the registry.
///
[StructLayout(LayoutKind.Sequential)]
private struct TZI {
public int bias;
public int standardBias;
public int daylightBias;
public SYSTEMTIME standardDate;
public SYSTEMTIME daylightDate;
}
///
/// The standard Win32 TIME_ZONE_INFORMATION structure.
/// Thanks to www.pinvoke.net.
///
[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;
}
///
/// A container for P/Invoke declarations.
///
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);
///
/// Convert a local time to UTC, using the supplied time zone information.
/// Windows XP and Server 2003 and later only.
///
/// The time zone to use.
/// The local time to convert.
/// The resultant time in UTC.
/// true if successful, false otherwise.
[DllImport(KERNEL32)]
public static extern bool TzSpecificLocalTimeToSystemTime([In] ref TIME_ZONE_INFORMATION lpTimeZone, [In] ref SYSTEMTIME lpLocalTime, out SYSTEMTIME lpUniversalTime);
}
///
/// Initialise the m_tzi member.
///
/// The Tzi data from the registry.
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
}
}
