DateTime.cs 70 KB

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