Skip to content

Commit

Permalink
Optimize DateTime.Year/.DayOfYear properties; fix comments (#73277)
Browse files Browse the repository at this point in the history
  • Loading branch information
SergeiPavlov committed Aug 6, 2022
1 parent 772444d commit bf17dd6
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 37 deletions.
64 changes: 27 additions & 37 deletions src/libraries/System.Private.CoreLib/src/System/DateTime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,16 @@ public readonly partial struct DateTime
// All OA dates must be less than (not <=) OADateMaxAsDouble
private const double OADateMaxAsDouble = 2958466.0;

// Euclidean Affine Functions Algorithm constants
// Euclidean Affine Functions Algorithm (EAF) constants

// Constants used for fast calculation of following subexpressions
// x / DaysPer4Years
// x % DaysPer4Years / 4
private const uint EafMultiplier = (uint)(((1UL << 32) + DaysPer4Years - 1) / DaysPer4Years); // 2,939,745
private const uint EafDivider = EafMultiplier * 4; // 11,758,980

private const ulong TicksPer6Hours = TicksPerHour * 6;
private const int March1BasedDayOfNewYear = 306; // Days between March 1 and January 1
private const int March1BasedDayOfNewYear = 306; // Days between March 1 and January 1

private static readonly uint[] s_daysToMonth365 = {
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
Expand Down Expand Up @@ -1347,13 +1354,13 @@ public DateTime Date
// Cassio Neri, Lorenz Schneiderhttps - Euclidean Affine Functions and Applications to Calendar Algorithms - 2021
internal void GetDate(out int year, out int month, out int day)
{
// y400 = number of whole 400-year periods since 3/1/0000
// r1 = day number within 400-year period
(uint y400, uint r1) = Math.DivRem(((uint)(UTicks / TicksPer6Hours) | 3U) + 1224, DaysPer400Years);
ulong u2 = (ulong)Math.BigMul(2939745, (int)r1 | 3);
ushort daySinceMarch1 = (ushort)((uint)u2 / 11758980);
// y100 = number of whole 100-year periods since 3/1/0000
// r1 = (day number within 100-year period) * 4
(uint y100, uint r1) = Math.DivRem(((uint)(UTicks / TicksPer6Hours) | 3U) + 1224, DaysPer400Years);
ulong u2 = (ulong)Math.BigMul((int)EafMultiplier, (int)r1 | 3);
ushort daySinceMarch1 = (ushort)((uint)u2 / EafDivider);
int n3 = 2141 * daySinceMarch1 + 197913;
year = (int)(100 * y400 + (uint)(u2 >> 32));
year = (int)(100 * y100 + (uint)(u2 >> 32));
// compute month and day
month = (ushort)(n3 >> 16);
day = (ushort)n3 / 2141 + 1;
Expand Down Expand Up @@ -1410,10 +1417,10 @@ public int Day
{
get
{
// r1 = day number within 400-year period
// r1 = (day number within 100-year period) * 4
uint r1 = (((uint)(UTicks / TicksPer6Hours) | 3U) + 1224) % DaysPer400Years;
ulong u2 = (ulong)Math.BigMul(2939745, (int)r1 | 3);
ushort daySinceMarch1 = (ushort)((uint)u2 / 11758980);
ulong u2 = (ulong)Math.BigMul((int)EafMultiplier, (int)r1 | 3);
ushort daySinceMarch1 = (ushort)((uint)u2 / EafDivider);
int n3 = 2141 * daySinceMarch1 + 197913;
// Return 1-based day-of-month
return (ushort)n3 / 2141 + 1;
Expand All @@ -1430,22 +1437,8 @@ public int Day
// Returns the day-of-year part of this DateTime. The returned value
// is an integer between 1 and 366.
//
public int DayOfYear
{
get
{
// y400 = number of whole 400-year periods since 3/1/0000
// r1 = day number within 400-year period
(uint y400, uint r1) = Math.DivRem(((uint)(UTicks / TicksPer6Hours) | 3U) + 1224, DaysPer400Years);
ulong u2 = (ulong)Math.BigMul(2939745, (int)r1 | 3);
ushort daySinceMarch1 = (ushort)((uint)u2 / 11758980);

int year = (int)(100 * y400 + (uint)(u2 >> 32)) + (daySinceMarch1 >= March1BasedDayOfNewYear ? 1 : 0);
return daySinceMarch1 >= March1BasedDayOfNewYear // DatePartDayOfYear case
? daySinceMarch1 - March1BasedDayOfNewYear + 1 // rollover December 31
: daySinceMarch1 + (366 - March1BasedDayOfNewYear) + (IsLeapYear(year) ? 1 : 0);
}
}
public int DayOfYear =>
1 + (int)(((((uint)(UTicks / TicksPer6Hours) | 3U) % (uint)DaysPer400Years) | 3U) * EafMultiplier / EafDivider);

// Returns the hash code for this DateTime.
//
Expand Down Expand Up @@ -1501,10 +1494,10 @@ public int Month
{
get
{
// r1 = day number within 400-year period
// r1 = (day number within 100-year period) * 4
uint r1 = (((uint)(UTicks / TicksPer6Hours) | 3U) + 1224) % DaysPer400Years;
ulong u2 = (ulong)Math.BigMul(2939745, (int)r1 | 3);
ushort daySinceMarch1 = (ushort)((uint)u2 / 11758980);
ulong u2 = (ulong)Math.BigMul((int)EafMultiplier, (int)r1 | 3);
ushort daySinceMarch1 = (ushort)((uint)u2 / EafDivider);
int n3 = 2141 * daySinceMarch1 + 197913;
return (ushort)(n3 >> 16) - (daySinceMarch1 >= March1BasedDayOfNewYear ? 12 : 0);
}
Expand Down Expand Up @@ -1560,13 +1553,10 @@ public int Year
{
get
{
// y400 = number of whole 400-year periods since 3/1/0000
// r1 = day number within 400-year period
(uint y400, uint r1) = Math.DivRem(((uint)(UTicks / TicksPer6Hours) | 3U) + 1224, DaysPer400Years);
ulong u2 = (ulong)Math.BigMul(2939745, (int)r1 | 3);
ushort daySinceMarch1 = (ushort)((uint)u2 / 11758980);

return (int)(100 * y400 + (uint)(u2 >> 32)) + (daySinceMarch1 >= March1BasedDayOfNewYear ? 1 : 0);
// y100 = number of whole 100-year periods since 1/1/0001
// r1 = (day number within 100-year period) * 4
(uint y100, uint r1) = Math.DivRem(((uint)(UTicks / TicksPer6Hours) | 3U), DaysPer400Years);
return 1 + (int)(100 * y100 + (r1 | 3) / DaysPer4Years);
}
}

Expand Down
14 changes: 14 additions & 0 deletions src/libraries/System.Runtime/tests/System/DateTimeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,20 @@ public void DayOfYear_Get_ReturnsExpected()
Assert.Equal(170, dateTime.DayOfYear);
}

[Fact]
public void DayOfYear_Random()
{
var random = new Random(2022);
var tries = 1000;
for (int i = 0; i < tries; ++i)
{
var dateTime = new DateTime(random.NextInt64(DateTime.MaxValue.Ticks));
var startOfYear = new DateTime(dateTime.Year, 1, 1);
var expectedDayOfYear = 1 + (dateTime - startOfYear).Days;
Assert.Equal(expectedDayOfYear, dateTime.DayOfYear);
}
}

[Fact]
public void TimeOfDay_Get_ReturnsExpected()
{
Expand Down

0 comments on commit bf17dd6

Please sign in to comment.