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