2 * System.DateTimeOffset
5 * Stephane Delcroix <stephane@delcroix.org>
6 * Marek Safar (marek.safar@gmail.com)
8 * Copyright (C) 2007 Novell, Inc (http://www.novell.com)
10 * Permission is hereby granted, free of charge, to any person obtaining
11 * a copy of this software and associated documentation files (the
12 * "Software"), to deal in the Software without restriction, including
13 * without limitation the rights to use, copy, modify, merge, publish,
14 * distribute, sublicense, and/or sell copies of the Software, and to
15 * permit persons to whom the Software is furnished to do so, subject to
16 * the following conditions:
18 * The above copyright notice and this permission notice shall be
19 * included in all copies or substantial portions of the Software.
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 #if NET_2_0 // Introduced by .NET 3.5 for 2.0 mscorlib
33 using System.Globalization;
34 using System.Runtime.InteropServices;
35 using System.Runtime.Serialization;
41 [StructLayout (LayoutKind.Auto)]
42 public struct DateTimeOffset : IComparable, IFormattable, ISerializable, IDeserializationCallback, IComparable<DateTimeOffset>, IEquatable<DateTimeOffset>
44 public static readonly DateTimeOffset MaxValue = new DateTimeOffset (DateTime.MaxValue, TimeSpan.Zero);
45 public static readonly DateTimeOffset MinValue = new DateTimeOffset (DateTime.MinValue, TimeSpan.Zero);
50 public DateTimeOffset (DateTime dateTime)
54 if (dateTime.Kind == DateTimeKind.Utc)
55 utc_offset = TimeSpan.Zero;
57 utc_offset = TimeZone.CurrentTimeZone.GetUtcOffset (dateTime);
59 if (UtcDateTime < DateTime.MinValue || UtcDateTime > DateTime.MaxValue)
60 throw new ArgumentOutOfRangeException ("The UTC date and time that results from applying the offset is earlier than MinValue or later than MaxValue.");
64 public DateTimeOffset (DateTime dateTime, TimeSpan offset)
66 if (dateTime.Kind == DateTimeKind.Utc && offset != TimeSpan.Zero)
67 throw new ArgumentException ("dateTime.Kind equals Utc and offset does not equal zero.");
69 if (dateTime.Kind == DateTimeKind.Local && offset != TimeZone.CurrentTimeZone.GetUtcOffset (dateTime))
70 throw new ArgumentException ("dateTime.Kind equals Local and offset does not equal the offset of the system's local time zone.");
72 if (offset.Ticks % TimeSpan.TicksPerMinute != 0)
73 throw new ArgumentException ("offset is not specified in whole minutes.");
75 if (offset < new TimeSpan (-14, 0 ,0) || offset > new TimeSpan (14, 0, 0))
76 throw new ArgumentOutOfRangeException ("offset is less than -14 hours or greater than 14 hours.");
81 if (UtcDateTime < DateTime.MinValue || UtcDateTime > DateTime.MaxValue)
82 throw new ArgumentOutOfRangeException ("The UtcDateTime property is earlier than MinValue or later than MaxValue.");
85 public DateTimeOffset (long ticks, TimeSpan offset) : this (new DateTime (ticks), offset)
89 public DateTimeOffset (int year, int month, int day, int hour, int minute, int second, TimeSpan offset) :
90 this (new DateTime (year, month, day, hour, minute, second), offset)
94 public DateTimeOffset (int year, int month, int day, int hour, int minute, int second, int millisecond, TimeSpan offset) :
95 this (new DateTime (year, month, day, hour, minute, second, millisecond), offset)
99 public DateTimeOffset (int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar, TimeSpan offset) :
100 this (new DateTime (year, month, day, hour, minute, second, millisecond, calendar), offset)
104 public DateTimeOffset Add (TimeSpan timeSpan)
106 return new DateTimeOffset (dt.Add (timeSpan), utc_offset);
109 public DateTimeOffset AddDays (double days)
111 return new DateTimeOffset (dt.AddDays (days), utc_offset);
114 public DateTimeOffset AddHours (double hours)
116 return new DateTimeOffset (dt.AddHours (hours), utc_offset);
119 public static DateTimeOffset operator + (DateTimeOffset dateTimeTz, TimeSpan timeSpan)
121 return dateTimeTz.Add (timeSpan);
124 public DateTimeOffset AddMilliseconds (double milliseconds)
126 return new DateTimeOffset (dt.AddMilliseconds (milliseconds), utc_offset);
129 public DateTimeOffset AddMinutes (double minutes)
131 return new DateTimeOffset (dt.AddMinutes (minutes), utc_offset);
134 public DateTimeOffset AddMonths (int months)
136 return new DateTimeOffset (dt.AddMonths (months), utc_offset);
139 public DateTimeOffset AddSeconds (double seconds)
141 return new DateTimeOffset (dt.AddSeconds (seconds), utc_offset);
144 public DateTimeOffset AddTicks (long ticks)
146 return new DateTimeOffset (dt.AddTicks (ticks), utc_offset);
149 public DateTimeOffset AddYears (int years)
151 return new DateTimeOffset (dt.AddYears (years), utc_offset);
154 public static int Compare (DateTimeOffset first, DateTimeOffset second)
156 return first.CompareTo (second);
159 public int CompareTo (DateTimeOffset other)
161 return UtcDateTime.CompareTo (other.UtcDateTime);
164 public int CompareTo (object other)
166 return CompareTo ((DateTimeOffset) other);
169 public static bool operator == (DateTimeOffset left, DateTimeOffset right)
171 return left.Equals (right);
174 public bool Equals (DateTimeOffset other)
176 return UtcDateTime == other.UtcDateTime;
179 public override bool Equals (object other)
181 return UtcDateTime == ((DateTimeOffset) other).UtcDateTime;
184 public static bool Equals (DateTimeOffset first, DateTimeOffset second)
186 return first.Equals (second);
189 public bool EqualsExact (DateTimeOffset other)
191 return dt == other.dt && utc_offset == other.utc_offset;
194 public static DateTimeOffset FromFileTime (long fileTime)
196 if (fileTime < 0 || fileTime > MaxValue.Ticks)
197 throw new ArgumentOutOfRangeException ("fileTime is less than zero or greater than DateTimeOffset.MaxValue.Ticks.");
199 return new DateTimeOffset (DateTime.FromFileTime (fileTime), TimeZone.CurrentTimeZone.GetUtcOffset (DateTime.FromFileTime (fileTime)));
203 public override int GetHashCode ()
205 return dt.GetHashCode () ^ utc_offset.GetHashCode ();
208 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
211 throw new ArgumentNullException ("info");
213 info.AddValue ("datetime", dt);
214 info.AddValue ("offset", utc_offset);
217 public static bool operator > (DateTimeOffset left, DateTimeOffset right)
219 return left.UtcDateTime > right.UtcDateTime;
222 public static bool operator >= (DateTimeOffset left, DateTimeOffset right)
224 return left.UtcDateTime >= right.UtcDateTime;
227 public static implicit operator DateTimeOffset (DateTime dateTime)
229 return new DateTimeOffset (dateTime);
232 public static bool operator != (DateTimeOffset left, DateTimeOffset right)
234 return left.UtcDateTime != right.UtcDateTime;
237 public static bool operator < (DateTimeOffset left, DateTimeOffset right)
239 return left.UtcDateTime < right.UtcDateTime;
242 public static bool operator <= (DateTimeOffset left, DateTimeOffset right)
244 return left.UtcDateTime <= right.UtcDateTime;
248 void IDeserializationCallback.OnDeserialization (object sender)
252 public static DateTimeOffset Parse (string input)
254 return Parse (input, null);
257 public static DateTimeOffset Parse (string input, IFormatProvider formatProvider)
259 return Parse (input, formatProvider, DateTimeStyles.AllowWhiteSpaces);
263 public static DateTimeOffset Parse (string input, IFormatProvider formatProvider, DateTimeStyles styles)
266 throw new ArgumentNullException ("input");
268 throw new NotImplementedException ();
271 public static DateTimeOffset ParseExact (string input, string format, IFormatProvider formatProvider)
273 return ParseExact (input, format, formatProvider, DateTimeStyles.AssumeLocal);
276 public static DateTimeOffset ParseExact (string input, string format, IFormatProvider formatProvider, DateTimeStyles styles)
279 throw new ArgumentNullException ("format");
281 if (format == String.Empty)
282 throw new FormatException ("format is an empty string");
284 return ParseExact (input, new string [] {format}, formatProvider, styles);
287 public static DateTimeOffset ParseExact (string input, string[] formats, IFormatProvider formatProvider, DateTimeStyles styles)
290 throw new ArgumentNullException ("input");
292 if (input == String.Empty)
293 throw new FormatException ("input is an empty string");
296 throw new ArgumentNullException ("formats");
298 if (formats.Length == 0)
299 throw new FormatException ("Invalid format specifier");
301 DateTimeOffset result;
302 if (!ParseExact (input, formats, DateTimeFormatInfo.GetInstance (formatProvider), styles, out result))
303 throw new FormatException ();
308 private static bool ParseExact (string input, string [] formats,
309 DateTimeFormatInfo dfi, DateTimeStyles styles, out DateTimeOffset ret)
311 foreach (string format in formats)
313 if (format == null || format == String.Empty)
314 throw new FormatException ("Invlid Format Sting");
316 DateTimeOffset result;
317 if (DoParse (input, format, false, out result, dfi, styles)) {
322 ret = DateTimeOffset.MinValue;
326 private static bool DoParse (string input,
329 out DateTimeOffset result,
330 DateTimeFormatInfo dfi,
331 DateTimeStyles styles)
333 bool useutc=false, use_invariants = false;
334 if (format.Length == 1)
335 format = DateTimeUtils.GetStandardPattern (format[0], dfi, out useutc, out use_invariants, true);
342 TimeSpan offset = TimeSpan.Zero;
344 int fi = 0; //format iterator
345 int ii = 0; //input iterator
346 while (fi < format.Length) {
348 char ch = format [fi];
352 tokLen = DateTimeUtils.CountRepeat (format, fi, ch);
356 if (input.Length != ii + 1 && System.Char.IsDigit (input[ii + 1]))
358 day = Int32.Parse (input.Substring (ii, l));
362 day = Int32.Parse (input.Substring (ii, 2));
367 ii = input.IndexOf (format [fi + tokLen]);
370 throw new FormatException ();
376 tokLen = DateTimeUtils.CountRepeat (format, fi, ch);
380 if (input.Length != ii + 1 && System.Char.IsDigit (input[ii + 1]))
384 hour += Int32.Parse (input.Substring (ii, l));
390 hour += Int32.Parse (input.Substring (ii, 2));
394 throw new FormatException ();
399 tokLen = DateTimeUtils.CountRepeat (format, fi, ch);
403 if (input.Length != ii + 1 && System.Char.IsDigit (input[ii + 1]))
405 hour = Int32.Parse (input.Substring (ii, l));
409 hour = Int32.Parse (input.Substring (ii, 2));
413 throw new FormatException ();
418 tokLen = DateTimeUtils.CountRepeat (format, fi, ch);
422 if (input.Length != ii + 1 && System.Char.IsDigit (input[ii + 1]))
424 minute = Int32.Parse (input.Substring (ii, l));
428 minute = Int32.Parse (input.Substring (ii, 2));
432 throw new FormatException ();
437 tokLen = DateTimeUtils.CountRepeat (format, fi, ch);
441 if (input.Length != ii + 1 && System.Char.IsDigit (input[ii + 1]))
443 month = Int32.Parse (input.Substring (ii, l));
447 month = Int32.Parse (input.Substring (ii, 2));
451 for (int i = 0; i < 13; i++)
452 if (input.Substring (ii).StartsWith (dfi.AbbreviatedMonthNames [i])) {
454 ii += dfi.AbbreviatedMonthNames [i].Length;
459 for (int i = 0; i < 13; i++)
460 if (input.Substring (ii).StartsWith (dfi.MonthNames [i])) {
462 ii += dfi.MonthNames [i].Length;
467 throw new FormatException ();
471 tokLen = DateTimeUtils.CountRepeat (format, fi, ch);
474 if (input [ii] == dfi.PMDesignator [0]) {
478 } else if (input [ii] != dfi.AMDesignator [0])
479 throw new FormatException ();
483 if (input.Substring (ii).StartsWith (dfi.PMDesignator)) {
487 ii += dfi.PMDesignator.Length;
488 } else if (input.Substring (ii).StartsWith (dfi.AMDesignator))
489 ii += dfi.AMDesignator.Length;
491 throw new FormatException ();
495 throw new FormatException ();
500 tokLen = DateTimeUtils.CountRepeat (format, fi, ch);
504 if (input.Length != ii + 1 && System.Char.IsDigit (input[ii + 1]))
506 year = Int32.Parse (input.Substring (ii, l));
507 year += DateTime.Now.Year - DateTime.Now.Year % 100;
511 year = Int32.Parse (input.Substring (ii, 2));
512 year += DateTime.Now.Year - DateTime.Now.Year % 100;
516 throw new NotImplementedException ();
517 //ToString ("yyy") seems to print 4-digits year...
518 year = Int32.Parse (input.Substring (ii, 3));
519 year += DateTime.Now.Year - DateTime.Now.Year % 1000;
523 year = Int32.Parse (input.Substring (ii, tokLen));
529 tokLen = DateTimeUtils.CountRepeat (format, fi, ch);
530 int off_h, off_m = 0, sign;
532 case '+': sign = 1; break;
533 case '-': sign = -1; break;
535 throw new FormatException ();
542 if (input.Length != ii + 1 && System.Char.IsDigit (input[ii + 1]))
544 off_h = Int32.Parse (input.Substring (ii, l));
547 off_h = Int32.Parse (input.Substring (ii, 2));
551 off_h = Int32.Parse (input.Substring (ii, 2));
553 if (input [ii++] != ':')
554 throw new FormatException ();
555 off_m = Int32.Parse (input.Substring (ii, 2));
559 throw new FormatException ();
562 offset = new TimeSpan (sign * off_h, sign * off_m, 0);
566 if (!input.Substring (ii).StartsWith (dfi.TimeSeparator))
567 throw new FormatException ();
568 ii += dfi.TimeSeparator.Length;
572 if (!input.Substring (ii).StartsWith (dfi.DateSeparator))
573 throw new FormatException ();
574 ii += dfi.DateSeparator.Length;
583 Console.WriteLine ("un-parsed character: {0}", ch);
589 if (day > 0 && month > 0 && year > 0 && hour > 0 && minute > 0) {
590 result = new DateTimeOffset (year, month, day, hour, minute, 0, offset);
593 if (day > 0 && month > 0 && year > 0) {
594 result = new DateTimeOffset (year, month, day, 0, 0, 0, offset);
598 result = DateTimeOffset.MinValue;
602 public TimeSpan Subtract (DateTimeOffset other)
604 return UtcDateTime - other.UtcDateTime;
607 public DateTimeOffset Subtract (TimeSpan timeSpan)
609 return Add (-timeSpan);
612 public static TimeSpan operator - (DateTimeOffset left, DateTimeOffset right)
614 return left.Subtract (right);
617 public static DateTimeOffset operator - (DateTimeOffset dateTimeTz, TimeSpan timeSpan)
619 return dateTimeTz.Subtract (timeSpan);
622 public long ToFileTime ()
624 return UtcDateTime.ToFileTime ();
627 public DateTimeOffset ToLocalTime ()
629 return new DateTimeOffset (UtcDateTime.ToLocalTime (), TimeZone.CurrentTimeZone.GetUtcOffset (UtcDateTime.ToLocalTime ()));
632 public DateTimeOffset ToOffset (TimeSpan offset)
634 return new DateTimeOffset (dt - utc_offset + offset, offset);
637 public override string ToString ()
639 return ToString (null, null);
642 public string ToString (IFormatProvider formatProvider)
644 return ToString (null, formatProvider);
647 public string ToString (string format)
649 return ToString (format, null);
652 public string ToString (string format, IFormatProvider formatProvider)
654 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance(formatProvider);
656 if (format == null || format == String.Empty)
657 format = dfi.ShortDatePattern + " " + dfi.LongTimePattern + " zzz";
659 bool to_utc = false, use_invariant = false;
660 if (format.Length == 1) {
661 char fchar = format [0];
663 format = DateTimeUtils.GetStandardPattern (fchar, dfi, out to_utc, out use_invariant, true);
669 throw new FormatException ("format is not one of the format specifier characters defined for DateTimeFormatInfo");
671 return to_utc ? DateTimeUtils.ToString (UtcDateTime, TimeSpan.Zero, format, dfi) : DateTimeUtils.ToString (DateTime, Offset, format, dfi);
674 public DateTimeOffset ToUniversalTime ()
676 return new DateTimeOffset (UtcDateTime, TimeSpan.Zero);
679 public static bool TryParse (string input, out DateTimeOffset result)
682 result = Parse (input);
690 public static bool TryParse (string input, IFormatProvider formatProvider, DateTimeStyles styles, out DateTimeOffset result)
693 result = Parse (input, formatProvider, styles);
701 public static bool TryParseExact (string input, string format, IFormatProvider formatProvider, DateTimeStyles styles, out DateTimeOffset result)
704 result = ParseExact (input, format, formatProvider, styles);
712 public static bool TryParseExact (string input, string[] formats, IFormatProvider formatProvider, DateTimeStyles styles, out DateTimeOffset result)
715 result = ParseExact (input, formats, formatProvider, styles);
723 public DateTime Date {
724 get { return DateTime.SpecifyKind (dt.Date, DateTimeKind.Unspecified); }
727 public DateTime DateTime {
728 get { return DateTime.SpecifyKind (dt, DateTimeKind.Unspecified); }
732 get { return dt.Day; }
735 public DayOfWeek DayOfWeek {
736 get { return dt.DayOfWeek; }
739 public int DayOfYear {
740 get { return dt.DayOfYear; }
744 get { return dt.Hour; }
747 public DateTime LocalDateTime {
748 get { return UtcDateTime.ToLocalTime (); }
751 public int Millisecond {
752 get { return dt.Millisecond; }
756 get { return dt.Minute; }
760 get { return dt.Month; }
763 public static DateTimeOffset Now {
764 get { return new DateTimeOffset (DateTime.Now);}
767 public TimeSpan Offset {
768 get { return utc_offset; }
772 get { return dt.Second; }
776 get { return dt.Ticks; }
779 public TimeSpan TimeOfDay {
780 get { return dt.TimeOfDay; }
783 public DateTime UtcDateTime {
784 get { return DateTime.SpecifyKind (dt - utc_offset, DateTimeKind.Utc); }
787 public static DateTimeOffset UtcNow {
788 get { return new DateTimeOffset (DateTime.UtcNow); }
791 public long UtcTicks {
792 get { return UtcDateTime.Ticks; }
796 get { return dt.Year; }