488 lines
19 KiB
C#
488 lines
19 KiB
C#
// ==++==
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// ==--==
|
|
namespace System {
|
|
using System.Text;
|
|
using System.Runtime.CompilerServices;
|
|
using System;
|
|
|
|
// TimeSpan represents a duration of time. A TimeSpan can be negative
|
|
// or positive.
|
|
//
|
|
// TimeSpan is internally represented as a number of ticks (100ns). While
|
|
// this maps well into units of time such as hours and days, any
|
|
// periods longer than that aren't representable in a nice fashion.
|
|
// For instance, a month can be between 28 and 31 days, while a year
|
|
// can contain 365 or 364 days. A decade can have between 1 and 3 leap years,
|
|
// depending on when you map the TimeSpan into the calendar. This is why
|
|
// we do not provide Years() or Months().
|
|
//
|
|
// TimeSpans are limited to approximately 14623 years.
|
|
//
|
|
//| <include path='docs/doc[@for="TimeSpan"]/*' />
|
|
public struct TimeSpan : IComparable
|
|
{
|
|
//| <include path='docs/doc[@for="TimeSpan.TicksPerMillisecond"]/*' />
|
|
public const long TicksPerMillisecond = 10000;
|
|
//| <include path='docs/doc[@for="TimeSpan.TicksPerSecond"]/*' />
|
|
public const long TicksPerSecond = TicksPerMillisecond * 1000;
|
|
//| <include path='docs/doc[@for="TimeSpan.TicksPerMinute"]/*' />
|
|
public const long TicksPerMinute = TicksPerSecond * 60;
|
|
//| <include path='docs/doc[@for="TimeSpan.TicksPerHour"]/*' />
|
|
public const long TicksPerHour = TicksPerMinute * 60;
|
|
//| <include path='docs/doc[@for="TimeSpan.TicksPerDay"]/*' />
|
|
public const long TicksPerDay = TicksPerHour * 24;
|
|
|
|
private const int MillisPerSecond = 1000;
|
|
private const int MillisPerMinute = MillisPerSecond * 60;
|
|
private const int MillisPerHour = MillisPerMinute * 60;
|
|
private const int MillisPerDay = MillisPerHour * 24;
|
|
|
|
private const long MaxSeconds = Int64.MaxValue / TicksPerSecond;
|
|
private const long MinSeconds = Int64.MinValue / TicksPerSecond;
|
|
|
|
private const long MaxMilliSeconds = Int64.MaxValue / TicksPerMillisecond;
|
|
private const long MinMilliSeconds = Int64.MinValue / TicksPerMillisecond;
|
|
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.Zero"]/*' />
|
|
public static readonly TimeSpan Zero = new TimeSpan(0);
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.MaxValue"]/*' />
|
|
public static readonly TimeSpan MaxValue = new TimeSpan(Int64.MaxValue);
|
|
//| <include path='docs/doc[@for="TimeSpan.MinValue"]/*' />
|
|
public static readonly TimeSpan MinValue = new TimeSpan(Int64.MinValue);
|
|
|
|
public static readonly TimeSpan Infinite = new TimeSpan(Int64.MaxValue);
|
|
|
|
// internal so that DateTime doesn't have to call an extra get
|
|
// method for some arithmetic operations.
|
|
internal long _ticks;
|
|
|
|
//public TimeSpan() {
|
|
// _ticks = 0;
|
|
//}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.TimeSpan"]/*' />
|
|
[NoHeapAllocation]
|
|
public TimeSpan(long ticks) {
|
|
this._ticks = ticks;
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.TimeSpan1"]/*' />
|
|
public TimeSpan(int hours, int minutes, int seconds) {
|
|
_ticks = TimeToTicks(hours, minutes, seconds);
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.TimeSpan2"]/*' />
|
|
public TimeSpan(int days, int hours, int minutes, int seconds)
|
|
: this(days,hours,minutes,seconds,0)
|
|
{
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.TimeSpan3"]/*' />
|
|
public TimeSpan(int days, int hours, int minutes, int seconds, int milliseconds)
|
|
{
|
|
long totalMilliSeconds = ((long)days * 3600 * 24 + hours * 3600 + minutes * 60 + seconds) * 1000 + milliseconds;
|
|
if (totalMilliSeconds > MaxMilliSeconds || totalMilliSeconds < MinMilliSeconds)
|
|
throw new ArgumentOutOfRangeException("Overflow_TimeSpanTooLong");
|
|
_ticks = (long)totalMilliSeconds * TicksPerMillisecond;
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.Ticks"]/*' />
|
|
public long Ticks {
|
|
[NoHeapAllocation]
|
|
get { return _ticks; }
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.Days"]/*' />
|
|
public int Days {
|
|
get { return (int)(_ticks / TicksPerDay); }
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.Hours"]/*' />
|
|
public int Hours {
|
|
get { return (int)((_ticks / TicksPerHour) % 24); }
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.Milliseconds"]/*' />
|
|
public int Milliseconds {
|
|
get { return (int)((_ticks / TicksPerMillisecond) % 1000); }
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.Minutes"]/*' />
|
|
public int Minutes {
|
|
get { return (int)((_ticks / TicksPerMinute) % 60); }
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.Seconds"]/*' />
|
|
public int Seconds {
|
|
get { return (int)((_ticks / TicksPerSecond) % 60); }
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.TotalDays"]/*' />
|
|
public long TotalDays {
|
|
get { return _ticks / TicksPerDay; }
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.TotalHours"]/*' />
|
|
public long TotalHours {
|
|
get { return _ticks / TicksPerHour; }
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.TotalMilliseconds"]/*' />
|
|
public long TotalMilliseconds {
|
|
get {
|
|
return _ticks / TicksPerMillisecond;
|
|
}
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.TotalMinutes"]/*' />
|
|
public long TotalMinutes {
|
|
get { return _ticks / TicksPerMinute; }
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.TotalSeconds"]/*' />
|
|
public long TotalSeconds {
|
|
get { return _ticks / TicksPerSecond; }
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.Add"]/*' />
|
|
public TimeSpan Add(TimeSpan ts) {
|
|
long result = _ticks + ts._ticks;
|
|
// Overflow if signs of operands was identical and result's
|
|
// sign was opposite.
|
|
// >> 63 gives the sign bit (either 64 1's or 64 0's).
|
|
if ((_ticks >> 63 == ts._ticks >> 63) && (_ticks >> 63 != result >> 63))
|
|
throw new OverflowException("Overflow_TimeSpanTooLong");
|
|
return new TimeSpan(result);
|
|
}
|
|
|
|
[NoHeapAllocation]
|
|
public TimeSpan AddNoAlloc(TimeSpan ts) {
|
|
long result = _ticks + ts._ticks;
|
|
// Overflow if signs of operands was identical and result's
|
|
// sign was opposite.
|
|
// >> 63 gives the sign bit (either 64 1's or 64 0's).
|
|
if ((_ticks >> 63 == ts._ticks >> 63) && (_ticks >> 63 != result >> 63))
|
|
return TimeSpan.Infinite;
|
|
return new TimeSpan(result);
|
|
}
|
|
|
|
|
|
// Compares two TimeSpan values, returning an integer that indicates their
|
|
// relationship.
|
|
//
|
|
//| <include path='docs/doc[@for="TimeSpan.Compare"]/*' />
|
|
public static int Compare(TimeSpan t1, TimeSpan t2) {
|
|
if (t1._ticks > t2._ticks) return 1;
|
|
if (t1._ticks < t2._ticks) return -1;
|
|
return 0;
|
|
}
|
|
|
|
// Returns a value less than zero if this object
|
|
//| <include path='docs/doc[@for="TimeSpan.CompareTo"]/*' />
|
|
public int CompareTo(Object value) {
|
|
if (value == null) return 1;
|
|
if (!(value is TimeSpan))
|
|
throw new ArgumentException("Arg_MustBeTimeSpan");
|
|
long t = ((TimeSpan)value)._ticks;
|
|
if (_ticks > t) return 1;
|
|
if (_ticks < t) return -1;
|
|
return 0;
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.FromDays"]/*' />
|
|
public static TimeSpan FromDays(long value) {
|
|
return Interval(value, MillisPerDay);
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.Duration"]/*' />
|
|
public TimeSpan Duration() {
|
|
if (_ticks == TimeSpan.MinValue._ticks)
|
|
throw new OverflowException("Overflow_NegateTwosCompNum");
|
|
return new TimeSpan(_ticks >= 0? _ticks: -_ticks);
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.Equals"]/*' />
|
|
public override bool Equals(Object value) {
|
|
if (value is TimeSpan) {
|
|
return _ticks == ((TimeSpan)value)._ticks;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.Equals1"]/*' />
|
|
public static bool Equals(TimeSpan t1, TimeSpan t2) {
|
|
return t1._ticks == t2._ticks;
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.GetHashCode"]/*' />
|
|
public override int GetHashCode() {
|
|
return (int)_ticks ^ (int)(_ticks >> 32);
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.FromHours"]/*' />
|
|
public static TimeSpan FromHours(long value) {
|
|
return Interval(value, MillisPerHour);
|
|
}
|
|
|
|
private static TimeSpan Interval(long value, int scale) {
|
|
long millis = value * scale;
|
|
if ((millis > MaxMilliSeconds) || (millis < MinMilliSeconds)) {
|
|
throw new OverflowException("Overflow_TimeSpanTooLong");
|
|
}
|
|
return new TimeSpan(millis * TicksPerMillisecond);
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.FromMilliseconds"]/*' />
|
|
public static TimeSpan FromMilliseconds(long value) {
|
|
return Interval(value, 1);
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.FromMinutes"]/*' />
|
|
public static TimeSpan FromMinutes(long value) {
|
|
return Interval(value, MillisPerMinute);
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.Negate"]/*' />
|
|
public TimeSpan Negate() {
|
|
if (_ticks==TimeSpan.MinValue._ticks)
|
|
throw new OverflowException("Overflow_NegateTwosCompNum");
|
|
return new TimeSpan(-_ticks);
|
|
}
|
|
|
|
// Constructs a TimeSpan from a string. Leading and trailing white
|
|
// space characters are allowed.
|
|
//
|
|
//| <include path='docs/doc[@for="TimeSpan.Parse"]/*' />
|
|
public static TimeSpan Parse(String s) {
|
|
return new TimeSpan(new StringParser().Parse(s, true));
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.FromSeconds"]/*' />
|
|
public static TimeSpan FromSeconds(long value) {
|
|
return Interval(value, MillisPerSecond);
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.Subtract"]/*' />
|
|
public TimeSpan Subtract(TimeSpan ts) {
|
|
long result = _ticks - ts._ticks;
|
|
// Overflow if signs of operands was different and result's
|
|
// sign was opposite from the first argument's sign.
|
|
// >> 63 gives the sign bit (either 64 1's or 64 0's).
|
|
if ((_ticks >> 63 != ts._ticks >> 63) && (_ticks >> 63 != result >> 63))
|
|
throw new OverflowException("Overflow_TimeSpanTooLong");
|
|
return new TimeSpan(result);
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.FromTicks"]/*' />
|
|
public static TimeSpan FromTicks(long value) {
|
|
return new TimeSpan(value);
|
|
}
|
|
|
|
internal static long TimeToTicks(int hour, int minute, int second) {
|
|
// totalSeconds is bounded by 2^31 * 2^12 + 2^31 * 2^8 + 2^31,
|
|
// which is less than 2^44, meaning we won't overflow totalSeconds.
|
|
long totalSeconds = (long)hour * 3600 + (long)minute * 60 + (long)second;
|
|
if (totalSeconds > MaxSeconds || totalSeconds < MinSeconds)
|
|
throw new ArgumentOutOfRangeException("Overflow_TimeSpanTooLong");
|
|
return totalSeconds * TicksPerSecond;
|
|
}
|
|
|
|
private String IntToString(int n, int digits) {
|
|
return ParseNumbers.IntToString(n, 10, digits, '0', 0);
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.ToString"]/*' />
|
|
public override String ToString() {
|
|
StringBuilder sb = new StringBuilder();
|
|
int day = (int)(_ticks / TicksPerDay);
|
|
long time = _ticks % TicksPerDay;
|
|
if (_ticks < 0) {
|
|
sb.Append("-");
|
|
day = -day;
|
|
time = -time;
|
|
}
|
|
if (day != 0) {
|
|
sb.Append(day);
|
|
sb.Append(".");
|
|
}
|
|
sb.Append(IntToString((int)(time / TicksPerHour % 24), 2));
|
|
sb.Append(":");
|
|
sb.Append(IntToString((int)(time / TicksPerMinute % 60), 2));
|
|
sb.Append(":");
|
|
sb.Append(IntToString((int)(time / TicksPerSecond % 60), 2));
|
|
int t = (int)(time % TicksPerSecond);
|
|
if (t != 0) {
|
|
sb.Append(".");
|
|
sb.Append(IntToString(t, 7));
|
|
}
|
|
return sb.ToString();
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.operatorSUB1"]/*' />
|
|
public static TimeSpan operator -(TimeSpan t) {
|
|
if (t._ticks==TimeSpan.MinValue._ticks)
|
|
throw new OverflowException("Overflow_NegateTwosCompNum");
|
|
return new TimeSpan(-t._ticks);
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.operatorSUB2"]/*' />
|
|
public static TimeSpan operator -(TimeSpan t1, TimeSpan t2) {
|
|
return t1.Subtract(t2);
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.operatorADD1"]/*' />
|
|
public static TimeSpan operator +(TimeSpan t) {
|
|
return t;
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.operatorADD2"]/*' />
|
|
public static TimeSpan operator +(TimeSpan t1, TimeSpan t2) {
|
|
return t1.Add(t2);
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.operatorEQ"]/*' />
|
|
[NoHeapAllocation]
|
|
public static bool operator ==(TimeSpan t1, TimeSpan t2) {
|
|
return t1._ticks == t2._ticks;
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.operatorNE"]/*' />
|
|
public static bool operator !=(TimeSpan t1, TimeSpan t2) {
|
|
return t1._ticks != t2._ticks;
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.operatorLT"]/*' />
|
|
public static bool operator <(TimeSpan t1, TimeSpan t2) {
|
|
return t1._ticks < t2._ticks;
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.operatorLE"]/*' />
|
|
public static bool operator <=(TimeSpan t1, TimeSpan t2) {
|
|
return t1._ticks <= t2._ticks;
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.operatorGT"]/*' />
|
|
public static bool operator >(TimeSpan t1, TimeSpan t2) {
|
|
return t1._ticks > t2._ticks;
|
|
}
|
|
|
|
//| <include path='docs/doc[@for="TimeSpan.operatorGE"]/*' />
|
|
public static bool operator >=(TimeSpan t1, TimeSpan t2) {
|
|
return t1._ticks >= t2._ticks;
|
|
}
|
|
|
|
private class StringParser
|
|
{
|
|
private String str;
|
|
private char ch;
|
|
private int pos;
|
|
private int len;
|
|
|
|
internal static void Error() {
|
|
throw new FormatException("Format_InvalidString");
|
|
}
|
|
|
|
internal static void OverflowError() {
|
|
throw new OverflowException("Overflow_TimeSpanTooLong");
|
|
}
|
|
|
|
internal void NextChar() {
|
|
if (pos < len) pos++;
|
|
ch = pos < len? str[pos]: (char) 0;
|
|
}
|
|
|
|
internal char NextNonDigit() {
|
|
int i = pos;
|
|
while (i < len) {
|
|
char ch = str[i];
|
|
if (ch < '0' || ch > '9') return ch;
|
|
i++;
|
|
}
|
|
return (char) 0;
|
|
}
|
|
|
|
internal long Parse(String s, bool allowWhiteSpace) {
|
|
if (s == null) throw new ArgumentNullException("s");
|
|
str = s;
|
|
len = s.Length;
|
|
pos = -1;
|
|
NextChar();
|
|
if (allowWhiteSpace)
|
|
SkipBlanks();
|
|
bool negative = false;
|
|
if (ch == '-') {
|
|
negative = true;
|
|
NextChar();
|
|
}
|
|
long time;
|
|
if (NextNonDigit() == ':') {
|
|
time = ParseTime();
|
|
}
|
|
else {
|
|
time = ParseInt((int)(0x7FFFFFFFFFFFFFFFL / TicksPerDay)) * TicksPerDay;
|
|
if (ch == '.') {
|
|
NextChar();
|
|
time += ParseTime();
|
|
}
|
|
}
|
|
if (negative) {
|
|
time = -time;
|
|
// Allow -0 as well
|
|
if (time > 0) OverflowError();
|
|
}
|
|
else {
|
|
if (time < 0) OverflowError();
|
|
}
|
|
if (allowWhiteSpace)
|
|
SkipBlanks();
|
|
if (pos < len) Error();
|
|
return time;
|
|
}
|
|
|
|
internal int ParseInt(int max) {
|
|
int i = 0;
|
|
int p = pos;
|
|
while (ch >= '0' && ch <= '9') {
|
|
if ((i & 0xF0000000) != 0) OverflowError();
|
|
i = i * 10 + ch - '0';
|
|
if (i < 0) OverflowError();
|
|
NextChar();
|
|
}
|
|
if (p == pos) Error();
|
|
if (i > max) OverflowError();
|
|
return i;
|
|
}
|
|
|
|
internal long ParseTime() {
|
|
long time = ParseInt(23) * TicksPerHour;
|
|
if (ch != ':') Error();
|
|
NextChar();
|
|
time += ParseInt(59) * TicksPerMinute;
|
|
if (ch == ':') {
|
|
NextChar();
|
|
time += ParseInt(59) * TicksPerSecond;
|
|
if (ch == '.') {
|
|
NextChar();
|
|
int f = (int)TicksPerSecond;
|
|
while (f > 1 && ch >= '0' && ch <= '9') {
|
|
f /= 10;
|
|
time += (ch - '0') * f;
|
|
NextChar();
|
|
}
|
|
}
|
|
}
|
|
return time;
|
|
}
|
|
|
|
internal void SkipBlanks() {
|
|
while (ch == ' ' || ch == '\t') NextChar();
|
|
}
|
|
}
|
|
}
|
|
}
|