From 9b79839d9abdc18d436a430ec8ad1cb091df8aff Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 15 Jun 2017 11:31:12 -0400 Subject: [PATCH] Avoid duplicated compuations with DateTime.GetDatePart - Remove duplicate code in GregorianCalendar. GetDatePart is identical to the implementation in DateTime. Just reuse it via the Year/Month/Day properties. Also avoids an unnecessary virtual call. - Then copy GetDatePart into a version that returns year/month/day rather than just one of them. - And use that in several places throughout corelib where all three are retrieved, avoiding redoing all of the calculations three times. Commit migrated from https://github.com/dotnet/coreclr/commit/17c23bed6395f3e12dc0b4471b2bb566c3aa136c --- src/coreclr/src/mscorlib/shared/System/DateTime.cs | 49 ++++++++++++++- .../shared/System/Globalization/DateTimeFormat.cs | 14 +++-- .../shared/System/Globalization/HebrewCalendar.cs | 4 +- .../System/Globalization/UmAlQuraCalendar.cs | 4 +- .../src/System/Globalization/GregorianCalendar.cs | 69 ++-------------------- .../src/System/TimeZoneInfo.TransitionTime.cs | 3 +- 6 files changed, 63 insertions(+), 80 deletions(-) diff --git a/src/coreclr/src/mscorlib/shared/System/DateTime.cs b/src/coreclr/src/mscorlib/shared/System/DateTime.cs index 4fd9727..16a75fd 100644 --- a/src/coreclr/src/mscorlib/shared/System/DateTime.cs +++ b/src/coreclr/src/mscorlib/shared/System/DateTime.cs @@ -453,9 +453,7 @@ namespace System { if (months < -120000 || months > 120000) throw new ArgumentOutOfRangeException(nameof(months), SR.ArgumentOutOfRange_DateTimeBadMonths); Contract.EndContractBlock(); - int y = GetDatePart(DatePartYear); - int m = GetDatePart(DatePartMonth); - int d = GetDatePart(DatePartDay); + GetDatePart(out int y, out int m, out int d); int i = m - 1 + months; if (i >= 0) { @@ -859,6 +857,51 @@ namespace System return n - days[m - 1] + 1; } + // Exactly the same as GetDatePart(int part), except computing all of + // year/month/day rather than just one of them. Used when all three + // are needed rather than redoing the computations for each. + internal void GetDatePart(out int year, out int month, out int day) + { + Int64 ticks = InternalTicks; + // n = number of days since 1/1/0001 + int n = (int)(ticks / TicksPerDay); + // y400 = number of whole 400-year periods since 1/1/0001 + int y400 = n / DaysPer400Years; + // n = day number within 400-year period + n -= y400 * DaysPer400Years; + // y100 = number of whole 100-year periods within 400-year period + int y100 = n / DaysPer100Years; + // Last 100-year period has an extra day, so decrement result if 4 + if (y100 == 4) y100 = 3; + // n = day number within 100-year period + n -= y100 * DaysPer100Years; + // y4 = number of whole 4-year periods within 100-year period + int y4 = n / DaysPer4Years; + // n = day number within 4-year period + n -= y4 * DaysPer4Years; + // y1 = number of whole years within 4-year period + int y1 = n / DaysPerYear; + // Last year has an extra day, so decrement result if 4 + if (y1 == 4) y1 = 3; + // compute year + year = y400 * 400 + y100 * 100 + y4 * 4 + y1 + 1; + // n = day number within year + n -= y1 * DaysPerYear; + // dayOfYear = n + 1; + // Leap year calculation looks different from IsLeapYear since y1, y4, + // and y100 are relative to year 1, not year 0 + bool leapYear = y1 == 3 && (y4 != 24 || y100 == 3); + int[] days = leapYear ? s_daysToMonth366 : s_daysToMonth365; + // All months have less than 32 days, so n >> 5 is a good conservative + // estimate for the month + int m = (n >> 5) + 1; + // m = 1-based month number + while (n >= days[m]) m++; + // compute month and day + month = m; + day = n - days[m - 1] + 1; + } + // Returns the day-of-month part of this DateTime. The returned // value is an integer between 1 and 31. // diff --git a/src/coreclr/src/mscorlib/shared/System/Globalization/DateTimeFormat.cs b/src/coreclr/src/mscorlib/shared/System/Globalization/DateTimeFormat.cs index 840409f..d6afc1b 100644 --- a/src/coreclr/src/mscorlib/shared/System/Globalization/DateTimeFormat.cs +++ b/src/coreclr/src/mscorlib/shared/System/Globalization/DateTimeFormat.cs @@ -1059,14 +1059,15 @@ namespace System dateTime = dateTime - offset; } + dateTime.GetDatePart(out int year, out int month, out int day); result.Append(InvariantAbbreviatedDayNames[(int)dateTime.DayOfWeek]); result.Append(','); result.Append(' '); - AppendNumber(result, dateTime.Day, 2); + AppendNumber(result, day, 2); result.Append(' '); - result.Append(InvariantAbbreviatedMonthNames[dateTime.Month - 1]); + result.Append(InvariantAbbreviatedMonthNames[month - 1]); result.Append(' '); - AppendNumber(result, dateTime.Year, 4); + AppendNumber(result, year, 4); result.Append(' '); AppendHHmmssTimeOfDay(result, dateTime); result.Append(' '); @@ -1081,11 +1082,12 @@ namespace System const int roundTripFormatLength = 28; StringBuilder result = StringBuilderCache.Acquire(roundTripFormatLength); - AppendNumber(result, dateTime.Year, 4); + dateTime.GetDatePart(out int year, out int month, out int day); + AppendNumber(result, year, 4); result.Append('-'); - AppendNumber(result, dateTime.Month, 2); + AppendNumber(result, month, 2); result.Append('-'); - AppendNumber(result, dateTime.Day, 2); + AppendNumber(result, day, 2); result.Append('T'); AppendHHmmssTimeOfDay(result, dateTime); result.Append('.'); diff --git a/src/coreclr/src/mscorlib/shared/System/Globalization/HebrewCalendar.cs b/src/coreclr/src/mscorlib/shared/System/Globalization/HebrewCalendar.cs index 6ba4f08..92f4cab 100644 --- a/src/coreclr/src/mscorlib/shared/System/Globalization/HebrewCalendar.cs +++ b/src/coreclr/src/mscorlib/shared/System/Globalization/HebrewCalendar.cs @@ -536,9 +536,7 @@ namespace System.Globalization // // Save the Gregorian date values. // - gregorianYear = time.Year; - gregorianMonth = time.Month; - gregorianDay = time.Day; + time.GetDatePart(out gregorianYear, out gregorianMonth, out gregorianDay); __DateBuffer lunarDate = new __DateBuffer(); // lunar month and day for Jan 1 diff --git a/src/coreclr/src/mscorlib/shared/System/Globalization/UmAlQuraCalendar.cs b/src/coreclr/src/mscorlib/shared/System/Globalization/UmAlQuraCalendar.cs index c03ac23..99b99d6 100644 --- a/src/coreclr/src/mscorlib/shared/System/Globalization/UmAlQuraCalendar.cs +++ b/src/coreclr/src/mscorlib/shared/System/Globalization/UmAlQuraCalendar.cs @@ -331,9 +331,7 @@ namespace System.Globalization } dt = dt.AddDays(nDays); - yg = dt.Year; - mg = dt.Month; - dg = dt.Day; + dt.GetDatePart(out yg, out mg, out dg); } /*=================================GetAbsoluteDateUmAlQura========================== diff --git a/src/coreclr/src/mscorlib/src/System/Globalization/GregorianCalendar.cs b/src/coreclr/src/mscorlib/src/System/Globalization/GregorianCalendar.cs index 9115b12..707a9d0 100644 --- a/src/coreclr/src/mscorlib/src/System/Globalization/GregorianCalendar.cs +++ b/src/coreclr/src/mscorlib/src/System/Globalization/GregorianCalendar.cs @@ -21,12 +21,6 @@ namespace System.Globalization public const int ADEra = 1; - - internal const int DatePartYear = 0; - internal const int DatePartDayOfYear = 1; - internal const int DatePartMonth = 2; - internal const int DatePartDay = 3; - // // This is the max Gregorian year can be represented by DateTime class. The limitation // is derived from DateTime class. @@ -161,57 +155,6 @@ namespace System.Globalization } - // Returns a given date part of this DateTime. This method is used - // to compute the year, day-of-year, month, or day part. - internal virtual int GetDatePart(long ticks, int part) - { - // n = number of days since 1/1/0001 - int n = (int)(ticks / TicksPerDay); - // y400 = number of whole 400-year periods since 1/1/0001 - int y400 = n / DaysPer400Years; - // n = day number within 400-year period - n -= y400 * DaysPer400Years; - // y100 = number of whole 100-year periods within 400-year period - int y100 = n / DaysPer100Years; - // Last 100-year period has an extra day, so decrement result if 4 - if (y100 == 4) y100 = 3; - // n = day number within 100-year period - n -= y100 * DaysPer100Years; - // y4 = number of whole 4-year periods within 100-year period - int y4 = n / DaysPer4Years; - // n = day number within 4-year period - n -= y4 * DaysPer4Years; - // y1 = number of whole years within 4-year period - int y1 = n / DaysPerYear; - // Last year has an extra day, so decrement result if 4 - if (y1 == 4) y1 = 3; - // If year was requested, compute and return it - if (part == DatePartYear) - { - return (y400 * 400 + y100 * 100 + y4 * 4 + y1 + 1); - } - // n = day number within year - n -= y1 * DaysPerYear; - // If day-of-year was requested, return it - if (part == DatePartDayOfYear) - { - return (n + 1); - } - // Leap year calculation looks different from IsLeapYear since y1, y4, - // and y100 are relative to year 1, not year 0 - bool leapYear = (y1 == 3 && (y4 != 24 || y100 == 3)); - int[] days = leapYear ? DaysToMonth366 : DaysToMonth365; - // All months have less than 32 days, so n >> 5 is a good conservative - // estimate for the month - int m = (n >> 5) + 1; - // m = 1-based month number - while (n >= days[m]) m++; - // If month was requested, return it - if (part == DatePartMonth) return (m); - // Return 1-based day-of-month - return (n - days[m - 1] + 1); - } - /*=================================GetAbsoluteDate========================== **Action: Gets the absolute date for the given Gregorian date. The absolute date means ** the number of days from January 1st, 1 A.D. @@ -283,9 +226,7 @@ namespace System.Globalization 120000)); } Contract.EndContractBlock(); - int y = GetDatePart(time.Ticks, DatePartYear); - int m = GetDatePart(time.Ticks, DatePartMonth); - int d = GetDatePart(time.Ticks, DatePartDay); + time.GetDatePart(out int y, out int m, out int d); int i = m - 1 + months; if (i >= 0) { @@ -331,7 +272,7 @@ namespace System.Globalization public override int GetDayOfMonth(DateTime time) { - return (GetDatePart(time.Ticks, DatePartDay)); + return time.Day; } // Returns the day-of-week part of the specified DateTime. The returned value @@ -351,7 +292,7 @@ namespace System.Globalization public override int GetDayOfYear(DateTime time) { - return (GetDatePart(time.Ticks, DatePartDayOfYear)); + return time.DayOfYear; } // Returns the number of days in the month given by the year and @@ -422,7 +363,7 @@ namespace System.Globalization public override int GetMonth(DateTime time) { - return (GetDatePart(time.Ticks, DatePartMonth)); + return time.Month; } // Returns the number of months in the specified year and era. @@ -452,7 +393,7 @@ namespace System.Globalization public override int GetYear(DateTime time) { - return (GetDatePart(time.Ticks, DatePartYear)); + return time.Year; } // Checks whether a given day in the specified era is a leap day. This method returns true if diff --git a/src/coreclr/src/mscorlib/src/System/TimeZoneInfo.TransitionTime.cs b/src/coreclr/src/mscorlib/src/System/TimeZoneInfo.TransitionTime.cs index 0fe3dab..90304c9 100644 --- a/src/coreclr/src/mscorlib/src/System/TimeZoneInfo.TransitionTime.cs +++ b/src/coreclr/src/mscorlib/src/System/TimeZoneInfo.TransitionTime.cs @@ -103,7 +103,8 @@ namespace System } Contract.EndContractBlock(); - if (timeOfDay.Year != 1 || timeOfDay.Month != 1 || timeOfDay.Day != 1 || (timeOfDay.Ticks % TimeSpan.TicksPerMillisecond != 0)) + timeOfDay.GetDatePart(out int timeOfDayYear, out int timeOfDayMonth, out int timeOfDayDay); + if (timeOfDayYear != 1 || timeOfDayMonth != 1 || timeOfDayDay != 1 || (timeOfDay.Ticks % TimeSpan.TicksPerMillisecond != 0)) { throw new ArgumentException(SR.Argument_DateTimeHasTicks, nameof(timeOfDay)); } -- 2.7.4