@@ -47,8 +47,8 @@ internal EraInfo(int era, int startYear, int startMonth, int startDay, int yearO
47
47
}
48
48
49
49
// This calendar recognizes two era values:
50
- // 0 CurrentEra (AD)
51
- // 1 BeforeCurrentEra (BC)
50
+ // 0 CurrentEra (AD)
51
+ // 1 BeforeCurrentEra (BC)
52
52
internal class GregorianCalendarHelper
53
53
{
54
54
// 1 tick = 100ns = 10E-7 second
@@ -87,7 +87,7 @@ internal class GregorianCalendarHelper
87
87
//
88
88
// This is the max Gregorian year can be represented by DateTime class. The limitation
89
89
// is derived from DateTime class.
90
- //
90
+ //
91
91
internal int MaxYear
92
92
{
93
93
get
@@ -123,22 +123,19 @@ internal GregorianCalendarHelper(Calendar cal, EraInfo[] eraInfo)
123
123
m_minYear = m_EraInfo [ 0 ] . minEraYear ; ;
124
124
}
125
125
126
- /*=================================GetGregorianYear==========================
127
- **Action: Get the Gregorian year value for the specified year in an era.
128
- **Returns: The Gregorian year value.
129
- **Arguments:
130
- ** year the year value in Japanese calendar
131
- ** era the Japanese emperor era value.
132
- **Exceptions:
133
- ** ArgumentOutOfRangeException if year value is invalid or era value is invalid.
134
- ============================================================================*/
135
-
136
- internal int GetGregorianYear ( int year , int era )
126
+ // EraInfo.yearOffset: The offset to Gregorian year when the era starts. Gregorian Year = Era Year + yearOffset
127
+ // Era Year = Gregorian Year - yearOffset
128
+ // EraInfo.minEraYear: Min year value in this era. Generally, this value is 1, but this may be affected by the DateTime.MinValue;
129
+ // EraInfo.maxEraYear: Max year value in this era. (== the year length of the era + 1)
130
+ private int GetYearOffset ( int year , int era , bool throwOnError )
137
131
{
138
132
if ( year < 0 )
139
133
{
140
- throw new ArgumentOutOfRangeException ( nameof ( year ) ,
141
- SR . ArgumentOutOfRange_NeedNonNegNum ) ;
134
+ if ( throwOnError )
135
+ {
136
+ throw new ArgumentOutOfRangeException ( nameof ( year ) , SR . ArgumentOutOfRange_NeedNonNegNum ) ;
137
+ }
138
+ return - 1 ;
142
139
}
143
140
144
141
if ( era == Calendar . CurrentEra )
@@ -150,7 +147,38 @@ internal int GetGregorianYear(int year, int era)
150
147
{
151
148
if ( era == m_EraInfo [ i ] . era )
152
149
{
153
- if ( year < m_EraInfo [ i ] . minEraYear || year > m_EraInfo [ i ] . maxEraYear )
150
+ if ( year >= m_EraInfo [ i ] . minEraYear )
151
+ {
152
+ if ( year <= m_EraInfo [ i ] . maxEraYear )
153
+ {
154
+ return m_EraInfo [ i ] . yearOffset ;
155
+ }
156
+ else if ( ! AppContextSwitches . EnforceJapaneseEraYearRanges )
157
+ {
158
+ // If we got the year number exceeding the era max year number, this still possible be valid as the date can be created before
159
+ // introducing new eras after the era we are checking. we'll loop on the eras after the era we have and ensure the year
160
+ // can exist in one of these eras. otherwise, we'll throw.
161
+ // Note, we always return the offset associated with the requested era.
162
+ //
163
+ // Here is some example:
164
+ // 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
165
+ // 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
166
+ // the subsequent eras (e.g era 5 and up)
167
+
168
+ int remainingYears = year - m_EraInfo [ i ] . maxEraYear ;
169
+
170
+ for ( int j = i - 1 ; j >= 0 ; j -- )
171
+ {
172
+ if ( remainingYears <= m_EraInfo [ j ] . maxEraYear )
173
+ {
174
+ return m_EraInfo [ i ] . yearOffset ;
175
+ }
176
+ remainingYears -= m_EraInfo [ j ] . maxEraYear ;
177
+ }
178
+ }
179
+ }
180
+
181
+ if ( throwOnError )
154
182
{
155
183
throw new ArgumentOutOfRangeException (
156
184
nameof ( year ) ,
@@ -160,38 +188,37 @@ internal int GetGregorianYear(int year, int era)
160
188
m_EraInfo [ i ] . minEraYear ,
161
189
m_EraInfo [ i ] . maxEraYear ) ) ;
162
190
}
163
- return ( m_EraInfo [ i ] . yearOffset + year ) ;
191
+
192
+ break ; // no need to iterate more on eras.
164
193
}
165
194
}
166
- throw new ArgumentOutOfRangeException ( nameof ( era ) , SR . ArgumentOutOfRange_InvalidEraValue ) ;
167
- }
168
195
169
- internal bool IsValidYear ( int year , int era )
170
- {
171
- if ( year < 0 )
196
+ if ( throwOnError )
172
197
{
173
- return false ;
198
+ throw new ArgumentOutOfRangeException ( nameof ( era ) , SR . ArgumentOutOfRange_InvalidEraValue ) ;
174
199
}
175
200
176
- if ( era == Calendar . CurrentEra )
177
- {
178
- era = m_Cal . CurrentEraValue ;
179
- }
201
+ return - 1 ;
202
+ }
180
203
181
- for ( int i = 0 ; i < m_EraInfo . Length ; i ++ )
182
- {
183
- if ( era == m_EraInfo [ i ] . era )
184
- {
185
- if ( year < m_EraInfo [ i ] . minEraYear || year > m_EraInfo [ i ] . maxEraYear )
186
- {
187
- return false ;
188
- }
189
- return true ;
190
- }
191
- }
192
- return false ;
204
+ /*=================================GetGregorianYear==========================
205
+ **Action: Get the Gregorian year value for the specified year in an era.
206
+ **Returns: The Gregorian year value.
207
+ **Arguments:
208
+ ** year the year value in Japanese calendar
209
+ ** era the Japanese emperor era value.
210
+ **Exceptions:
211
+ ** ArgumentOutOfRangeException if year value is invalid or era value is invalid.
212
+ ============================================================================*/
213
+ internal int GetGregorianYear ( int year , int era )
214
+ {
215
+ return GetYearOffset ( year , era , throwOnError : true ) + year ;
193
216
}
194
217
218
+ internal bool IsValidYear ( int year , int era )
219
+ {
220
+ return GetYearOffset ( year , era , throwOnError : false ) >= 0 ;
221
+ }
195
222
196
223
// Returns a given date part of this DateTime. This method is used
197
224
// to compute the year, day-of-year, month, or day part.
0 commit comments