Skip to content

Instantly share code, notes, and snippets.

@soeminnminn
Created May 9, 2017 16:01
Show Gist options
  • Save soeminnminn/cc7002856c381c3e2861792a68ea7aba to your computer and use it in GitHub Desktop.
Save soeminnminn/cc7002856c381c3e2861792a68ea7aba to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Text;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Collections;
using System.Runtime.InteropServices;
namespace S16.App
{
public class UtcDateTime
{
#region Native Methods
/// <summary>
/// [Win32 API call]
/// The GetTimeZoneInformation function retrieves the current time-zone parameters.
/// These parameters control the translations between Coordinated Universal Time (UTC)
/// and local time.
/// </summary>
/// <param name="lpTimeZoneInformation">[out] Pointer to a TIME_ZONE_INFORMATION structure to receive the current time-zone parameters.</param>
/// <returns>
/// If the function succeeds, the return value is one of the following values.
/// <list type="table">
/// <listheader>
/// <term>Return code/value</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>TIME_ZONE_ID_UNKNOWN == 0</term>
/// <description>
/// The system cannot determine the current time zone. This error is also returned if you call the SetTimeZoneInformation function and supply the bias values but no transition dates.
/// This value is returned if daylight saving time is not used in the current time zone, because there are no transition dates.
/// </description>
/// </item>
/// <item>
/// <term>TIME_ZONE_ID_STANDARD == 1</term>
/// <description>
/// The system is operating in the range covered by the StandardDate member of the TIME_ZONE_INFORMATION structure.
/// </description>
/// </item>
/// <item>
/// <term>TIME_ZONE_ID_DAYLIGHT == 2</term>
/// <description>
/// The system is operating in the range covered by the DaylightDate member of the TIME_ZONE_INFORMATION structure.
/// </description>
/// </item>
/// </list>
/// If the function fails, the return value is TIME_ZONE_ID_INVALID. To get extended error information, call GetLastError.
/// </returns>
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern int GetTimeZoneInformation(out TimeZoneInformation lpTimeZoneInformation);
/// <summary>
/// [Win32 API call]
/// The SetTimeZoneInformation function sets the current time-zone parameters.
/// These parameters control translations from Coordinated Universal Time (UTC)
/// to local time.
/// </summary>
/// <param name="lpTimeZoneInformation">[in] Pointer to a TIME_ZONE_INFORMATION structure that contains the time-zone parameters to set.</param>
/// <returns>
/// If the function succeeds, the return value is nonzero.
/// If the function fails, the return value is zero. To get extended error information, call GetLastError.
/// </returns>
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern bool SetTimeZoneInformation([In] ref TimeZoneInformation lpTimeZoneInformation);
#endregion
#region Variables
private static Hashtable c_timeZones = null;
private DateTime m_value = new DateTime();
#endregion
#region Constructor
static UtcDateTime()
{
UtcDateTime.c_timeZones = new Hashtable();
UtcDateTime.c_timeZones.Add("ADT", new TimeSpan(-3, 0, 0));
UtcDateTime.c_timeZones.Add("AFT", new TimeSpan(4, 30, 0));
UtcDateTime.c_timeZones.Add("AKDT", new TimeSpan(-8, 0, 0));
UtcDateTime.c_timeZones.Add("AKST", new TimeSpan(-9, 0, 0));
UtcDateTime.c_timeZones.Add("ALMT", new TimeSpan(6, 0, 0));
UtcDateTime.c_timeZones.Add("AMST", new TimeSpan(5, 0, 0));
UtcDateTime.c_timeZones.Add("AMT", new TimeSpan(4, 0, 0));
UtcDateTime.c_timeZones.Add("ART", new TimeSpan(-3, 0, 0));
UtcDateTime.c_timeZones.Add("AST", new TimeSpan(3, 0, 0));
UtcDateTime.c_timeZones.Add("AZST", new TimeSpan(5, 0, 0));
UtcDateTime.c_timeZones.Add("AZT", new TimeSpan(4, 0, 0));
UtcDateTime.c_timeZones.Add("BDT", new TimeSpan(6, 0, 0));
UtcDateTime.c_timeZones.Add("BNT", new TimeSpan(8, 0, 0));
UtcDateTime.c_timeZones.Add("BOT", new TimeSpan(-4, 0, 0));
UtcDateTime.c_timeZones.Add("BRST", new TimeSpan(-2, 0, 0));
UtcDateTime.c_timeZones.Add("BRT", new TimeSpan(-3, 0, 0));
UtcDateTime.c_timeZones.Add("BST", new TimeSpan(1, 0, 0));
UtcDateTime.c_timeZones.Add("BTT", new TimeSpan(6, 0, 0));
UtcDateTime.c_timeZones.Add("CAT", new TimeSpan(2, 0, 0));
UtcDateTime.c_timeZones.Add("CDT", new TimeSpan(-5, 0, 0));
UtcDateTime.c_timeZones.Add("CEST", new TimeSpan(2, 0, 0));
UtcDateTime.c_timeZones.Add("CET", new TimeSpan(1, 0, 0));
UtcDateTime.c_timeZones.Add("ChST", new TimeSpan(10, 0, 0));
UtcDateTime.c_timeZones.Add("CKT", new TimeSpan(-10, 0, 0));
UtcDateTime.c_timeZones.Add("CLST", new TimeSpan(-3, 0, 0));
UtcDateTime.c_timeZones.Add("CLT", new TimeSpan(-4, 0, 0));
UtcDateTime.c_timeZones.Add("COT", new TimeSpan(-5, 0, 0));
UtcDateTime.c_timeZones.Add("CST", new TimeSpan(-6, 0, 0));
UtcDateTime.c_timeZones.Add("CVT", new TimeSpan(-1, 0, 0));
UtcDateTime.c_timeZones.Add("CXT", new TimeSpan(7, 0, 0));
UtcDateTime.c_timeZones.Add("EAT", new TimeSpan(3, 0, 0));
UtcDateTime.c_timeZones.Add("EDT", new TimeSpan(-4, 0, 0));
UtcDateTime.c_timeZones.Add("EEST", new TimeSpan(3, 0, 0));
UtcDateTime.c_timeZones.Add("EET", new TimeSpan(2, 0, 0));
UtcDateTime.c_timeZones.Add("EST", new TimeSpan(-5, 0, 0));
UtcDateTime.c_timeZones.Add("FJST", new TimeSpan(13, 0, 0));
UtcDateTime.c_timeZones.Add("FJT", new TimeSpan(12, 0, 0));
UtcDateTime.c_timeZones.Add("FKST", new TimeSpan(-3, 0, 0));
UtcDateTime.c_timeZones.Add("FKT", new TimeSpan(-4, 0, 0));
UtcDateTime.c_timeZones.Add("GET", new TimeSpan(4, 0, 0));
UtcDateTime.c_timeZones.Add("GFT", new TimeSpan(-3, 0, 0));
UtcDateTime.c_timeZones.Add("GILT", new TimeSpan(12, 0, 0));
UtcDateTime.c_timeZones.Add("GMT", new TimeSpan(0, 0, 0));
UtcDateTime.c_timeZones.Add("GST", new TimeSpan(4, 0, 0));
UtcDateTime.c_timeZones.Add("GYT", new TimeSpan(-4, 0, 0));
UtcDateTime.c_timeZones.Add("HKT", new TimeSpan(8, 0, 0));
UtcDateTime.c_timeZones.Add("HST", new TimeSpan(-10, 0, 0));
UtcDateTime.c_timeZones.Add("ICT", new TimeSpan(7, 0, 0));
UtcDateTime.c_timeZones.Add("IDT", new TimeSpan(3, 0, 0));
UtcDateTime.c_timeZones.Add("IRDT", new TimeSpan(4, 30, 0));
UtcDateTime.c_timeZones.Add("IRST", new TimeSpan(3, 30, 0));
UtcDateTime.c_timeZones.Add("IST", new TimeSpan(5, 30, 0));
UtcDateTime.c_timeZones.Add("JST", new TimeSpan(9, 0, 0));
UtcDateTime.c_timeZones.Add("KGT", new TimeSpan(6, 0, 0));
UtcDateTime.c_timeZones.Add("KST", new TimeSpan(9, 0, 0));
UtcDateTime.c_timeZones.Add("MDT", new TimeSpan(-6, 0, 0));
UtcDateTime.c_timeZones.Add("MHT", new TimeSpan(12, 0, 0));
UtcDateTime.c_timeZones.Add("MMT", new TimeSpan(630));
UtcDateTime.c_timeZones.Add("MSD", new TimeSpan(4, 0, 0));
UtcDateTime.c_timeZones.Add("MSK", new TimeSpan(3, 0, 0));
UtcDateTime.c_timeZones.Add("MST", new TimeSpan(-7, 0, 0));
UtcDateTime.c_timeZones.Add("MUT", new TimeSpan(4, 0, 0));
UtcDateTime.c_timeZones.Add("MVT", new TimeSpan(5, 0, 0));
UtcDateTime.c_timeZones.Add("MYT", new TimeSpan(8, 0, 0));
UtcDateTime.c_timeZones.Add("NCT", new TimeSpan(11, 0, 0));
UtcDateTime.c_timeZones.Add("NDT", new TimeSpan(-3, 30, 0));
UtcDateTime.c_timeZones.Add("NPT", new TimeSpan(5, 45, 0));
UtcDateTime.c_timeZones.Add("NRT", new TimeSpan(12, 0, 0));
UtcDateTime.c_timeZones.Add("NST", new TimeSpan(-4, 30, 0));
UtcDateTime.c_timeZones.Add("NUT", new TimeSpan(-11, 0, 0));
UtcDateTime.c_timeZones.Add("NZDT", new TimeSpan(13, 0, 0));
UtcDateTime.c_timeZones.Add("NZST", new TimeSpan(12, 0, 0));
UtcDateTime.c_timeZones.Add("PDT", new TimeSpan(-7, 0, 0));
UtcDateTime.c_timeZones.Add("PET", new TimeSpan(-5, 0, 0));
UtcDateTime.c_timeZones.Add("PHT", new TimeSpan(8, 0, 0));
UtcDateTime.c_timeZones.Add("PKT", new TimeSpan(5, 0, 0));
UtcDateTime.c_timeZones.Add("PONT", new TimeSpan(11, 0, 0));
UtcDateTime.c_timeZones.Add("PST", new TimeSpan(-8, 0, 0));
UtcDateTime.c_timeZones.Add("PWT", new TimeSpan(9, 0, 0));
UtcDateTime.c_timeZones.Add("PYST", new TimeSpan(-3, 0, 0));
UtcDateTime.c_timeZones.Add("PYT", new TimeSpan(-4, 0, 0));
UtcDateTime.c_timeZones.Add("RET", new TimeSpan(4, 0, 0));
UtcDateTime.c_timeZones.Add("SAST", new TimeSpan(2, 0, 0));
UtcDateTime.c_timeZones.Add("SBT", new TimeSpan(11, 0, 0));
UtcDateTime.c_timeZones.Add("SCT", new TimeSpan(4, 0, 0));
UtcDateTime.c_timeZones.Add("SGT", new TimeSpan(8, 0, 0));
UtcDateTime.c_timeZones.Add("SRT", new TimeSpan(-3, 0, 0));
UtcDateTime.c_timeZones.Add("SST", new TimeSpan(-11, 0, 0));
UtcDateTime.c_timeZones.Add("TAHT", new TimeSpan(-10, 0, 0));
UtcDateTime.c_timeZones.Add("TJT", new TimeSpan(5, 0, 0));
UtcDateTime.c_timeZones.Add("TLT", new TimeSpan(9, 0, 0));
UtcDateTime.c_timeZones.Add("TMT", new TimeSpan(5, 0, 0));
UtcDateTime.c_timeZones.Add("TOT", new TimeSpan(13, 0, 0));
UtcDateTime.c_timeZones.Add("TVT", new TimeSpan(12, 0, 0));
UtcDateTime.c_timeZones.Add("ULAT", new TimeSpan(8, 0, 0));
UtcDateTime.c_timeZones.Add("UYST", new TimeSpan(-2, 0, 0));
UtcDateTime.c_timeZones.Add("UYT", new TimeSpan(-3, 0, 0));
UtcDateTime.c_timeZones.Add("UZT", new TimeSpan(5, 0, 0));
UtcDateTime.c_timeZones.Add("VET", new TimeSpan(-5, 30, 0));
UtcDateTime.c_timeZones.Add("VLAST", new TimeSpan(11, 0, 0));
UtcDateTime.c_timeZones.Add("VLAT", new TimeSpan(10, 0, 0));
UtcDateTime.c_timeZones.Add("VUT", new TimeSpan(11, 0, 0));
UtcDateTime.c_timeZones.Add("WAST", new TimeSpan(2, 0, 0));
UtcDateTime.c_timeZones.Add("WAT", new TimeSpan(1, 0, 0));
UtcDateTime.c_timeZones.Add("WEST", new TimeSpan(1, 0, 0));
UtcDateTime.c_timeZones.Add("WET", new TimeSpan(0, 0, 0));
UtcDateTime.c_timeZones.Add("WIT", new TimeSpan(7, 0, 0));
UtcDateTime.c_timeZones.Add("WSDT", new TimeSpan(-10, 0, 0));
UtcDateTime.c_timeZones.Add("WST", new TimeSpan(8, 0, 0));
}
public UtcDateTime(DateTime dateTime)
{
this.m_value = dateTime.ToUniversalTime();
}
public UtcDateTime(int year, int month, int day)
{
this.m_value = this.GetDateTime(year, month, day, 12, 00, 00, DateTimeKind.Utc);
}
public UtcDateTime(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind)
{
DateTime value = this.GetDateTime(year, month, day, hour, minute, second, kind);
this.m_value = value.ToUniversalTime();
}
public UtcDateTime(int year, int month, int day, int hour, int minute, int second, int offsetHour, int offsetMinute)
{
DateTimeOffset dtOffset = new DateTimeOffset(this.GetDateTime(year, month, day, hour, minute, second, DateTimeKind.Unspecified), new TimeSpan(offsetHour, offsetMinute, 0));
this.m_value = dtOffset.DateTime;
}
public UtcDateTime(int year, int month, int day, int hour, int minute, int second, string timeZone)
{
if (UtcDateTime.c_timeZones.ContainsKey(timeZone))
{
DateTimeOffset dtOffset = new DateTimeOffset(this.GetDateTime(year, month, day, hour, minute, second, DateTimeKind.Unspecified), (TimeSpan)UtcDateTime.c_timeZones[timeZone]);
this.m_value = dtOffset.DateTime;
}
else
{
TimeZoneInfo timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZone);
this.m_value = TimeZoneInfo.ConvertTimeToUtc(this.GetDateTime(year, month, day, hour, minute, second, DateTimeKind.Unspecified), timeZoneInfo);
}
}
public UtcDateTime(int year, int month, int day, int hour, int minute, int second, TimeSpan offset)
{
DateTimeOffset dtOffset = new DateTimeOffset(this.GetDateTime(year, month, day, hour, minute, second, DateTimeKind.Unspecified), offset);
this.m_value = dtOffset.DateTime;
}
#endregion
#region Private Methods
private DateTime GetDateTime(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind)
{
return new DateTime(this.Validate(year, "year"), this.Validate(month, "month"), this.Validate(day, "day")
, this.Validate(hour, "hour"), this.Validate(minute, "minute"), this.Validate(second, "second"), kind);
}
private int Validate(int value, string key)
{
if (key.Equals("year", StringComparison.InvariantCultureIgnoreCase))
{
if ((value < DateTime.MinValue.Year) || (value > DateTime.MaxValue.Year)) return DateTime.MinValue.Year;
}
else if (key.Equals("month", StringComparison.InvariantCultureIgnoreCase))
{
if ((value < 0) || (value > 12)) return 1;
}
else if (key.Equals("day", StringComparison.InvariantCultureIgnoreCase))
{
if ((value < 0) || (value > 31)) return 1;
}
else if (key.Equals("hour", StringComparison.InvariantCultureIgnoreCase))
{
if ((value < 0) || (value > 24)) return 1;
}
else if (key.Equals("minute", StringComparison.InvariantCultureIgnoreCase))
{
if ((value < 0) || (value > 60)) return 1;
}
else if (key.Equals("second", StringComparison.InvariantCultureIgnoreCase))
{
if ((value < 0) || (value > 60)) return 1;
}
return value;
}
#endregion
#region Public Methods
public override string ToString()
{
return this.m_value.ToString("ddd M/d/yyyy hh:mm:ss tt");
}
public DateTime Offset(float offsetHours)
{
int offsetHour = (int)Math.Floor((double)offsetHours);
int offsetMinute = (int)((((offsetHours - (float)offsetHour) * 100f) * 60) / 100);
return this.Offset(offsetHour, offsetMinute);
}
public DateTime Offset(int offsetHour, int offsetMinute)
{
DateTimeOffset dtOffset = new DateTimeOffset(this.Date, new TimeSpan(offsetHour, offsetMinute, 0));
return dtOffset.DateTime;
}
public DateTime AdjustTimeZone(TimeZoneInfo timeZoneInfo)
{
//if (timeZoneInfo.SupportsDaylightSavingTime)
//{
// TimeZoneInfo.AdjustmentRule[] adjustments = timeZoneInfo.GetAdjustmentRules();
// if ((adjustments != null) && (adjustments.Length > 0))
// {
// TimeZoneInfo.AdjustmentRule adjustment = null;
// for (int i = 0; i < adjustments.Length; i++)
// {
// if ((adjustments[i].DateStart.Year <= this.m_value.Year) && (adjustments[i].DateEnd.Year >= this.m_value.Year))
// {
// adjustment = adjustments[i];
// break;
// }
// }
// if (adjustment != null)
// {
// //TimeZoneInfo.TransitionTime startTransition = adjustment.DaylightTransitionStart;
// //TimeZoneInfo.TransitionTime endTransition = adjustment.DaylightTransitionEnd;
// }
// }
//}
return TimeZoneInfo.ConvertTime(this.m_value, TimeZoneInfo.Utc, timeZoneInfo);
}
public DateTime ToLocalTime()
{
return this.m_value.ToLocalTime();
}
#endregion
#region Static Methods
/// <summary>
/// Converts the specified string representation of a date and time to its UtcDateTime equivalent.
/// </summary>
/// <param name="dateTimeString">A string containing a date and time to convert.</param>
/// <param name="kind">One of the enumeration values that indicates whether the new object represents local time, UTC, or neither.</param>
/// <returns>A System.DateTime equivalent to the date and time contained in dateTimeString.</returns>
public static UtcDateTime Parse(string dateTimeString, DateTimeKind kind)
{
UtcDateTime dateTime = null;
int month = 1;
int day = 1;
int year = 1;
int hour = 0;
int minute = 0;
int second = 0;
#region Parse Date
Match match = Regex.Match(dateTimeString, @"(?<=^|[^\d])D\:(?'year'\d{4})(?'month'\d{2})(?'day'\d{2})", RegexOptions.Compiled | RegexOptions.IgnoreCase);
if (!match.Success) // mm/dd/yy[yy]
match = Regex.Match(dateTimeString, @"(?<=^|[^\d])(?'month'\d{1,2})\s*(?'separator'[\\/\.])+\s*(?'day'\d{1,2})\s*\'separator'+\s*(?'year'\d{2}|\d{4})(?=$|[^\d])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
if (!match.Success) // [yy]yy-mm-dd
match = Regex.Match(dateTimeString, @"(?<=^|[^\d])(?'year'\d{2}|\d{4})\s*(?'separator'[\-])\s*(?'month'\d{1,2})\s*\'separator'+\s*(?'day'\d{1,2})(?=$|[^\d])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
if (!match.Success) // month dd yyyy
match = Regex.Match(dateTimeString, @"(?:^|[^\d\w])(?'month'Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[uarychilestmbro]*\s+(?'day'\d{1,2})(?:-?st|-?th|-?rd|-?nd)?\s*,?\s*(?'year'\d{4})(?=$|[^\d\w])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
if (!match.Success) // dd month [yy]yy
match = Regex.Match(dateTimeString, @"(?:^|[^\d\w:])(?'day'\d{1,2})(?:-?st\s+|-?th\s+|-?rd\s+|-?nd\s+|-|\s+)(?'month'Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[uarychilestmbro]*(?:\s*,?\s*|-)'?(?'year'\d{2}|\d{4})(?=$|[^\d\w])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
if (!match.Success) // yyyy month dd
match = Regex.Match(dateTimeString, @"(?:^|[^\d\w])(?'year'\d{4})\s+(?'month'Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[uarychilestmbro]*\s+(?'day'\d{1,2})(?:-?st|-?th|-?rd|-?nd)?(?=$|[^\d\w])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
if (!match.Success) // month dd hh:mm:ss MDT|UTC yyyy
match = Regex.Match(dateTimeString, @"(?:^|[^\d\w])(?'month'Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[uarychilestmbro]*\s+(?'day'\d{1,2})\s+\d{2}\:\d{2}\:\d{2}\s+(?:MDT|UTC)\s+(?'year'\d{4})(?=$|[^\d\w])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
if (!match.Success) // month dd [yyyy]
match = Regex.Match(dateTimeString, @"(?:^|[^\d\w])(?'month'Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[uarychilestmbro]*\s+(?'day'\d{1,2})(?:-?st|-?th|-?rd|-?nd)?(?:\s*,?\s*(?'year'\d{4}))?(?=$|[^\d\w])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
if (match.Success)
{
if (!int.TryParse(match.Groups["month"].Value, out month))
{
switch (match.Groups["month"].Value)
{
case "Jan":
case "JAN":
month = 1;
break;
case "Feb":
case "FEB":
month = 2;
break;
case "Mar":
case "MAR":
month = 3;
break;
case "Apr":
case "APR":
month = 4;
break;
case "May":
case "MAY":
month = 5;
break;
case "Jun":
case "JUN":
month = 6;
break;
case "Jul":
month = 7;
break;
case "Aug":
case "AUG":
month = 8;
break;
case "Sep":
case "SEP":
month = 9;
break;
case "Oct":
case "OCT":
month = 10;
break;
case "Nov":
case "NOV":
month = 11;
break;
case "Dec":
case "DEC":
month = 12;
break;
}
}
int.TryParse(match.Groups["day"].Value, out day);
int.TryParse(match.Groups["year"].Value, out year);
}
#endregion
#region Parse Time
match = Regex.Match(dateTimeString, @"(?<=^|[^\d])D\:(?'date'\d{8})(?'hour'\d{2})(?'minute'\d{2})(?'second'\d{2})(?'offset_sign'[\+\-])(?'offset_hh'\d{2}):?(?'offset_mm'\d{2})?", RegexOptions.Compiled | RegexOptions.IgnoreCase);
if (!match.Success) // <date> hh:mm:ss <UTC offset>
match = Regex.Match(dateTimeString, @"(?<=^\s*,?\s+|^\s*at\s*|^\s*[T\-]\s*)(?'hour'\d{2})\s*:\s*(?'minute'\d{2})\s*:\s*(?'second'\d{2})\s+(?'offset_sign'[\+\-])(?'offset_hh'\d{2}):?(?'offset_mm'\d{2})(?=$|[^\d\w])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
if (!match.Success) // <date> [h]h:mm[:ss] [PM/AM] [UTC/GMT]
match = Regex.Match(dateTimeString, @"(?<=^\s*,?\s+|^\s*at\s*|^\s*[T\-]\s*)(?'hour'\d{1,2})\s*:\s*(?'minute'\d{2})\s*(?::\s*(?'second'\d{2}))?(?:\s*(?'ampm'AM|am|PM|pm))?(?:\s*(?'time_zone'[a-zA-Z]{3,4}))?(?=$|[^\d\w])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
if (!match.Success) // [h]h:mm:ss [PM/AM] [UTC/GMT] <date>
match = Regex.Match(dateTimeString, @"(?<=^|[^\d])(?'hour'\d{1,2})\s*:\s*(?'minute'\d{2})\s*(?::\s*(?'second'\d{2}))?(?:\s*(?'ampm'AM|am|PM|pm))?(?:\s*(?'time_zone'[a-zA-Z]{3,4}))?(?=$|[\s,]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
if (match.Success)
{
int.TryParse(match.Groups["hour"].Value, out hour);
if (match.Groups["ampm"].Success)
{
if (string.Compare(match.Groups["ampm"].Value, "PM", true) == 0 && hour < 12)
hour += 12;
else if (string.Compare(match.Groups["ampm"].Value, "AM", true) == 0 && hour == 12)
hour -= 12;
}
int.TryParse(match.Groups["minute"].Value, out minute);
int.TryParse(match.Groups["second"].Value, out second);
if (match.Groups["offset_sign"].Success)
{
int offsetHour = 0;
int offsetMinute = 0;
if (match.Groups["offset_hh"].Success)
int.TryParse(match.Groups["offset_hh"].Value, out offsetHour);
if (match.Groups["offset_mm"].Success)
int.TryParse(match.Groups["offset_mm"].Value, out offsetMinute);
if (string.Compare(match.Groups["offset_sign"].Value, "-", true) == 0)
offsetHour = -offsetHour;
dateTime = new UtcDateTime(year, month, day, hour, minute, second, offsetHour, offsetMinute);
}
else if (match.Groups["time_zone"].Success)
{
dateTime = new UtcDateTime(year, month, day, hour, minute, second, match.Groups["time_zone"].Value);
}
else
{
dateTime = new UtcDateTime(year, month, day, hour, minute, second, kind);
}
}
else
{
dateTime = new UtcDateTime(year, month, day);
}
#endregion
return dateTime;
}
/// <summary>
/// Sets new time-zone information for the local system.
/// </summary>
/// <param name="tzi">Struct containing the time-zone parameters to set.</param>
public static void SetTimeZone(TimeZoneInformation tzi)
{
// set local system timezone
SetTimeZoneInformation(ref tzi);
}
/// <summary>
/// Gets current timezone information for the local system.
/// </summary>
/// <returns>Struct containing the current time-zone parameters.</returns>
public static TimeZoneInformation GetTimeZone()
{
// create struct instance
TimeZoneInformation tzi;
// retrieve timezone info
int currentTimeZone = GetTimeZoneInformation(out tzi);
return tzi;
}
#endregion
#region Properties
public DateTime Date
{
get { return this.m_value; }
}
#endregion
#region Operators
public static implicit operator UtcDateTime(DateTime dateTime)
{
return new UtcDateTime(dateTime);
}
public static explicit operator DateTime(UtcDateTime dateTime)
{
return dateTime.Date;
}
public static implicit operator UtcDateTime(DateTime? dateTime)
{
if ((dateTime == null) || (!dateTime.HasValue)) return null;
return new UtcDateTime(dateTime.Value);
}
public static explicit operator DateTime?(UtcDateTime dateTime)
{
if (dateTime == null) return null;
return dateTime.Date;
}
#endregion
#region Nested Types
[StructLayout(LayoutKind.Sequential)]
public struct SystemTime
{
public short year;
public short month;
public short dayOfWeek;
public short day;
public short hour;
public short minute;
public short second;
public short milliseconds;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct TimeZoneInformation
{
/// <summary>
/// Current bias for local time translation on this computer, in minutes. The bias is the difference, in minutes, between Coordinated Universal Time (UTC) and local time. All translations between UTC and local time are based on the following formula:
/// <para>UTC = local time + bias</para>
/// <para>This member is required.</para>
/// </summary>
public int bias;
/// <summary>
/// Pointer to a null-terminated string associated with standard time. For example, "EST" could indicate Eastern Standard Time. The string will be returned unchanged by the GetTimeZoneInformation function. This string can be empty.
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string standardName;
/// <summary>
/// A SystemTime structure that contains a date and local time when the transition from daylight saving time to standard time occurs on this operating system. If the time zone does not support daylight saving time or if the caller needs to disable daylight saving time, the wMonth member in the SystemTime structure must be zero. If this date is specified, the DaylightDate value in the TimeZoneInformation structure must also be specified. Otherwise, the system assumes the time zone data is invalid and no changes will be applied.
/// <para>To select the correct day in the month, set the wYear member to zero, the wHour and wMinute members to the transition time, the wDayOfWeek member to the appropriate weekday, and the wDay member to indicate the occurence of the day of the week within the month (first through fifth).</para>
/// <para>Using this notation, specify the 2:00a.m. on the first Sunday in April as follows: wHour = 2, wMonth = 4, wDayOfWeek = 0, wDay = 1. Specify 2:00a.m. on the last Thursday in October as follows: wHour = 2, wMonth = 10, wDayOfWeek = 4, wDay = 5.</para>
/// </summary>
public SystemTime standardDate;
/// <summary>
/// Bias value to be used during local time translations that occur during standard time. This member is ignored if a value for the StandardDate member is not supplied.
/// <para>This value is added to the value of the Bias member to form the bias used during standard time. In most time zones, the value of this member is zero.</para>
/// </summary>
public int standardBias;
/// <summary>
/// Pointer to a null-terminated string associated with daylight saving time. For example, "PDT" could indicate Pacific Daylight Time. The string will be returned unchanged by the GetTimeZoneInformation function. This string can be empty.
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string daylightName;
/// <summary>
/// A SystemTime structure that contains a date and local time when the transition from standard time to daylight saving time occurs on this operating system. If the time zone does not support daylight saving time or if the caller needs to disable daylight saving time, the wMonth member in the SystemTime structure must be zero. If this date is specified, the StandardDate value in the TimeZoneInformation structure must also be specified. Otherwise, the system assumes the time zone data is invalid and no changes will be applied.
/// <para>To select the correct day in the month, set the wYear member to zero, the wHour and wMinute members to the transition time, the wDayOfWeek member to the appropriate weekday, and the wDay member to indicate the occurence of the day of the week within the month (first through fifth).</para>
/// </summary>
public SystemTime daylightDate;
/// <summary>
/// Bias value to be used during local time translations that occur during daylight saving time. This member is ignored if a value for the DaylightDate member is not supplied.
/// <para>This value is added to the value of the Bias member to form the bias used during daylight saving time. In most time zones, the value of this member is –60.</para>
/// </summary>
public int daylightBias;
}
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment