// Copyright (C) XFX Team // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // //* Redistributions of source code must retain the above copyright //notice, this list of conditions and the following disclaimer. //* Redistributions in binary form must reproduce the above copyright //notice, this list of conditions and the following disclaimer in the //documentation and/or other materials provided with the distribution. //* Neither the name of the copyright holder nor the names of any //contributors may be used to endorse or promote products derived from //this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. #include #include #include #include #include #include #include #include #include namespace System { // Encodes the DateTime in 64 bits, top two bits contain the DateTimeKind, // the rest contains the 62 bit value for the ticks. This reduces the // memory usage from 16 to 8 bytes. // const long long DateTime::TicksMask = 0x3fffffffffffffffLL; const long long DateTime::KindMask = ((long long)0xC000000000000000LL); const int DateTime::KindShift = 62; // // w32 file time starts counting from 1/1/1601 00:00 GMT // which is the constant ticks from the .NET epoch const long long DateTime::w32file_epoch = 504911232000000000LL; // //private const long MAX_VALUE_TICKS = 3155378975400000000L; // -- Microsoft .NET has this value. const long long DateTime::MAX_VALUE_TICKS = 3155378975999999999LL; // // The UnixEpoch. It begins on Jan 1, 1970 at 0:0:0, expressed in Ticks // const long long DateTime::UnixEpoch = 621355968000000000LL; // for OLE Automation dates long long ticks18991230 = 599264352000000000LL; double OAMinValue = -657435.0; double OAMaxValue = 2958466.0; int daysmonth[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; int daysmonthleap[] = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; const Type DateTimeTypeInfo("DateTime", "System::DateTime", TypeCode::DateTime); int DateTime::AbsoluteDays(int year, int month, int day) { int *days; int temp = 0, m=1 ; days = (IsLeapYear(year) ? daysmonthleap : daysmonth); while (m < month) { temp += days[m++]; } return ((day-1) + temp + (365* (year-1)) + ((year-1)/4) - ((year-1)/100) + ((year-1)/400)); } const DateTime DateTime::MaxValue = DateTime(3155378975999999999LL); const DateTime DateTime::MinValue = DateTime(0); void DateTime::InvalidTickValue(long long ticks) { sassert(false, String::Format("Value %ll is outside the valid range [0,%ll].", ticks, MAX_VALUE_TICKS)); } DateTime DateTime::Add(double value, int scale) { long long num = (long long)((value * scale) + ((value >= 0.0) ? 0.5 : -0.5)); if ((num <= -315537897600000LL) || (num >= 0x11efae44cb400LL)) { #if DEBUG //throw ArgumentOutOfRangeException("value", "ArgumentOutOfRange_AddValue"); #endif return *this; } return AddTicks(num * 0x2710L); } DateTime::DateTime(int year, int month, int day) { sassert(!(year < 1 || year > 9999 || month < 1 || month >12 || day < 1 || day > DaysInMonth(year, month)), "Parameters describe an unrepresentable DateTime."); DateTime(year, month, day, 0, 0, 0, 0); } DateTime::DateTime(int year, int month, int day, System::Globalization::Calendar calendar) { DateTime(year, month, day, 0, 0, 0, 0, calendar); } DateTime::DateTime(int year, int month, int day, int hour, int minute, int second) { DateTime(year, month, day, hour, minute, second, 0); } DateTime::DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond) { sassert(!(year < 1 || year > 9999 || month < 1 || month >12 || day < 1 || day > DaysInMonth(year, month) || hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59 || millisecond < 0 || millisecond > 999), "Parameters describe an unrepresentable DateTime."); encoded = TimeSpan(AbsoluteDays(year,month,day), hour, minute, second, millisecond).Ticks(); } DateTime::DateTime(long long ticks) { if (ticks < 0 || ticks > MAX_VALUE_TICKS) { InvalidTickValue(ticks); } encoded = ticks; } DateTime::DateTime(long long ticks, DateTimeKind_t kind) { if (ticks < 0 || ticks > MAX_VALUE_TICKS) { InvalidTickValue(ticks); } sassert(!(kind < 0 || kind > DateTimeKind::Local), "kind is an invalid DateTimeKind value."); encoded = ((long long)kind << KindShift) | ticks; } DateTime::DateTime(const DateTime &obj) { encoded = obj.encoded; } DateTime DateTime::AddDays(double value) { return Add(value, 0x5265c00); } DateTime DateTime::AddHours(double value) { return Add(value, 0x36ee80); } DateTime DateTime::AddMilliseconds(double value) { return Add(value, 1); } DateTime DateTime::AddMinutes(double value) { return Add(value, 0xea60); } DateTime DateTime::AddMonths(int months) { int day, month, year, maxday; day = Day(); month = Month() + (months % 12); year = Year() + months/12; if (month < 1) { month = 12 + month; year--; } else if (month > 12) { month = month -12; year++; } maxday = DaysInMonth(year, month); if (day > maxday) { day = maxday; } DateTime temp = DateTime(year, month, day); temp.encoded |= encoded & KindMask; return temp.Add(TimeOfDay()); } DateTime DateTime::AddSeconds(double value) { return Add(value, 0x3e8); } DateTime DateTime::AddYears(int value) { return AddMonths(value * 12); } int DateTime::Compare(const DateTime t1, const DateTime t2) { long long t1t = t1.encoded & TicksMask; long long t2t = t2.encoded & TicksMask; if (t1t < t2t) return -1; else if (t1t > t2t) return 1; else return 0; } int DateTime::CompareTo(const DateTime value) const { return Compare(*this, value); } int DateTime::DaysInMonth(int year, int month) { int *days; sassert(!(month < 1 || month > 12), "month must be greater than 0 and smaller than, or equal to 12."); sassert(!(year < 1 || year > 9999), "year must be greater than 0 and smaller than, or equal to 9999."); days = (IsLeapYear(year) ? daysmonthleap : daysmonth); return days[month]; } bool DateTime::Equals(Object const * const obj) const { return is(obj, this) ? this->Equals(*(DateTime *)obj) : false; } bool DateTime::Equals(const DateTime obj) const { return (encoded & TicksMask) == (obj.encoded & TicksMask); } bool DateTime::Equals(const DateTime t1, const DateTime t2) { return t1.Equals(t2); } DateTime DateTime::FromFileTime(long long fileTime) { sassert(fileTime >= 0, "fileTime must be non-negative."); return DateTime(w32file_epoch + fileTime).ToLocalTime(); } DateTime DateTime::FromFileTimeUtc(long long fileTime) { sassert(fileTime >= 0, "fileTime must be non-negative."); return DateTime(w32file_epoch + fileTime); } DateTime DateTime::FromOADate(double d) { // An OLE Automation date is implemented as a floating-point number // whose value is the number of days from midnight, 30 December 1899. // d must be negative 657435.0 through positive 2958466.0. sassert(!((d <= OAMinValue) || (d >= OAMaxValue)), ""); DateTime dt = DateTime(ticks18991230); if (d < 0.0) { double days = Math::Ceiling(d); // integer part is the number of days (negative) dt = dt.AddMilliseconds(days * 86400000); // but decimals are the number of hours (in days fractions) and positive double hours = (days - d); dt = dt.AddMilliseconds(hours * 86400000); } else { dt = dt.AddMilliseconds(d * 86400000); } return dt; } int DateTime::GetHashCode() const { return (int)encoded; } const Type& DateTime::GetType() { return DateTimeTypeInfo; } bool DateTime::IsDaylighSavingTime() { if ((int)((ulong)encoded >> KindShift) == (int)DateTimeKind::Utc) { return false; } return TimeZone::CurrentTimeZone().IsDaylightSavingTime(*this); } bool DateTime::IsLeapYear(int year) { sassert(!(year < 1 || year > 9999), "year must be greater than 0 and smaller than, or equal to 9999."); return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0); } DateTime DateTime::Now() { // TODO: take UtcNow and convert to local time } TimeSpan DateTime::Subtract(const DateTime value) { return TimeSpan(Ticks()) - TimeSpan(value.Ticks()); } DateTime DateTime::Subtract(const TimeSpan value) { long long newticks; newticks = Ticks() - value.Ticks(); sassert(!(newticks < 0 || newticks > MAX_VALUE_TICKS), "argument out of range."); DateTime ret = DateTime(newticks); ret.encoded |= (encoded & KindMask); return ret; } DateTime DateTime::Today() { LARGE_INTEGER systemTime; TIME_FIELDS time; KeQuerySystemTime(&systemTime); RtlTimeToTimeFields(&systemTime, &time); return DateTime(time.Year, time.Month, time.Day, 0, 0, 0, DateTimeKind::Local); } long long DateTime::ToFileTime() { DateTime universalTime = ToUniversalTime(); sassert(universalTime.Ticks() >= w32file_epoch, "file time is not valid."); return(universalTime.Ticks() - w32file_epoch); } long long DateTime::ToFileTimeUtc() { sassert(Ticks() >= w32file_epoch, "file time is not valid."); return (Ticks() - w32file_epoch); } double DateTime::ToOADate() { long long t = Ticks(); // uninitialized DateTime case if (t == 0) { return 0; } // we can't reach minimum value if (t < 31242239136000000LL) { return OAMinValue + 0.001; } TimeSpan ts = TimeSpan(Ticks() - ticks18991230); double result = ts.TotalDays(); // t < 0 (where 599264352000000000 == 0.0d for OA) if (t < 599264352000000000LL) { // negative days (int) but decimals are positive double d = Math::Ceiling(result); result = d - 2 - (result - d); } else { // we can't reach maximum value if (result >= OAMaxValue) { result = OAMaxValue - 0.00000001; } } return result; } DateTime DateTime::UtcNow() { LARGE_INTEGER systemTime; TIME_FIELDS time; KeQuerySystemTime(&systemTime); RtlTimeToTimeFields(&systemTime, &time); return DateTime(time.Year, time.Month, time.Day, time.Hour, time.Minute, time.Second, DateTimeKind::Utc); } DateTime DateTime::operator +(TimeSpan other) { long long res = ((encoded & TicksMask) + other.Ticks()); if (res < 0 || res > MAX_VALUE_TICKS) { #if DEBUG //throw new ArgumentOutOfRangeException(); #endif return DateTime(0); } return DateTime(res, Kind()); } bool DateTime::operator==(const DateTime& other) const { return Equals(other); } bool DateTime::operator >(const DateTime other) const { return ((encoded & TicksMask) > (other.encoded & TicksMask)); } bool DateTime::operator>=(const DateTime other) const { return ((encoded & TicksMask) >= (other.encoded & TicksMask)); } bool DateTime::operator!=(const DateTime& other) const { return !Equals(other); } bool DateTime::operator <(const DateTime other) const { return ((encoded & TicksMask) < (other.encoded & TicksMask)); } bool DateTime::operator<=(const DateTime other) const { return ((encoded & TicksMask) <= (other.encoded & TicksMask)); } TimeSpan DateTime::operator -(DateTime other) { return TimeSpan((encoded & TicksMask) - (other.encoded & TicksMask)); } DateTime DateTime::operator -(TimeSpan t) { long long res = ((encoded & TicksMask) - t.Ticks()); if (res < 0 || res > MAX_VALUE_TICKS) { #if DEBUG //throw ArgumentOutOfRangeException(); #endif return DateTime(0); } return DateTime(res, Kind()); } }