Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Support new Japanese calendar eras #20727

Merged
merged 1 commit into from
Nov 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 67 additions & 40 deletions src/mscorlib/shared/System/Globalization/GregorianCalendarHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ internal EraInfo(int era, int startYear, int startMonth, int startDay, int yearO
}

// This calendar recognizes two era values:
// 0 CurrentEra (AD)
// 1 BeforeCurrentEra (BC)
// 0 CurrentEra (AD)
// 1 BeforeCurrentEra (BC)
internal class GregorianCalendarHelper
{
// 1 tick = 100ns = 10E-7 second
Expand Down Expand Up @@ -87,7 +87,7 @@ internal class GregorianCalendarHelper
//
// This is the max Gregorian year can be represented by DateTime class. The limitation
// is derived from DateTime class.
//
//
internal int MaxYear
{
get
Expand Down Expand Up @@ -123,22 +123,19 @@ internal GregorianCalendarHelper(Calendar cal, EraInfo[] eraInfo)
m_minYear = m_EraInfo[0].minEraYear; ;
}

/*=================================GetGregorianYear==========================
**Action: Get the Gregorian year value for the specified year in an era.
**Returns: The Gregorian year value.
**Arguments:
** year the year value in Japanese calendar
** era the Japanese emperor era value.
**Exceptions:
** ArgumentOutOfRangeException if year value is invalid or era value is invalid.
============================================================================*/

internal int GetGregorianYear(int year, int era)
// EraInfo.yearOffset: The offset to Gregorian year when the era starts. Gregorian Year = Era Year + yearOffset
// Era Year = Gregorian Year - yearOffset
// EraInfo.minEraYear: Min year value in this era. Generally, this value is 1, but this may be affected by the DateTime.MinValue;
// EraInfo.maxEraYear: Max year value in this era. (== the year length of the era + 1)
private int GetYearOffset(int year, int era, bool throwOnError)
{
if (year < 0)
{
throw new ArgumentOutOfRangeException(nameof(year),
SR.ArgumentOutOfRange_NeedNonNegNum);
if (throwOnError)
{
throw new ArgumentOutOfRangeException(nameof(year), SR.ArgumentOutOfRange_NeedNonNegNum);
}
return -1;
}

if (era == Calendar.CurrentEra)
Expand All @@ -150,7 +147,38 @@ internal int GetGregorianYear(int year, int era)
{
if (era == m_EraInfo[i].era)
{
if (year < m_EraInfo[i].minEraYear || year > m_EraInfo[i].maxEraYear)
if (year >= m_EraInfo[i].minEraYear)
{
if (year <= m_EraInfo[i].maxEraYear)
{
return m_EraInfo[i].yearOffset;
}
else if (!AppContextSwitches.EnforceJapaneseEraYearRanges)
{
// If we got the year number exceeding the era max year number, this still possible be valid as the date can be created before
// introducing new eras after the era we are checking. we'll loop on the eras after the era we have and ensure the year
// can exist in one of these eras. otherwise, we'll throw.
// Note, we always return the offset associated with the requested era.
//
// Here is some example:
// if we are getting the era number 4 (Heisei) and getting the year number 32. if the era 4 has year range from 1 to 31
// then year 32 exceeded the range of era 4 and we'll try to find out if the years difference (32 - 31 = 1) would lay in
// the subsequent eras (e.g era 5 and up)

int remainingYears = year - m_EraInfo[i].maxEraYear;

for (int j = i - 1; j >= 0; j--)
{
if (remainingYears <= m_EraInfo[j].maxEraYear)
{
return m_EraInfo[i].yearOffset;
}
remainingYears -= m_EraInfo[j].maxEraYear;
}
}
}

if (throwOnError)
{
throw new ArgumentOutOfRangeException(
nameof(year),
Expand All @@ -160,38 +188,37 @@ internal int GetGregorianYear(int year, int era)
m_EraInfo[i].minEraYear,
m_EraInfo[i].maxEraYear));
}
return (m_EraInfo[i].yearOffset + year);

break; // no need to iterate more on eras.
}
}
throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue);
}

internal bool IsValidYear(int year, int era)
{
if (year < 0)
if (throwOnError)
{
return false;
throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue);
}

if (era == Calendar.CurrentEra)
{
era = m_Cal.CurrentEraValue;
}
return -1;
}

for (int i = 0; i < m_EraInfo.Length; i++)
{
if (era == m_EraInfo[i].era)
{
if (year < m_EraInfo[i].minEraYear || year > m_EraInfo[i].maxEraYear)
{
return false;
}
return true;
}
}
return false;
/*=================================GetGregorianYear==========================
**Action: Get the Gregorian year value for the specified year in an era.
**Returns: The Gregorian year value.
**Arguments:
** year the year value in Japanese calendar
** era the Japanese emperor era value.
**Exceptions:
** ArgumentOutOfRangeException if year value is invalid or era value is invalid.
============================================================================*/
internal int GetGregorianYear(int year, int era)
{
return GetYearOffset(year, era, throwOnError: true) + year;
}

internal bool IsValidYear(int year, int era)
{
return GetYearOffset(year, era, throwOnError: false) >= 0;
}

// Returns a given date part of this DateTime. This method is used
// to compute the year, day-of-year, month, or day part.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace System
internal static partial class AppContextDefaultValues
{
internal static readonly string SwitchNoAsyncCurrentCulture = "Switch.System.Globalization.NoAsyncCurrentCulture";
internal static readonly string SwitchEnforceJapaneseEraYearRanges = "Switch.System.Globalization.EnforceJapaneseEraYearRanges";
internal static readonly string SwitchPreserveEventListnerObjectIdentity = "Switch.System.Diagnostics.EventSource.PreserveEventListnerObjectIdentity";

// This is a partial method. Platforms can provide an implementation of it that will set override values
Expand Down
10 changes: 10 additions & 0 deletions src/mscorlib/src/System/AppContext/AppContextSwitches.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ public static bool NoAsyncCurrentCulture
}
}

private static int _enforceJapaneseEraYearRanges;
public static bool EnforceJapaneseEraYearRanges
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return GetCachedSwitchValue(AppContextDefaultValues.SwitchEnforceJapaneseEraYearRanges, ref _enforceJapaneseEraYearRanges);
}
}

private static int _preserveEventListnerObjectIdentity;
public static bool PreserveEventListnerObjectIdentity
{
Expand Down