2010-04-07 Jb Evain <jbevain@novell.com>
[mcs.git] / class / corlib / System / TimeSpan.cs
bloba765aa2f16d5634f22e564d205ae6707a093e865
1 //
2 // System.TimeSpan.cs
3 //
4 // Authors:
5 // Duco Fijma (duco@lorentz.xs4all.nl)
6 // Andreas Nahr (ClassDevelopment@A-SoftTech.com)
7 // Sebastien Pouliot <sebastien@ximian.com>
8 //
9 // (C) 2001 Duco Fijma
10 // (C) 2004 Andreas Nahr
11 // Copyright (C) 2004 Novell (http://www.novell.com)
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 //
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 //
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System.Text;
34 using System.Threading;
35 using System.Globalization;
37 namespace System
39 [Serializable]
40 [System.Runtime.InteropServices.ComVisible (true)]
41 public struct TimeSpan : IComparable, IComparable<TimeSpan>, IEquatable <TimeSpan>
42 #if NET_4_0
43 , IFormattable
44 #endif
46 #if MONOTOUCH
47 static TimeSpan () {
48 if (MonoTouchAOTHelper.FalseFlag) {
49 var comparer = new System.Collections.Generic.GenericComparer <TimeSpan> ();
50 var eqcomparer = new System.Collections.Generic.GenericEqualityComparer <TimeSpan> ();
53 #endif
54 public static readonly TimeSpan MaxValue = new TimeSpan (long.MaxValue);
55 public static readonly TimeSpan MinValue = new TimeSpan (long.MinValue);
56 public static readonly TimeSpan Zero = new TimeSpan (0L);
58 public const long TicksPerDay = 864000000000L;
59 public const long TicksPerHour = 36000000000L;
60 public const long TicksPerMillisecond = 10000L;
61 public const long TicksPerMinute = 600000000L;
62 public const long TicksPerSecond = 10000000L;
64 private long _ticks;
66 public TimeSpan (long ticks)
68 _ticks = ticks;
71 public TimeSpan (int hours, int minutes, int seconds)
73 CalculateTicks (0, hours, minutes, seconds, 0, true, out _ticks);
76 public TimeSpan (int days, int hours, int minutes, int seconds)
78 CalculateTicks (days, hours, minutes, seconds, 0, true, out _ticks);
81 public TimeSpan (int days, int hours, int minutes, int seconds, int milliseconds)
83 CalculateTicks (days, hours, minutes, seconds, milliseconds, true, out _ticks);
86 internal static bool CalculateTicks (int days, int hours, int minutes, int seconds, int milliseconds, bool throwExc, out long result)
88 // there's no overflow checks for hours, minutes, ...
89 // so big hours/minutes values can overflow at some point and change expected values
90 int hrssec = (hours * 3600); // break point at (Int32.MaxValue - 596523)
91 int minsec = (minutes * 60);
92 long t = ((long)(hrssec + minsec + seconds) * 1000L + (long)milliseconds);
93 t *= 10000;
95 result = 0;
97 bool overflow = false;
98 // days is problematic because it can overflow but that overflow can be
99 // "legal" (i.e. temporary) (e.g. if other parameters are negative) or
100 // illegal (e.g. sign change).
101 if (days > 0) {
102 long td = TicksPerDay * days;
103 if (t < 0) {
104 long ticks = t;
105 t += td;
106 // positive days -> total ticks should be lower
107 overflow = (ticks > t);
109 else {
110 t += td;
111 // positive + positive != negative result
112 overflow = (t < 0);
115 else if (days < 0) {
116 long td = TicksPerDay * days;
117 if (t <= 0) {
118 t += td;
119 // negative + negative != positive result
120 overflow = (t > 0);
122 else {
123 long ticks = t;
124 t += td;
125 // negative days -> total ticks should be lower
126 overflow = (t > ticks);
130 if (overflow) {
131 if (throwExc)
132 throw new ArgumentOutOfRangeException (Locale.GetText ("The timespan is too big or too small."));
133 return false;
136 result = t;
137 return true;
140 public int Days {
141 get {
142 return (int) (_ticks / TicksPerDay);
146 public int Hours {
147 get {
148 return (int) (_ticks % TicksPerDay / TicksPerHour);
152 public int Milliseconds {
153 get {
154 return (int) (_ticks % TicksPerSecond / TicksPerMillisecond);
158 public int Minutes {
159 get {
160 return (int) (_ticks % TicksPerHour / TicksPerMinute);
164 public int Seconds {
165 get {
166 return (int) (_ticks % TicksPerMinute / TicksPerSecond);
170 public long Ticks {
171 get {
172 return _ticks;
176 public double TotalDays {
177 get {
178 return (double) _ticks / TicksPerDay;
182 public double TotalHours {
183 get {
184 return (double) _ticks / TicksPerHour;
188 public double TotalMilliseconds {
189 get {
190 return (double) _ticks / TicksPerMillisecond;
194 public double TotalMinutes {
195 get {
196 return (double) _ticks / TicksPerMinute;
200 public double TotalSeconds {
201 get {
202 return (double) _ticks / TicksPerSecond;
206 public TimeSpan Add (TimeSpan ts)
208 try {
209 checked {
210 return new TimeSpan (_ticks + ts.Ticks);
213 catch (OverflowException) {
214 throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
218 public static int Compare (TimeSpan t1, TimeSpan t2)
220 if (t1._ticks < t2._ticks)
221 return -1;
222 if (t1._ticks > t2._ticks)
223 return 1;
224 return 0;
227 public int CompareTo (object value)
229 if (value == null)
230 return 1;
232 if (!(value is TimeSpan)) {
233 throw new ArgumentException (Locale.GetText ("Argument has to be a TimeSpan."), "value");
236 return Compare (this, (TimeSpan) value);
239 public int CompareTo (TimeSpan value)
241 return Compare (this, value);
244 public bool Equals (TimeSpan obj)
246 return obj._ticks == _ticks;
249 public TimeSpan Duration ()
251 try {
252 checked {
253 return new TimeSpan (Math.Abs (_ticks));
256 catch (OverflowException) {
257 throw new OverflowException (Locale.GetText (
258 "This TimeSpan value is MinValue so you cannot get the duration."));
262 public override bool Equals (object value)
264 if (!(value is TimeSpan))
265 return false;
267 return _ticks == ((TimeSpan) value)._ticks;
270 public static bool Equals (TimeSpan t1, TimeSpan t2)
272 return t1._ticks == t2._ticks;
275 public static TimeSpan FromDays (double value)
277 return From (value, TicksPerDay);
280 public static TimeSpan FromHours (double value)
282 return From (value, TicksPerHour);
285 public static TimeSpan FromMinutes (double value)
287 return From (value, TicksPerMinute);
290 public static TimeSpan FromSeconds (double value)
292 return From (value, TicksPerSecond);
295 public static TimeSpan FromMilliseconds (double value)
297 return From (value, TicksPerMillisecond);
300 private static TimeSpan From (double value, long tickMultiplicator)
302 if (Double.IsNaN (value))
303 throw new ArgumentException (Locale.GetText ("Value cannot be NaN."), "value");
304 if (Double.IsNegativeInfinity (value) || Double.IsPositiveInfinity (value) ||
305 (value < MinValue.Ticks) || (value > MaxValue.Ticks))
306 throw new OverflowException (Locale.GetText ("Outside range [MinValue,MaxValue]"));
308 try {
309 value = (value * (tickMultiplicator / TicksPerMillisecond));
311 checked {
312 long val = (long) Math.Round(value);
313 return new TimeSpan (val * TicksPerMillisecond);
316 catch (OverflowException) {
317 throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
321 public static TimeSpan FromTicks (long value)
323 return new TimeSpan (value);
326 public override int GetHashCode ()
328 return _ticks.GetHashCode ();
331 public TimeSpan Negate ()
333 if (_ticks == MinValue._ticks)
334 throw new OverflowException (Locale.GetText (
335 "This TimeSpan value is MinValue and cannot be negated."));
336 return new TimeSpan (-_ticks);
339 public static TimeSpan Parse (string s)
341 if (s == null) {
342 throw new ArgumentNullException ("s");
345 TimeSpan result;
346 Parser p = new Parser (s);
347 p.Execute (false, out result);
348 return result;
351 public static bool TryParse (string s, out TimeSpan result)
353 if (s == null) {
354 result = TimeSpan.Zero;
355 return false;
358 Parser p = new Parser (s);
359 return p.Execute (true, out result);
362 #if NET_4_0
363 public static TimeSpan Parse (string s, IFormatProvider formatProvider)
365 if (s == null)
366 throw new ArgumentNullException ("s");
368 TimeSpan result;
369 Parser p = new Parser (s, formatProvider);
370 p.Execute (false, out result);
371 return result;
374 public static bool TryParse (string s, IFormatProvider formatProvider, out TimeSpan result)
376 if (s == null || s.Length == 0) {
377 result = TimeSpan.Zero;
378 return false;
381 Parser p = new Parser (s, formatProvider);
382 return p.Execute (true, out result);
385 public static TimeSpan ParseExact (string input, string format, IFormatProvider formatProvider)
387 if (format == null)
388 throw new ArgumentNullException ("format");
390 return ParseExact (input, new string [] { format }, formatProvider, TimeSpanStyles.None);
393 public static TimeSpan ParseExact (string input, string format, IFormatProvider formatProvider, TimeSpanStyles styles)
395 if (format == null)
396 throw new ArgumentNullException ("format");
398 return ParseExact (input, new string [] { format }, formatProvider, styles);
401 public static TimeSpan ParseExact (string input, string [] formats, IFormatProvider formatProvider)
403 return ParseExact (input, formats, formatProvider, TimeSpanStyles.None);
406 public static TimeSpan ParseExact (string input, string [] formats, IFormatProvider formatProvider, TimeSpanStyles styles)
408 if (input == null)
409 throw new ArgumentNullException ("input");
410 if (formats == null)
411 throw new ArgumentNullException ("formats");
413 // All the errors found during the parsing process are reported as FormatException.
414 TimeSpan result;
415 if (!TryParseExact (input, formats, formatProvider, styles, out result))
416 throw new FormatException ("Invalid format.");
418 return result;
421 public static bool TryParseExact (string input, string format, IFormatProvider formatProvider, out TimeSpan result)
423 return TryParseExact (input, new string [] { format }, formatProvider, TimeSpanStyles.None, out result);
426 public static bool TryParseExact (string input, string format, IFormatProvider formatProvider, TimeSpanStyles styles,
427 out TimeSpan result)
429 return TryParseExact (input, new string [] { format }, formatProvider, styles, out result);
432 public static bool TryParseExact (string input, string [] formats, IFormatProvider formatProvider, out TimeSpan result)
434 return TryParseExact (input, formats, formatProvider, TimeSpanStyles.None, out result);
437 public static bool TryParseExact (string input, string [] formats, IFormatProvider formatProvider, TimeSpanStyles styles,
438 out TimeSpan result)
440 result = TimeSpan.Zero;
442 if (formats == null || formats.Length == 0)
443 return false;
445 Parser p = new Parser (input, formatProvider);
446 p.Exact = true;
448 foreach (string format in formats) {
449 if (format == null || format.Length == 0)
450 return false; // wrong format, return immediately.
452 switch (format) {
453 case "g":
454 p.AllMembersRequired = false;
455 p.CultureSensitive = true;
456 p.UseColonAsDaySeparator = true;
457 break;
458 case "G":
459 p.AllMembersRequired = true;
460 p.CultureSensitive = true;
461 p.UseColonAsDaySeparator = true;
462 break;
463 case "c":
464 p.AllMembersRequired = false;
465 p.CultureSensitive = false;
466 p.UseColonAsDaySeparator = false;
467 break;
468 default: // custom format
469 throw new NotImplementedException ();
472 if (p.Execute (true, out result))
473 return true;
476 return false;
478 #endif
480 public TimeSpan Subtract (TimeSpan ts)
482 try {
483 checked {
484 return new TimeSpan (_ticks - ts.Ticks);
487 catch (OverflowException) {
488 throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
492 public override string ToString ()
494 StringBuilder sb = new StringBuilder (14);
496 if (_ticks < 0)
497 sb.Append ('-');
499 // We need to take absolute values of all components.
500 // Can't handle negative timespans by negating the TimeSpan
501 // as a whole. This would lead to an overflow for the
502 // degenerate case "TimeSpan.MinValue.ToString()".
503 if (Days != 0) {
504 sb.Append (Math.Abs (Days));
505 sb.Append ('.');
508 sb.Append (Math.Abs (Hours).ToString ("D2"));
509 sb.Append (':');
510 sb.Append (Math.Abs (Minutes).ToString ("D2"));
511 sb.Append (':');
512 sb.Append (Math.Abs (Seconds).ToString ("D2"));
514 int fractional = (int) Math.Abs (_ticks % TicksPerSecond);
515 if (fractional != 0) {
516 sb.Append ('.');
517 sb.Append (fractional.ToString ("D7"));
520 return sb.ToString ();
523 #if NET_4_0
524 public string ToString (string format)
526 return ToString (format, null);
529 public string ToString (string format, IFormatProvider formatProvider)
531 if (format == null || format.Length == 0 || format == "c") // Default version
532 return ToString ();
534 if (format != "g" && format != "G")
535 throw new FormatException ("The format is not recognized.");
537 NumberFormatInfo number_info = null;
538 if (formatProvider != null)
539 number_info = (NumberFormatInfo)formatProvider.GetFormat (typeof (NumberFormatInfo));
540 if (number_info == null)
541 number_info = Thread.CurrentThread.CurrentCulture.NumberFormat;
543 string decimal_separator = number_info.NumberDecimalSeparator;
544 int days, hours, minutes, seconds, milliseconds, fractional;
546 days = Math.Abs (Days);
547 hours = Math.Abs (Hours);
548 minutes = Math.Abs (Minutes);
549 seconds = Math.Abs (Seconds);
550 milliseconds = Math.Abs (Milliseconds);
551 fractional = (int) Math.Abs (_ticks % TicksPerSecond);
553 // Set Capacity depending on whether it's long or shot format
554 StringBuilder sb = new StringBuilder (format == "g" ? 16 : 32);
555 if (_ticks < 0)
556 sb.Append ('-');
558 switch (format) {
559 case "g": // short version
560 if (days != 0) {
561 sb.Append (days.ToString ());
562 sb.Append (':');
564 sb.Append (hours.ToString ());
565 sb.Append (':');
566 sb.Append (minutes.ToString ("D2"));
567 sb.Append (':');
568 sb.Append (seconds.ToString ("D2"));
569 if (milliseconds != 0) {
570 sb.Append (decimal_separator);
571 sb.Append (milliseconds.ToString ("D3"));
573 break;
574 case "G": // long version
575 sb.Append (days.ToString ("D1"));
576 sb.Append (':');
577 sb.Append (hours.ToString ("D2"));
578 sb.Append (':');
579 sb.Append (minutes.ToString ("D2"));
580 sb.Append (':');
581 sb.Append (seconds.ToString ("D2"));
582 sb.Append (decimal_separator);
583 sb.Append (fractional.ToString ("D7"));
584 break;
587 return sb.ToString ();
589 #endif
591 public static TimeSpan operator + (TimeSpan t1, TimeSpan t2)
593 return t1.Add (t2);
596 public static bool operator == (TimeSpan t1, TimeSpan t2)
598 return t1._ticks == t2._ticks;
601 public static bool operator > (TimeSpan t1, TimeSpan t2)
603 return t1._ticks > t2._ticks;
606 public static bool operator >= (TimeSpan t1, TimeSpan t2)
608 return t1._ticks >= t2._ticks;
611 public static bool operator != (TimeSpan t1, TimeSpan t2)
613 return t1._ticks != t2._ticks;
616 public static bool operator < (TimeSpan t1, TimeSpan t2)
618 return t1._ticks < t2._ticks;
621 public static bool operator <= (TimeSpan t1, TimeSpan t2)
623 return t1._ticks <= t2._ticks;
626 public static TimeSpan operator - (TimeSpan t1, TimeSpan t2)
628 return t1.Subtract (t2);
631 public static TimeSpan operator - (TimeSpan t)
633 return t.Negate ();
636 public static TimeSpan operator + (TimeSpan t)
638 return t;
641 enum ParseError {
642 None,
643 Format,
644 Overflow
647 // Class Parser implements parser for TimeSpan.Parse
648 private class Parser
650 private string _src;
651 private int _cur = 0;
652 private int _length;
653 ParseError parse_error;
654 #if NET_4_0
655 bool parsed_ticks;
656 NumberFormatInfo number_format;
657 int parsed_numbers_count;
658 bool parsed_days_separator;
660 public bool Exact; // no fallback, strict pattern.
661 public bool AllMembersRequired;
662 public bool CultureSensitive = true;
663 public bool UseColonAsDaySeparator = true;
664 #endif
666 public Parser (string src)
668 _src = src;
669 _length = _src.Length;
670 #if NET_4_0
671 number_format = GetNumberFormatInfo (null);
672 #endif
675 #if NET_4_0
676 // Reset state data, so we can execute another parse over the input.
677 void Reset ()
679 _cur = 0;
680 parse_error = ParseError.None;
681 parsed_ticks = parsed_days_separator = false;
682 parsed_numbers_count = 0;
685 public Parser (string src, IFormatProvider formatProvider) :
686 this (src)
688 number_format = GetNumberFormatInfo (formatProvider);
691 NumberFormatInfo GetNumberFormatInfo (IFormatProvider formatProvider)
693 NumberFormatInfo format = null;
694 if (formatProvider != null)
695 format = (NumberFormatInfo) formatProvider.GetFormat (typeof (NumberFormatInfo));
696 if (format == null)
697 format = Thread.CurrentThread.CurrentCulture.NumberFormat;
699 return format;
701 #endif
703 public bool AtEnd {
704 get {
705 return _cur >= _length;
709 // All "Parse" functions throw a FormatException on syntax error.
710 // Their return value is semantic value of the item parsed.
712 // Range checking is spread over three different places:
713 // 1) When parsing "int" values, an exception is thrown immediately
714 // when the value parsed exceeds the maximum value for an int.
715 // 2) An explicit check is built in that checks for hours > 23 and
716 // for minutes and seconds > 59.
717 // 3) Throwing an exceptions for a final TimeSpan value > MaxValue
718 // or < MinValue is left to the TimeSpan constructor called.
720 // Parse zero or more whitespace chars.
721 private void ParseWhiteSpace ()
723 while (!AtEnd && Char.IsWhiteSpace (_src, _cur)) {
724 _cur++;
728 // Parse optional sign character.
729 private bool ParseSign ()
731 bool res = false;
733 if (!AtEnd && _src[_cur] == '-') {
734 res = true;
735 _cur++;
738 return res;
741 // Parse simple int value
742 private int ParseInt (bool optional)
744 if (optional && AtEnd)
745 return 0;
747 long res = 0;
748 int count = 0;
750 while (!AtEnd && Char.IsDigit (_src, _cur)) {
751 res = res * 10 + _src[_cur] - '0';
752 #if NET_4_0
753 // more than one preceding zero will case an OverflowException
754 if (res > Int32.MaxValue || (count >= 1 && res == 0)) {
755 #else
756 if (res > Int32.MaxValue) {
757 #endif
758 SetParseError (ParseError.Overflow);
759 break;
761 _cur++;
762 count++;
765 if (!optional && (count == 0))
766 SetParseError (ParseError.Format);
767 #if NET_4_0
768 if (count > 0)
769 parsed_numbers_count++;
770 #endif
772 return (int)res;
775 // Parse optional dot
776 private bool ParseOptDot ()
778 if (AtEnd)
779 return false;
781 if (_src[_cur] == '.') {
782 _cur++;
783 return true;
785 return false;
788 #if NET_4_0
789 // This behaves pretty much like ParseOptDot, but we need to have it
790 // as a separated routine for both days and decimal separators.
791 private bool ParseOptDaysSeparator ()
793 if (AtEnd)
794 return false;
796 if (_src[_cur] == '.') {
797 _cur++;
798 parsed_days_separator = true;
799 return true;
801 return false;
804 // Just as ParseOptDot, but for decimal separator
805 private bool ParseOptDecimalSeparator ()
807 if (AtEnd)
808 return false;
810 // we may need to provide compatibility with old versions using '.'
811 // for culture insensitve and non exact formats.
812 if (!Exact || !CultureSensitive)
813 if (_src [_cur] == '.') {
814 _cur++;
815 return true;
818 string decimal_separator = number_format.NumberDecimalSeparator;
819 if (CultureSensitive && String.Compare (_src, _cur, decimal_separator, 0, decimal_separator.Length) == 0) {
820 _cur += decimal_separator.Length;
821 return true;
824 return false;
826 #endif
828 private void ParseColon (bool optional)
830 if (!AtEnd) {
831 if (_src[_cur] == ':')
832 _cur++;
833 else if (!optional)
834 SetParseError (ParseError.Format);
838 // Parse [1..7] digits, representing fractional seconds (ticks)
839 // In 4.0 more than 7 digits will cause an OverflowException
840 private long ParseTicks ()
842 long mag = 1000000;
843 long res = 0;
844 bool digitseen = false;
846 while (mag > 0 && !AtEnd && Char.IsDigit (_src, _cur)) {
847 res = res + (_src[_cur] - '0') * mag;
848 _cur++;
849 mag = mag / 10;
850 digitseen = true;
853 if (!digitseen)
854 SetParseError (ParseError.Format);
855 #if NET_4_0
856 else if (!AtEnd && Char.IsDigit (_src, _cur))
857 SetParseError (ParseError.Overflow);
859 parsed_ticks = true;
860 #endif
862 return res;
865 void SetParseError (ParseError error)
867 // We preserve the very first error.
868 if (parse_error != ParseError.None)
869 return;
871 parse_error = error;
874 #if NET_4_0
875 bool CheckParseSuccess (bool tryParse)
876 #else
877 bool CheckParseSuccess (int hours, int minutes, int seconds, bool tryParse)
878 #endif
880 // We always report the first error, but for 2.0 we need to give a higher
881 // precence to per-element overflow (as opposed to int32 overflow).
882 #if NET_4_0
883 if (parse_error == ParseError.Overflow) {
884 #else
885 if (parse_error == ParseError.Overflow || hours > 23 || minutes > 59 || seconds > 59) {
886 #endif
887 if (tryParse)
888 return false;
889 throw new OverflowException (
890 Locale.GetText ("Invalid time data."));
893 if (parse_error == ParseError.Format) {
894 if (tryParse)
895 return false;
896 throw new FormatException (
897 Locale.GetText ("Invalid format for TimeSpan.Parse."));
900 return true;
903 #if NET_4_0
904 // We are using a different parse approach in 4.0, due to some changes in the behaviour
905 // of the parse routines.
906 // The input string is documented as:
907 // Parse [ws][-][dd.]hh:mm:ss[.ff][ws]
909 // There are some special cases as part of 4.0, however:
910 // 1. ':' *can* be used as days separator, instead of '.', making valid the format 'dd:hh:mm:ss'
911 // 2. A input in the format 'hh:mm:ss' will end up assigned as 'dd.hh:mm' if the first int has a value
912 // exceeding the valid range for hours: 0-23.
913 // 3. The decimal separator can be retrieved from the current culture, as well as keeping support
914 // for the '.' value as part of keeping compatibility.
916 // So we take the approach to parse, if possible, 4 integers, and depending on both how many were
917 // actually parsed and what separators were read, assign the values to days/hours/minutes/seconds.
919 public bool Execute (bool tryParse, out TimeSpan result)
921 bool sign;
922 int value1, value2, value3, value4;
923 int days, hours, minutes, seconds;
924 long ticks = 0;
926 result = TimeSpan.Zero;
927 value1 = value2 = value3 = value4 = 0;
928 days = hours = minutes = seconds = 0;
930 Reset ();
932 ParseWhiteSpace ();
933 sign = ParseSign ();
935 // Parse 4 integers, making only the first one non-optional.
936 value1 = ParseInt (false);
937 if (!ParseOptDaysSeparator ()) // Parse either day separator or colon
938 ParseColon (false);
939 value2 = ParseInt (true);
940 ParseColon (true);
941 value3 = ParseInt (true);
942 ParseColon (true);
943 value4 = ParseInt (true);
945 // We know the precise separator for ticks, so there's no need to guess.
946 if (ParseOptDecimalSeparator ())
947 ticks = ParseTicks ();
949 ParseWhiteSpace ();
951 if (!AtEnd)
952 SetParseError (ParseError.Format);
954 if (Exact)
955 // In Exact mode we cannot allow both ':' and '.' as day separator.
956 if (UseColonAsDaySeparator && parsed_days_separator ||
957 AllMembersRequired && (parsed_numbers_count < 4 || !parsed_ticks))
958 SetParseError (ParseError.Format);
960 switch (parsed_numbers_count) {
961 case 1:
962 days = value1;
963 break;
964 case 2: // Two elements are valid only if they are *exactly* in the format: 'hh:mm'
965 if (parsed_days_separator)
966 SetParseError (ParseError.Format);
967 else {
968 hours = value1;
969 minutes = value2;
971 break;
972 case 3: // Assign the first value to days if we parsed a day separator or the value
973 // is not in the valid range for hours.
974 if (parsed_days_separator || value1 > 23) {
975 days = value1;
976 hours = value2;
977 minutes = value3;
978 } else {
979 hours = value1;
980 minutes = value2;
981 seconds = value3;
983 break;
984 case 4: // We are either on 'dd.hh:mm:ss' or 'dd:hh:mm:ss'
985 if (!UseColonAsDaySeparator && !parsed_days_separator)
986 SetParseError (ParseError.Format);
987 else {
988 days = value1;
989 hours = value2;
990 minutes = value3;
991 seconds = value4;
993 break;
996 if (hours > 23 || minutes > 59 || seconds > 59)
997 SetParseError (ParseError.Overflow);
999 if (!CheckParseSuccess (tryParse))
1000 return false;
1002 long t;
1003 if (!TimeSpan.CalculateTicks (days, hours, minutes, seconds, 0, false, out t))
1004 return false;
1006 try {
1007 t = checked ((sign) ? (-t - ticks) : (t + ticks));
1008 } catch (OverflowException) {
1009 if (tryParse)
1010 return false;
1011 throw;
1014 result = new TimeSpan (t);
1015 return true;
1017 #else
1018 public bool Execute (bool tryParse, out TimeSpan result)
1020 bool sign;
1021 int days;
1022 int hours = 0;
1023 int minutes;
1024 int seconds;
1025 long ticks;
1027 result = TimeSpan.Zero;
1029 // documented as...
1030 // Parse [ws][-][dd.]hh:mm:ss[.ff][ws]
1031 // ... but not entirely true as an lonely
1032 // integer will be parsed as a number of days
1033 ParseWhiteSpace ();
1034 sign = ParseSign ();
1035 days = ParseInt (false);
1036 if (ParseOptDot ()) {
1037 hours = ParseInt (true);
1039 else if (!AtEnd) {
1040 hours = days;
1041 days = 0;
1043 ParseColon(false);
1044 minutes = ParseInt (true);
1045 ParseColon (true);
1046 seconds = ParseInt (true);
1048 if ( ParseOptDot () ) {
1049 ticks = ParseTicks ();
1051 else {
1052 ticks = 0;
1054 ParseWhiteSpace ();
1056 if (!AtEnd)
1057 SetParseError (ParseError.Format);
1059 if (!CheckParseSuccess (hours, minutes, seconds, tryParse))
1060 return false;
1062 long t;
1063 if (!TimeSpan.CalculateTicks (days, hours, minutes, seconds, 0, false, out t))
1064 return false;
1066 try {
1067 t = checked ((sign) ? (-t - ticks) : (t + ticks));
1068 } catch (OverflowException) {
1069 if (tryParse)
1070 return false;
1071 throw;
1074 result = new TimeSpan (t);
1075 return true;
1077 #endif