The Labs \ Source Viewer \ SSCLI \ System \ TimeSpan

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. namespace System
  16. {
  17.     using System.Text;
  18.     using System;
  19.    
  20.     // TimeSpan represents a duration of time. A TimeSpan can be negative
  21.     // or positive.
  22.     //
  23.     // TimeSpan is internally represented as a number of milliseconds. While
  24.     // this maps well into units of time such as hours and days, any
  25.     // periods longer than that aren't representable in a nice fashion.
  26.     // For instance, a month can be between 28 and 31 days, while a year
  27.     // can contain 365 or 364 days. A decade can have between 1 and 3 leapyears,
  28.     // depending on when you map the TimeSpan into the calendar. This is why
  29.     // we do not provide Years() or Months().
  30.     //
  31.     [System.Runtime.InteropServices.ComVisible(true)]
  32.     [Serializable()]
  33.     public struct TimeSpan : IComparable, IComparable<TimeSpan>, IEquatable<TimeSpan>
  34.     {
  35.         public const long TicksPerMillisecond = 10000;
  36.         private const double MillisecondsPerTick = 1.0 / TicksPerMillisecond;
  37.        
  38.         public const long TicksPerSecond = TicksPerMillisecond * 1000;
  39.         private const double SecondsPerTick = 1.0 / TicksPerSecond;
  40.        
  41.         public const long TicksPerMinute = TicksPerSecond * 60;
  42.         private const double MinutesPerTick = 1.0 / TicksPerMinute;
  43.        
  44.         public const long TicksPerHour = TicksPerMinute * 60;
  45.         private const double HoursPerTick = 1.0 / TicksPerHour;
  46.        
  47.         public const long TicksPerDay = TicksPerHour * 24;
  48.         private const double DaysPerTick = 1.0 / TicksPerDay;
  49.        
  50.         private const int MillisPerSecond = 1000;
  51.         private const int MillisPerMinute = MillisPerSecond * 60;
  52.         private const int MillisPerHour = MillisPerMinute * 60;
  53.         private const int MillisPerDay = MillisPerHour * 24;
  54.        
  55.         private const long MaxSeconds = Int64.MaxValue / TicksPerSecond;
  56.         private const long MinSeconds = Int64.MinValue / TicksPerSecond;
  57.        
  58.         private const long MaxMilliSeconds = Int64.MaxValue / TicksPerMillisecond;
  59.         private const long MinMilliSeconds = Int64.MinValue / TicksPerMillisecond;
  60.        
  61.        
  62.         public static readonly TimeSpan Zero = new TimeSpan(0);
  63.        
  64.         public static readonly TimeSpan MaxValue = new TimeSpan(Int64.MaxValue);
  65.         public static readonly TimeSpan MinValue = new TimeSpan(Int64.MinValue);
  66.        
  67.         // internal so that DateTime doesn't have to call an extra get
  68.         // method for some arithmetic operations.
  69.         internal long _ticks;
  70.        
  71.         //public TimeSpan() {
  72.         // _ticks = 0;
  73.         //}
  74.        
  75.         public TimeSpan(long ticks)
  76.         {
  77.             this._ticks = ticks;
  78.         }
  79.        
  80.         public TimeSpan(int hours, int minutes, int seconds)
  81.         {
  82.             _ticks = TimeToTicks(hours, minutes, seconds);
  83.         }
  84.        
  85.         public TimeSpan(int days, int hours, int minutes, int seconds) : this(days, hours, minutes, seconds, 0)
  86.         {
  87.         }
  88.        
  89.         public TimeSpan(int days, int hours, int minutes, int seconds, int milliseconds)
  90.         {
  91.             Int64 totalMilliSeconds = ((Int64)days * 3600 * 24 + (Int64)hours * 3600 + (Int64)minutes * 60 + seconds) * 1000 + milliseconds;
  92.             if (totalMilliSeconds > MaxMilliSeconds || totalMilliSeconds < MinMilliSeconds)
  93.                 throw new ArgumentOutOfRangeException(null, Environment.GetResourceString("Overflow_TimeSpanTooLong"));
  94.             _ticks = (long)totalMilliSeconds * TicksPerMillisecond;
  95.         }
  96.        
  97.         public long Ticks {
  98.             get { return _ticks; }
  99.         }
  100.        
  101.         public int Days {
  102.             get { return (int)(_ticks / TicksPerDay); }
  103.         }
  104.        
  105.         public int Hours {
  106.             get { return (int)((_ticks / TicksPerHour) % 24); }
  107.         }
  108.        
  109.         public int Milliseconds {
  110.             get { return (int)((_ticks / TicksPerMillisecond) % 1000); }
  111.         }
  112.        
  113.         public int Minutes {
  114.             get { return (int)((_ticks / TicksPerMinute) % 60); }
  115.         }
  116.        
  117.         public int Seconds {
  118.             get { return (int)((_ticks / TicksPerSecond) % 60); }
  119.         }
  120.        
  121.         public double TotalDays {
  122.             get { return ((double)_ticks) * DaysPerTick; }
  123.         }
  124.        
  125.         public double TotalHours {
  126.             get { return (double)_ticks * HoursPerTick; }
  127.         }
  128.        
  129.         public double TotalMilliseconds {
  130.             get {
  131.                 double temp = (double)_ticks * MillisecondsPerTick;
  132.                 if (temp > MaxMilliSeconds)
  133.                     return (double)MaxMilliSeconds;
  134.                
  135.                 if (temp < MinMilliSeconds)
  136.                     return (double)MinMilliSeconds;
  137.                
  138.                 return temp;
  139.             }
  140.         }
  141.        
  142.         public double TotalMinutes {
  143.             get { return (double)_ticks * MinutesPerTick; }
  144.         }
  145.        
  146.         public double TotalSeconds {
  147.             get { return (double)_ticks * SecondsPerTick; }
  148.         }
  149.        
  150.         public TimeSpan Add(TimeSpan ts)
  151.         {
  152.             long result = _ticks + ts._ticks;
  153.             // Overflow if signs of operands was identical and result's
  154.             // sign was opposite.
  155.             // >> 63 gives the sign bit (either 64 1's or 64 0's).
  156.             if ((_ticks >> 63 == ts._ticks >> 63) && (_ticks >> 63 != result >> 63))
  157.                 throw new OverflowException(Environment.GetResourceString("Overflow_TimeSpanTooLong"));
  158.             return new TimeSpan(result);
  159.         }
  160.        
  161.        
  162.         // Compares two TimeSpan values, returning an integer that indicates their
  163.         // relationship.
  164.         //
  165.         public static int Compare(TimeSpan t1, TimeSpan t2)
  166.         {
  167.             if (t1._ticks > t2._ticks)
  168.                 return 1;
  169.             if (t1._ticks < t2._ticks)
  170.                 return -1;
  171.             return 0;
  172.         }
  173.        
  174.         // Returns a value less than zero if this object
  175.         public int CompareTo(object value)
  176.         {
  177.             if (value == null)
  178.                 return 1;
  179.             if (!(value is TimeSpan))
  180.                 throw new ArgumentException(Environment.GetResourceString("Arg_MustBeTimeSpan"));
  181.             long t = ((TimeSpan)value)._ticks;
  182.             if (_ticks > t)
  183.                 return 1;
  184.             if (_ticks < t)
  185.                 return -1;
  186.             return 0;
  187.         }
  188.        
  189.         public int CompareTo(TimeSpan value)
  190.         {
  191.             long t = value._ticks;
  192.             if (_ticks > t)
  193.                 return 1;
  194.             if (_ticks < t)
  195.                 return -1;
  196.             return 0;
  197.         }
  198.        
  199.         public static TimeSpan FromDays(double value)
  200.         {
  201.             return Interval(value, MillisPerDay);
  202.         }
  203.        
  204.         public TimeSpan Duration()
  205.         {
  206.             if (_ticks == TimeSpan.MinValue._ticks)
  207.                 throw new OverflowException(Environment.GetResourceString("Overflow_Duration"));
  208.             return new TimeSpan(_ticks >= 0 ? _ticks : -_ticks);
  209.         }
  210.        
  211.         public override bool Equals(object value)
  212.         {
  213.             if (value is TimeSpan) {
  214.                 return _ticks == ((TimeSpan)value)._ticks;
  215.             }
  216.             return false;
  217.         }
  218.        
  219.         public bool Equals(TimeSpan obj)
  220.         {
  221.             return _ticks == obj._ticks;
  222.         }
  223.        
  224.         public static bool Equals(TimeSpan t1, TimeSpan t2)
  225.         {
  226.             return t1._ticks == t2._ticks;
  227.         }
  228.        
  229.         public override int GetHashCode()
  230.         {
  231.             return (int)_ticks ^ (int)(_ticks >> 32);
  232.         }
  233.        
  234.         public static TimeSpan FromHours(double value)
  235.         {
  236.             return Interval(value, MillisPerHour);
  237.         }
  238.        
  239.         private static TimeSpan Interval(double value, int scale)
  240.         {
  241.             if (Double.IsNaN(value))
  242.                 throw new ArgumentException(Environment.GetResourceString("Arg_CannotBeNaN"));
  243.             double tmp = value * scale;
  244.             double millis = tmp + (value >= 0 ? 0.5 : -0.5);
  245.             if ((millis > Int64.MaxValue / TicksPerMillisecond) || (millis < Int64.MinValue / TicksPerMillisecond))
  246.                 throw new OverflowException(Environment.GetResourceString("Overflow_TimeSpanTooLong"));
  247.             return new TimeSpan((long)millis * TicksPerMillisecond);
  248.         }
  249.        
  250.         public static TimeSpan FromMilliseconds(double value)
  251.         {
  252.             return Interval(value, 1);
  253.         }
  254.        
  255.         public static TimeSpan FromMinutes(double value)
  256.         {
  257.             return Interval(value, MillisPerMinute);
  258.         }
  259.        
  260.         public TimeSpan Negate()
  261.         {
  262.             if (_ticks == TimeSpan.MinValue._ticks)
  263.                 throw new OverflowException(Environment.GetResourceString("Overflow_NegateTwosCompNum"));
  264.             return new TimeSpan(-_ticks);
  265.         }
  266.        
  267.         // Constructs a TimeSpan from a string. Leading and trailing white
  268.         // space characters are allowed.
  269.         //
  270.         public static TimeSpan Parse(string s)
  271.         {
  272.             return new TimeSpan(new StringParser().Parse(s));
  273.         }
  274.        
  275.         public static bool TryParse(string s, out TimeSpan result)
  276.         {
  277.             long longResult;
  278.             if (new StringParser().TryParse(s, out longResult)) {
  279.                 result = new TimeSpan(longResult);
  280.                 return true;
  281.             }
  282.             else {
  283.                 result = TimeSpan.Zero;
  284.                 return false;
  285.             }
  286.         }
  287.        
  288.         public static TimeSpan FromSeconds(double value)
  289.         {
  290.             return Interval(value, MillisPerSecond);
  291.         }
  292.        
  293.         public TimeSpan Subtract(TimeSpan ts)
  294.         {
  295.             long result = _ticks - ts._ticks;
  296.             // Overflow if signs of operands was different and result's
  297.             // sign was opposite from the first argument's sign.
  298.             // >> 63 gives the sign bit (either 64 1's or 64 0's).
  299.             if ((_ticks >> 63 != ts._ticks >> 63) && (_ticks >> 63 != result >> 63))
  300.                 throw new OverflowException(Environment.GetResourceString("Overflow_TimeSpanTooLong"));
  301.             return new TimeSpan(result);
  302.         }
  303.        
  304.         public static TimeSpan FromTicks(long value)
  305.         {
  306.             return new TimeSpan(value);
  307.         }
  308.        
  309.         static internal long TimeToTicks(int hour, int minute, int second)
  310.         {
  311.             // totalSeconds is bounded by 2^31 * 2^12 + 2^31 * 2^8 + 2^31,
  312.             // which is less than 2^44, meaning we won't overflow totalSeconds.
  313.             long totalSeconds = (long)hour * 3600 + (long)minute * 60 + (long)second;
  314.             if (totalSeconds > MaxSeconds || totalSeconds < MinSeconds)
  315.                 throw new ArgumentOutOfRangeException(null, Environment.GetResourceString("Overflow_TimeSpanTooLong"));
  316.             return totalSeconds * TicksPerSecond;
  317.         }
  318.        
  319.         private string IntToString(int n, int digits)
  320.         {
  321.             return ParseNumbers.IntToString(n, 10, digits, '0', 0);
  322.         }
  323.        
  324.         public override string ToString()
  325.         {
  326.             StringBuilder sb = new StringBuilder();
  327.             int day = (int)(_ticks / TicksPerDay);
  328.             long time = _ticks % TicksPerDay;
  329.             if (_ticks < 0) {
  330.                 sb.Append("-");
  331.                 day = -day;
  332.                 time = -time;
  333.             }
  334.             if (day != 0) {
  335.                 sb.Append(day);
  336.                 sb.Append(".");
  337.             }
  338.             sb.Append(IntToString((int)(time / TicksPerHour % 24), 2));
  339.             sb.Append(":");
  340.             sb.Append(IntToString((int)(time / TicksPerMinute % 60), 2));
  341.             sb.Append(":");
  342.             sb.Append(IntToString((int)(time / TicksPerSecond % 60), 2));
  343.             int t = (int)(time % TicksPerSecond);
  344.             if (t != 0) {
  345.                 sb.Append(".");
  346.                 sb.Append(IntToString(t, 7));
  347.             }
  348.             return sb.ToString();
  349.         }
  350.        
  351.         public static TimeSpan operator -(TimeSpan t)
  352.         {
  353.             if (t._ticks == TimeSpan.MinValue._ticks)
  354.                 throw new OverflowException(Environment.GetResourceString("Overflow_NegateTwosCompNum"));
  355.             return new TimeSpan(-t._ticks);
  356.         }
  357.        
  358.         public static TimeSpan operator -(TimeSpan t1, TimeSpan t2)
  359.         {
  360.             return t1.Subtract(t2);
  361.         }
  362.        
  363.         public static TimeSpan operator +(TimeSpan t)
  364.         {
  365.             return t;
  366.         }
  367.        
  368.         public static TimeSpan operator +(TimeSpan t1, TimeSpan t2)
  369.         {
  370.             return t1.Add(t2);
  371.         }
  372.        
  373.         public static bool operator ==(TimeSpan t1, TimeSpan t2)
  374.         {
  375.             return t1._ticks == t2._ticks;
  376.         }
  377.        
  378.         public static bool operator !=(TimeSpan t1, TimeSpan t2)
  379.         {
  380.             return t1._ticks != t2._ticks;
  381.         }
  382.        
  383.         public static bool operator <(TimeSpan t1, TimeSpan t2)
  384.         {
  385.             return t1._ticks < t2._ticks;
  386.         }
  387.        
  388.         public static bool operator <=(TimeSpan t1, TimeSpan t2)
  389.         {
  390.             return t1._ticks <= t2._ticks;
  391.         }
  392.        
  393.         public static bool operator >(TimeSpan t1, TimeSpan t2)
  394.         {
  395.             return t1._ticks > t2._ticks;
  396.         }
  397.        
  398.         public static bool operator >=(TimeSpan t1, TimeSpan t2)
  399.         {
  400.             return t1._ticks >= t2._ticks;
  401.         }
  402.        
  403.         private struct StringParser
  404.         {
  405.            
  406.             private enum ParseError
  407.             {
  408.                 Format = 1,
  409.                 Overflow = 2,
  410.                 OverflowHoursMinutesSeconds = 3,
  411.                 ArgumentNull = 4
  412.             }
  413.            
  414.             private string str;
  415.             private char ch;
  416.             private int pos;
  417.             private int len;
  418.             private ParseError error;
  419.            
  420.             internal void NextChar()
  421.             {
  422.                 if (pos < len)
  423.                     pos++;
  424.                 ch = pos < len ? str[pos] : (char)0;
  425.             }
  426.            
  427.             internal char NextNonDigit()
  428.             {
  429.                 int i = pos;
  430.                 while (i < len) {
  431.                     char ch = str[i];
  432.                     if (ch < '0' || ch > '9')
  433.                         return ch;
  434.                     i++;
  435.                 }
  436.                 return (char)0;
  437.             }
  438.            
  439.             internal long Parse(string s)
  440.             {
  441.                 long value;
  442.                 if (TryParse(s, out value)) {
  443.                     return value;
  444.                 }
  445.                 else {
  446.                     switch (error) {
  447.                         case ParseError.ArgumentNull:
  448.                             throw new ArgumentNullException("s");
  449.                             break;
  450.                         case ParseError.Format:
  451.                             throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
  452.                             break;
  453.                         case ParseError.Overflow:
  454.                             throw new OverflowException(Environment.GetResourceString("Overflow_TimeSpanTooLong"));
  455.                             break;
  456.                         case ParseError.OverflowHoursMinutesSeconds:
  457.                             throw new OverflowException(Environment.GetResourceString("Overflow_TimeSpanElementTooLarge"));
  458.                             break;
  459.                         default:
  460.                             BCLDebug.Assert(false, "Unknown error: " + error.ToString());
  461.                             return 0;
  462.                     }
  463.                 }
  464.             }
  465.            
  466.             internal bool TryParse(string s, out long value)
  467.             {
  468.                 value = 0;
  469.                 if (s == null) {
  470.                     error = ParseError.ArgumentNull;
  471.                     return false;
  472.                 }
  473.                 str = s;
  474.                 len = s.Length;
  475.                 pos = -1;
  476.                 NextChar();
  477.                 SkipBlanks();
  478.                 bool negative = false;
  479.                 if (ch == '-') {
  480.                     negative = true;
  481.                     NextChar();
  482.                 }
  483.                 long time;
  484.                 if (NextNonDigit() == ':') {
  485.                     if (!ParseTime(out time)) {
  486.                         return false;
  487.                     }
  488.                     ;
  489.                 }
  490.                 else {
  491.                     int days;
  492.                     if (!ParseInt((int)(9223372036854775807l / TicksPerDay), out days)) {
  493.                         return false;
  494.                     }
  495.                     time = days * TicksPerDay;
  496.                     if (ch == '.') {
  497.                         NextChar();
  498.                         long remainingTime;
  499.                         if (!ParseTime(out remainingTime)) {
  500.                             return false;
  501.                         }
  502.                         ;
  503.                         time += remainingTime;
  504.                     }
  505.                 }
  506.                 if (negative) {
  507.                     time = -time;
  508.                     // Allow -0 as well
  509.                     if (time > 0) {
  510.                         error = ParseError.Overflow;
  511.                         return false;
  512.                     }
  513.                 }
  514.                 else {
  515.                     if (time < 0) {
  516.                         error = ParseError.Overflow;
  517.                         return false;
  518.                     }
  519.                 }
  520.                 SkipBlanks();
  521.                 if (pos < len) {
  522.                     error = ParseError.Format;
  523.                     return false;
  524.                 }
  525.                 value = time;
  526.                 return true;
  527.             }
  528.            
  529.             internal bool ParseInt(int max, out int i)
  530.             {
  531.                 i = 0;
  532.                 int p = pos;
  533.                 while (ch >= '0' && ch <= '9') {
  534.                     if ((i & 4026531840u) != 0) {
  535.                         error = ParseError.Overflow;
  536.                         return false;
  537.                     }
  538.                     i = i * 10 + ch - '0';
  539.                     if (i < 0) {
  540.                         error = ParseError.Overflow;
  541.                         return false;
  542.                     }
  543.                     NextChar();
  544.                 }
  545.                 if (p == pos) {
  546.                     error = ParseError.Format;
  547.                     return false;
  548.                 }
  549.                 if (i > max) {
  550.                     error = ParseError.Overflow;
  551.                     return false;
  552.                 }
  553.                 return true;
  554.             }
  555.            
  556.             internal bool ParseTime(out long time)
  557.             {
  558.                 time = 0;
  559.                 int unit;
  560.                 if (!ParseInt(23, out unit)) {
  561.                     if (error == ParseError.Overflow) {
  562.                         error = ParseError.OverflowHoursMinutesSeconds;
  563.                     }
  564.                     return false;
  565.                 }
  566.                 time = unit * TicksPerHour;
  567.                 if (ch != ':') {
  568.                     error = ParseError.Format;
  569.                     return false;
  570.                 }
  571.                 NextChar();
  572.                 if (!ParseInt(59, out unit)) {
  573.                     if (error == ParseError.Overflow) {
  574.                         error = ParseError.OverflowHoursMinutesSeconds;
  575.                     }
  576.                     return false;
  577.                 }
  578.                 time += unit * TicksPerMinute;
  579.                 if (ch == ':') {
  580.                     NextChar();
  581.                     // allow seconds with the leading zero
  582.                     if (ch != '.') {
  583.                         if (!ParseInt(59, out unit)) {
  584.                             if (error == ParseError.Overflow) {
  585.                                 error = ParseError.OverflowHoursMinutesSeconds;
  586.                             }
  587.                             return false;
  588.                         }
  589.                         time += unit * TicksPerSecond;
  590.                     }
  591.                     if (ch == '.') {
  592.                         NextChar();
  593.                         int f = (int)TicksPerSecond;
  594.                         while (f > 1 && ch >= '0' && ch <= '9') {
  595.                             f /= 10;
  596.                             time += (ch - '0') * f;
  597.                             NextChar();
  598.                         }
  599.                     }
  600.                 }
  601.                 return true;
  602.             }
  603.            
  604.             internal void SkipBlanks()
  605.             {
  606.                 while (ch == ' ' || ch == '\t')
  607.                     NextChar();
  608.             }
  609.         }
  610.     }
  611. }

Developer Fusion