2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[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 #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                 int IComparable.CompareTo (object obj)
165                 {
166                         return CompareTo ((DateTimeOffset) obj);
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 obj)
180                 {
181                         if (obj is DateTimeOffset)
182                                 return UtcDateTime == ((DateTimeOffset) obj).UtcDateTime;
183                         return false;
184                 }
185
186                 public static bool Equals (DateTimeOffset first, DateTimeOffset second)
187                 {
188                         return first.Equals (second);   
189                 }
190
191                 public bool EqualsExact (DateTimeOffset other)
192                 {
193                         return dt == other.dt && utc_offset == other.utc_offset;        
194                 }
195
196                 public static DateTimeOffset FromFileTime (long fileTime)
197                 {
198                         if (fileTime < 0 || fileTime > MaxValue.Ticks)
199                                 throw new ArgumentOutOfRangeException ("fileTime is less than zero or greater than DateTimeOffset.MaxValue.Ticks.");
200                         
201                         return new DateTimeOffset (DateTime.FromFileTime (fileTime), TimeZone.CurrentTimeZone.GetUtcOffset (DateTime.FromFileTime (fileTime)));
202
203                 }
204
205                 public override int GetHashCode ()
206                 {
207                         return dt.GetHashCode () ^ utc_offset.GetHashCode ();
208                 }
209
210                 [System.Security.Permissions.SecurityPermission (System.Security.Permissions.SecurityAction.LinkDemand, SerializationFormatter = true)]
211                 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
212                 {
213                         if (info == null)
214                                 throw new ArgumentNullException ("info");
215                         // An example SOAP serialization on MSFT is the following, so field 
216                         // names "DateTime" and "OffsetMinutes":
217                         //    <SOAP-ENV:Envelope ...>
218                         //    <SOAP-ENV:Body>
219                         //    <a1:DateTimeOffset id="ref-1" xmlns:a1="http://schemas.microsoft.com/clr/ns/System">
220                         //    <DateTime xsi:type="xsd:dateTime">2007-01-02T12:30:50.0000000+00:00</DateTime>
221                         //    <OffsetMinutes>0</OffsetMinutes>
222                         //    </a1:DateTimeOffset>
223                         //    </SOAP-ENV:Body>
224                         //    </SOAP-ENV:Envelope>
225                         DateTime dt0 = new DateTime (dt.Ticks).Subtract (utc_offset);
226                         info.AddValue ("DateTime", dt0);
227                         // MSFT BinaryFormatter output contains primitive code 6, i.e. Int16.
228                         info.AddValue ("OffsetMinutes", (Int16)utc_offset.TotalMinutes);
229                 }
230
231                 [System.Security.Permissions.SecurityPermission (System.Security.Permissions.SecurityAction.LinkDemand, SerializationFormatter = true)]
232                 private DateTimeOffset(SerializationInfo info, StreamingContext context)
233                 {
234                         DateTime dt0 = (DateTime)info.GetValue ("DateTime", typeof(DateTime));
235                         Int16 totalMinutes = info.GetInt16 ("OffsetMinutes");
236                         utc_offset = TimeSpan.FromMinutes(totalMinutes);
237                         dt = dt0.Add(utc_offset);
238                 }
239
240                 public static bool operator > (DateTimeOffset left, DateTimeOffset right)
241                 {
242                         return left.UtcDateTime > right.UtcDateTime;
243                 }                       
244
245                 public static bool operator >= (DateTimeOffset left, DateTimeOffset right)
246                 {
247                         return left.UtcDateTime >= right.UtcDateTime;
248                 }                       
249
250                 public static implicit operator DateTimeOffset (DateTime dateTime)
251                 {
252                         return new DateTimeOffset (dateTime);
253                 }
254
255                 public static bool operator != (DateTimeOffset left, DateTimeOffset right)
256                 {
257                         return left.UtcDateTime != right.UtcDateTime;
258                 }
259
260                 public static bool operator < (DateTimeOffset left, DateTimeOffset right)
261                 {
262                         return left.UtcDateTime < right.UtcDateTime;
263                 }
264                 
265                 public static bool operator <= (DateTimeOffset left, DateTimeOffset right)
266                 {
267                         return left.UtcDateTime <= right.UtcDateTime;
268                 }
269         
270                 [MonoTODO]
271                 void IDeserializationCallback.OnDeserialization (object sender)
272                 {
273                 }
274
275                 public static DateTimeOffset Parse (string input)
276                 {
277                         return Parse (input, null);
278                 }
279
280                 public static DateTimeOffset Parse (string input, IFormatProvider formatProvider)
281                 {
282                         return Parse (input, formatProvider, DateTimeStyles.AllowWhiteSpaces);
283                 }
284
285                 public static DateTimeOffset Parse (string input, IFormatProvider formatProvider, DateTimeStyles styles)
286                 {
287                         if (input == null)
288                                 throw new ArgumentNullException ("input");
289
290                         DateTime d;
291                         DateTimeOffset dto;
292                         Exception exception = null;
293                         if (!DateTime.CoreParse (input, formatProvider, styles, out d, out dto, true, ref exception))
294                                 throw exception;
295
296                         if (d.Ticks != 0 && dto.Ticks == 0)
297                                 throw new ArgumentOutOfRangeException ("The UTC representation falls outside the 1-9999 year range");
298
299                         return dto;
300                 }
301
302                 public static DateTimeOffset ParseExact (string input, string format, IFormatProvider formatProvider)
303                 {
304                         return ParseExact (input, format, formatProvider, DateTimeStyles.AssumeLocal);
305                 }
306
307                 public static DateTimeOffset ParseExact (string input, string format, IFormatProvider formatProvider, DateTimeStyles styles)
308                 {
309                         if (format == null)
310                                 throw new ArgumentNullException ("format");
311
312                         if (format == String.Empty)
313                                 throw new FormatException ("format is an empty string");
314
315                         return ParseExact (input, new string [] {format}, formatProvider, styles);
316                 }
317
318                 public static DateTimeOffset ParseExact (string input, string[] formats, IFormatProvider formatProvider, DateTimeStyles styles)
319                 {
320                         if (input == null)
321                                 throw new ArgumentNullException ("input");
322
323                         if (input == String.Empty)
324                                 throw new FormatException ("input is an empty string");
325
326                         if (formats == null)
327                                 throw new ArgumentNullException ("formats");
328
329                         if (formats.Length == 0)
330                                 throw new FormatException ("Invalid format specifier");
331
332                         if ((styles & DateTimeStyles.AssumeLocal) != 0 && (styles & DateTimeStyles.AssumeUniversal) != 0)
333                                 throw new ArgumentException ("styles parameter contains incompatible flags");
334
335                         DateTimeOffset result;
336                         if (!ParseExact (input, formats, DateTimeFormatInfo.GetInstance (formatProvider), styles, out result))
337                                 throw new FormatException ("Invalid format string");
338
339                         return result;
340                 }
341
342                 private static bool ParseExact (string input, string [] formats,
343                                 DateTimeFormatInfo dfi, DateTimeStyles styles, out DateTimeOffset ret)
344                 {
345                         foreach (string format in formats)
346                         {
347                                 if (format == null || format == String.Empty)
348                                         throw new FormatException ("Invalid format string");
349
350                                 DateTimeOffset result;
351                                 if (DoParse (input, format, false, out result, dfi, styles)) {
352                                         ret = result;
353                                         return true;
354                                 }
355                         }
356                         ret = DateTimeOffset.MinValue;
357                         return false;
358                 }
359
360                 private static bool DoParse (string input, 
361                                 string format,
362                                 bool exact,
363                                 out DateTimeOffset result,
364                                 DateTimeFormatInfo dfi,
365                                 DateTimeStyles styles)
366                 {
367                         if ((styles & DateTimeStyles.AllowLeadingWhite) != 0) {
368                                 format = format.TrimStart (null);
369                                 input = input.TrimStart (null);
370                         }
371
372                         if ((styles & DateTimeStyles.AllowTrailingWhite) != 0) {
373                                 format = format.TrimEnd (null);
374                                 input = input.TrimEnd (null);
375                         }
376
377                         bool allow_white_spaces = false;
378                         if ((styles & DateTimeStyles.AllowInnerWhite) != 0)
379                                 allow_white_spaces = true;
380
381                         bool useutc = false, use_invariants = false;
382                         if (format.Length == 1)
383                                 format = DateTimeUtils.GetStandardPattern (format[0], dfi, out useutc, out use_invariants, true);
384
385                         int year = -1;
386                         int month = -1;
387                         int day = -1;
388                         int partial_hour = -1; // for 'hh tt' formats
389                         int hour = -1;
390                         int minute = -1;
391                         int second = -1;
392                         double fraction = -1;
393                         int temp_int = -1;
394                         TimeSpan offset = TimeSpan.MinValue;
395
396                         result = DateTimeOffset.MinValue;
397
398                         int fi = 0; //format iterator
399                         int ii = 0; //input iterator
400                         while (fi < format.Length) {
401                                 int tokLen;
402                                 char ch = format [fi];
403
404                                 switch (ch) {
405                                 case 'd':
406                                         tokLen = DateTimeUtils.CountRepeat (format, fi, ch);
407                                         if (day != -1 || tokLen > 4)
408                                                 return false;
409
410                                         if (tokLen <= 2)
411                                                 ii += ParseNumber (input, ii, 2, tokLen == 2, allow_white_spaces, out day);
412                                         else
413                                                 ii += ParseEnum (input, ii, tokLen == 3 ? dfi.AbbreviatedDayNames : dfi.DayNames, allow_white_spaces, out temp_int); 
414                                         break;
415                                 case 'f':
416                                         tokLen = DateTimeUtils.CountRepeat (format, fi, ch);
417                                         ii += ParseNumber (input, ii, tokLen, true, allow_white_spaces, out temp_int);
418                                         if (fraction >= 0 || tokLen > 7 || temp_int == -1)
419                                                 return false;
420                                         fraction = (double)temp_int / Math.Pow (10, tokLen);
421                                         break;
422                                 case 'F':
423                                         tokLen = DateTimeUtils.CountRepeat (format, fi, ch);
424                                         int digits;
425                                         int read = ParseNumber (input, ii, tokLen, true, allow_white_spaces, out temp_int, out digits);
426                                         if (temp_int == -1)
427                                                 ii += ParseNumber (input, ii, digits, true, allow_white_spaces, out temp_int);
428                                         else
429                                                 ii += read;
430                                         if (fraction >= 0 || tokLen > 7 || temp_int == -1)
431                                                 return false;   
432                                         fraction = (double)temp_int / Math.Pow (10, digits);    
433                                         break;
434                                 case 'h':
435                                         tokLen = DateTimeUtils.CountRepeat (format, fi, ch);
436                                         if (hour != -1 || tokLen > 2)
437                                                 return false;
438
439                                         ii += ParseNumber (input, ii, 2, tokLen == 2, allow_white_spaces, out temp_int);
440                                         if (temp_int == -1)
441                                                 return false;
442
443                                         if (partial_hour == -1)
444                                                 partial_hour = temp_int;
445                                         else 
446                                                 hour = partial_hour + temp_int;
447                                         break;
448                                 case 'H':
449                                         tokLen = DateTimeUtils.CountRepeat (format, fi, ch);
450                                         if (hour != -1 || tokLen > 2)
451                                                 return false;
452
453                                         ii += ParseNumber (input, ii, 2, tokLen == 2, allow_white_spaces, out hour);
454                                         break;
455                                 case 'm':
456                                         tokLen = DateTimeUtils.CountRepeat (format, fi, ch);
457                                         if (minute != -1 || tokLen > 2)
458                                                 return false;
459
460                                         ii += ParseNumber (input, ii, 2, tokLen == 2, allow_white_spaces, out minute);
461                                         break;
462                                 case 'M':
463                                         tokLen = DateTimeUtils.CountRepeat (format, fi, ch);
464                                         if (month != -1 || tokLen > 4)
465                                                 return false;
466
467                                         if (tokLen <= 2)
468                                                 ii += ParseNumber (input, ii, 2, tokLen == 2, allow_white_spaces, out month);
469                                         else {
470                                                 ii += ParseEnum (input, ii, tokLen == 3 ? dfi.AbbreviatedMonthNames : dfi.MonthNames, allow_white_spaces, out month);
471                                                 month += 1;
472                                         }
473
474                                         break;
475                                 case 's':
476                                         tokLen = DateTimeUtils.CountRepeat (format, fi, ch);
477                                         if (second != -1 || tokLen > 2)
478                                                 return false;
479                                         ii += ParseNumber (input, ii, 2, tokLen == 2, allow_white_spaces, out second);
480                                         break;
481                                 case 't':
482                                         tokLen = DateTimeUtils.CountRepeat (format, fi, ch);
483                                         if (hour != -1 || tokLen > 2)
484                                                 return false;
485
486                                         ii += ParseEnum (input, ii,
487                                                          tokLen == 1 ? new string[] {new string (dfi.AMDesignator[0], 1), new string (dfi.PMDesignator[0], 0)} 
488                                                                      : new string[] {dfi.AMDesignator, dfi.PMDesignator},
489                                                          allow_white_spaces, out temp_int);
490                                         if (temp_int == -1)
491                                                 return false;
492
493                                         if (partial_hour == -1)
494                                                 partial_hour = temp_int * 12;
495                                         else
496                                                 hour = partial_hour + temp_int * 12;
497                                         break;
498                                 case 'y':
499                                         if (year != -1)
500                                                 return false;
501
502                                         tokLen = DateTimeUtils.CountRepeat (format, fi, ch);
503                                         if (tokLen <= 2) {
504                                                 ii += ParseNumber (input, ii, 2, tokLen == 2, allow_white_spaces, out year);
505                                                 if (year != -1)
506                                                         year += DateTime.Now.Year - DateTime.Now.Year % 100;
507                                         } else if (tokLen <= 4) { // yyy and yyyy accept up to 5 digits with leading 0
508                                                 int digit_parsed;
509                                                 ii += ParseNumber (input, ii, 5, false, allow_white_spaces, out year, out digit_parsed);
510                                                 if (digit_parsed < tokLen || (digit_parsed > tokLen && (year / Math.Pow (10, digit_parsed - 1) < 1)))
511                                                         return false;
512                                         } else
513                                                 ii += ParseNumber (input, ii, tokLen, true, allow_white_spaces, out year);
514                                         break;
515                                 case 'z':
516                                         tokLen = DateTimeUtils.CountRepeat (format, fi, ch);
517                                         if (offset != TimeSpan.MinValue || tokLen > 3)
518                                                 return false;
519
520                                         int off_h, off_m = 0, sign;
521                                         temp_int = 0;
522                                         ii += ParseEnum (input, ii, new string [] {"-", "+"}, allow_white_spaces, out sign);
523                                         ii += ParseNumber (input, ii, 2, tokLen != 1, false, out off_h);
524                                         if (tokLen == 3) {
525                                                 ii += ParseEnum (input, ii, new string [] {dfi.TimeSeparator}, false, out temp_int);
526                                                 ii += ParseNumber (input, ii, 2, true, false, out off_m);
527                                         }
528                                         if (off_h == -1 || off_m == -1 || sign == -1 || temp_int == -1)
529                                                 return false;
530
531                                         if (sign == 0)
532                                                 sign = -1;
533                                         offset = new TimeSpan (sign * off_h, sign * off_m, 0);
534                                         break;
535                                 case ':':
536                                         tokLen = 1;
537                                         ii += ParseEnum (input, ii, new string [] {dfi.TimeSeparator}, false, out temp_int);
538                                         if (temp_int == -1)
539                                                 return false;
540                                         break;
541                                 case '/':
542                                         tokLen = 1;
543                                         ii += ParseEnum (input, ii, new string [] {dfi.DateSeparator}, false, out temp_int);
544                                         if (temp_int == -1)
545                                                 return false;
546                                         break;
547                                 case '%':
548                                         tokLen = 1;
549                                         if (fi != 0) 
550                                                 return false;
551                                         break;
552                                 case ' ':
553                                         tokLen = 1;
554                                         ii += ParseChar (input, ii, ' ', false, out temp_int);
555                                         if (temp_int == -1)
556                                                 return false;
557                                         break;
558                                 case '\\':
559                                         tokLen = 2;
560                                         ii += ParseChar (input, ii, format [fi + 1], allow_white_spaces, out temp_int);
561                                         if (temp_int == -1)
562                                                 return false;
563                                         break;
564                                 default:
565                                         //Console.WriteLine ("un-parsed character: {0}", ch);
566                                         tokLen = 1;
567                                         ii += ParseChar (input, ii, format [fi], allow_white_spaces, out temp_int);
568                                         if (temp_int == -1)
569                                                 return false;
570                                         break;
571                                 }
572                                 fi += tokLen;
573                         }
574
575                         //Console.WriteLine ("{0}-{1}-{2} {3}:{4} {5}", year, month, day, hour, minute, offset);
576                         if (offset == TimeSpan.MinValue && (styles & DateTimeStyles.AssumeLocal) != 0)
577                                 offset = TimeZone.CurrentTimeZone.GetUtcOffset (DateTime.Now);
578
579                         if (offset == TimeSpan.MinValue && (styles & DateTimeStyles.AssumeUniversal) != 0)
580                                 offset = TimeSpan.Zero;
581
582                         if (hour < 0)           hour = 0;
583                         if (minute < 0)         minute = 0;
584                         if (second < 0)         second = 0;
585                         if (fraction < 0)       fraction = 0;
586                         if (year > 0 && month > 0 && day > 0) {
587                                 result = new DateTimeOffset (year, month, day, hour, minute, second, (int) (1000 * fraction), offset);
588                                 if ((styles & DateTimeStyles.AdjustToUniversal) != 0)
589                                         result = result.ToUniversalTime ();
590                                 return true;
591                         }
592
593                         return false;
594                 }
595
596                 private static int ParseNumber (string input, int pos, int digits, bool leading_zero, bool allow_leading_white, out int result)
597                 {
598                         int digit_parsed;
599                         return ParseNumber (input, pos, digits, leading_zero, allow_leading_white, out result, out digit_parsed);
600                 }
601
602                 private static int ParseNumber (string input, int pos, int digits, bool leading_zero, bool allow_leading_white, out int result, out int digit_parsed)
603                 {
604                         int char_parsed = 0;
605                         digit_parsed = 0;
606                         result = 0;
607                         for (; allow_leading_white && pos < input.Length && input[pos] == ' '; pos++)
608                                 char_parsed++;
609
610                         for (; pos < input.Length && Char.IsDigit (input[pos]) && digits > 0; pos ++, char_parsed++, digit_parsed++, digits --)
611                                 result = 10 * result + (byte) (input[pos] - '0');
612
613                         if (leading_zero && digits > 0)
614                                 result = -1;
615
616                         if (digit_parsed == 0)
617                                 result = -1;
618
619                         return char_parsed;
620                 }
621
622                 private static int ParseEnum (string input, int pos, string [] enums, bool allow_leading_white, out int result)
623                 {
624                         int char_parsed = 0;
625                         result = -1;
626                         for (; allow_leading_white && pos < input.Length && input[pos] == ' '; pos++)
627                                 char_parsed ++;
628                         
629                         for (int i = 0; i < enums.Length; i++)
630                                 if (input.Substring(pos).StartsWith (enums [i])) {
631                                         result = i;
632                                         break;
633                                 }
634
635                         if (result >= 0)
636                                 char_parsed += enums[result].Length;
637
638                         return char_parsed;     
639                 }
640         
641                 private static int ParseChar (string input, int pos, char c, bool allow_leading_white, out int result)
642                 {
643                         int char_parsed = 0;
644                         result = -1;
645                         for (; allow_leading_white && pos < input.Length && input[pos] == ' '; pos++, char_parsed++)
646                                 ;
647
648                         if (pos < input.Length && input[pos] == c){
649                                 result = (int) c;
650                                 char_parsed ++;
651                         }
652
653                         return char_parsed;
654                 }
655
656                 public TimeSpan Subtract (DateTimeOffset value)
657                 {
658                         return UtcDateTime - value.UtcDateTime;
659                 }
660
661                 public DateTimeOffset Subtract (TimeSpan value)
662                 {
663                         return Add (-value);
664                 }
665
666                 public static TimeSpan operator - (DateTimeOffset left, DateTimeOffset right)
667                 {
668                         return left.Subtract (right);
669                 }
670
671                 public static DateTimeOffset operator - (DateTimeOffset dateTimeTz, TimeSpan timeSpan)
672                 {
673                         return dateTimeTz.Subtract (timeSpan);  
674                 }
675
676                 public long ToFileTime ()
677                 {
678                         return UtcDateTime.ToFileTime ();
679                 }
680
681                 public DateTimeOffset ToLocalTime ()
682                 {
683                         return new DateTimeOffset (UtcDateTime.ToLocalTime (), TimeZone.CurrentTimeZone.GetUtcOffset (UtcDateTime.ToLocalTime ()));
684                 }
685
686                 public DateTimeOffset ToOffset (TimeSpan offset)
687                 {
688                         return new DateTimeOffset (dt - utc_offset + offset, offset);
689                 }
690
691                 public override string ToString ()
692                 {
693                         return ToString (null, null);
694                 }
695
696                 public string ToString (IFormatProvider formatProvider)
697                 {
698                         return ToString (null, formatProvider);
699                 }
700
701                 public string ToString (string format)
702                 {
703                         return ToString (format, null);
704                 }
705
706                 public string ToString (string format, IFormatProvider formatProvider)
707                 {
708                         DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance(formatProvider);
709
710                         if (format == null || format == String.Empty)
711                                 format = dfi.ShortDatePattern + " " + dfi.LongTimePattern + " zzz";
712
713                         bool to_utc = false, use_invariant = false;
714                         if (format.Length == 1) {
715                                 char fchar = format [0];
716                                 try {
717                                         format = DateTimeUtils.GetStandardPattern (fchar, dfi, out to_utc, out use_invariant, true);
718                                 } catch {
719                                         format = null;
720                                 }
721                 
722                                 if (format == null)
723                                         throw new FormatException ("format is not one of the format specifier characters defined for DateTimeFormatInfo");
724                         }
725                         return to_utc ? DateTimeUtils.ToString (UtcDateTime, TimeSpan.Zero, format, dfi) : DateTimeUtils.ToString (DateTime, Offset, format, dfi);
726                 }
727
728                 public DateTimeOffset ToUniversalTime ()
729                 {
730                         return new DateTimeOffset (UtcDateTime, TimeSpan.Zero); 
731                 }
732
733                 public static bool TryParse (string input, out DateTimeOffset result)
734                 {
735                         try {
736                                 result = Parse (input);
737                                 return true;
738                         } catch {
739                                 result = MinValue;
740                                 return false;
741                         }
742                 }
743
744                 public static bool TryParse (string input, IFormatProvider formatProvider, DateTimeStyles styles, out DateTimeOffset result)
745                 {
746                         try {
747                                 result = Parse (input, formatProvider, styles);
748                                 return true;
749                         } catch {
750                                 result = MinValue;
751                                 return false;
752                         }       
753                 }
754
755                 public static bool TryParseExact (string input, string format, IFormatProvider formatProvider, DateTimeStyles styles, out DateTimeOffset result)
756                 {
757                         try {
758                                 result = ParseExact (input, format, formatProvider, styles);
759                                 return true;
760                         } catch {
761                                 result = MinValue;
762                                 return false;
763                         }
764                 }
765
766                 public static bool TryParseExact (string input, string[] formats, IFormatProvider formatProvider, DateTimeStyles styles, out DateTimeOffset result)
767                 {
768                         try {
769                                 result = ParseExact (input, formats, formatProvider, styles);
770                                 return true;
771                         } catch {
772                                 result = MinValue;
773                                 return false;
774                         }
775                 }
776
777                 public DateTime Date {
778                         get { return DateTime.SpecifyKind (dt.Date, DateTimeKind.Unspecified); }
779                 }
780
781                 public DateTime DateTime {
782                         get { return DateTime.SpecifyKind (dt, DateTimeKind.Unspecified); }
783                 }
784
785                 public int Day {
786                         get { return dt.Day; }
787                 }
788
789                 public DayOfWeek DayOfWeek {
790                         get { return dt.DayOfWeek; }
791                 }
792
793                 public int DayOfYear {
794                         get { return dt.DayOfYear; }
795                 }
796
797                 public int Hour {
798                         get { return dt.Hour; }
799                 }
800
801                 public DateTime LocalDateTime {
802                         get { return UtcDateTime.ToLocalTime (); }
803                 }
804
805                 public int Millisecond {
806                         get { return dt.Millisecond; }
807                 }
808
809                 public int Minute {
810                         get { return dt.Minute; }
811                 }
812
813                 public int Month {
814                         get { return dt.Month; }
815                 }
816
817                 public static DateTimeOffset Now {
818                         get { return new DateTimeOffset (DateTime.Now);}
819                 }
820
821                 public TimeSpan Offset {
822                         get { return utc_offset; }      
823                 }
824
825                 public int Second {
826                         get { return dt.Second; }
827                 }
828
829                 public long Ticks {
830                         get { return dt.Ticks; }        
831                 }
832
833                 public TimeSpan TimeOfDay {
834                         get { return dt.TimeOfDay; }
835                 }
836
837                 public DateTime UtcDateTime {
838                         get { return DateTime.SpecifyKind (dt - utc_offset, DateTimeKind.Utc); }        
839                 }
840                 
841                 public static DateTimeOffset UtcNow {
842                         get { return new DateTimeOffset (DateTime.UtcNow); }
843                 }
844
845                 public long UtcTicks {
846                         get { return UtcDateTime.Ticks; }
847                 }
848
849                 public int Year {
850                         get { return dt.Year; }
851                 }
852         }
853 }
854 #endif