2010-03-12 Jb Evain <jbevain@novell.com>
[mono.git] / mcs / class / corlib / System / DateTimeOffset.cs
1 /*
2  * System.DateTimeOffset.cs
3  *
4  * Author(s)
5  *      Stephane Delcroix <stephane@delcroix.org>
6  *      Marek Safar (marek.safar@gmail.com)
7  *
8  *  Copyright (C) 2007 Novell, Inc (http://www.novell.com) 
9  *
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:
17  * 
18  * The above copyright notice and this permission notice shall be
19  * included in all copies or substantial portions of the Software.
20  * 
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.
28  */
29
30
31 using System.Globalization;
32 using System.Runtime.InteropServices;
33 using System.Runtime.Serialization;
34 using System.Text;
35
36 namespace System
37 {
38         [Serializable]
39         [StructLayout (LayoutKind.Auto)]
40         public struct DateTimeOffset : IComparable, IFormattable, ISerializable, IDeserializationCallback, IComparable<DateTimeOffset>, IEquatable<DateTimeOffset>
41         {
42 #if MONOTOUCH
43                 static DateTimeOffset () {
44                         if (MonoTouchAOTHelper.FalseFlag) {
45                                 var comparer = new System.Collections.Generic.GenericComparer <DateTimeOffset> ();
46                                 var eqcomparer = new System.Collections.Generic.GenericEqualityComparer <DateTimeOffset> ();
47                         }
48                 }
49 #endif
50                 public static readonly DateTimeOffset MaxValue = new DateTimeOffset (DateTime.MaxValue, TimeSpan.Zero);
51                 public static readonly DateTimeOffset MinValue = new DateTimeOffset (DateTime.MinValue, TimeSpan.Zero);
52                 
53                 DateTime dt;
54                 TimeSpan utc_offset;
55         
56                 public DateTimeOffset (DateTime dateTime)
57                 {
58                         dt = dateTime;
59
60                         if (dateTime.Kind == DateTimeKind.Utc)
61                                 utc_offset = TimeSpan.Zero;
62                         else 
63                                 utc_offset = TimeZone.CurrentTimeZone.GetUtcOffset (dateTime);
64                                 
65                         if (UtcDateTime < DateTime.MinValue || UtcDateTime > DateTime.MaxValue)
66                                 throw new ArgumentOutOfRangeException ("The UTC date and time that results from applying the offset is earlier than MinValue or later than MaxValue.");
67
68                 }
69                 
70                 public DateTimeOffset (DateTime dateTime, TimeSpan offset)
71                 {
72                         if (dateTime.Kind == DateTimeKind.Utc && offset != TimeSpan.Zero)
73                                 throw new ArgumentException ("dateTime.Kind equals Utc and offset does not equal zero.");
74
75                         if (dateTime.Kind == DateTimeKind.Local && offset != TimeZone.CurrentTimeZone.GetUtcOffset (dateTime))
76                                 throw new ArgumentException ("dateTime.Kind equals Local and offset does not equal the offset of the system's local time zone.");
77
78                         if (offset.Ticks % TimeSpan.TicksPerMinute != 0)
79                                 throw new ArgumentException ("offset is not specified in whole minutes.");
80
81                         if (offset < new TimeSpan (-14, 0 ,0) || offset > new TimeSpan (14, 0, 0))
82                                 throw new ArgumentOutOfRangeException ("offset is less than -14 hours or greater than 14 hours.");
83
84                         dt = dateTime;
85                         utc_offset = offset;
86
87                         if (UtcDateTime < DateTime.MinValue || UtcDateTime > DateTime.MaxValue)
88                                 throw new ArgumentOutOfRangeException ("The UtcDateTime property is earlier than MinValue or later than MaxValue.");
89                 }
90
91                 public DateTimeOffset (long ticks, TimeSpan offset) : this (new DateTime (ticks), offset)
92                 {
93                 }
94
95                 public DateTimeOffset (int year, int month, int day, int hour, int minute, int second, TimeSpan offset) : 
96                         this (new DateTime (year, month, day, hour, minute, second), offset)
97                 {
98                 }
99
100                 public DateTimeOffset (int year, int month, int day, int hour, int minute, int second, int millisecond, TimeSpan offset) :
101                         this (new DateTime (year, month, day, hour, minute, second, millisecond), offset)
102                 {
103                 }
104
105                 public DateTimeOffset (int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar, TimeSpan offset) :
106                         this (new DateTime (year, month, day, hour, minute, second, millisecond, calendar), offset)
107                 {
108                 }
109
110                 public DateTimeOffset Add (TimeSpan timeSpan)
111                 {
112                         return new DateTimeOffset (dt.Add (timeSpan).Ticks, utc_offset);
113                 }
114         
115                 public DateTimeOffset AddDays (double days)
116                 {
117                         return new DateTimeOffset (dt.AddDays (days).Ticks, utc_offset);        
118                 }
119                 
120                 public DateTimeOffset AddHours (double hours)
121                 {
122                         return new DateTimeOffset (dt.AddHours (hours).Ticks, utc_offset);
123                 }
124
125                 public static DateTimeOffset operator + (DateTimeOffset dateTimeTz, TimeSpan timeSpan)
126                 {
127                         return dateTimeTz.Add (timeSpan);
128                 }
129
130                 public DateTimeOffset AddMilliseconds (double milliseconds)
131                 {
132                         return new DateTimeOffset (dt.AddMilliseconds (milliseconds).Ticks, utc_offset);
133                 }
134
135                 public DateTimeOffset AddMinutes (double minutes)
136                 {
137                         return new DateTimeOffset (dt.AddMinutes (minutes).Ticks, utc_offset);  
138                 }
139
140                 public DateTimeOffset AddMonths (int months)
141                 {
142                         return new DateTimeOffset (dt.AddMonths (months).Ticks, utc_offset);
143                 }
144
145                 public DateTimeOffset AddSeconds (double seconds)
146                 {
147                         return new DateTimeOffset (dt.AddSeconds (seconds).Ticks, utc_offset);
148                 }
149
150                 public DateTimeOffset AddTicks (long ticks)
151                 {
152                         return new DateTimeOffset (dt.AddTicks (ticks).Ticks, utc_offset);      
153                 }
154
155                 public DateTimeOffset AddYears (int years)
156                 {
157                         return new DateTimeOffset (dt.AddYears (years).Ticks, utc_offset);
158                 }
159
160                 public static int Compare (DateTimeOffset first, DateTimeOffset second)
161                 {
162                         return first.CompareTo (second);        
163                 }
164
165                 public int CompareTo (DateTimeOffset other)
166                 {
167                         return UtcDateTime.CompareTo (other.UtcDateTime);
168                 }
169
170                 int IComparable.CompareTo (object obj)
171                 {
172                         return CompareTo ((DateTimeOffset) obj);
173                 }
174
175                 public static bool operator == (DateTimeOffset left, DateTimeOffset right)
176                 {
177                         return left.Equals (right);     
178                 }
179
180                 public bool Equals (DateTimeOffset other)
181                 {
182                         return UtcDateTime == other.UtcDateTime;
183                 }
184
185                 public override bool Equals (object obj)
186                 {
187                         if (obj is DateTimeOffset)
188                                 return UtcDateTime == ((DateTimeOffset) obj).UtcDateTime;
189                         return false;
190                 }
191
192                 public static bool Equals (DateTimeOffset first, DateTimeOffset second)
193                 {
194                         return first.Equals (second);   
195                 }
196
197                 public bool EqualsExact (DateTimeOffset other)
198                 {
199                         return dt == other.dt && utc_offset == other.utc_offset;        
200                 }
201
202                 public static DateTimeOffset FromFileTime (long fileTime)
203                 {
204                         if (fileTime < 0 || fileTime > MaxValue.Ticks)
205                                 throw new ArgumentOutOfRangeException ("fileTime is less than zero or greater than DateTimeOffset.MaxValue.Ticks.");
206                         
207                         return new DateTimeOffset (DateTime.FromFileTime (fileTime), TimeZone.CurrentTimeZone.GetUtcOffset (DateTime.FromFileTime (fileTime)));
208
209                 }
210
211                 public override int GetHashCode ()
212                 {
213                         return dt.GetHashCode () ^ utc_offset.GetHashCode ();
214                 }
215
216                 [System.Security.Permissions.SecurityPermission (System.Security.Permissions.SecurityAction.LinkDemand, SerializationFormatter = true)]
217                 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
218                 {
219                         if (info == null)
220                                 throw new ArgumentNullException ("info");
221                         // An example SOAP serialization on MSFT is the following, so field 
222                         // names "DateTime" and "OffsetMinutes":
223                         //    <SOAP-ENV:Envelope ...>
224                         //    <SOAP-ENV:Body>
225                         //    <a1:DateTimeOffset id="ref-1" xmlns:a1="http://schemas.microsoft.com/clr/ns/System">
226                         //    <DateTime xsi:type="xsd:dateTime">2007-01-02T12:30:50.0000000+00:00</DateTime>
227                         //    <OffsetMinutes>0</OffsetMinutes>
228                         //    </a1:DateTimeOffset>
229                         //    </SOAP-ENV:Body>
230                         //    </SOAP-ENV:Envelope>
231                         DateTime dt0 = new DateTime (dt.Ticks).Subtract (utc_offset);
232                         info.AddValue ("DateTime", dt0);
233                         // MSFT BinaryFormatter output contains primitive code 6, i.e. Int16.
234                         info.AddValue ("OffsetMinutes", (Int16)utc_offset.TotalMinutes);
235                 }
236
237                 [System.Security.Permissions.SecurityPermission (System.Security.Permissions.SecurityAction.LinkDemand, SerializationFormatter = true)]
238                 private DateTimeOffset(SerializationInfo info, StreamingContext context)
239                 {
240                         DateTime dt0 = (DateTime)info.GetValue ("DateTime", typeof(DateTime));
241                         Int16 totalMinutes = info.GetInt16 ("OffsetMinutes");
242                         utc_offset = TimeSpan.FromMinutes(totalMinutes);
243                         dt = dt0.Add(utc_offset);
244                 }
245
246                 public static bool operator > (DateTimeOffset left, DateTimeOffset right)
247                 {
248                         return left.UtcDateTime > right.UtcDateTime;
249                 }                       
250
251                 public static bool operator >= (DateTimeOffset left, DateTimeOffset right)
252                 {
253                         return left.UtcDateTime >= right.UtcDateTime;
254                 }                       
255
256                 public static implicit operator DateTimeOffset (DateTime dateTime)
257                 {
258                         return new DateTimeOffset (dateTime);
259                 }
260
261                 public static bool operator != (DateTimeOffset left, DateTimeOffset right)
262                 {
263                         return left.UtcDateTime != right.UtcDateTime;
264                 }
265
266                 public static bool operator < (DateTimeOffset left, DateTimeOffset right)
267                 {
268                         return left.UtcDateTime < right.UtcDateTime;
269                 }
270                 
271                 public static bool operator <= (DateTimeOffset left, DateTimeOffset right)
272                 {
273                         return left.UtcDateTime <= right.UtcDateTime;
274                 }
275         
276                 [MonoTODO]
277                 void IDeserializationCallback.OnDeserialization (object sender)
278                 {
279                 }
280
281                 public static DateTimeOffset Parse (string input)
282                 {
283                         return Parse (input, null);
284                 }
285
286                 public static DateTimeOffset Parse (string input, IFormatProvider formatProvider)
287                 {
288                         return Parse (input, formatProvider, DateTimeStyles.AllowWhiteSpaces);
289                 }
290
291                 public static DateTimeOffset Parse (string input, IFormatProvider formatProvider, DateTimeStyles styles)
292                 {
293                         if (input == null)
294                                 throw new ArgumentNullException ("input");
295
296                         DateTime d;
297                         DateTimeOffset dto;
298                         Exception exception = null;
299                         if (!DateTime.CoreParse (input, formatProvider, styles, out d, out dto, true, ref exception))
300                                 throw exception;
301
302                         if (d.Ticks != 0 && dto.Ticks == 0)
303                                 throw new ArgumentOutOfRangeException ("The UTC representation falls outside the 1-9999 year range");
304
305                         return dto;
306                 }
307
308                 public static DateTimeOffset ParseExact (string input, string format, IFormatProvider formatProvider)
309                 {
310                         return ParseExact (input, format, formatProvider, DateTimeStyles.AssumeLocal);
311                 }
312
313                 public static DateTimeOffset ParseExact (string input, string format, IFormatProvider formatProvider, DateTimeStyles styles)
314                 {
315                         if (format == null)
316                                 throw new ArgumentNullException ("format");
317
318                         if (format == String.Empty)
319                                 throw new FormatException ("format is an empty string");
320
321                         return ParseExact (input, new string [] {format}, formatProvider, styles);
322                 }
323
324                 public static DateTimeOffset ParseExact (string input, string[] formats, IFormatProvider formatProvider, DateTimeStyles styles)
325                 {
326                         if (input == null)
327                                 throw new ArgumentNullException ("input");
328
329                         if (input == String.Empty)
330                                 throw new FormatException ("input is an empty string");
331
332                         if (formats == null)
333                                 throw new ArgumentNullException ("formats");
334
335                         if (formats.Length == 0)
336                                 throw new FormatException ("Invalid format specifier");
337
338                         if ((styles & DateTimeStyles.AssumeLocal) != 0 && (styles & DateTimeStyles.AssumeUniversal) != 0)
339                                 throw new ArgumentException ("styles parameter contains incompatible flags");
340
341                         DateTimeOffset result;
342                         if (!ParseExact (input, formats, DateTimeFormatInfo.GetInstance (formatProvider), styles, out result))
343                                 throw new FormatException ("Invalid format string");
344
345                         return result;
346                 }
347
348                 private static bool ParseExact (string input, string [] formats,
349                                 DateTimeFormatInfo dfi, DateTimeStyles styles, out DateTimeOffset ret)
350                 {
351                         foreach (string format in formats)
352                         {
353                                 if (format == null || format == String.Empty)
354                                         throw new FormatException ("Invalid format string");
355
356                                 DateTimeOffset result;
357                                 if (DoParse (input, format, false, out result, dfi, styles)) {
358                                         ret = result;
359                                         return true;
360                                 }
361                         }
362                         ret = DateTimeOffset.MinValue;
363                         return false;
364                 }
365
366                 private static bool DoParse (string input, 
367                                 string format,
368                                 bool exact,
369                                 out DateTimeOffset result,
370                                 DateTimeFormatInfo dfi,
371                                 DateTimeStyles styles)
372                 {
373                         if ((styles & DateTimeStyles.AllowLeadingWhite) != 0) {
374                                 format = format.TrimStart (null);
375                                 input = input.TrimStart (null);
376                         }
377
378                         if ((styles & DateTimeStyles.AllowTrailingWhite) != 0) {
379                                 format = format.TrimEnd (null);
380                                 input = input.TrimEnd (null);
381                         }
382
383                         bool allow_white_spaces = false;
384                         if ((styles & DateTimeStyles.AllowInnerWhite) != 0)
385                                 allow_white_spaces = true;
386
387                         bool useutc = false, use_invariants = false;
388                         if (format.Length == 1)
389                                 format = DateTimeUtils.GetStandardPattern (format[0], dfi, out useutc, out use_invariants, true);
390
391                         int year = -1;
392                         int month = -1;
393                         int day = -1;
394                         int partial_hour = -1; // for 'hh tt' formats
395                         int hour = -1;
396                         int minute = -1;
397                         int second = -1;
398                         double fraction = -1;
399                         int temp_int = -1;
400                         TimeSpan offset = TimeSpan.MinValue;
401
402                         result = DateTimeOffset.MinValue;
403
404                         int fi = 0; //format iterator
405                         int ii = 0; //input iterator
406                         while (fi < format.Length) {
407                                 int tokLen;
408                                 char ch = format [fi];
409
410                                 switch (ch) {
411                                 case 'd':
412                                         tokLen = DateTimeUtils.CountRepeat (format, fi, ch);
413                                         if (day != -1 || tokLen > 4)
414                                                 return false;
415
416                                         if (tokLen <= 2)
417                                                 ii += ParseNumber (input, ii, 2, tokLen == 2, allow_white_spaces, out day);
418                                         else
419                                                 ii += ParseEnum (input, ii, tokLen == 3 ? dfi.AbbreviatedDayNames : dfi.DayNames, allow_white_spaces, out temp_int); 
420                                         break;
421                                 case 'f':
422                                         tokLen = DateTimeUtils.CountRepeat (format, fi, ch);
423                                         ii += ParseNumber (input, ii, tokLen, true, allow_white_spaces, out temp_int);
424                                         if (fraction >= 0 || tokLen > 7 || temp_int == -1)
425                                                 return false;
426                                         fraction = (double)temp_int / Math.Pow (10, tokLen);
427                                         break;
428                                 case 'F':
429                                         tokLen = DateTimeUtils.CountRepeat (format, fi, ch);
430                                         int digits;
431                                         int read = ParseNumber (input, ii, tokLen, true, allow_white_spaces, out temp_int, out digits);
432                                         if (temp_int == -1)
433                                                 ii += ParseNumber (input, ii, digits, true, allow_white_spaces, out temp_int);
434                                         else
435                                                 ii += read;
436                                         if (fraction >= 0 || tokLen > 7 || temp_int == -1)
437                                                 return false;   
438                                         fraction = (double)temp_int / Math.Pow (10, digits);    
439                                         break;
440                                 case 'h':
441                                         tokLen = DateTimeUtils.CountRepeat (format, fi, ch);
442                                         if (hour != -1 || tokLen > 2)
443                                                 return false;
444
445                                         ii += ParseNumber (input, ii, 2, tokLen == 2, allow_white_spaces, out temp_int);
446                                         if (temp_int == -1)
447                                                 return false;
448
449                                         if (partial_hour == -1)
450                                                 partial_hour = temp_int;
451                                         else 
452                                                 hour = partial_hour + temp_int;
453                                         break;
454                                 case 'H':
455                                         tokLen = DateTimeUtils.CountRepeat (format, fi, ch);
456                                         if (hour != -1 || tokLen > 2)
457                                                 return false;
458
459                                         ii += ParseNumber (input, ii, 2, tokLen == 2, allow_white_spaces, out hour);
460                                         break;
461                                 case 'm':
462                                         tokLen = DateTimeUtils.CountRepeat (format, fi, ch);
463                                         if (minute != -1 || tokLen > 2)
464                                                 return false;
465
466                                         ii += ParseNumber (input, ii, 2, tokLen == 2, allow_white_spaces, out minute);
467                                         break;
468                                 case 'M':
469                                         tokLen = DateTimeUtils.CountRepeat (format, fi, ch);
470                                         if (month != -1 || tokLen > 4)
471                                                 return false;
472
473                                         if (tokLen <= 2)
474                                                 ii += ParseNumber (input, ii, 2, tokLen == 2, allow_white_spaces, out month);
475                                         else {
476                                                 ii += ParseEnum (input, ii, tokLen == 3 ? dfi.AbbreviatedMonthNames : dfi.MonthNames, allow_white_spaces, out month);
477                                                 month += 1;
478                                         }
479
480                                         break;
481                                 case 's':
482                                         tokLen = DateTimeUtils.CountRepeat (format, fi, ch);
483                                         if (second != -1 || tokLen > 2)
484                                                 return false;
485                                         ii += ParseNumber (input, ii, 2, tokLen == 2, allow_white_spaces, out second);
486                                         break;
487                                 case 't':
488                                         tokLen = DateTimeUtils.CountRepeat (format, fi, ch);
489                                         if (hour != -1 || tokLen > 2)
490                                                 return false;
491
492                                         ii += ParseEnum (input, ii,
493                                                          tokLen == 1 ? new string[] {new string (dfi.AMDesignator[0], 1), new string (dfi.PMDesignator[0], 0)} 
494                                                                      : new string[] {dfi.AMDesignator, dfi.PMDesignator},
495                                                          allow_white_spaces, out temp_int);
496                                         if (temp_int == -1)
497                                                 return false;
498
499                                         if (partial_hour == -1)
500                                                 partial_hour = temp_int * 12;
501                                         else
502                                                 hour = partial_hour + temp_int * 12;
503                                         break;
504                                 case 'y':
505                                         if (year != -1)
506                                                 return false;
507
508                                         tokLen = DateTimeUtils.CountRepeat (format, fi, ch);
509                                         if (tokLen <= 2) {
510                                                 ii += ParseNumber (input, ii, 2, tokLen == 2, allow_white_spaces, out year);
511                                                 if (year != -1)
512                                                         year += DateTime.Now.Year - DateTime.Now.Year % 100;
513                                         } else if (tokLen <= 4) { // yyy and yyyy accept up to 5 digits with leading 0
514                                                 int digit_parsed;
515                                                 ii += ParseNumber (input, ii, 5, false, allow_white_spaces, out year, out digit_parsed);
516                                                 if (digit_parsed < tokLen || (digit_parsed > tokLen && (year / Math.Pow (10, digit_parsed - 1) < 1)))
517                                                         return false;
518                                         } else
519                                                 ii += ParseNumber (input, ii, tokLen, true, allow_white_spaces, out year);
520                                         break;
521                                 case 'z':
522                                         tokLen = DateTimeUtils.CountRepeat (format, fi, ch);
523                                         if (offset != TimeSpan.MinValue || tokLen > 3)
524                                                 return false;
525
526                                         int off_h, off_m = 0, sign;
527                                         temp_int = 0;
528                                         ii += ParseEnum (input, ii, new string [] {"-", "+"}, allow_white_spaces, out sign);
529                                         ii += ParseNumber (input, ii, 2, tokLen != 1, false, out off_h);
530                                         if (tokLen == 3) {
531                                                 ii += ParseEnum (input, ii, new string [] {dfi.TimeSeparator}, false, out temp_int);
532                                                 ii += ParseNumber (input, ii, 2, true, false, out off_m);
533                                         }
534                                         if (off_h == -1 || off_m == -1 || sign == -1)
535                                                 return false;
536
537                                         if (sign == 0)
538                                                 sign = -1;
539                                         offset = new TimeSpan (sign * off_h, sign * off_m, 0);
540                                         break;
541                                 case ':':
542                                         tokLen = 1;
543                                         ii += ParseEnum (input, ii, new string [] {dfi.TimeSeparator}, false, out temp_int);
544                                         if (temp_int == -1)
545                                                 return false;
546                                         break;
547                                 case '/':
548                                         tokLen = 1;
549                                         ii += ParseEnum (input, ii, new string [] {dfi.DateSeparator}, false, out temp_int);
550                                         if (temp_int == -1)
551                                                 return false;
552                                         break;
553                                 case '%':
554                                         tokLen = 1;
555                                         if (fi != 0) 
556                                                 return false;
557                                         break;
558                                 case ' ':
559                                         tokLen = 1;
560                                         ii += ParseChar (input, ii, ' ', false, out temp_int);
561                                         if (temp_int == -1)
562                                                 return false;
563                                         break;
564                                 case '\\':
565                                         tokLen = 2;
566                                         ii += ParseChar (input, ii, format [fi + 1], allow_white_spaces, out temp_int);
567                                         if (temp_int == -1)
568                                                 return false;
569                                         break;
570                                 default:
571                                         //Console.WriteLine ("un-parsed character: {0}", ch);
572                                         tokLen = 1;
573                                         ii += ParseChar (input, ii, format [fi], allow_white_spaces, out temp_int);
574                                         if (temp_int == -1)
575                                                 return false;
576                                         break;
577                                 }
578                                 fi += tokLen;
579                         }
580
581                         //Console.WriteLine ("{0}-{1}-{2} {3}:{4} {5}", year, month, day, hour, minute, offset);
582                         if (offset == TimeSpan.MinValue && (styles & DateTimeStyles.AssumeLocal) != 0)
583                                 offset = TimeZone.CurrentTimeZone.GetUtcOffset (DateTime.Now);
584
585                         if (offset == TimeSpan.MinValue && (styles & DateTimeStyles.AssumeUniversal) != 0)
586                                 offset = TimeSpan.Zero;
587
588                         if (hour < 0)           hour = 0;
589                         if (minute < 0)         minute = 0;
590                         if (second < 0)         second = 0;
591                         if (fraction < 0)       fraction = 0;
592                         if (year > 0 && month > 0 && day > 0) {
593                                 result = new DateTimeOffset (year, month, day, hour, minute, second, 0, offset);
594                                 result = result.AddSeconds (fraction);
595                                 if ((styles & DateTimeStyles.AdjustToUniversal) != 0)
596                                         result = result.ToUniversalTime ();
597                                 return true;
598                         }
599
600                         return false;
601                 }
602
603                 private static int ParseNumber (string input, int pos, int digits, bool leading_zero, bool allow_leading_white, out int result)
604                 {
605                         int digit_parsed;
606                         return ParseNumber (input, pos, digits, leading_zero, allow_leading_white, out result, out digit_parsed);
607                 }
608
609                 private static int ParseNumber (string input, int pos, int digits, bool leading_zero, bool allow_leading_white, out int result, out int digit_parsed)
610                 {
611                         int char_parsed = 0;
612                         digit_parsed = 0;
613                         result = 0;
614                         for (; allow_leading_white && pos < input.Length && input[pos] == ' '; pos++)
615                                 char_parsed++;
616
617                         for (; pos < input.Length && Char.IsDigit (input[pos]) && digits > 0; pos ++, char_parsed++, digit_parsed++, digits --)
618                                 result = 10 * result + (byte) (input[pos] - '0');
619
620                         if (leading_zero && digits > 0)
621                                 result = -1;
622
623                         if (digit_parsed == 0)
624                                 result = -1;
625
626                         return char_parsed;
627                 }
628
629                 private static int ParseEnum (string input, int pos, string [] enums, bool allow_leading_white, out int result)
630                 {
631                         int char_parsed = 0;
632                         result = -1;
633                         for (; allow_leading_white && pos < input.Length && input[pos] == ' '; pos++)
634                                 char_parsed ++;
635                         
636                         for (int i = 0; i < enums.Length; i++)
637                                 if (input.Substring(pos).StartsWith (enums [i])) {
638                                         result = i;
639                                         break;
640                                 }
641
642                         if (result >= 0)
643                                 char_parsed += enums[result].Length;
644
645                         return char_parsed;     
646                 }
647         
648                 private static int ParseChar (string input, int pos, char c, bool allow_leading_white, out int result)
649                 {
650                         int char_parsed = 0;
651                         result = -1;
652                         for (; allow_leading_white && pos < input.Length && input[pos] == ' '; pos++, char_parsed++)
653                                 ;
654
655                         if (pos < input.Length && input[pos] == c){
656                                 result = (int) c;
657                                 char_parsed ++;
658                         }
659
660                         return char_parsed;
661                 }
662
663                 public TimeSpan Subtract (DateTimeOffset value)
664                 {
665                         return UtcDateTime - value.UtcDateTime;
666                 }
667
668                 public DateTimeOffset Subtract (TimeSpan value)
669                 {
670                         return Add (-value);
671                 }
672
673                 public static TimeSpan operator - (DateTimeOffset left, DateTimeOffset right)
674                 {
675                         return left.Subtract (right);
676                 }
677
678                 public static DateTimeOffset operator - (DateTimeOffset dateTimeTz, TimeSpan timeSpan)
679                 {
680                         return dateTimeTz.Subtract (timeSpan);  
681                 }
682
683                 public long ToFileTime ()
684                 {
685                         return UtcDateTime.ToFileTime ();
686                 }
687
688                 public DateTimeOffset ToLocalTime ()
689                 {
690                         return new DateTimeOffset (UtcDateTime.ToLocalTime (), TimeZone.CurrentTimeZone.GetUtcOffset (UtcDateTime.ToLocalTime ()));
691                 }
692
693                 public DateTimeOffset ToOffset (TimeSpan offset)
694                 {
695                         return new DateTimeOffset (dt - utc_offset + offset, offset);
696                 }
697
698                 public override string ToString ()
699                 {
700                         return ToString (null, null);
701                 }
702
703                 public string ToString (IFormatProvider formatProvider)
704                 {
705                         return ToString (null, formatProvider);
706                 }
707
708                 public string ToString (string format)
709                 {
710                         return ToString (format, null);
711                 }
712
713                 public string ToString (string format, IFormatProvider formatProvider)
714                 {
715                         DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance(formatProvider);
716
717                         if (format == null || format == String.Empty)
718                                 format = dfi.ShortDatePattern + " " + dfi.LongTimePattern + " zzz";
719
720                         bool to_utc = false, use_invariant = false;
721                         if (format.Length == 1) {
722                                 char fchar = format [0];
723                                 try {
724                                         format = DateTimeUtils.GetStandardPattern (fchar, dfi, out to_utc, out use_invariant, true);
725                                 } catch {
726                                         format = null;
727                                 }
728                 
729                                 if (format == null)
730                                         throw new FormatException ("format is not one of the format specifier characters defined for DateTimeFormatInfo");
731                         }
732                         return to_utc ? DateTimeUtils.ToString (UtcDateTime, TimeSpan.Zero, format, dfi) : DateTimeUtils.ToString (DateTime, Offset, format, dfi);
733                 }
734
735                 public DateTimeOffset ToUniversalTime ()
736                 {
737                         return new DateTimeOffset (UtcDateTime, TimeSpan.Zero); 
738                 }
739
740                 public static bool TryParse (string input, out DateTimeOffset result)
741                 {
742                         try {
743                                 result = Parse (input);
744                                 return true;
745                         } catch {
746                                 result = MinValue;
747                                 return false;
748                         }
749                 }
750
751                 public static bool TryParse (string input, IFormatProvider formatProvider, DateTimeStyles styles, out DateTimeOffset result)
752                 {
753                         try {
754                                 result = Parse (input, formatProvider, styles);
755                                 return true;
756                         } catch {
757                                 result = MinValue;
758                                 return false;
759                         }       
760                 }
761
762                 public static bool TryParseExact (string input, string format, IFormatProvider formatProvider, DateTimeStyles styles, out DateTimeOffset result)
763                 {
764                         try {
765                                 result = ParseExact (input, format, formatProvider, styles);
766                                 return true;
767                         } catch {
768                                 result = MinValue;
769                                 return false;
770                         }
771                 }
772
773                 public static bool TryParseExact (string input, string[] formats, IFormatProvider formatProvider, DateTimeStyles styles, out DateTimeOffset result)
774                 {
775                         try {
776                                 result = ParseExact (input, formats, formatProvider, styles);
777                                 return true;
778                         } catch {
779                                 result = MinValue;
780                                 return false;
781                         }
782                 }
783
784                 public DateTime Date {
785                         get { return DateTime.SpecifyKind (dt.Date, DateTimeKind.Unspecified); }
786                 }
787
788                 public DateTime DateTime {
789                         get { return DateTime.SpecifyKind (dt, DateTimeKind.Unspecified); }
790                 }
791
792                 public int Day {
793                         get { return dt.Day; }
794                 }
795
796                 public DayOfWeek DayOfWeek {
797                         get { return dt.DayOfWeek; }
798                 }
799
800                 public int DayOfYear {
801                         get { return dt.DayOfYear; }
802                 }
803
804                 public int Hour {
805                         get { return dt.Hour; }
806                 }
807
808                 public DateTime LocalDateTime {
809                         get { return UtcDateTime.ToLocalTime (); }
810                 }
811
812                 public int Millisecond {
813                         get { return dt.Millisecond; }
814                 }
815
816                 public int Minute {
817                         get { return dt.Minute; }
818                 }
819
820                 public int Month {
821                         get { return dt.Month; }
822                 }
823
824                 public static DateTimeOffset Now {
825                         get { return new DateTimeOffset (DateTime.Now);}
826                 }
827
828                 public TimeSpan Offset {
829                         get { return utc_offset; }      
830                 }
831
832                 public int Second {
833                         get { return dt.Second; }
834                 }
835
836                 public long Ticks {
837                         get { return dt.Ticks; }        
838                 }
839
840                 public TimeSpan TimeOfDay {
841                         get { return dt.TimeOfDay; }
842                 }
843
844                 public DateTime UtcDateTime {
845                         get { return DateTime.SpecifyKind (dt - utc_offset, DateTimeKind.Utc); }        
846                 }
847                 
848                 public static DateTimeOffset UtcNow {
849                         get { return new DateTimeOffset (DateTime.UtcNow); }
850                 }
851
852                 public long UtcTicks {
853                         get { return UtcDateTime.Ticks; }
854                 }
855
856                 public int Year {
857                         get { return dt.Year; }
858                 }
859         }
860 }