Added new version of TimeSpan from Duco Fijma
[mono.git] / mcs / class / corlib / System / TimeSpan.cs
1 //
2 // System.TimeSpan.cs
3 //
4 // author:
5 //   Duco Fijma (duco@lorentz.xs4all.nl)
6 //
7 //   (C) 2001 Duco Fijma
8 //
9 // version: 07312001
10
11 namespace System {
12         
13         public struct TimeSpan :  IComparable  {
14                 private long _ticks;
15
16                 // Ctors
17
18                 public TimeSpan (long value) { _ticks = value; }
19                 public TimeSpan (int hours, int minutes, int seconds) 
20                         : this(false, 0, hours, minutes, seconds, 0, 0) {}
21                 public TimeSpan (int days, int hours, int minutes, int seconds) 
22                         : this(false, days, hours, minutes, seconds, 0, 0) {}
23                 public TimeSpan (int days, int hours, int minutes, int seconds, int milliseconds)
24                         : this(false, days, hours, minutes, seconds, milliseconds, 0) {}
25
26                 internal TimeSpan (bool sign, int days, int hours, int minutes, int seconds, int milliseconds, long ticks)
27                 {
28                         checked {
29                                 _ticks = TicksPerDay * days + 
30                                         TicksPerHour * hours +
31                                         TicksPerMinute * minutes +
32                                         TicksPerSecond * seconds +
33                                         TicksPerMillisecond * milliseconds +
34                                         ticks;
35                                 if ( sign ) {
36                                         _ticks = -_ticks;
37                                 }
38                         }
39                 }
40                 
41                 // Fields
42
43                 public static readonly TimeSpan MaxValue = new TimeSpan (long.MaxValue);
44                 public static readonly TimeSpan MinValue = new TimeSpan (long.MinValue);
45                 public const long TicksPerDay = 864000000000L;
46                 public const long TicksPerHour = 36000000000L;
47                 public const long TicksPerMillisecond = 10000L;
48                 public const long TicksPerMinute = 600000000L;
49                 public const long TicksPerSecond = 10000000L;
50                 public static readonly TimeSpan Zero = new TimeSpan (0L);
51
52                 // Properties
53
54                 public int Days
55                 {
56                         get {
57                                 return (int) TotalDays;
58                         }
59                 }
60
61                 public int Hours
62                 {
63                         get {
64                                 return (int) (_ticks % TicksPerDay / TicksPerHour);
65                         }
66                 }
67
68                 public int Milliseconds
69                 {
70                         get
71                         {
72                                 return (int) (_ticks % TicksPerSecond / TicksPerMillisecond);
73                         }
74                 }
75
76                 public int Minutes
77                 {
78                         get
79                         {
80                                 return (int) (_ticks % TicksPerHour / TicksPerMinute);
81                         }
82                 }
83
84                 public int Seconds
85                 {
86                         get
87                         {
88                                 return (int) (_ticks % TicksPerMinute / TicksPerSecond);
89                         }
90                 }
91
92                 public long Ticks
93                 { 
94                         get
95                         {
96                                 return _ticks;
97                         }
98                 }
99
100                 public double TotalDays
101                 {
102                         get
103                         {
104                                 return (double) _ticks / TicksPerDay;
105                         }
106                 }
107
108                 public double TotalHours
109                 {
110                         get
111                         {
112                                 return (double) _ticks / TicksPerHour;
113                         }
114                 }
115
116                 public double TotalMilliseconds
117                 {
118                         get
119                         {
120                                 return (double) _ticks  / TicksPerMillisecond;
121                         }
122                 }
123
124                 public double TotalMinutes
125                 {
126                         get {
127                                 return (double) _ticks / TicksPerMinute;
128                         }
129                 }
130
131                 public double TotalSeconds
132                 {
133                         get {
134                                 return (double) _ticks / TicksPerSecond;
135                         }
136                 }
137
138                 // Methods
139
140                 public TimeSpan Add (TimeSpan ts)
141                 {
142                         checked {
143                                 return new TimeSpan (_ticks + ts.Ticks);
144                         }
145                 }
146
147                 public static int Compare (TimeSpan t1, TimeSpan t2)
148                 {
149                         if (t1._ticks < t2._ticks) {
150                                 return -1;
151                         }
152                         else if (t1._ticks > t2._ticks) {
153                                 return 1;
154                         }
155                         else {
156                                 return 0;
157                         }
158                 }
159
160                 public int CompareTo (object value)
161                 {
162                         
163                         if (value == null ) {
164                                 return 1;
165                         }
166
167                         if (!(value is TimeSpan)) {
168                                 throw new ArgumentException ("Argument of System.TimeSpan.CompareTo should be a TimeSpan");
169                         }
170                 
171                         return Compare(this, (TimeSpan) value);
172                 }
173
174                 public TimeSpan Duration ()
175                 {
176                         checked {
177                                 return new TimeSpan (Math.Abs (_ticks));
178                         }
179                 }
180
181                 public override bool Equals (object value)
182                 {
183                         if (!(value is TimeSpan)) {
184                                 return false;
185                         }
186                         return Equals (this, (TimeSpan) value);
187                 }
188
189                 public static bool Equals (TimeSpan t1, TimeSpan t2)
190                 {
191                         return t1._ticks == t2._ticks;
192                 }
193
194                 // Implementing FromDays -> FromHours -> FromMinutes -> FromSeconds ->
195                 // FromMilliseconds as done here is probably not the most efficient
196                 // way. 
197                 public static TimeSpan FromDays (double value)
198                 {
199                         if (Double.IsNaN (value) || Double.IsNegativeInfinity (value)) {
200                                 return MinValue;
201                         }
202
203                         if (Double.IsPositiveInfinity (value)) {
204                                 return MaxValue;
205                         }
206
207                         return new TimeSpan ((int) value,0,0,0,0) + FromHours ((value - ((int) value)) * 24);
208                 }
209
210                 public static TimeSpan FromHours (double value)
211                 {
212                         if (Double.IsNaN (value) || Double.IsNegativeInfinity (value)) {
213                                 return MinValue;
214                         }
215
216                         if (Double.IsPositiveInfinity (value)) {
217                                 return MaxValue;
218                         }
219
220                         return new TimeSpan ((int) value,0,0) + FromMinutes ((value - ((int) value)) * 60);
221                 }
222
223                 public static TimeSpan FromMinutes (double value)
224                 {
225                         if (Double.IsNaN (value) || Double.IsNegativeInfinity (value)) {
226                                 return MinValue;
227                         }
228
229                         if (Double.IsPositiveInfinity (value)) {
230                                 return MaxValue;
231                         }
232
233                         return new TimeSpan (0, (int) value, 0) + FromSeconds((value - ((int) value)) * 60);
234                 }
235
236                 public static TimeSpan FromSeconds (double value)
237                 {
238                         if (Double.IsNaN (value) || Double.IsNegativeInfinity (value)) {
239                                 return MinValue;
240                         }
241
242                         if (Double.IsPositiveInfinity (value)) {
243                                 return MaxValue;
244                         }
245
246                         return new TimeSpan (0, 0, 0, (int) value, ((int) ((value - ((int) value)) * 1000)));
247
248                 }
249
250                 public static TimeSpan FromTicks (long value)
251                 {
252                         return new TimeSpan (value);
253                 }
254
255                 public override int GetHashCode ()
256                 {
257                         return _ticks.GetHashCode ();
258                 }
259
260                 public TimeSpan Negate ()
261                 {
262                         checked {
263                                 return new TimeSpan (-_ticks);
264                         }
265                 }
266
267                 public static TimeSpan Parse (string s)
268                 {
269                         if (s == null) {
270                                 throw new ArgumentNullException ("null reference passed to TimeSpan.Parse");
271                         }
272
273                         Parser p = new Parser (s); 
274                         return p.Execute ();
275                 }
276
277                 public TimeSpan Subtract (TimeSpan ts)
278                 {
279                         checked {
280                                 return new TimeSpan (_ticks - ts.Ticks);
281                         }
282                 }
283
284                 public override string ToString ()
285                 {
286                         string res = "";        
287
288                         if (_ticks < 0) {
289                                 res += "-";
290                         }
291
292                         // We need to take absolute values of all components.
293                         // Can't handle negative timespans by negating the TimeSpan
294                         // as a whole. This would lead to an overflow for the 
295                         // degenerate case "TimeSpan.MinValue.ToString()".
296                         if (Days != 0) {
297                                 res += Math.Abs (Days) + "." ;
298                         }
299
300                         res += string.Format ("{0:00}:{1:00}:{2:00}", Math.Abs(Hours), Math.Abs(Minutes), Math.Abs(Seconds));
301
302                         int fractional = (int) Math.Abs (_ticks % TicksPerSecond);
303                         if (fractional != 0) {
304                                 res += string.Format (".{0:0000000}", fractional);
305                         }
306  
307                         return res;
308                 }
309
310                 public static TimeSpan operator + (TimeSpan t1, TimeSpan t2)
311                 {
312                         return t1.Add (t2);
313                 }
314
315                 public static bool operator == (TimeSpan t1, TimeSpan t2)
316                 {
317                         return Compare (t1, t2) == 0;
318                 }
319
320                 public static bool operator > (TimeSpan t1, TimeSpan t2)
321                 {
322                         return Compare (t1, t2) == 1;
323                 }
324
325                 public static bool operator >= (TimeSpan t1, TimeSpan t2)
326                 {
327                         return Compare (t1, t2) != -1;
328                 }
329
330                 public static bool operator != (TimeSpan t1, TimeSpan t2)
331                 {
332                         return Compare (t1, t2) != 0;
333                 }
334
335                 public static bool operator < (TimeSpan t1, TimeSpan t2)
336                 {
337                         return Compare (t1, t2) == -1;
338                 }
339
340                 public static bool operator <= (TimeSpan t1, TimeSpan t2)
341                 {
342                         return Compare (t1, t2) != 1;
343                 }
344
345                 public static TimeSpan operator - (TimeSpan t1, TimeSpan t2)
346                 {
347                         return t1.Subtract (t2);
348                 }
349
350                 public static TimeSpan operator - (TimeSpan t)
351                 {
352                         return t.Negate ();
353                 }
354
355                 public static TimeSpan operator + (TimeSpan t)
356                 {
357                         return t;
358                 }
359         }
360
361         // Class Parser implements simple parser for TimeSpan::Parse
362         internal class Parser {
363
364                 private string _src;
365                 private int _cur;
366                 private int _length;
367         
368                 public Parser (string src)
369                 {
370                         _src = src;
371                         Reset ();
372                 }
373
374                 public void Reset ()
375                 {
376                         _cur = 0;
377                         _length = _src.Length;
378                 }
379
380                 public bool AtEnd
381                 {
382                         get {
383                                 return _cur >= _length;
384                         }
385                 }
386
387                 private void ThrowFormatException() 
388                 {
389                         throw new FormatException ("Invalid format for TimeSpan.Parse");
390                 }
391
392                 // All "Parse" functions throw a FormatException on syntax error.
393                 // Their return value is semantic value of the item parsed.
394
395                 // Range checking is spread over three different places:
396                 // 1) When parsing "int" values, an exception is thrown immediately
397                 //    when the value parsed exceeds the maximum value for an int.
398                 // 2) An explicit check is built in that checks for hours > 23 and
399                 //    for minutes and seconds > 59.
400                 // 3) Throwing an exceptions for a final TimeSpan value > MaxValue
401                 //    or < MinValue is left to the TimeSpan constructor called.
402
403                 // Parse zero or more whitespace chars.
404                 private void ParseWhiteSpace ()
405                 {
406                         while (!AtEnd && Char.IsWhiteSpace (_src, _cur)) {
407                                 _cur++; 
408                         }
409                 }
410
411                 // Parse optional sign character.
412                 private bool ParseSign () 
413                 {
414                         bool res = false;
415
416                         if (!AtEnd && _src[_cur] == '-') { 
417                                 res = true;
418                                 _cur++;
419                         }
420
421                         return res;
422                 }
423
424                 // Parse simple int value
425                 private int ParseInt ()
426                 {
427                         int res = 0;
428                         int count = 0;
429
430                         while (!AtEnd && Char.IsDigit (_src, _cur)) {
431                                 checked {
432                                         res = res*10 + _src[_cur] - '0';
433                                 }
434                                 _cur++;
435                                 count++;
436                         }
437                         
438                         if (count == 0) {
439                                 ThrowFormatException ();
440                         }
441
442                         return res;
443                 }
444
445                 // Parse optional dot
446                 private bool ParseOptDot ()
447                 {
448                         if (AtEnd) {
449                                 return false;
450                         }
451                         
452                         if (_src[_cur] == '.') {
453                                 _cur++;
454                                 return true;
455                         }
456                         else {
457                                 return false;
458                         }
459                 }       
460                 
461                 // Parse NON-optional colon 
462                 private void ParseColon ()
463                 {
464                         if (!AtEnd && _src[_cur] == ':') {
465                                 _cur++;
466                         }
467                         else {
468                                 ThrowFormatException ();
469                         }
470                 }
471
472                 // Parse [1..7] digits, representing fractional seconds (ticks)
473                 private long ParseTicks ()
474                 {
475                         long mag = 1000000;
476                         long res = 0;
477                         bool digitseen = false;
478                         
479                         while ( mag > 0 && !AtEnd && Char.IsDigit (_src, _cur) ) {
480                                 res = res + (_src[_cur] - '0') * mag;
481                                 _cur++;
482                                 mag = mag / 10;
483                                 digitseen = true;
484                         }
485
486                         if (!digitseen) {
487                                 ThrowFormatException ();
488                         }
489
490                         return res;
491                 }
492
493                 public TimeSpan Execute ()
494                 {
495                         bool sign;
496                         int days;
497                         int hours;
498                         int minutes;
499                         int seconds;
500                         long ticks;
501
502                         // Parse [ws][dd.]hh:mm:ss[.ff][ws]
503                         ParseWhiteSpace ();
504                         sign = ParseSign ();
505                         days = ParseInt ();
506                         if (ParseOptDot ()) {
507                                 hours = ParseInt ();
508                         }
509                         else {
510                                 hours = days;
511                                 days = 0;
512                         }
513                         ParseColon();
514                         minutes = ParseInt ();
515                         ParseColon();
516                         seconds = ParseInt ();
517                         if ( ParseOptDot () ) {
518                                 ticks = ParseTicks ();
519                         }       
520                         else {
521                                 ticks = 0;
522                         }
523                         ParseWhiteSpace ();
524
525                         if ( !AtEnd ) {
526                                 ThrowFormatException ();
527                         }
528
529                         if ( hours > 23 || minutes > 59 || seconds > 59 ) {
530                                 throw new OverflowException ( "Value outside range in TimeSpan.Parse" );
531                         }
532
533                         TimeSpan ts = new TimeSpan (sign, days, hours, minutes, seconds, 0, ticks);
534
535                         return ts;
536                 }       
537         }
538 }
539
540