1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System; 6 using System.Diagnostics; 7 using System.Threading; 8 using System.Globalization; 9 using System.Runtime; 10 using System.Runtime.InteropServices; 11 using System.Runtime.CompilerServices; 12 using System.Runtime.Serialization; 13 using System.Runtime.Versioning; 14 using System.Security; 15 using CultureInfo = System.Globalization.CultureInfo; 16 using Calendar = System.Globalization.Calendar; 17 18 namespace System 19 { 20 // This value type represents a date and time. Every DateTime 21 // object has a private field (Ticks) of type Int64 that stores the 22 // date and time as the number of 100 nanosecond intervals since 23 // 12:00 AM January 1, year 1 A.D. in the proleptic Gregorian Calendar. 24 // 25 // Starting from V2.0, DateTime also stored some context about its time 26 // zone in the form of a 3-state value representing Unspecified, Utc or 27 // Local. This is stored in the two top bits of the 64-bit numeric value 28 // with the remainder of the bits storing the tick count. This information 29 // is only used during time zone conversions and is not part of the 30 // identity of the DateTime. Thus, operations like Compare and Equals 31 // ignore this state. This is to stay compatible with earlier behavior 32 // and performance characteristics and to avoid forcing people into dealing 33 // with the effects of daylight savings. Note, that this has little effect 34 // on how the DateTime works except in a context where its specific time 35 // zone is needed, such as during conversions and some parsing and formatting 36 // cases. 37 // 38 // There is also 4th state stored that is a special type of Local value that 39 // is used to avoid data loss when round-tripping between local and UTC time. 40 // See below for more information on this 4th state, although it is 41 // effectively hidden from most users, who just see the 3-state DateTimeKind 42 // enumeration. 43 // 44 // For compatibility, DateTime does not serialize the Kind data when used in 45 // binary serialization. 46 // 47 // For a description of various calendar issues, look at 48 // 49 // Calendar Studies web site, at 50 // http://serendipity.nofadz.com/hermetic/cal_stud.htm. 51 // 52 // 53 [StructLayout(LayoutKind.Auto)] 54 [Serializable] 55 [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] 56 public readonly partial struct DateTime : IComparable, IFormattable, IConvertible, IComparable<DateTime>, IEquatable<DateTime>, ISerializable, ISpanFormattable 57 { 58 // Number of 100ns ticks per time unit 59 private const long TicksPerMillisecond = 10000; 60 private const long TicksPerSecond = TicksPerMillisecond * 1000; 61 private const long TicksPerMinute = TicksPerSecond * 60; 62 private const long TicksPerHour = TicksPerMinute * 60; 63 private const long TicksPerDay = TicksPerHour * 24; 64 65 // Number of milliseconds per time unit 66 private const int MillisPerSecond = 1000; 67 private const int MillisPerMinute = MillisPerSecond * 60; 68 private const int MillisPerHour = MillisPerMinute * 60; 69 private const int MillisPerDay = MillisPerHour * 24; 70 71 // Number of days in a non-leap year 72 private const int DaysPerYear = 365; 73 // Number of days in 4 years 74 private const int DaysPer4Years = DaysPerYear * 4 + 1; // 1461 75 // Number of days in 100 years 76 private const int DaysPer100Years = DaysPer4Years * 25 - 1; // 36524 77 // Number of days in 400 years 78 private const int DaysPer400Years = DaysPer100Years * 4 + 1; // 146097 79 80 // Number of days from 1/1/0001 to 12/31/1600 81 private const int DaysTo1601 = DaysPer400Years * 4; // 584388 82 // Number of days from 1/1/0001 to 12/30/1899 83 private const int DaysTo1899 = DaysPer400Years * 4 + DaysPer100Years * 3 - 367; 84 // Number of days from 1/1/0001 to 12/31/1969 85 internal const int DaysTo1970 = DaysPer400Years * 4 + DaysPer100Years * 3 + DaysPer4Years * 17 + DaysPerYear; // 719,162 86 // Number of days from 1/1/0001 to 12/31/9999 87 private const int DaysTo10000 = DaysPer400Years * 25 - 366; // 3652059 88 89 internal const long MinTicks = 0; 90 internal const long MaxTicks = DaysTo10000 * TicksPerDay - 1; 91 private const long MaxMillis = (long)DaysTo10000 * MillisPerDay; 92 93 internal const long UnixEpochTicks = DaysTo1970 * TicksPerDay; 94 private const long FileTimeOffset = DaysTo1601 * TicksPerDay; 95 private const long DoubleDateOffset = DaysTo1899 * TicksPerDay; 96 // The minimum OA date is 0100/01/01 (Note it's year 100). 97 // The maximum OA date is 9999/12/31 98 private const long OADateMinAsTicks = (DaysPer100Years - DaysPerYear) * TicksPerDay; 99 // All OA dates must be greater than (not >=) OADateMinAsDouble 100 private const double OADateMinAsDouble = -657435.0; 101 // All OA dates must be less than (not <=) OADateMaxAsDouble 102 private const double OADateMaxAsDouble = 2958466.0; 103 104 private const int DatePartYear = 0; 105 private const int DatePartDayOfYear = 1; 106 private const int DatePartMonth = 2; 107 private const int DatePartDay = 3; 108 109 private static readonly int[] s_daysToMonth365 = { 110 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}; 111 private static readonly int[] s_daysToMonth366 = { 112 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}; 113 114 public static readonly DateTime MinValue = new DateTime(MinTicks, DateTimeKind.Unspecified); 115 public static readonly DateTime MaxValue = new DateTime(MaxTicks, DateTimeKind.Unspecified); 116 public static readonly DateTime UnixEpoch = new DateTime(UnixEpochTicks, DateTimeKind.Utc); 117 118 private const UInt64 TicksMask = 0x3FFFFFFFFFFFFFFF; 119 private const UInt64 FlagsMask = 0xC000000000000000; 120 private const UInt64 LocalMask = 0x8000000000000000; 121 private const Int64 TicksCeiling = 0x4000000000000000; 122 private const UInt64 KindUnspecified = 0x0000000000000000; 123 private const UInt64 KindUtc = 0x4000000000000000; 124 private const UInt64 KindLocal = 0x8000000000000000; 125 private const UInt64 KindLocalAmbiguousDst = 0xC000000000000000; 126 private const Int32 KindShift = 62; 127 128 private const String TicksField = "ticks"; // Do not rename (binary serialization) 129 private const String DateDataField = "dateData"; // Do not rename (binary serialization) 130 131 // The data is stored as an unsigned 64-bit integer 132 // Bits 01-62: The value of 100-nanosecond ticks where 0 represents 1/1/0001 12:00am, up until the value 133 // 12/31/9999 23:59:59.9999999 134 // Bits 63-64: A four-state value that describes the DateTimeKind value of the date time, with a 2nd 135 // value for the rare case where the date time is local, but is in an overlapped daylight 136 // savings time hour and it is in daylight savings time. This allows distinction of these 137 // otherwise ambiguous local times and prevents data loss when round tripping from Local to 138 // UTC time. 139 private readonly UInt64 _dateData; 140 141 // Constructs a DateTime from a tick count. The ticks 142 // argument specifies the date as the number of 100-nanosecond intervals 143 // that have elapsed since 1/1/0001 12:00am. 144 // DateTimeSystem.DateTime145 public DateTime(long ticks) 146 { 147 if (ticks < MinTicks || ticks > MaxTicks) 148 throw new ArgumentOutOfRangeException(nameof(ticks), SR.ArgumentOutOfRange_DateTimeBadTicks); 149 _dateData = (UInt64)ticks; 150 } 151 DateTimeSystem.DateTime152 private DateTime(UInt64 dateData) 153 { 154 this._dateData = dateData; 155 } 156 DateTimeSystem.DateTime157 public DateTime(long ticks, DateTimeKind kind) 158 { 159 if (ticks < MinTicks || ticks > MaxTicks) 160 { 161 throw new ArgumentOutOfRangeException(nameof(ticks), SR.ArgumentOutOfRange_DateTimeBadTicks); 162 } 163 if (kind < DateTimeKind.Unspecified || kind > DateTimeKind.Local) 164 { 165 throw new ArgumentException(SR.Argument_InvalidDateTimeKind, nameof(kind)); 166 } 167 _dateData = ((UInt64)ticks | ((UInt64)kind << KindShift)); 168 } 169 DateTimeSystem.DateTime170 internal DateTime(long ticks, DateTimeKind kind, Boolean isAmbiguousDst) 171 { 172 if (ticks < MinTicks || ticks > MaxTicks) 173 { 174 throw new ArgumentOutOfRangeException(nameof(ticks), SR.ArgumentOutOfRange_DateTimeBadTicks); 175 } 176 Debug.Assert(kind == DateTimeKind.Local, "Internal Constructor is for local times only"); 177 _dateData = ((UInt64)ticks | (isAmbiguousDst ? KindLocalAmbiguousDst : KindLocal)); 178 } 179 180 // Constructs a DateTime from a given year, month, and day. The 181 // time-of-day of the resulting DateTime is always midnight. 182 // DateTimeSystem.DateTime183 public DateTime(int year, int month, int day) 184 { 185 _dateData = (UInt64)DateToTicks(year, month, day); 186 } 187 188 // Constructs a DateTime from a given year, month, and day for 189 // the specified calendar. The 190 // time-of-day of the resulting DateTime is always midnight. 191 // DateTimeSystem.DateTime192 public DateTime(int year, int month, int day, Calendar calendar) 193 : this(year, month, day, 0, 0, 0, calendar) 194 { 195 } 196 197 // Constructs a DateTime from a given year, month, day, hour, 198 // minute, and second. 199 // DateTimeSystem.DateTime200 public DateTime(int year, int month, int day, int hour, int minute, int second) 201 { 202 _dateData = (UInt64)(DateToTicks(year, month, day) + TimeToTicks(hour, minute, second)); 203 } 204 DateTimeSystem.DateTime205 public DateTime(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind) 206 { 207 if (kind < DateTimeKind.Unspecified || kind > DateTimeKind.Local) 208 { 209 throw new ArgumentException(SR.Argument_InvalidDateTimeKind, nameof(kind)); 210 } 211 Int64 ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second); 212 _dateData = ((UInt64)ticks | ((UInt64)kind << KindShift)); 213 } 214 215 // Constructs a DateTime from a given year, month, day, hour, 216 // minute, and second for the specified calendar. 217 // DateTimeSystem.DateTime218 public DateTime(int year, int month, int day, int hour, int minute, int second, Calendar calendar) 219 { 220 if (calendar == null) 221 throw new ArgumentNullException(nameof(calendar)); 222 _dateData = (UInt64)calendar.ToDateTime(year, month, day, hour, minute, second, 0).Ticks; 223 } 224 225 // Constructs a DateTime from a given year, month, day, hour, 226 // minute, and second. 227 // DateTimeSystem.DateTime228 public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond) 229 { 230 if (millisecond < 0 || millisecond >= MillisPerSecond) 231 { 232 throw new ArgumentOutOfRangeException(nameof(millisecond), SR.Format(SR.ArgumentOutOfRange_Range, 0, MillisPerSecond - 1)); 233 } 234 Int64 ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second); 235 ticks += millisecond * TicksPerMillisecond; 236 if (ticks < MinTicks || ticks > MaxTicks) 237 throw new ArgumentException(SR.Arg_DateTimeRange); 238 _dateData = (UInt64)ticks; 239 } 240 DateTimeSystem.DateTime241 public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, DateTimeKind kind) 242 { 243 if (millisecond < 0 || millisecond >= MillisPerSecond) 244 { 245 throw new ArgumentOutOfRangeException(nameof(millisecond), SR.Format(SR.ArgumentOutOfRange_Range, 0, MillisPerSecond - 1)); 246 } 247 if (kind < DateTimeKind.Unspecified || kind > DateTimeKind.Local) 248 { 249 throw new ArgumentException(SR.Argument_InvalidDateTimeKind, nameof(kind)); 250 } 251 Int64 ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second); 252 ticks += millisecond * TicksPerMillisecond; 253 if (ticks < MinTicks || ticks > MaxTicks) 254 throw new ArgumentException(SR.Arg_DateTimeRange); 255 _dateData = ((UInt64)ticks | ((UInt64)kind << KindShift)); 256 } 257 258 // Constructs a DateTime from a given year, month, day, hour, 259 // minute, and second for the specified calendar. 260 // DateTimeSystem.DateTime261 public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar) 262 { 263 if (calendar == null) 264 throw new ArgumentNullException(nameof(calendar)); 265 if (millisecond < 0 || millisecond >= MillisPerSecond) 266 { 267 throw new ArgumentOutOfRangeException(nameof(millisecond), SR.Format(SR.ArgumentOutOfRange_Range, 0, MillisPerSecond - 1)); 268 } 269 Int64 ticks = calendar.ToDateTime(year, month, day, hour, minute, second, 0).Ticks; 270 ticks += millisecond * TicksPerMillisecond; 271 if (ticks < MinTicks || ticks > MaxTicks) 272 throw new ArgumentException(SR.Arg_DateTimeRange); 273 _dateData = (UInt64)ticks; 274 } 275 DateTimeSystem.DateTime276 public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar, DateTimeKind kind) 277 { 278 if (calendar == null) 279 throw new ArgumentNullException(nameof(calendar)); 280 if (millisecond < 0 || millisecond >= MillisPerSecond) 281 { 282 throw new ArgumentOutOfRangeException(nameof(millisecond), SR.Format(SR.ArgumentOutOfRange_Range, 0, MillisPerSecond - 1)); 283 } 284 if (kind < DateTimeKind.Unspecified || kind > DateTimeKind.Local) 285 { 286 throw new ArgumentException(SR.Argument_InvalidDateTimeKind, nameof(kind)); 287 } 288 Int64 ticks = calendar.ToDateTime(year, month, day, hour, minute, second, 0).Ticks; 289 ticks += millisecond * TicksPerMillisecond; 290 if (ticks < MinTicks || ticks > MaxTicks) 291 throw new ArgumentException(SR.Arg_DateTimeRange); 292 _dateData = ((UInt64)ticks | ((UInt64)kind << KindShift)); 293 } 294 DateTimeSystem.DateTime295 private DateTime(SerializationInfo info, StreamingContext context) 296 { 297 if (info == null) 298 throw new ArgumentNullException(nameof(info)); 299 300 Boolean foundTicks = false; 301 Boolean foundDateData = false; 302 Int64 serializedTicks = 0; 303 UInt64 serializedDateData = 0; 304 305 306 // Get the data 307 SerializationInfoEnumerator enumerator = info.GetEnumerator(); 308 while (enumerator.MoveNext()) 309 { 310 switch (enumerator.Name) 311 { 312 case TicksField: 313 serializedTicks = Convert.ToInt64(enumerator.Value, CultureInfo.InvariantCulture); 314 foundTicks = true; 315 break; 316 case DateDataField: 317 serializedDateData = Convert.ToUInt64(enumerator.Value, CultureInfo.InvariantCulture); 318 foundDateData = true; 319 break; 320 default: 321 // Ignore other fields for forward compatibility. 322 break; 323 } 324 } 325 if (foundDateData) 326 { 327 _dateData = serializedDateData; 328 } 329 else if (foundTicks) 330 { 331 _dateData = (UInt64)serializedTicks; 332 } 333 else 334 { 335 throw new SerializationException(SR.Serialization_MissingDateTimeData); 336 } 337 Int64 ticks = InternalTicks; 338 if (ticks < MinTicks || ticks > MaxTicks) 339 { 340 throw new SerializationException(SR.Serialization_DateTimeTicksOutOfRange); 341 } 342 } 343 344 345 346 internal Int64 InternalTicks 347 { 348 get 349 { 350 return (Int64)(_dateData & TicksMask); 351 } 352 } 353 354 private UInt64 InternalKind 355 { 356 get 357 { 358 return (_dateData & FlagsMask); 359 } 360 } 361 362 // Returns the DateTime resulting from adding the given 363 // TimeSpan to this DateTime. 364 // AddSystem.DateTime365 public DateTime Add(TimeSpan value) 366 { 367 return AddTicks(value._ticks); 368 } 369 370 // Returns the DateTime resulting from adding a fractional number of 371 // time units to this DateTime. AddSystem.DateTime372 private DateTime Add(double value, int scale) 373 { 374 long millis = (long)(value * scale + (value >= 0 ? 0.5 : -0.5)); 375 if (millis <= -MaxMillis || millis >= MaxMillis) 376 throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_AddValue); 377 return AddTicks(millis * TicksPerMillisecond); 378 } 379 380 // Returns the DateTime resulting from adding a fractional number of 381 // days to this DateTime. The result is computed by rounding the 382 // fractional number of days given by value to the nearest 383 // millisecond, and adding that interval to this DateTime. The 384 // value argument is permitted to be negative. 385 // AddDaysSystem.DateTime386 public DateTime AddDays(double value) 387 { 388 return Add(value, MillisPerDay); 389 } 390 391 // Returns the DateTime resulting from adding a fractional number of 392 // hours to this DateTime. The result is computed by rounding the 393 // fractional number of hours given by value to the nearest 394 // millisecond, and adding that interval to this DateTime. The 395 // value argument is permitted to be negative. 396 // AddHoursSystem.DateTime397 public DateTime AddHours(double value) 398 { 399 return Add(value, MillisPerHour); 400 } 401 402 // Returns the DateTime resulting from the given number of 403 // milliseconds to this DateTime. The result is computed by rounding 404 // the number of milliseconds given by value to the nearest integer, 405 // and adding that interval to this DateTime. The value 406 // argument is permitted to be negative. 407 // AddMillisecondsSystem.DateTime408 public DateTime AddMilliseconds(double value) 409 { 410 return Add(value, 1); 411 } 412 413 // Returns the DateTime resulting from adding a fractional number of 414 // minutes to this DateTime. The result is computed by rounding the 415 // fractional number of minutes given by value to the nearest 416 // millisecond, and adding that interval to this DateTime. The 417 // value argument is permitted to be negative. 418 // AddMinutesSystem.DateTime419 public DateTime AddMinutes(double value) 420 { 421 return Add(value, MillisPerMinute); 422 } 423 424 // Returns the DateTime resulting from adding the given number of 425 // months to this DateTime. The result is computed by incrementing 426 // (or decrementing) the year and month parts of this DateTime by 427 // months months, and, if required, adjusting the day part of the 428 // resulting date downwards to the last day of the resulting month in the 429 // resulting year. The time-of-day part of the result is the same as the 430 // time-of-day part of this DateTime. 431 // 432 // In more precise terms, considering this DateTime to be of the 433 // form y / m / d + t, where y is the 434 // year, m is the month, d is the day, and t is the 435 // time-of-day, the result is y1 / m1 / d1 + t, 436 // where y1 and m1 are computed by adding months months 437 // to y and m, and d1 is the largest value less than 438 // or equal to d that denotes a valid day in month m1 of year 439 // y1. 440 // AddMonthsSystem.DateTime441 public DateTime AddMonths(int months) 442 { 443 if (months < -120000 || months > 120000) throw new ArgumentOutOfRangeException(nameof(months), SR.ArgumentOutOfRange_DateTimeBadMonths); 444 GetDatePart(out int y, out int m, out int d); 445 int i = m - 1 + months; 446 if (i >= 0) 447 { 448 m = i % 12 + 1; 449 y = y + i / 12; 450 } 451 else 452 { 453 m = 12 + (i + 1) % 12; 454 y = y + (i - 11) / 12; 455 } 456 if (y < 1 || y > 9999) 457 { 458 throw new ArgumentOutOfRangeException(nameof(months), SR.ArgumentOutOfRange_DateArithmetic); 459 } 460 int days = DaysInMonth(y, m); 461 if (d > days) d = days; 462 return new DateTime((UInt64)(DateToTicks(y, m, d) + InternalTicks % TicksPerDay) | InternalKind); 463 } 464 465 // Returns the DateTime resulting from adding a fractional number of 466 // seconds to this DateTime. The result is computed by rounding the 467 // fractional number of seconds given by value to the nearest 468 // millisecond, and adding that interval to this DateTime. The 469 // value argument is permitted to be negative. 470 // AddSecondsSystem.DateTime471 public DateTime AddSeconds(double value) 472 { 473 return Add(value, MillisPerSecond); 474 } 475 476 // Returns the DateTime resulting from adding the given number of 477 // 100-nanosecond ticks to this DateTime. The value argument 478 // is permitted to be negative. 479 // AddTicksSystem.DateTime480 public DateTime AddTicks(long value) 481 { 482 long ticks = InternalTicks; 483 if (value > MaxTicks - ticks || value < MinTicks - ticks) 484 { 485 throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_DateArithmetic); 486 } 487 return new DateTime((UInt64)(ticks + value) | InternalKind); 488 } 489 490 // Returns the DateTime resulting from adding the given number of 491 // years to this DateTime. The result is computed by incrementing 492 // (or decrementing) the year part of this DateTime by value 493 // years. If the month and day of this DateTime is 2/29, and if the 494 // resulting year is not a leap year, the month and day of the resulting 495 // DateTime becomes 2/28. Otherwise, the month, day, and time-of-day 496 // parts of the result are the same as those of this DateTime. 497 // AddYearsSystem.DateTime498 public DateTime AddYears(int value) 499 { 500 if (value < -10000 || value > 10000) 501 { 502 // DateTimeOffset.AddYears(int years) is implemented on top of DateTime.AddYears(int value). Use the more appropriate 503 // parameter name out of the two for the exception. 504 throw new ArgumentOutOfRangeException("years", SR.ArgumentOutOfRange_DateTimeBadYears); 505 } 506 return AddMonths(value * 12); 507 } 508 509 // Compares two DateTime values, returning an integer that indicates 510 // their relationship. 511 // CompareSystem.DateTime512 public static int Compare(DateTime t1, DateTime t2) 513 { 514 Int64 ticks1 = t1.InternalTicks; 515 Int64 ticks2 = t2.InternalTicks; 516 if (ticks1 > ticks2) return 1; 517 if (ticks1 < ticks2) return -1; 518 return 0; 519 } 520 521 // Compares this DateTime to a given object. This method provides an 522 // implementation of the IComparable interface. The object 523 // argument must be another DateTime, or otherwise an exception 524 // occurs. Null is considered less than any instance. 525 // 526 // Returns a value less than zero if this object CompareToSystem.DateTime527 public int CompareTo(Object value) 528 { 529 if (value == null) return 1; 530 if (!(value is DateTime)) 531 { 532 throw new ArgumentException(SR.Arg_MustBeDateTime); 533 } 534 535 return Compare(this, (DateTime)value); 536 } 537 CompareToSystem.DateTime538 public int CompareTo(DateTime value) 539 { 540 return Compare(this, value); 541 } 542 543 // Returns the tick count corresponding to the given year, month, and day. 544 // Will check the if the parameters are valid. DateToTicksSystem.DateTime545 private static long DateToTicks(int year, int month, int day) 546 { 547 if (year >= 1 && year <= 9999 && month >= 1 && month <= 12) 548 { 549 int[] days = IsLeapYear(year) ? s_daysToMonth366 : s_daysToMonth365; 550 if (day >= 1 && day <= days[month] - days[month - 1]) 551 { 552 int y = year - 1; 553 int n = y * 365 + y / 4 - y / 100 + y / 400 + days[month - 1] + day - 1; 554 return n * TicksPerDay; 555 } 556 } 557 throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadYearMonthDay); 558 } 559 560 // Return the tick count corresponding to the given hour, minute, second. 561 // Will check the if the parameters are valid. TimeToTicksSystem.DateTime562 private static long TimeToTicks(int hour, int minute, int second) 563 { 564 //TimeSpan.TimeToTicks is a family access function which does no error checking, so 565 //we need to put some error checking out here. 566 if (hour >= 0 && hour < 24 && minute >= 0 && minute < 60 && second >= 0 && second < 60) 567 { 568 return (TimeSpan.TimeToTicks(hour, minute, second)); 569 } 570 throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadHourMinuteSecond); 571 } 572 573 // Returns the number of days in the month given by the year and 574 // month arguments. 575 // DaysInMonthSystem.DateTime576 public static int DaysInMonth(int year, int month) 577 { 578 if (month < 1 || month > 12) throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month); 579 // IsLeapYear checks the year argument 580 int[] days = IsLeapYear(year) ? s_daysToMonth366 : s_daysToMonth365; 581 return days[month] - days[month - 1]; 582 } 583 584 // Converts an OLE Date to a tick count. 585 // This function is duplicated in COMDateTime.cpp DoubleDateToTicksSystem.DateTime586 internal static long DoubleDateToTicks(double value) 587 { 588 // The check done this way will take care of NaN 589 if (!(value < OADateMaxAsDouble) || !(value > OADateMinAsDouble)) 590 throw new ArgumentException(SR.Arg_OleAutDateInvalid); 591 592 // Conversion to long will not cause an overflow here, as at this point the "value" is in between OADateMinAsDouble and OADateMaxAsDouble 593 long millis = (long)(value * MillisPerDay + (value >= 0 ? 0.5 : -0.5)); 594 // The interesting thing here is when you have a value like 12.5 it all positive 12 days and 12 hours from 01/01/1899 595 // However if you a value of -12.25 it is minus 12 days but still positive 6 hours, almost as though you meant -11.75 all negative 596 // This line below fixes up the millis in the negative case 597 if (millis < 0) 598 { 599 millis -= (millis % MillisPerDay) * 2; 600 } 601 602 millis += DoubleDateOffset / TicksPerMillisecond; 603 604 if (millis < 0 || millis >= MaxMillis) throw new ArgumentException(SR.Arg_OleAutDateScale); 605 return millis * TicksPerMillisecond; 606 } 607 608 // Checks if this DateTime is equal to a given object. Returns 609 // true if the given object is a boxed DateTime and its value 610 // is equal to the value of this DateTime. Returns false 611 // otherwise. 612 // EqualsSystem.DateTime613 public override bool Equals(Object value) 614 { 615 if (value is DateTime) 616 { 617 return InternalTicks == ((DateTime)value).InternalTicks; 618 } 619 return false; 620 } 621 EqualsSystem.DateTime622 public bool Equals(DateTime value) 623 { 624 return InternalTicks == value.InternalTicks; 625 } 626 627 // Compares two DateTime values for equality. Returns true if 628 // the two DateTime values are equal, or false if they are 629 // not equal. 630 // EqualsSystem.DateTime631 public static bool Equals(DateTime t1, DateTime t2) 632 { 633 return t1.InternalTicks == t2.InternalTicks; 634 } 635 FromBinarySystem.DateTime636 public static DateTime FromBinary(Int64 dateData) 637 { 638 if ((dateData & (unchecked((Int64)LocalMask))) != 0) 639 { 640 // Local times need to be adjusted as you move from one time zone to another, 641 // just as they are when serializing in text. As such the format for local times 642 // changes to store the ticks of the UTC time, but with flags that look like a 643 // local date. 644 Int64 ticks = dateData & (unchecked((Int64)TicksMask)); 645 // Negative ticks are stored in the top part of the range and should be converted back into a negative number 646 if (ticks > TicksCeiling - TicksPerDay) 647 { 648 ticks = ticks - TicksCeiling; 649 } 650 // Convert the ticks back to local. If the UTC ticks are out of range, we need to default to 651 // the UTC offset from MinValue and MaxValue to be consistent with Parse. 652 Boolean isAmbiguousLocalDst = false; 653 Int64 offsetTicks; 654 if (ticks < MinTicks) 655 { 656 offsetTicks = TimeZoneInfo.GetLocalUtcOffset(DateTime.MinValue, TimeZoneInfoOptions.NoThrowOnInvalidTime).Ticks; 657 } 658 else if (ticks > MaxTicks) 659 { 660 offsetTicks = TimeZoneInfo.GetLocalUtcOffset(DateTime.MaxValue, TimeZoneInfoOptions.NoThrowOnInvalidTime).Ticks; 661 } 662 else 663 { 664 // Because the ticks conversion between UTC and local is lossy, we need to capture whether the 665 // time is in a repeated hour so that it can be passed to the DateTime constructor. 666 DateTime utcDt = new DateTime(ticks, DateTimeKind.Utc); 667 Boolean isDaylightSavings = false; 668 offsetTicks = TimeZoneInfo.GetUtcOffsetFromUtc(utcDt, TimeZoneInfo.Local, out isDaylightSavings, out isAmbiguousLocalDst).Ticks; 669 } 670 ticks += offsetTicks; 671 // Another behaviour of parsing is to cause small times to wrap around, so that they can be used 672 // to compare times of day 673 if (ticks < 0) 674 { 675 ticks += TicksPerDay; 676 } 677 if (ticks < MinTicks || ticks > MaxTicks) 678 { 679 throw new ArgumentException(SR.Argument_DateTimeBadBinaryData, nameof(dateData)); 680 } 681 return new DateTime(ticks, DateTimeKind.Local, isAmbiguousLocalDst); 682 } 683 else 684 { 685 return DateTime.FromBinaryRaw(dateData); 686 } 687 } 688 689 // A version of ToBinary that uses the real representation and does not adjust local times. This is needed for 690 // scenarios where the serialized data must maintain compatibility FromBinaryRawSystem.DateTime691 internal static DateTime FromBinaryRaw(Int64 dateData) 692 { 693 Int64 ticks = dateData & (Int64)TicksMask; 694 if (ticks < MinTicks || ticks > MaxTicks) 695 throw new ArgumentException(SR.Argument_DateTimeBadBinaryData, nameof(dateData)); 696 return new DateTime((UInt64)dateData); 697 } 698 699 // Creates a DateTime from a Windows filetime. A Windows filetime is 700 // a long representing the date and time as the number of 701 // 100-nanosecond intervals that have elapsed since 1/1/1601 12:00am. 702 // FromFileTimeSystem.DateTime703 public static DateTime FromFileTime(long fileTime) 704 { 705 return FromFileTimeUtc(fileTime).ToLocalTime(); 706 } 707 FromFileTimeUtcSystem.DateTime708 public static DateTime FromFileTimeUtc(long fileTime) 709 { 710 if (fileTime < 0 || fileTime > MaxTicks - FileTimeOffset) 711 { 712 throw new ArgumentOutOfRangeException(nameof(fileTime), SR.ArgumentOutOfRange_FileTimeInvalid); 713 } 714 715 // This is the ticks in Universal time for this fileTime. 716 long universalTicks = fileTime + FileTimeOffset; 717 return new DateTime(universalTicks, DateTimeKind.Utc); 718 } 719 720 // Creates a DateTime from an OLE Automation Date. 721 // FromOADateSystem.DateTime722 public static DateTime FromOADate(double d) 723 { 724 return new DateTime(DoubleDateToTicks(d), DateTimeKind.Unspecified); 725 } 726 ISerializable.GetObjectDataSystem.DateTime727 void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) 728 { 729 if (info == null) 730 { 731 throw new ArgumentNullException(nameof(info)); 732 } 733 734 // Serialize both the old and the new format 735 info.AddValue(TicksField, InternalTicks); 736 info.AddValue(DateDataField, _dateData); 737 } 738 IsDaylightSavingTimeSystem.DateTime739 public Boolean IsDaylightSavingTime() 740 { 741 if (Kind == DateTimeKind.Utc) 742 { 743 return false; 744 } 745 return TimeZoneInfo.Local.IsDaylightSavingTime(this, TimeZoneInfoOptions.NoThrowOnInvalidTime); 746 } 747 SpecifyKindSystem.DateTime748 public static DateTime SpecifyKind(DateTime value, DateTimeKind kind) 749 { 750 return new DateTime(value.InternalTicks, kind); 751 } 752 ToBinarySystem.DateTime753 public Int64 ToBinary() 754 { 755 if (Kind == DateTimeKind.Local) 756 { 757 // Local times need to be adjusted as you move from one time zone to another, 758 // just as they are when serializing in text. As such the format for local times 759 // changes to store the ticks of the UTC time, but with flags that look like a 760 // local date. 761 762 // To match serialization in text we need to be able to handle cases where 763 // the UTC value would be out of range. Unused parts of the ticks range are 764 // used for this, so that values just past max value are stored just past the 765 // end of the maximum range, and values just below minimum value are stored 766 // at the end of the ticks area, just below 2^62. 767 TimeSpan offset = TimeZoneInfo.GetLocalUtcOffset(this, TimeZoneInfoOptions.NoThrowOnInvalidTime); 768 Int64 ticks = Ticks; 769 Int64 storedTicks = ticks - offset.Ticks; 770 if (storedTicks < 0) 771 { 772 storedTicks = TicksCeiling + storedTicks; 773 } 774 return storedTicks | (unchecked((Int64)LocalMask)); 775 } 776 else 777 { 778 return (Int64)_dateData; 779 } 780 } 781 782 // Returns the date part of this DateTime. The resulting value 783 // corresponds to this DateTime with the time-of-day part set to 784 // zero (midnight). 785 // 786 public DateTime Date 787 { 788 get 789 { 790 Int64 ticks = InternalTicks; 791 return new DateTime((UInt64)(ticks - ticks % TicksPerDay) | InternalKind); 792 } 793 } 794 795 // Returns a given date part of this DateTime. This method is used 796 // to compute the year, day-of-year, month, or day part. GetDatePartSystem.DateTime797 private int GetDatePart(int part) 798 { 799 Int64 ticks = InternalTicks; 800 // n = number of days since 1/1/0001 801 int n = (int)(ticks / TicksPerDay); 802 // y400 = number of whole 400-year periods since 1/1/0001 803 int y400 = n / DaysPer400Years; 804 // n = day number within 400-year period 805 n -= y400 * DaysPer400Years; 806 // y100 = number of whole 100-year periods within 400-year period 807 int y100 = n / DaysPer100Years; 808 // Last 100-year period has an extra day, so decrement result if 4 809 if (y100 == 4) y100 = 3; 810 // n = day number within 100-year period 811 n -= y100 * DaysPer100Years; 812 // y4 = number of whole 4-year periods within 100-year period 813 int y4 = n / DaysPer4Years; 814 // n = day number within 4-year period 815 n -= y4 * DaysPer4Years; 816 // y1 = number of whole years within 4-year period 817 int y1 = n / DaysPerYear; 818 // Last year has an extra day, so decrement result if 4 819 if (y1 == 4) y1 = 3; 820 // If year was requested, compute and return it 821 if (part == DatePartYear) 822 { 823 return y400 * 400 + y100 * 100 + y4 * 4 + y1 + 1; 824 } 825 // n = day number within year 826 n -= y1 * DaysPerYear; 827 // If day-of-year was requested, return it 828 if (part == DatePartDayOfYear) return n + 1; 829 // Leap year calculation looks different from IsLeapYear since y1, y4, 830 // and y100 are relative to year 1, not year 0 831 bool leapYear = y1 == 3 && (y4 != 24 || y100 == 3); 832 int[] days = leapYear ? s_daysToMonth366 : s_daysToMonth365; 833 // All months have less than 32 days, so n >> 5 is a good conservative 834 // estimate for the month 835 int m = (n >> 5) + 1; 836 // m = 1-based month number 837 while (n >= days[m]) m++; 838 // If month was requested, return it 839 if (part == DatePartMonth) return m; 840 // Return 1-based day-of-month 841 return n - days[m - 1] + 1; 842 } 843 844 // Exactly the same as GetDatePart(int part), except computing all of 845 // year/month/day rather than just one of them. Used when all three 846 // are needed rather than redoing the computations for each. GetDatePartSystem.DateTime847 internal void GetDatePart(out int year, out int month, out int day) 848 { 849 Int64 ticks = InternalTicks; 850 // n = number of days since 1/1/0001 851 int n = (int)(ticks / TicksPerDay); 852 // y400 = number of whole 400-year periods since 1/1/0001 853 int y400 = n / DaysPer400Years; 854 // n = day number within 400-year period 855 n -= y400 * DaysPer400Years; 856 // y100 = number of whole 100-year periods within 400-year period 857 int y100 = n / DaysPer100Years; 858 // Last 100-year period has an extra day, so decrement result if 4 859 if (y100 == 4) y100 = 3; 860 // n = day number within 100-year period 861 n -= y100 * DaysPer100Years; 862 // y4 = number of whole 4-year periods within 100-year period 863 int y4 = n / DaysPer4Years; 864 // n = day number within 4-year period 865 n -= y4 * DaysPer4Years; 866 // y1 = number of whole years within 4-year period 867 int y1 = n / DaysPerYear; 868 // Last year has an extra day, so decrement result if 4 869 if (y1 == 4) y1 = 3; 870 // compute year 871 year = y400 * 400 + y100 * 100 + y4 * 4 + y1 + 1; 872 // n = day number within year 873 n -= y1 * DaysPerYear; 874 // dayOfYear = n + 1; 875 // Leap year calculation looks different from IsLeapYear since y1, y4, 876 // and y100 are relative to year 1, not year 0 877 bool leapYear = y1 == 3 && (y4 != 24 || y100 == 3); 878 int[] days = leapYear ? s_daysToMonth366 : s_daysToMonth365; 879 // All months have less than 32 days, so n >> 5 is a good conservative 880 // estimate for the month 881 int m = (n >> 5) + 1; 882 // m = 1-based month number 883 while (n >= days[m]) m++; 884 // compute month and day 885 month = m; 886 day = n - days[m - 1] + 1; 887 } 888 889 // Returns the day-of-month part of this DateTime. The returned 890 // value is an integer between 1 and 31. 891 // 892 public int Day 893 { 894 get 895 { 896 return GetDatePart(DatePartDay); 897 } 898 } 899 900 // Returns the day-of-week part of this DateTime. The returned value 901 // is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates 902 // Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates 903 // Thursday, 5 indicates Friday, and 6 indicates Saturday. 904 // 905 public DayOfWeek DayOfWeek 906 { 907 get 908 { 909 return (DayOfWeek)((InternalTicks / TicksPerDay + 1) % 7); 910 } 911 } 912 913 // Returns the day-of-year part of this DateTime. The returned value 914 // is an integer between 1 and 366. 915 // 916 public int DayOfYear 917 { 918 get 919 { 920 return GetDatePart(DatePartDayOfYear); 921 } 922 } 923 924 // Returns the hash code for this DateTime. 925 // GetHashCodeSystem.DateTime926 public override int GetHashCode() 927 { 928 Int64 ticks = InternalTicks; 929 return unchecked((int)ticks) ^ (int)(ticks >> 32); 930 } 931 932 // Returns the hour part of this DateTime. The returned value is an 933 // integer between 0 and 23. 934 // 935 public int Hour 936 { 937 get 938 { 939 return (int)((InternalTicks / TicksPerHour) % 24); 940 } 941 } 942 IsAmbiguousDaylightSavingTimeSystem.DateTime943 internal Boolean IsAmbiguousDaylightSavingTime() 944 { 945 return (InternalKind == KindLocalAmbiguousDst); 946 } 947 948 public DateTimeKind Kind 949 { 950 get 951 { 952 switch (InternalKind) 953 { 954 case KindUnspecified: 955 return DateTimeKind.Unspecified; 956 case KindUtc: 957 return DateTimeKind.Utc; 958 default: 959 return DateTimeKind.Local; 960 } 961 } 962 } 963 964 // Returns the millisecond part of this DateTime. The returned value 965 // is an integer between 0 and 999. 966 // 967 public int Millisecond 968 { 969 get 970 { 971 return (int)((InternalTicks / TicksPerMillisecond) % 1000); 972 } 973 } 974 975 // Returns the minute part of this DateTime. The returned value is 976 // an integer between 0 and 59. 977 // 978 public int Minute 979 { 980 get 981 { 982 return (int)((InternalTicks / TicksPerMinute) % 60); 983 } 984 } 985 986 // Returns the month part of this DateTime. The returned value is an 987 // integer between 1 and 12. 988 // 989 public int Month 990 { 991 get 992 { 993 return GetDatePart(DatePartMonth); 994 } 995 } 996 997 // Returns a DateTime representing the current date and time. The 998 // resolution of the returned value depends on the system timer. 999 public static DateTime Now 1000 { 1001 get 1002 { 1003 DateTime utc = UtcNow; 1004 Boolean isAmbiguousLocalDst = false; 1005 Int64 offset = TimeZoneInfo.GetDateTimeNowUtcOffsetFromUtc(utc, out isAmbiguousLocalDst).Ticks; 1006 long tick = utc.Ticks + offset; 1007 if (tick > DateTime.MaxTicks) 1008 { 1009 return new DateTime(DateTime.MaxTicks, DateTimeKind.Local); 1010 } 1011 if (tick < DateTime.MinTicks) 1012 { 1013 return new DateTime(DateTime.MinTicks, DateTimeKind.Local); 1014 } 1015 return new DateTime(tick, DateTimeKind.Local, isAmbiguousLocalDst); 1016 } 1017 } 1018 1019 // Returns the second part of this DateTime. The returned value is 1020 // an integer between 0 and 59. 1021 // 1022 public int Second 1023 { 1024 get 1025 { 1026 return (int)((InternalTicks / TicksPerSecond) % 60); 1027 } 1028 } 1029 1030 // Returns the tick count for this DateTime. The returned value is 1031 // the number of 100-nanosecond intervals that have elapsed since 1/1/0001 1032 // 12:00am. 1033 // 1034 public long Ticks 1035 { 1036 get 1037 { 1038 return InternalTicks; 1039 } 1040 } 1041 1042 // Returns the time-of-day part of this DateTime. The returned value 1043 // is a TimeSpan that indicates the time elapsed since midnight. 1044 // 1045 public TimeSpan TimeOfDay 1046 { 1047 get 1048 { 1049 return new TimeSpan(InternalTicks % TicksPerDay); 1050 } 1051 } 1052 1053 // Returns a DateTime representing the current date. The date part 1054 // of the returned value is the current date, and the time-of-day part of 1055 // the returned value is zero (midnight). 1056 // 1057 public static DateTime Today 1058 { 1059 get 1060 { 1061 return DateTime.Now.Date; 1062 } 1063 } 1064 1065 // Returns the year part of this DateTime. The returned value is an 1066 // integer between 1 and 9999. 1067 // 1068 public int Year 1069 { 1070 get 1071 { 1072 return GetDatePart(DatePartYear); 1073 } 1074 } 1075 1076 // Checks whether a given year is a leap year. This method returns true if 1077 // year is a leap year, or false if not. 1078 // IsLeapYearSystem.DateTime1079 public static bool IsLeapYear(int year) 1080 { 1081 if (year < 1 || year > 9999) 1082 { 1083 throw new ArgumentOutOfRangeException(nameof(year), SR.ArgumentOutOfRange_Year); 1084 } 1085 return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); 1086 } 1087 1088 // Constructs a DateTime from a string. The string must specify a 1089 // date and optionally a time in a culture-specific or universal format. 1090 // Leading and trailing whitespace characters are allowed. 1091 // ParseSystem.DateTime1092 public static DateTime Parse(String s) 1093 { 1094 if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); 1095 return (DateTimeParse.Parse(s, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.None)); 1096 } 1097 1098 // Constructs a DateTime from a string. The string must specify a 1099 // date and optionally a time in a culture-specific or universal format. 1100 // Leading and trailing whitespace characters are allowed. 1101 // ParseSystem.DateTime1102 public static DateTime Parse(String s, IFormatProvider provider) 1103 { 1104 if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); 1105 return (DateTimeParse.Parse(s, DateTimeFormatInfo.GetInstance(provider), DateTimeStyles.None)); 1106 } 1107 ParseSystem.DateTime1108 public static DateTime Parse(String s, IFormatProvider provider, DateTimeStyles styles) 1109 { 1110 DateTimeFormatInfo.ValidateStyles(styles, nameof(styles)); 1111 if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); 1112 return (DateTimeParse.Parse(s, DateTimeFormatInfo.GetInstance(provider), styles)); 1113 } 1114 ParseSystem.DateTime1115 public static DateTime Parse(ReadOnlySpan<char> s, IFormatProvider provider = null, DateTimeStyles styles = DateTimeStyles.None) 1116 { 1117 DateTimeFormatInfo.ValidateStyles(styles, nameof(styles)); 1118 return DateTimeParse.Parse(s, DateTimeFormatInfo.GetInstance(provider), styles); 1119 } 1120 1121 // Constructs a DateTime from a string. The string must specify a 1122 // date and optionally a time in a culture-specific or universal format. 1123 // Leading and trailing whitespace characters are allowed. 1124 // ParseExactSystem.DateTime1125 public static DateTime ParseExact(String s, String format, IFormatProvider provider) 1126 { 1127 if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); 1128 if (format == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.format); 1129 return (DateTimeParse.ParseExact(s, format, DateTimeFormatInfo.GetInstance(provider), DateTimeStyles.None)); 1130 } 1131 1132 // Constructs a DateTime from a string. The string must specify a 1133 // date and optionally a time in a culture-specific or universal format. 1134 // Leading and trailing whitespace characters are allowed. 1135 // ParseExactSystem.DateTime1136 public static DateTime ParseExact(String s, String format, IFormatProvider provider, DateTimeStyles style) 1137 { 1138 DateTimeFormatInfo.ValidateStyles(style, nameof(style)); 1139 if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); 1140 if (format == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.format); 1141 return (DateTimeParse.ParseExact(s, format, DateTimeFormatInfo.GetInstance(provider), style)); 1142 } 1143 1144 // TODO https://github.com/dotnet/corefx/issues/25337: Remove this overload once corefx is updated to target the new signatures ParseExactSystem.DateTime1145 public static DateTime ParseExact(ReadOnlySpan<char> s, string format, IFormatProvider provider, DateTimeStyles style) 1146 { 1147 if (format == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.format); 1148 return ParseExact(s, (ReadOnlySpan<char>)format, provider, style); 1149 } 1150 ParseExactSystem.DateTime1151 public static DateTime ParseExact(ReadOnlySpan<char> s, ReadOnlySpan<char> format, IFormatProvider provider, DateTimeStyles style = DateTimeStyles.None) 1152 { 1153 DateTimeFormatInfo.ValidateStyles(style, nameof(style)); 1154 return DateTimeParse.ParseExact(s, format, DateTimeFormatInfo.GetInstance(provider), style); 1155 } 1156 ParseExactSystem.DateTime1157 public static DateTime ParseExact(String s, String[] formats, IFormatProvider provider, DateTimeStyles style) 1158 { 1159 DateTimeFormatInfo.ValidateStyles(style, nameof(style)); 1160 if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); 1161 return DateTimeParse.ParseExactMultiple(s, formats, DateTimeFormatInfo.GetInstance(provider), style); 1162 } 1163 ParseExactSystem.DateTime1164 public static DateTime ParseExact(ReadOnlySpan<char> s, string[] formats, IFormatProvider provider, DateTimeStyles style = DateTimeStyles.None) 1165 { 1166 DateTimeFormatInfo.ValidateStyles(style, nameof(style)); 1167 return DateTimeParse.ParseExactMultiple(s, formats, DateTimeFormatInfo.GetInstance(provider), style); 1168 } 1169 SubtractSystem.DateTime1170 public TimeSpan Subtract(DateTime value) 1171 { 1172 return new TimeSpan(InternalTicks - value.InternalTicks); 1173 } 1174 SubtractSystem.DateTime1175 public DateTime Subtract(TimeSpan value) 1176 { 1177 long ticks = InternalTicks; 1178 long valueTicks = value._ticks; 1179 if (ticks - MinTicks < valueTicks || ticks - MaxTicks > valueTicks) 1180 { 1181 throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_DateArithmetic); 1182 } 1183 return new DateTime((UInt64)(ticks - valueTicks) | InternalKind); 1184 } 1185 1186 // This function is duplicated in COMDateTime.cpp TicksToOADateSystem.DateTime1187 private static double TicksToOADate(long value) 1188 { 1189 if (value == 0) 1190 return 0.0; // Returns OleAut's zero'ed date value. 1191 if (value < TicksPerDay) // This is a fix for VB. They want the default day to be 1/1/0001 rathar then 12/30/1899. 1192 value += DoubleDateOffset; // We could have moved this fix down but we would like to keep the bounds check. 1193 if (value < OADateMinAsTicks) 1194 throw new OverflowException(SR.Arg_OleAutDateInvalid); 1195 // Currently, our max date == OA's max date (12/31/9999), so we don't 1196 // need an overflow check in that direction. 1197 long millis = (value - DoubleDateOffset) / TicksPerMillisecond; 1198 if (millis < 0) 1199 { 1200 long frac = millis % MillisPerDay; 1201 if (frac != 0) millis -= (MillisPerDay + frac) * 2; 1202 } 1203 return (double)millis / MillisPerDay; 1204 } 1205 1206 // Converts the DateTime instance into an OLE Automation compatible 1207 // double date. ToOADateSystem.DateTime1208 public double ToOADate() 1209 { 1210 return TicksToOADate(InternalTicks); 1211 } 1212 ToFileTimeSystem.DateTime1213 public long ToFileTime() 1214 { 1215 // Treats the input as local if it is not specified 1216 return ToUniversalTime().ToFileTimeUtc(); 1217 } 1218 ToFileTimeUtcSystem.DateTime1219 public long ToFileTimeUtc() 1220 { 1221 // Treats the input as universal if it is not specified 1222 long ticks = ((InternalKind & LocalMask) != 0) ? ToUniversalTime().InternalTicks : this.InternalTicks; 1223 ticks -= FileTimeOffset; 1224 if (ticks < 0) 1225 { 1226 throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_FileTimeInvalid); 1227 } 1228 return ticks; 1229 } 1230 ToLocalTimeSystem.DateTime1231 public DateTime ToLocalTime() 1232 { 1233 return ToLocalTime(false); 1234 } 1235 ToLocalTimeSystem.DateTime1236 internal DateTime ToLocalTime(bool throwOnOverflow) 1237 { 1238 if (Kind == DateTimeKind.Local) 1239 { 1240 return this; 1241 } 1242 Boolean isDaylightSavings = false; 1243 Boolean isAmbiguousLocalDst = false; 1244 Int64 offset = TimeZoneInfo.GetUtcOffsetFromUtc(this, TimeZoneInfo.Local, out isDaylightSavings, out isAmbiguousLocalDst).Ticks; 1245 long tick = Ticks + offset; 1246 if (tick > DateTime.MaxTicks) 1247 { 1248 if (throwOnOverflow) 1249 throw new ArgumentException(SR.Arg_ArgumentOutOfRangeException); 1250 else 1251 return new DateTime(DateTime.MaxTicks, DateTimeKind.Local); 1252 } 1253 if (tick < DateTime.MinTicks) 1254 { 1255 if (throwOnOverflow) 1256 throw new ArgumentException(SR.Arg_ArgumentOutOfRangeException); 1257 else 1258 return new DateTime(DateTime.MinTicks, DateTimeKind.Local); 1259 } 1260 return new DateTime(tick, DateTimeKind.Local, isAmbiguousLocalDst); 1261 } 1262 ToLongDateStringSystem.DateTime1263 public String ToLongDateString() 1264 { 1265 return DateTimeFormat.Format(this, "D", DateTimeFormatInfo.CurrentInfo); 1266 } 1267 ToLongTimeStringSystem.DateTime1268 public String ToLongTimeString() 1269 { 1270 return DateTimeFormat.Format(this, "T", DateTimeFormatInfo.CurrentInfo); 1271 } 1272 ToShortDateStringSystem.DateTime1273 public String ToShortDateString() 1274 { 1275 return DateTimeFormat.Format(this, "d", DateTimeFormatInfo.CurrentInfo); 1276 } 1277 ToShortTimeStringSystem.DateTime1278 public String ToShortTimeString() 1279 { 1280 return DateTimeFormat.Format(this, "t", DateTimeFormatInfo.CurrentInfo); 1281 } 1282 ToStringSystem.DateTime1283 public override String ToString() 1284 { 1285 return DateTimeFormat.Format(this, null, DateTimeFormatInfo.CurrentInfo); 1286 } 1287 ToStringSystem.DateTime1288 public String ToString(String format) 1289 { 1290 return DateTimeFormat.Format(this, format, DateTimeFormatInfo.CurrentInfo); 1291 } 1292 ToStringSystem.DateTime1293 public String ToString(IFormatProvider provider) 1294 { 1295 return DateTimeFormat.Format(this, null, DateTimeFormatInfo.GetInstance(provider)); 1296 } 1297 ToStringSystem.DateTime1298 public String ToString(String format, IFormatProvider provider) 1299 { 1300 return DateTimeFormat.Format(this, format, DateTimeFormatInfo.GetInstance(provider)); 1301 } 1302 1303 // TODO https://github.com/dotnet/corefx/issues/25337: Remove this overload once corefx is updated to target the new signatures TryFormatSystem.DateTime1304 public bool TryFormat(Span<char> destination, out int charsWritten, string format, IFormatProvider provider) => 1305 TryFormat(destination, out charsWritten, (ReadOnlySpan<char>)format, provider); 1306 TryFormatSystem.DateTime1307 public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider provider = null) => 1308 DateTimeFormat.TryFormat(this, destination, out charsWritten, format, DateTimeFormatInfo.GetInstance(provider)); 1309 ToUniversalTimeSystem.DateTime1310 public DateTime ToUniversalTime() 1311 { 1312 return TimeZoneInfo.ConvertTimeToUtc(this, TimeZoneInfoOptions.NoThrowOnInvalidTime); 1313 } 1314 TryParseSystem.DateTime1315 public static Boolean TryParse(String s, out DateTime result) 1316 { 1317 if (s == null) 1318 { 1319 result = default; 1320 return false; 1321 } 1322 return DateTimeParse.TryParse(s, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.None, out result); 1323 } 1324 TryParseSystem.DateTime1325 public static bool TryParse(ReadOnlySpan<char> s, out DateTime result) 1326 { 1327 return DateTimeParse.TryParse(s, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.None, out result); 1328 } 1329 TryParseSystem.DateTime1330 public static Boolean TryParse(String s, IFormatProvider provider, DateTimeStyles styles, out DateTime result) 1331 { 1332 DateTimeFormatInfo.ValidateStyles(styles, nameof(styles)); 1333 1334 if (s == null) 1335 { 1336 result = default; 1337 return false; 1338 } 1339 1340 return DateTimeParse.TryParse(s, DateTimeFormatInfo.GetInstance(provider), styles, out result); 1341 } 1342 TryParseSystem.DateTime1343 public static bool TryParse(ReadOnlySpan<char> s, IFormatProvider provider, DateTimeStyles styles, out DateTime result) 1344 { 1345 DateTimeFormatInfo.ValidateStyles(styles, nameof(styles)); 1346 return DateTimeParse.TryParse(s, DateTimeFormatInfo.GetInstance(provider), styles, out result); 1347 } 1348 TryParseExactSystem.DateTime1349 public static Boolean TryParseExact(String s, String format, IFormatProvider provider, DateTimeStyles style, out DateTime result) 1350 { 1351 DateTimeFormatInfo.ValidateStyles(style, nameof(style)); 1352 1353 if (s == null || format == null) 1354 { 1355 result = default; 1356 return false; 1357 } 1358 1359 return DateTimeParse.TryParseExact(s, format, DateTimeFormatInfo.GetInstance(provider), style, out result); 1360 } 1361 1362 // TODO https://github.com/dotnet/corefx/issues/25337: Remove this overload once corefx is updated to target the new signatures TryParseExactSystem.DateTime1363 public static bool TryParseExact(ReadOnlySpan<char> s, string format, IFormatProvider provider, DateTimeStyles style, out DateTime result) 1364 { 1365 if (format == null) 1366 { 1367 result = default; 1368 return false; 1369 } 1370 1371 return TryParseExact(s, (ReadOnlySpan<char>)format, provider, style, out result); 1372 } 1373 TryParseExactSystem.DateTime1374 public static bool TryParseExact(ReadOnlySpan<char> s, ReadOnlySpan<char> format, IFormatProvider provider, DateTimeStyles style, out DateTime result) 1375 { 1376 DateTimeFormatInfo.ValidateStyles(style, nameof(style)); 1377 return DateTimeParse.TryParseExact(s, format, DateTimeFormatInfo.GetInstance(provider), style, out result); 1378 } 1379 TryParseExactSystem.DateTime1380 public static Boolean TryParseExact(String s, String[] formats, IFormatProvider provider, DateTimeStyles style, out DateTime result) 1381 { 1382 DateTimeFormatInfo.ValidateStyles(style, nameof(style)); 1383 1384 if (s == null) 1385 { 1386 result = default; 1387 return false; 1388 } 1389 1390 return DateTimeParse.TryParseExactMultiple(s, formats, DateTimeFormatInfo.GetInstance(provider), style, out result); 1391 } 1392 TryParseExactSystem.DateTime1393 public static bool TryParseExact(ReadOnlySpan<char> s, string[] formats, IFormatProvider provider, DateTimeStyles style, out DateTime result) 1394 { 1395 DateTimeFormatInfo.ValidateStyles(style, nameof(style)); 1396 return DateTimeParse.TryParseExactMultiple(s, formats, DateTimeFormatInfo.GetInstance(provider), style, out result); 1397 } 1398 operator +System.DateTime1399 public static DateTime operator +(DateTime d, TimeSpan t) 1400 { 1401 long ticks = d.InternalTicks; 1402 long valueTicks = t._ticks; 1403 if (valueTicks > MaxTicks - ticks || valueTicks < MinTicks - ticks) 1404 { 1405 throw new ArgumentOutOfRangeException(nameof(t), SR.ArgumentOutOfRange_DateArithmetic); 1406 } 1407 return new DateTime((UInt64)(ticks + valueTicks) | d.InternalKind); 1408 } 1409 operator -System.DateTime1410 public static DateTime operator -(DateTime d, TimeSpan t) 1411 { 1412 long ticks = d.InternalTicks; 1413 long valueTicks = t._ticks; 1414 if (ticks - MinTicks < valueTicks || ticks - MaxTicks > valueTicks) 1415 { 1416 throw new ArgumentOutOfRangeException(nameof(t), SR.ArgumentOutOfRange_DateArithmetic); 1417 } 1418 return new DateTime((UInt64)(ticks - valueTicks) | d.InternalKind); 1419 } 1420 operator -System.DateTime1421 public static TimeSpan operator -(DateTime d1, DateTime d2) 1422 { 1423 return new TimeSpan(d1.InternalTicks - d2.InternalTicks); 1424 } 1425 operator ==System.DateTime1426 public static bool operator ==(DateTime d1, DateTime d2) 1427 { 1428 return d1.InternalTicks == d2.InternalTicks; 1429 } 1430 operator !=System.DateTime1431 public static bool operator !=(DateTime d1, DateTime d2) 1432 { 1433 return d1.InternalTicks != d2.InternalTicks; 1434 } 1435 operator <System.DateTime1436 public static bool operator <(DateTime t1, DateTime t2) 1437 { 1438 return t1.InternalTicks < t2.InternalTicks; 1439 } 1440 operator <=System.DateTime1441 public static bool operator <=(DateTime t1, DateTime t2) 1442 { 1443 return t1.InternalTicks <= t2.InternalTicks; 1444 } 1445 operator >System.DateTime1446 public static bool operator >(DateTime t1, DateTime t2) 1447 { 1448 return t1.InternalTicks > t2.InternalTicks; 1449 } 1450 operator >=System.DateTime1451 public static bool operator >=(DateTime t1, DateTime t2) 1452 { 1453 return t1.InternalTicks >= t2.InternalTicks; 1454 } 1455 1456 1457 // Returns a string array containing all of the known date and time options for the 1458 // current culture. The strings returned are properly formatted date and 1459 // time strings for the current instance of DateTime. GetDateTimeFormatsSystem.DateTime1460 public String[] GetDateTimeFormats() 1461 { 1462 return (GetDateTimeFormats(CultureInfo.CurrentCulture)); 1463 } 1464 1465 // Returns a string array containing all of the known date and time options for the 1466 // using the information provided by IFormatProvider. The strings returned are properly formatted date and 1467 // time strings for the current instance of DateTime. GetDateTimeFormatsSystem.DateTime1468 public String[] GetDateTimeFormats(IFormatProvider provider) 1469 { 1470 return (DateTimeFormat.GetAllDateTimes(this, DateTimeFormatInfo.GetInstance(provider))); 1471 } 1472 1473 1474 // Returns a string array containing all of the date and time options for the 1475 // given format format and current culture. The strings returned are properly formatted date and 1476 // time strings for the current instance of DateTime. GetDateTimeFormatsSystem.DateTime1477 public String[] GetDateTimeFormats(char format) 1478 { 1479 return (GetDateTimeFormats(format, CultureInfo.CurrentCulture)); 1480 } 1481 1482 // Returns a string array containing all of the date and time options for the 1483 // given format format and given culture. The strings returned are properly formatted date and 1484 // time strings for the current instance of DateTime. GetDateTimeFormatsSystem.DateTime1485 public String[] GetDateTimeFormats(char format, IFormatProvider provider) 1486 { 1487 return (DateTimeFormat.GetAllDateTimes(this, format, DateTimeFormatInfo.GetInstance(provider))); 1488 } 1489 1490 // 1491 // IConvertible implementation 1492 // 1493 GetTypeCodeSystem.DateTime1494 public TypeCode GetTypeCode() 1495 { 1496 return TypeCode.DateTime; 1497 } 1498 1499 IConvertible.ToBooleanSystem.DateTime1500 bool IConvertible.ToBoolean(IFormatProvider provider) 1501 { 1502 throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Boolean")); 1503 } 1504 IConvertible.ToCharSystem.DateTime1505 char IConvertible.ToChar(IFormatProvider provider) 1506 { 1507 throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Char")); 1508 } 1509 IConvertible.ToSByteSystem.DateTime1510 sbyte IConvertible.ToSByte(IFormatProvider provider) 1511 { 1512 throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "SByte")); 1513 } 1514 IConvertible.ToByteSystem.DateTime1515 byte IConvertible.ToByte(IFormatProvider provider) 1516 { 1517 throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Byte")); 1518 } 1519 IConvertible.ToInt16System.DateTime1520 short IConvertible.ToInt16(IFormatProvider provider) 1521 { 1522 throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Int16")); 1523 } 1524 IConvertible.ToUInt16System.DateTime1525 ushort IConvertible.ToUInt16(IFormatProvider provider) 1526 { 1527 throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "UInt16")); 1528 } 1529 IConvertible.ToInt32System.DateTime1530 int IConvertible.ToInt32(IFormatProvider provider) 1531 { 1532 throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Int32")); 1533 } 1534 IConvertible.ToUInt32System.DateTime1535 uint IConvertible.ToUInt32(IFormatProvider provider) 1536 { 1537 throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "UInt32")); 1538 } 1539 IConvertible.ToInt64System.DateTime1540 long IConvertible.ToInt64(IFormatProvider provider) 1541 { 1542 throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Int64")); 1543 } 1544 IConvertible.ToUInt64System.DateTime1545 ulong IConvertible.ToUInt64(IFormatProvider provider) 1546 { 1547 throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "UInt64")); 1548 } 1549 IConvertible.ToSingleSystem.DateTime1550 float IConvertible.ToSingle(IFormatProvider provider) 1551 { 1552 throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Single")); 1553 } 1554 IConvertible.ToDoubleSystem.DateTime1555 double IConvertible.ToDouble(IFormatProvider provider) 1556 { 1557 throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Double")); 1558 } 1559 IConvertible.ToDecimalSystem.DateTime1560 Decimal IConvertible.ToDecimal(IFormatProvider provider) 1561 { 1562 throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Decimal")); 1563 } 1564 IConvertible.ToDateTimeSystem.DateTime1565 DateTime IConvertible.ToDateTime(IFormatProvider provider) 1566 { 1567 return this; 1568 } 1569 IConvertible.ToTypeSystem.DateTime1570 Object IConvertible.ToType(Type type, IFormatProvider provider) 1571 { 1572 return Convert.DefaultToType((IConvertible)this, type, provider); 1573 } 1574 1575 // Tries to construct a DateTime from a given year, month, day, hour, 1576 // minute, second and millisecond. 1577 // TryCreateSystem.DateTime1578 internal static Boolean TryCreate(int year, int month, int day, int hour, int minute, int second, int millisecond, out DateTime result) 1579 { 1580 result = DateTime.MinValue; 1581 if (year < 1 || year > 9999 || month < 1 || month > 12) 1582 { 1583 return false; 1584 } 1585 int[] days = IsLeapYear(year) ? s_daysToMonth366 : s_daysToMonth365; 1586 if (day < 1 || day > days[month] - days[month - 1]) 1587 { 1588 return false; 1589 } 1590 if (hour < 0 || hour >= 24 || minute < 0 || minute >= 60 || second < 0 || second >= 60) 1591 { 1592 return false; 1593 } 1594 if (millisecond < 0 || millisecond >= MillisPerSecond) 1595 { 1596 return false; 1597 } 1598 long ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second); 1599 1600 ticks += millisecond * TicksPerMillisecond; 1601 if (ticks < MinTicks || ticks > MaxTicks) 1602 { 1603 return false; 1604 } 1605 result = new DateTime(ticks, DateTimeKind.Unspecified); 1606 return true; 1607 } 1608 } 1609 } 1610