/* * System.DateTimeOffset.cs * * Author(s) * Stephane Delcroix * Marek Safar (marek.safar@gmail.com) * * Copyright (C) 2007 Novell, Inc (http://www.novell.com) * Copyright 2012 Xamarin, Inc (http://www.xamarin.com) * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ using System.Globalization; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Text; namespace System { [Serializable] [StructLayout (LayoutKind.Auto)] public struct DateTimeOffset : IComparable, IFormattable, ISerializable, IDeserializationCallback, IComparable, IEquatable { #if MONOTOUCH static DateTimeOffset () { if (MonoTouchAOTHelper.FalseFlag) { var comparer = new System.Collections.Generic.GenericComparer (); var eqcomparer = new System.Collections.Generic.GenericEqualityComparer (); } } #endif public static readonly DateTimeOffset MaxValue = new DateTimeOffset (DateTime.MaxValue, TimeSpan.Zero); public static readonly DateTimeOffset MinValue = new DateTimeOffset (DateTime.MinValue, TimeSpan.Zero); DateTime dt; TimeSpan utc_offset; public DateTimeOffset (DateTime dateTime) { dt = dateTime; if (dateTime.Kind == DateTimeKind.Utc) utc_offset = TimeSpan.Zero; else utc_offset = TimeZone.CurrentTimeZone.GetUtcOffset (dateTime); if (UtcDateTime < DateTime.MinValue || UtcDateTime > DateTime.MaxValue) throw new ArgumentOutOfRangeException ("The UTC date and time that results from applying the offset is earlier than MinValue or later than MaxValue."); } public DateTimeOffset (DateTime dateTime, TimeSpan offset) { if (dateTime.Kind == DateTimeKind.Utc && offset != TimeSpan.Zero) throw new ArgumentException ("dateTime.Kind equals Utc and offset does not equal zero."); if (dateTime.Kind == DateTimeKind.Local && offset != TimeZone.CurrentTimeZone.GetUtcOffset (dateTime)) throw new ArgumentException ("dateTime.Kind equals Local and offset does not equal the offset of the system's local time zone."); if (offset.Ticks % TimeSpan.TicksPerMinute != 0) throw new ArgumentException ("offset is not specified in whole minutes."); if (offset < new TimeSpan (-14, 0 ,0) || offset > new TimeSpan (14, 0, 0)) throw new ArgumentOutOfRangeException ("offset is less than -14 hours or greater than 14 hours."); dt = dateTime; utc_offset = offset; if (UtcDateTime < DateTime.MinValue || UtcDateTime > DateTime.MaxValue) throw new ArgumentOutOfRangeException ("The UtcDateTime property is earlier than MinValue or later than MaxValue."); } public DateTimeOffset (long ticks, TimeSpan offset) : this (new DateTime (ticks), offset) { } public DateTimeOffset (int year, int month, int day, int hour, int minute, int second, TimeSpan offset) : this (new DateTime (year, month, day, hour, minute, second), offset) { } public DateTimeOffset (int year, int month, int day, int hour, int minute, int second, int millisecond, TimeSpan offset) : this (new DateTime (year, month, day, hour, minute, second, millisecond), offset) { } public DateTimeOffset (int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar, TimeSpan offset) : this (new DateTime (year, month, day, hour, minute, second, millisecond, calendar), offset) { } public DateTimeOffset Add (TimeSpan timeSpan) { return new DateTimeOffset (dt.Add (timeSpan).Ticks, utc_offset); } public DateTimeOffset AddDays (double days) { return new DateTimeOffset (dt.AddDays (days).Ticks, utc_offset); } public DateTimeOffset AddHours (double hours) { return new DateTimeOffset (dt.AddHours (hours).Ticks, utc_offset); } public static DateTimeOffset operator + (DateTimeOffset dateTimeOffset, TimeSpan timeSpan) { return dateTimeOffset.Add (timeSpan); } public DateTimeOffset AddMilliseconds (double milliseconds) { return new DateTimeOffset (dt.AddMilliseconds (milliseconds).Ticks, utc_offset); } public DateTimeOffset AddMinutes (double minutes) { return new DateTimeOffset (dt.AddMinutes (minutes).Ticks, utc_offset); } public DateTimeOffset AddMonths (int months) { return new DateTimeOffset (dt.AddMonths (months).Ticks, utc_offset); } public DateTimeOffset AddSeconds (double seconds) { return new DateTimeOffset (dt.AddSeconds (seconds).Ticks, utc_offset); } public DateTimeOffset AddTicks (long ticks) { return new DateTimeOffset (dt.AddTicks (ticks).Ticks, utc_offset); } public DateTimeOffset AddYears (int years) { return new DateTimeOffset (dt.AddYears (years).Ticks, utc_offset); } public static int Compare (DateTimeOffset first, DateTimeOffset second) { return first.CompareTo (second); } public int CompareTo (DateTimeOffset other) { return UtcDateTime.CompareTo (other.UtcDateTime); } int IComparable.CompareTo (object obj) { return CompareTo ((DateTimeOffset) obj); } public static bool operator == (DateTimeOffset left, DateTimeOffset right) { return left.Equals (right); } public bool Equals (DateTimeOffset other) { return UtcDateTime == other.UtcDateTime; } public override bool Equals (object obj) { if (obj is DateTimeOffset) return UtcDateTime == ((DateTimeOffset) obj).UtcDateTime; return false; } public static bool Equals (DateTimeOffset first, DateTimeOffset second) { return first.Equals (second); } public bool EqualsExact (DateTimeOffset other) { return dt == other.dt && utc_offset == other.utc_offset; } public static DateTimeOffset FromFileTime (long fileTime) { if (fileTime < 0 || fileTime > MaxValue.Ticks) throw new ArgumentOutOfRangeException ("fileTime is less than zero or greater than DateTimeOffset.MaxValue.Ticks."); return new DateTimeOffset (DateTime.FromFileTime (fileTime), TimeZone.CurrentTimeZone.GetUtcOffset (DateTime.FromFileTime (fileTime))); } public override int GetHashCode () { return dt.GetHashCode () ^ utc_offset.GetHashCode (); } [System.Security.Permissions.SecurityPermission (System.Security.Permissions.SecurityAction.LinkDemand, SerializationFormatter = true)] void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context) { if (info == null) throw new ArgumentNullException ("info"); // An example SOAP serialization on MSFT is the following, so field // names "DateTime" and "OffsetMinutes": // // // // 2007-01-02T12:30:50.0000000+00:00 // 0 // // // DateTime dt0 = new DateTime (dt.Ticks).Subtract (utc_offset); info.AddValue ("DateTime", dt0); // MSFT BinaryFormatter output contains primitive code 6, i.e. Int16. info.AddValue ("OffsetMinutes", (Int16)utc_offset.TotalMinutes); } [System.Security.Permissions.SecurityPermission (System.Security.Permissions.SecurityAction.LinkDemand, SerializationFormatter = true)] private DateTimeOffset(SerializationInfo info, StreamingContext context) { DateTime dt0 = (DateTime)info.GetValue ("DateTime", typeof(DateTime)); Int16 totalMinutes = info.GetInt16 ("OffsetMinutes"); utc_offset = TimeSpan.FromMinutes(totalMinutes); dt = dt0.Add(utc_offset); } public static bool operator > (DateTimeOffset left, DateTimeOffset right) { return left.UtcDateTime > right.UtcDateTime; } public static bool operator >= (DateTimeOffset left, DateTimeOffset right) { return left.UtcDateTime >= right.UtcDateTime; } public static implicit operator DateTimeOffset (DateTime dateTime) { return new DateTimeOffset (dateTime); } public static bool operator != (DateTimeOffset left, DateTimeOffset right) { return left.UtcDateTime != right.UtcDateTime; } public static bool operator < (DateTimeOffset left, DateTimeOffset right) { return left.UtcDateTime < right.UtcDateTime; } public static bool operator <= (DateTimeOffset left, DateTimeOffset right) { return left.UtcDateTime <= right.UtcDateTime; } [MonoTODO] void IDeserializationCallback.OnDeserialization (object sender) { } public static DateTimeOffset Parse (string input) { return Parse (input, null); } public static DateTimeOffset Parse (string input, IFormatProvider formatProvider) { return Parse (input, formatProvider, DateTimeStyles.AllowWhiteSpaces); } public static DateTimeOffset Parse (string input, IFormatProvider formatProvider, DateTimeStyles styles) { if (input == null) throw new ArgumentNullException ("input"); DateTime d; DateTimeOffset dto; Exception exception = null; try { if (!DateTime.CoreParse (input, formatProvider, styles, out d, out dto, true, ref exception)) throw exception; } catch (ArgumentOutOfRangeException ex) { throw new FormatException ("The UTC representation falls outside the 1-9999 year range", ex); } if (d.Ticks != 0 && dto.Ticks == 0) throw new FormatException ("The UTC representation falls outside the 1-9999 year range"); return dto; } public static DateTimeOffset ParseExact (string input, string format, IFormatProvider formatProvider) { return ParseExact (input, format, formatProvider, DateTimeStyles.AssumeLocal); } public static DateTimeOffset ParseExact (string input, string format, IFormatProvider formatProvider, DateTimeStyles styles) { if (format == null) throw new ArgumentNullException ("format"); if (format == String.Empty) throw new FormatException ("format is an empty string"); return ParseExact (input, new string [] {format}, formatProvider, styles); } public static DateTimeOffset ParseExact (string input, string[] formats, IFormatProvider formatProvider, DateTimeStyles styles) { if (input == null) throw new ArgumentNullException ("input"); if (input == String.Empty) throw new FormatException ("input is an empty string"); if (formats == null) throw new ArgumentNullException ("formats"); if (formats.Length == 0) throw new FormatException ("Invalid format specifier"); if ((styles & DateTimeStyles.AssumeLocal) != 0 && (styles & DateTimeStyles.AssumeUniversal) != 0) throw new ArgumentException ("styles parameter contains incompatible flags"); DateTimeOffset result; if (!ParseExact (input, formats, DateTimeFormatInfo.GetInstance (formatProvider), styles, out result)) throw new FormatException ("Invalid format string"); return result; } private static bool ParseExact (string input, string [] formats, DateTimeFormatInfo dfi, DateTimeStyles styles, out DateTimeOffset ret) { foreach (string format in formats) { if (format == null || format == String.Empty) throw new FormatException ("Invalid format string"); DateTimeOffset result; if (DoParse (input, format, false, out result, dfi, styles)) { ret = result; return true; } } ret = DateTimeOffset.MinValue; return false; } private static bool DoParse (string input, string format, bool exact, out DateTimeOffset result, DateTimeFormatInfo dfi, DateTimeStyles styles) { if ((styles & DateTimeStyles.AllowLeadingWhite) != 0) { format = format.TrimStart (null); input = input.TrimStart (null); } if ((styles & DateTimeStyles.AllowTrailingWhite) != 0) { format = format.TrimEnd (null); input = input.TrimEnd (null); } bool allow_white_spaces = false; if ((styles & DateTimeStyles.AllowInnerWhite) != 0) allow_white_spaces = true; result = DateTimeOffset.MinValue; bool useutc = false, use_invariants = false; if (format.Length == 1) { format = DateTimeUtils.GetStandardPattern (format[0], dfi, out useutc, out use_invariants, true); if (format == null) return false; } int year = -1; int month = -1; int day = -1; int partial_hour = -1; // for 'hh tt' formats int hour = -1; int minute = -1; int second = -1; double fraction = -1; int temp_int = -1; TimeSpan offset = TimeSpan.MinValue; int fi = 0; //format iterator int ii = 0; //input iterator while (fi < format.Length) { int tokLen; char ch = format [fi]; switch (ch) { case 'd': tokLen = DateTimeUtils.CountRepeat (format, fi, ch); if (day != -1 || tokLen > 4) return false; if (tokLen <= 2) ii += ParseNumber (input, ii, 2, tokLen == 2, allow_white_spaces, out day); else ii += ParseEnum (input, ii, tokLen == 3 ? dfi.AbbreviatedDayNames : dfi.DayNames, allow_white_spaces, out temp_int); break; case 'f': tokLen = DateTimeUtils.CountRepeat (format, fi, ch); ii += ParseNumber (input, ii, tokLen, true, allow_white_spaces, out temp_int); if (fraction >= 0 || tokLen > 7 || temp_int == -1) return false; fraction = (double)temp_int / Math.Pow (10, tokLen); break; case 'F': tokLen = DateTimeUtils.CountRepeat (format, fi, ch); int digits; int read = ParseNumber (input, ii, tokLen, true, allow_white_spaces, out temp_int, out digits); if (temp_int == -1) ii += ParseNumber (input, ii, digits, true, allow_white_spaces, out temp_int); else ii += read; if (fraction >= 0 || tokLen > 7 || temp_int == -1) return false; fraction = (double)temp_int / Math.Pow (10, digits); break; case 'h': tokLen = DateTimeUtils.CountRepeat (format, fi, ch); if (hour != -1 || tokLen > 2) return false; ii += ParseNumber (input, ii, 2, tokLen == 2, allow_white_spaces, out temp_int); if (temp_int == -1) return false; if (partial_hour == -1) partial_hour = temp_int; else hour = partial_hour + temp_int; break; case 'H': tokLen = DateTimeUtils.CountRepeat (format, fi, ch); if (hour != -1 || tokLen > 2) return false; ii += ParseNumber (input, ii, 2, tokLen == 2, allow_white_spaces, out hour); break; case 'm': tokLen = DateTimeUtils.CountRepeat (format, fi, ch); if (minute != -1 || tokLen > 2) return false; ii += ParseNumber (input, ii, 2, tokLen == 2, allow_white_spaces, out minute); break; case 'M': tokLen = DateTimeUtils.CountRepeat (format, fi, ch); if (month != -1 || tokLen > 4) return false; if (tokLen <= 2) ii += ParseNumber (input, ii, 2, tokLen == 2, allow_white_spaces, out month); else { ii += ParseEnum (input, ii, tokLen == 3 ? dfi.AbbreviatedMonthNames : dfi.MonthNames, allow_white_spaces, out month); month += 1; } break; case 's': tokLen = DateTimeUtils.CountRepeat (format, fi, ch); if (second != -1 || tokLen > 2) return false; ii += ParseNumber (input, ii, 2, tokLen == 2, allow_white_spaces, out second); break; case 't': tokLen = DateTimeUtils.CountRepeat (format, fi, ch); if (hour != -1 || tokLen > 2) return false; ii += ParseEnum (input, ii, tokLen == 1 ? new string[] {new string (dfi.AMDesignator[0], 1), new string (dfi.PMDesignator[0], 0)} : new string[] {dfi.AMDesignator, dfi.PMDesignator}, allow_white_spaces, out temp_int); if (temp_int == -1) return false; if (partial_hour == -1) partial_hour = temp_int * 12; else hour = partial_hour + temp_int * 12; break; case 'y': if (year != -1) return false; tokLen = DateTimeUtils.CountRepeat (format, fi, ch); if (tokLen <= 2) { ii += ParseNumber (input, ii, 2, tokLen == 2, allow_white_spaces, out year); if (year != -1) year = dfi.Calendar.ToFourDigitYear (year); } else if (tokLen <= 4) { // yyy and yyyy accept up to 5 digits with leading 0 int digit_parsed; ii += ParseNumber (input, ii, 5, false, allow_white_spaces, out year, out digit_parsed); if (digit_parsed < tokLen || (digit_parsed > tokLen && (year / Math.Pow (10, digit_parsed - 1) < 1))) return false; } else ii += ParseNumber (input, ii, tokLen, true, allow_white_spaces, out year); break; // The documentation is incorrect, they claim that K is the same as 'zz', but // it actually allows the format to contain 4 digits for the offset case 'K': tokLen = 1; int off_h, off_m = 0, sign; temp_int = 0; ii += ParseEnum (input, ii, new string [] {"-", "+"}, allow_white_spaces, out sign); ii += ParseNumber (input, ii, 4, false, false, out off_h); if (off_h == -1 || off_m == -1 || sign == -1) return false; if (sign == 0) sign = -1; offset = new TimeSpan (sign * off_h, sign * off_m, 0); break; case 'z': tokLen = DateTimeUtils.CountRepeat (format, fi, ch); if (offset != TimeSpan.MinValue || tokLen > 3) return false; off_m = 0; temp_int = 0; ii += ParseEnum (input, ii, new string [] {"-", "+"}, allow_white_spaces, out sign); ii += ParseNumber (input, ii, 2, tokLen != 1, false, out off_h); if (tokLen == 3) { ii += ParseEnum (input, ii, new string [] {dfi.TimeSeparator}, false, out temp_int); ii += ParseNumber (input, ii, 2, true, false, out off_m); } if (off_h == -1 || off_m == -1 || sign == -1) return false; if (sign == 0) sign = -1; offset = new TimeSpan (sign * off_h, sign * off_m, 0); break; case ':': tokLen = 1; ii += ParseEnum (input, ii, new string [] {dfi.TimeSeparator}, false, out temp_int); if (temp_int == -1) return false; break; case '/': tokLen = 1; ii += ParseEnum (input, ii, new string [] {dfi.DateSeparator}, false, out temp_int); if (temp_int == -1) return false; break; case '%': tokLen = 1; if (fi != 0) return false; break; case ' ': tokLen = 1; ii += ParseChar (input, ii, ' ', false, out temp_int); if (temp_int == -1) return false; break; case '\\': tokLen = 2; ii += ParseChar (input, ii, format [fi + 1], allow_white_spaces, out temp_int); if (temp_int == -1) return false; break; case '\'': case '"': tokLen = 1; while (ii < input.Length) { var ftoken = format [fi + tokLen]; ++tokLen; if (ftoken == format [fi]) { if (useutc && tokLen == 5 && input [ii - 3] == 'G' && input [ii - 2] == 'M' && input [ii - 1] == 'T') { offset = TimeSpan.Zero; } break; } if (ftoken != input [ii++]) return false; } break; default: //Console.WriteLine ("un-parsed character: {0}", ch); tokLen = 1; ii += ParseChar (input, ii, format [fi], allow_white_spaces, out temp_int); if (temp_int == -1) return false; break; } fi += tokLen; } //Console.WriteLine ("{0}-{1}-{2} {3}:{4} {5}", year, month, day, hour, minute, offset); if (offset == TimeSpan.MinValue) { if ((styles & DateTimeStyles.AssumeUniversal) != 0) { offset = TimeSpan.Zero; } else if ((styles & DateTimeStyles.AssumeLocal) != 0) { offset = use_invariants ? TimeSpan.Zero : TimeZone.CurrentTimeZone.GetUtcOffset (DateTime.Now); } } if (hour < 0) hour = 0; if (minute < 0) minute = 0; if (second < 0) second = 0; if (fraction < 0) fraction = 0; if (year > 0 && month > 0 && day > 0) { result = new DateTimeOffset (year, month, day, hour, minute, second, 0, offset); result = result.AddSeconds (fraction); if ((styles & DateTimeStyles.AdjustToUniversal) != 0) result = result.ToUniversalTime (); return true; } return false; } private static int ParseNumber (string input, int pos, int digits, bool leading_zero, bool allow_leading_white, out int result) { int digit_parsed; return ParseNumber (input, pos, digits, leading_zero, allow_leading_white, out result, out digit_parsed); } private static int ParseNumber (string input, int pos, int digits, bool leading_zero, bool allow_leading_white, out int result, out int digit_parsed) { int char_parsed = 0; digit_parsed = 0; result = 0; for (; allow_leading_white && pos < input.Length && input[pos] == ' '; pos++) char_parsed++; for (; pos < input.Length && Char.IsDigit (input[pos]) && digits > 0; pos ++, char_parsed++, digit_parsed++, digits --) result = 10 * result + (byte) (input[pos] - '0'); if (leading_zero && digits > 0) result = -1; if (digit_parsed == 0) result = -1; return char_parsed; } private static int ParseEnum (string input, int pos, string [] enums, bool allow_leading_white, out int result) { int char_parsed = 0; result = -1; for (; allow_leading_white && pos < input.Length && input[pos] == ' '; pos++) char_parsed ++; for (int i = 0; i < enums.Length; i++) if (input.Substring(pos).StartsWith (enums [i])) { result = i; break; } if (result >= 0) char_parsed += enums[result].Length; return char_parsed; } private static int ParseChar (string input, int pos, char c, bool allow_leading_white, out int result) { int char_parsed = 0; result = -1; for (; allow_leading_white && pos < input.Length && input[pos] == ' '; pos++, char_parsed++) ; if (pos < input.Length && input[pos] == c){ result = (int) c; char_parsed ++; } return char_parsed; } public TimeSpan Subtract (DateTimeOffset value) { return UtcDateTime - value.UtcDateTime; } public DateTimeOffset Subtract (TimeSpan value) { return Add (-value); } public static TimeSpan operator - (DateTimeOffset left, DateTimeOffset right) { return left.Subtract (right); } public static DateTimeOffset operator - (DateTimeOffset dateTimeOffset, TimeSpan timeSpan) { return dateTimeOffset.Subtract (timeSpan); } public long ToFileTime () { return UtcDateTime.ToFileTime (); } public DateTimeOffset ToLocalTime () { return new DateTimeOffset (UtcDateTime.ToLocalTime (), TimeZone.CurrentTimeZone.GetUtcOffset (UtcDateTime.ToLocalTime ())); } public DateTimeOffset ToOffset (TimeSpan offset) { return new DateTimeOffset (dt - utc_offset + offset, offset); } public override string ToString () { return ToString (null, null); } public string ToString (IFormatProvider formatProvider) { return ToString (null, formatProvider); } public string ToString (string format) { return ToString (format, null); } public string ToString (string format, IFormatProvider formatProvider) { DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance(formatProvider); if (format == null || format == String.Empty) format = dfi.ShortDatePattern + " " + dfi.LongTimePattern + " zzz"; bool to_utc = false, use_invariant = false; if (format.Length == 1) { char fchar = format [0]; try { format = DateTimeUtils.GetStandardPattern (fchar, dfi, out to_utc, out use_invariant, true); } catch { format = null; } if (format == null) throw new FormatException ("format is not one of the format specifier characters defined for DateTimeFormatInfo"); } return to_utc ? DateTimeUtils.ToString (UtcDateTime, TimeSpan.Zero, format, dfi) : DateTimeUtils.ToString (DateTime, Offset, format, dfi); } public DateTimeOffset ToUniversalTime () { return new DateTimeOffset (UtcDateTime, TimeSpan.Zero); } public static bool TryParse (string input, out DateTimeOffset result) { try { result = Parse (input); return true; } catch { result = MinValue; return false; } } public static bool TryParse (string input, IFormatProvider formatProvider, DateTimeStyles styles, out DateTimeOffset result) { try { result = Parse (input, formatProvider, styles); return true; } catch { result = MinValue; return false; } } public static bool TryParseExact (string input, string format, IFormatProvider formatProvider, DateTimeStyles styles, out DateTimeOffset result) { try { result = ParseExact (input, format, formatProvider, styles); return true; } catch { result = MinValue; return false; } } public static bool TryParseExact (string input, string[] formats, IFormatProvider formatProvider, DateTimeStyles styles, out DateTimeOffset result) { try { result = ParseExact (input, formats, formatProvider, styles); return true; } catch { result = MinValue; return false; } } public DateTime Date { get { return DateTime.SpecifyKind (dt.Date, DateTimeKind.Unspecified); } } public DateTime DateTime { get { return DateTime.SpecifyKind (dt, DateTimeKind.Unspecified); } } public int Day { get { return dt.Day; } } public DayOfWeek DayOfWeek { get { return dt.DayOfWeek; } } public int DayOfYear { get { return dt.DayOfYear; } } public int Hour { get { return dt.Hour; } } public DateTime LocalDateTime { get { return UtcDateTime.ToLocalTime (); } } public int Millisecond { get { return dt.Millisecond; } } public int Minute { get { return dt.Minute; } } public int Month { get { return dt.Month; } } public static DateTimeOffset Now { get { return new DateTimeOffset (DateTime.Now);} } public TimeSpan Offset { get { return utc_offset; } } public int Second { get { return dt.Second; } } public long Ticks { get { return dt.Ticks; } } public TimeSpan TimeOfDay { get { return dt.TimeOfDay; } } public DateTime UtcDateTime { get { return DateTime.SpecifyKind (dt - utc_offset, DateTimeKind.Utc); } } public static DateTimeOffset UtcNow { get { return new DateTimeOffset (DateTime.UtcNow); } } public long UtcTicks { get { return UtcDateTime.Ticks; } } public int Year { get { return dt.Year; } } } }