Lets ensure we generate some types in AOT when using these valuetypes for MonoTouch
[mono.git] / mcs / class / corlib / System / TimeSpan.cs
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)
12 //
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.
31 //
32
33 using System.Text;
34
35 namespace System
36 {
37         [Serializable]
38         [System.Runtime.InteropServices.ComVisible (true)]
39         public struct TimeSpan : IComparable, IComparable<TimeSpan>, IEquatable <TimeSpan>
40         {
41 #if MONOTOUCH
42                 static TimeSpan () {
43                         if (MonoTouchAOTHelper.FalseFlag) {
44                                 var comparer = new System.Collections.Generic.GenericComparer <TimeSpan> ();
45                                 var eqcomparer = new System.Collections.Generic.GenericEqualityComparer <TimeSpan> ();
46                         }
47                 }
48 #endif
49                 public static readonly TimeSpan MaxValue = new TimeSpan (long.MaxValue);
50                 public static readonly TimeSpan MinValue = new TimeSpan (long.MinValue);
51                 public static readonly TimeSpan Zero = new TimeSpan (0L);
52
53                 public const long TicksPerDay = 864000000000L;
54                 public const long TicksPerHour = 36000000000L;
55                 public const long TicksPerMillisecond = 10000L;
56                 public const long TicksPerMinute = 600000000L;
57                 public const long TicksPerSecond = 10000000L;
58
59                 private long _ticks;
60
61                 public TimeSpan (long ticks)
62                 {
63                         _ticks = ticks;
64                 }
65
66                 public TimeSpan (int hours, int minutes, int seconds)
67                 {
68                         _ticks = CalculateTicks (0, hours, minutes, seconds, 0);
69                 }
70
71                 public TimeSpan (int days, int hours, int minutes, int seconds)
72                 {
73                         _ticks = CalculateTicks (days, hours, minutes, seconds, 0);
74                 }
75
76                 public TimeSpan (int days, int hours, int minutes, int seconds, int milliseconds)
77                 {
78                         _ticks = CalculateTicks (days, hours, minutes, seconds, milliseconds);
79                 }
80
81                 internal static long CalculateTicks (int days, int hours, int minutes, int seconds, int milliseconds)
82                 {
83                         // there's no overflow checks for hours, minutes, ...
84                         // so big hours/minutes values can overflow at some point and change expected values
85                         int hrssec = (hours * 3600); // break point at (Int32.MaxValue - 596523)
86                         int minsec = (minutes * 60);
87                         long t = ((long)(hrssec + minsec + seconds) * 1000L + (long)milliseconds);
88                         t *= 10000;
89
90                         bool overflow = false;
91                         // days is problematic because it can overflow but that overflow can be 
92                         // "legal" (i.e. temporary) (e.g. if other parameters are negative) or 
93                         // illegal (e.g. sign change).
94                         if (days > 0) {
95                                 long td = TicksPerDay * days;
96                                 if (t < 0) {
97                                         long ticks = t;
98                                         t += td;
99                                         // positive days -> total ticks should be lower
100                                         overflow = (ticks > t);
101                                 }
102                                 else {
103                                         t += td;
104                                         // positive + positive != negative result
105                                         overflow = (t < 0);
106                                 }
107                         }
108                         else if (days < 0) {
109                                 long td = TicksPerDay * days;
110                                 if (t <= 0) {
111                                         t += td;
112                                         // negative + negative != positive result
113                                         overflow = (t > 0);
114                                 }
115                                 else {
116                                         long ticks = t;
117                                         t += td;
118                                         // negative days -> total ticks should be lower
119                                         overflow = (t > ticks);
120                                 }
121                         }
122
123                         if (overflow)
124                                 throw new ArgumentOutOfRangeException (Locale.GetText ("The timespan is too big or too small."));
125
126                         return t;
127                 }
128
129                 public int Days {
130                         get {
131                                 return (int) (_ticks / TicksPerDay);
132                         }
133                 }
134
135                 public int Hours {
136                         get {
137                                 return (int) (_ticks % TicksPerDay / TicksPerHour);
138                         }
139                 }
140
141                 public int Milliseconds {
142                         get {
143                                 return (int) (_ticks % TicksPerSecond / TicksPerMillisecond);
144                         }
145                 }
146
147                 public int Minutes {
148                         get {
149                                 return (int) (_ticks % TicksPerHour / TicksPerMinute);
150                         }
151                 }
152
153                 public int Seconds {
154                         get {
155                                 return (int) (_ticks % TicksPerMinute / TicksPerSecond);
156                         }
157                 }
158
159                 public long Ticks {
160                         get {
161                                 return _ticks;
162                         }
163                 }
164
165                 public double TotalDays {
166                         get {
167                                 return (double) _ticks / TicksPerDay;
168                         }
169                 }
170
171                 public double TotalHours {
172                         get {
173                                 return (double) _ticks / TicksPerHour;
174                         }
175                 }
176
177                 public double TotalMilliseconds {
178                         get {
179                                 return (double) _ticks  / TicksPerMillisecond;
180                         }
181                 }
182
183                 public double TotalMinutes {
184                         get {
185                                 return (double) _ticks / TicksPerMinute;
186                         }
187                 }
188
189                 public double TotalSeconds {
190                         get {
191                                 return (double) _ticks / TicksPerSecond;
192                         }
193                 }
194
195                 public TimeSpan Add (TimeSpan ts)
196                 {
197                         try {
198                                 checked {
199                                         return new TimeSpan (_ticks + ts.Ticks);
200                                 }
201                         }
202                         catch (OverflowException) {
203                                 throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
204                         }
205                 }
206
207                 public static int Compare (TimeSpan t1, TimeSpan t2)
208                 {
209                         if (t1._ticks < t2._ticks)
210                                 return -1;
211                         if (t1._ticks > t2._ticks)
212                                 return 1;
213                         return 0;
214                 }
215
216                 public int CompareTo (object value)
217                 {
218                         if (value == null)
219                                 return 1;
220
221                         if (!(value is TimeSpan)) {
222                                 throw new ArgumentException (Locale.GetText ("Argument has to be a TimeSpan."), "value");
223                         }
224
225                         return Compare (this, (TimeSpan) value);
226                 }
227
228                 public int CompareTo (TimeSpan value)
229                 {
230                         return Compare (this, value);
231                 }
232
233                 public bool Equals (TimeSpan obj)
234                 {
235                         return obj._ticks == _ticks;
236                 }
237
238                 public TimeSpan Duration ()
239                 {
240                         try {
241                                 checked {
242                                         return new TimeSpan (Math.Abs (_ticks));
243                                 }
244                         }
245                         catch (OverflowException) {
246                                 throw new OverflowException (Locale.GetText (
247                                         "This TimeSpan value is MinValue so you cannot get the duration."));
248                         }
249                 }
250
251                 public override bool Equals (object value)
252                 {
253                         if (!(value is TimeSpan))
254                                 return false;
255
256                         return _ticks == ((TimeSpan) value)._ticks;
257                 }
258
259                 public static bool Equals (TimeSpan t1, TimeSpan t2)
260                 {
261                         return t1._ticks == t2._ticks;
262                 }
263
264                 public static TimeSpan FromDays (double value)
265                 {
266                         return From (value, TicksPerDay);
267                 }
268
269                 public static TimeSpan FromHours (double value)
270                 {
271                         return From (value, TicksPerHour);
272                 }
273
274                 public static TimeSpan FromMinutes (double value)
275                 {
276                         return From (value, TicksPerMinute);
277                 }
278
279                 public static TimeSpan FromSeconds (double value)
280                 {
281                         return From (value, TicksPerSecond);
282                 }
283
284                 public static TimeSpan FromMilliseconds (double value)
285                 {
286                         return From (value, TicksPerMillisecond);
287                 }
288
289                 private static TimeSpan From (double value, long tickMultiplicator) 
290                 {
291                         if (Double.IsNaN (value))
292                                 throw new ArgumentException (Locale.GetText ("Value cannot be NaN."), "value");
293                         if (Double.IsNegativeInfinity (value) || Double.IsPositiveInfinity (value) ||
294                                 (value < MinValue.Ticks) || (value > MaxValue.Ticks))
295                                 throw new OverflowException (Locale.GetText ("Outside range [MinValue,MaxValue]"));
296
297                         try {
298                                 value = (value * (tickMultiplicator / TicksPerMillisecond));
299
300                                 checked {
301                                         long val = (long) Math.Round(value);
302                                         return new TimeSpan (val * TicksPerMillisecond);
303                                 }
304                         }
305                         catch (OverflowException) {
306                                 throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
307                         }
308                 }
309
310                 public static TimeSpan FromTicks (long value)
311                 {
312                         return new TimeSpan (value);
313                 }
314
315                 public override int GetHashCode ()
316                 {
317                         return _ticks.GetHashCode ();
318                 }
319
320                 public TimeSpan Negate ()
321                 {
322                         if (_ticks == MinValue._ticks)
323                                 throw new OverflowException (Locale.GetText (
324                                         "This TimeSpan value is MinValue and cannot be negated."));
325                         return new TimeSpan (-_ticks);
326                 }
327
328                 public static TimeSpan Parse (string s)
329                 {
330                         if (s == null) {
331                                 throw new ArgumentNullException ("s");
332                         }
333
334                         Parser p = new Parser (s);
335                         return p.Execute ();
336                 }
337
338                 public static bool TryParse (string s, out TimeSpan result)
339                 {
340                         if (s == null) {
341                                 result = TimeSpan.Zero;
342                                 return false;
343                         }
344                         try {
345                                 result = Parse (s);
346                                 return true;
347                         } catch {
348                                 result = TimeSpan.Zero;
349                                 return false;
350                         }
351                 }
352
353                 public TimeSpan Subtract (TimeSpan ts)
354                 {
355                         try {
356                                 checked {
357                                         return new TimeSpan (_ticks - ts.Ticks);
358                                 }
359                         }
360                         catch (OverflowException) {
361                                 throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
362                         }
363                 }
364
365                 public override string ToString ()
366                 {
367                         StringBuilder sb = new StringBuilder (14);
368                         
369                         if (_ticks < 0)
370                                 sb.Append ('-');
371
372                         // We need to take absolute values of all components.
373                         // Can't handle negative timespans by negating the TimeSpan
374                         // as a whole. This would lead to an overflow for the 
375                         // degenerate case "TimeSpan.MinValue.ToString()".
376                         if (Days != 0) {
377                                 sb.Append (Math.Abs (Days));
378                                 sb.Append ('.');
379                         }
380
381                         sb.Append (Math.Abs (Hours).ToString ("D2"));
382                         sb.Append (':');
383                         sb.Append (Math.Abs (Minutes).ToString ("D2"));
384                         sb.Append (':');
385                         sb.Append (Math.Abs (Seconds).ToString ("D2"));
386
387                         int fractional = (int) Math.Abs (_ticks % TicksPerSecond);
388                         if (fractional != 0) {
389                                 sb.Append ('.');
390                                 sb.Append (fractional.ToString ("D7"));
391                         }
392
393                         return sb.ToString ();
394                 }
395
396                 public static TimeSpan operator + (TimeSpan t1, TimeSpan t2)
397                 {
398                         return t1.Add (t2);
399                 }
400
401                 public static bool operator == (TimeSpan t1, TimeSpan t2)
402                 {
403                         return t1._ticks == t2._ticks;
404                 }
405
406                 public static bool operator > (TimeSpan t1, TimeSpan t2)
407                 {
408                         return t1._ticks > t2._ticks;
409                 }
410
411                 public static bool operator >= (TimeSpan t1, TimeSpan t2)
412                 {
413                         return t1._ticks >= t2._ticks;
414                 }
415
416                 public static bool operator != (TimeSpan t1, TimeSpan t2)
417                 {
418                         return t1._ticks != t2._ticks;
419                 }
420
421                 public static bool operator < (TimeSpan t1, TimeSpan t2)
422                 {
423                         return t1._ticks < t2._ticks;
424                 }
425
426                 public static bool operator <= (TimeSpan t1, TimeSpan t2)
427                 {
428                         return t1._ticks <= t2._ticks;
429                 }
430
431                 public static TimeSpan operator - (TimeSpan t1, TimeSpan t2)
432                 {
433                         return t1.Subtract (t2);
434                 }
435
436                 public static TimeSpan operator - (TimeSpan t)
437                 {
438                         return t.Negate ();
439                 }
440
441                 public static TimeSpan operator + (TimeSpan t)
442                 {
443                         return t;
444                 }
445
446                 // Class Parser implements parser for TimeSpan.Parse
447                 private class Parser
448                 {
449                         private string _src;
450                         private int _cur = 0;
451                         private int _length;
452                         private bool formatError;
453
454                         public Parser (string src)
455                         {
456                                 _src = src;
457                                 _length = _src.Length;
458                         }
459         
460                         public bool AtEnd {
461                                 get {
462                                         return _cur >= _length;
463                                 }
464                         }
465
466                         // All "Parse" functions throw a FormatException on syntax error.
467                         // Their return value is semantic value of the item parsed.
468
469                         // Range checking is spread over three different places:
470                         // 1) When parsing "int" values, an exception is thrown immediately
471                         //    when the value parsed exceeds the maximum value for an int.
472                         // 2) An explicit check is built in that checks for hours > 23 and
473                         //    for minutes and seconds > 59.
474                         // 3) Throwing an exceptions for a final TimeSpan value > MaxValue
475                         //    or < MinValue is left to the TimeSpan constructor called.
476
477                         // Parse zero or more whitespace chars.
478                         private void ParseWhiteSpace ()
479                         {
480                                 while (!AtEnd && Char.IsWhiteSpace (_src, _cur)) {
481                                         _cur++;
482                                 }
483                         }
484
485                         // Parse optional sign character.
486                         private bool ParseSign ()
487                         {
488                                 bool res = false;
489
490                                 if (!AtEnd && _src[_cur] == '-') {
491                                         res = true;
492                                         _cur++;
493                                 }
494
495                                 return res;
496                         }
497
498                         // Parse simple int value
499                         private int ParseInt (bool optional)
500                         {
501                                 if (optional && AtEnd)
502                                         return 0;
503
504                                 int res = 0;
505                                 int count = 0;
506
507                                 while (!AtEnd && Char.IsDigit (_src, _cur)) {
508                                         checked {
509                                                 res = res * 10 + _src[_cur] - '0';
510                                         }
511                                         _cur++;
512                                         count++;
513                                 }
514
515                                 if (!optional && (count == 0))
516                                         formatError = true;
517
518                                 return res;
519                         }
520
521                         // Parse optional dot
522                         private bool ParseOptDot ()
523                         {
524                                 if (AtEnd)
525                                         return false;
526
527                                 if (_src[_cur] == '.') {
528                                         _cur++;
529                                         return true;
530                                 }
531                                 return false;
532                         }       
533
534                         // Parse optional (LAMESPEC) colon
535                         private void ParseOptColon ()
536                         {
537                                 if (!AtEnd) {
538                                         if (_src[_cur] == ':')
539                                                 _cur++;
540                                         else 
541                                                 formatError = true;
542                                 }
543                         }
544
545                         // Parse [1..7] digits, representing fractional seconds (ticks)
546                         private long ParseTicks ()
547                         {
548                                 long mag = 1000000;
549                                 long res = 0;
550                                 bool digitseen = false;
551                                 
552                                 while (mag > 0 && !AtEnd && Char.IsDigit (_src, _cur)) {
553                                         res = res + (_src[_cur] - '0') * mag;
554                                         _cur++;
555                                         mag = mag / 10;
556                                         digitseen = true;
557                                 }
558
559                                 if (!digitseen)
560                                         formatError = true;
561
562                                 return res;
563                         }
564
565                         public TimeSpan Execute ()
566                         {
567                                 bool sign;
568                                 int days;
569                                 int hours = 0;
570                                 int minutes;
571                                 int seconds;
572                                 long ticks;
573
574                                 // documented as...
575                                 // Parse [ws][-][dd.]hh:mm:ss[.ff][ws]
576                                 // ... but not entirely true as an lonely 
577                                 // integer will be parsed as a number of days
578                                 ParseWhiteSpace ();
579                                 sign = ParseSign ();
580                                 days = ParseInt (false);
581                                 if (ParseOptDot ()) {
582                                         hours = ParseInt (true);
583                                 }
584                                 else if (!AtEnd) {
585                                         hours = days;
586                                         days = 0;
587                                 }
588                                 ParseOptColon();
589                                 minutes = ParseInt (true);
590                                 ParseOptColon ();
591                                 seconds = ParseInt (true);
592                                 if ( ParseOptDot () ) {
593                                         ticks = ParseTicks ();
594                                 }
595                                 else {
596                                         ticks = 0;
597                                 }
598                                 ParseWhiteSpace ();
599         
600                                 if (!AtEnd)
601                                         formatError = true;
602
603                                 // Overflow has presceance over FormatException
604                                 if (hours > 23 || minutes > 59 || seconds > 59) {
605                                         throw new OverflowException (
606                                                 Locale.GetText ("Invalid time data."));
607                                 }
608                                 else if (formatError) {
609                                         throw new FormatException (
610                                                 Locale.GetText ("Invalid format for TimeSpan.Parse."));
611                                 }
612
613                                 long t = TimeSpan.CalculateTicks (days, hours, minutes, seconds, 0);
614                                 t = checked ((sign) ? (-t - ticks) : (t + ticks));
615                                 return new TimeSpan (t);
616                         }
617                 }
618         }
619 }