1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 *******************************************************************************
5 * Copyright (C) 2003-2009,2012,2016 International Business Machines Corporation and
6 * others. All Rights Reserved.
7 *******************************************************************************
11 * Modification History:
12 * 05/16/2003 srl copied from buddhcal.cpp
16 #include "unicode/utypes.h"
18 #if !UCONFIG_NO_FORMATTING
19 #if U_PLATFORM_HAS_WINUWP_API == 0
20 #include <stdlib.h> // getenv() is not available in UWP env
22 #ifndef WIN32_LEAN_AND_MEAN
23 # define WIN32_LEAN_AND_MEAN
35 #include "unicode/gregocal.h"
41 static icu::EraRules
* gJapaneseEraRules
= nullptr;
42 static icu::UInitOnce gJapaneseEraRulesInitOnce
= U_INITONCE_INITIALIZER
;
43 static int32_t gCurrentEra
= 0;
46 static UBool
japanese_calendar_cleanup(void) {
47 if (gJapaneseEraRules
) {
48 delete gJapaneseEraRules
;
49 gJapaneseEraRules
= nullptr;
52 gJapaneseEraRulesInitOnce
.reset();
59 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(JapaneseCalendar
)
61 static const int32_t kGregorianEpoch
= 1970; // used as the default value of EXTENDED_YEAR
62 static const char* TENTATIVE_ERA_VAR_NAME
= "ICU_ENABLE_TENTATIVE_ERA";
65 // Export the following for use by test code.
66 UBool
JapaneseCalendar::enableTentativeEra() {
67 // Although start date of next Japanese era is planned ahead, a name of
68 // new era might not be available. This implementation allows tester to
69 // check a new era without era names by settings below (in priority order).
70 // By default, such tentative era is disabled.
72 // 1. Environment variable ICU_ENABLE_TENTATIVE_ERA=true or false
74 UBool includeTentativeEra
= FALSE
;
76 #if U_PLATFORM_HAS_WINUWP_API == 1
77 // UWP doesn't allow access to getenv(), but we can call GetEnvironmentVariableW to do the same thing.
78 UChar varName
[26] = {};
79 u_charsToUChars(TENTATIVE_ERA_VAR_NAME
, varName
, static_cast<int32_t>(uprv_strlen(TENTATIVE_ERA_VAR_NAME
)));
80 WCHAR varValue
[5] = {};
81 DWORD ret
= GetEnvironmentVariableW(reinterpret_cast<WCHAR
*>(varName
), varValue
, UPRV_LENGTHOF(varValue
));
82 if ((ret
== 4) && (_wcsicmp(varValue
, L
"true") == 0)) {
83 includeTentativeEra
= TRUE
;
86 char *envVarVal
= getenv(TENTATIVE_ERA_VAR_NAME
);
87 if (envVarVal
!= NULL
&& uprv_stricmp(envVarVal
, "true") == 0) {
88 includeTentativeEra
= TRUE
;
91 return includeTentativeEra
;
95 // Initialize global Japanese era data
96 static void U_CALLCONV
initializeEras(UErrorCode
&status
) {
97 gJapaneseEraRules
= EraRules::createInstance("japanese", JapaneseCalendar::enableTentativeEra(), status
);
98 if (U_FAILURE(status
)) {
101 gCurrentEra
= gJapaneseEraRules
->getCurrentEraIndex();
104 static void init(UErrorCode
&status
) {
105 umtx_initOnce(gJapaneseEraRulesInitOnce
, &initializeEras
, status
);
106 ucln_i18n_registerCleanup(UCLN_I18N_JAPANESE_CALENDAR
, japanese_calendar_cleanup
);
109 /* Some platforms don't like to export constants, like old Palm OS and some z/OS configurations. */
110 uint32_t JapaneseCalendar::getCurrentEra() {
114 JapaneseCalendar::JapaneseCalendar(const Locale
& aLocale
, UErrorCode
& success
)
115 : GregorianCalendar(aLocale
, success
)
118 setTimeInMillis(getNow(), success
); // Call this again now that the vtable is set up properly.
121 JapaneseCalendar::~JapaneseCalendar()
125 JapaneseCalendar::JapaneseCalendar(const JapaneseCalendar
& source
)
126 : GregorianCalendar(source
)
128 UErrorCode status
= U_ZERO_ERROR
;
130 U_ASSERT(U_SUCCESS(status
));
133 JapaneseCalendar
& JapaneseCalendar::operator= ( const JapaneseCalendar
& right
)
135 GregorianCalendar::operator=(right
);
139 Calendar
* JapaneseCalendar::clone(void) const
141 return new JapaneseCalendar(*this);
144 const char *JapaneseCalendar::getType() const
149 int32_t JapaneseCalendar::getDefaultMonthInYear(int32_t eyear
)
151 int32_t era
= internalGetEra();
152 // TODO do we assume we can trust 'era'? What if it is denormalized?
156 // Find out if we are at the edge of an era
157 int32_t eraStart
[3] = { 0,0,0 };
158 UErrorCode status
= U_ZERO_ERROR
;
159 gJapaneseEraRules
->getStartDate(era
, eraStart
, status
);
160 U_ASSERT(U_SUCCESS(status
));
161 if(eyear
== eraStart
[0]) {
162 // Yes, we're in the first year of this era.
163 return eraStart
[1] // month
164 -1; // return 0-based month
170 int32_t JapaneseCalendar::getDefaultDayInMonth(int32_t eyear
, int32_t month
)
172 int32_t era
= internalGetEra();
175 int32_t eraStart
[3] = { 0,0,0 };
176 UErrorCode status
= U_ZERO_ERROR
;
177 gJapaneseEraRules
->getStartDate(era
, eraStart
, status
);
178 U_ASSERT(U_SUCCESS(status
));
179 if(eyear
== eraStart
[0]) {
180 if(month
== eraStart
[1] - 1) {
189 int32_t JapaneseCalendar::internalGetEra() const
191 return internalGet(UCAL_ERA
, gCurrentEra
);
194 int32_t JapaneseCalendar::handleGetExtendedYear()
196 // EXTENDED_YEAR in JapaneseCalendar is a Gregorian year
197 // The default value of EXTENDED_YEAR is 1970 (Showa 45)
200 if (newerField(UCAL_EXTENDED_YEAR
, UCAL_YEAR
) == UCAL_EXTENDED_YEAR
&&
201 newerField(UCAL_EXTENDED_YEAR
, UCAL_ERA
) == UCAL_EXTENDED_YEAR
) {
202 year
= internalGet(UCAL_EXTENDED_YEAR
, kGregorianEpoch
);
204 UErrorCode status
= U_ZERO_ERROR
;
205 int32_t eraStartYear
= gJapaneseEraRules
->getStartYear(internalGet(UCAL_ERA
, gCurrentEra
), status
);
206 U_ASSERT(U_SUCCESS(status
));
208 // extended year is a gregorian year, where 1 = 1AD, 0 = 1BC, -1 = 2BC, etc
209 year
= internalGet(UCAL_YEAR
, 1) // pin to minimum of year 1 (first year)
210 + eraStartYear
// add gregorian starting year
211 - 1; // Subtract one because year starts at 1
217 void JapaneseCalendar::handleComputeFields(int32_t julianDay
, UErrorCode
& status
)
219 //Calendar::timeToFields(theTime, quick, status);
220 GregorianCalendar::handleComputeFields(julianDay
, status
);
221 int32_t year
= internalGet(UCAL_EXTENDED_YEAR
); // Gregorian year
222 int32_t eraIdx
= gJapaneseEraRules
->getEraIndex(year
, internalGet(UCAL_MONTH
) + 1, internalGet(UCAL_DAY_OF_MONTH
), status
);
224 internalSet(UCAL_ERA
, eraIdx
);
225 internalSet(UCAL_YEAR
, year
- gJapaneseEraRules
->getStartYear(eraIdx
, status
) + 1);
231 UBool
JapaneseCalendar::haveDefaultCentury() const
236 UDate
JapaneseCalendar::defaultCenturyStart() const
241 int32_t JapaneseCalendar::defaultCenturyStartYear() const
246 int32_t JapaneseCalendar::handleGetLimit(UCalendarDateFields field
, ELimitType limitType
) const
250 if (limitType
== UCAL_LIMIT_MINIMUM
|| limitType
== UCAL_LIMIT_GREATEST_MINIMUM
) {
253 return gJapaneseEraRules
->getNumberOfEras() - 1; // <rdar://problem/49714633> max known era, not gCurrentEra
257 case UCAL_LIMIT_MINIMUM
:
258 case UCAL_LIMIT_GREATEST_MINIMUM
:
260 case UCAL_LIMIT_LEAST_MAXIMUM
:
262 case UCAL_LIMIT_COUNT
: //added to avoid warning
263 case UCAL_LIMIT_MAXIMUM
:
265 UErrorCode status
= U_ZERO_ERROR
;
266 int32_t eraStartYear
= gJapaneseEraRules
->getStartYear(gCurrentEra
, status
);
267 U_ASSERT(U_SUCCESS(status
));
268 return GregorianCalendar::handleGetLimit(UCAL_YEAR
, UCAL_LIMIT_MAXIMUM
) - eraStartYear
;
271 return 1; // Error condition, invalid limitType
275 return GregorianCalendar::handleGetLimit(field
,limitType
);
279 int32_t JapaneseCalendar::getActualMaximum(UCalendarDateFields field
, UErrorCode
& status
) const {
280 if (field
== UCAL_YEAR
) {
281 int32_t era
= get(UCAL_ERA
, status
);
282 if (U_FAILURE(status
)) {
283 return 0; // error case... any value
285 if (era
== gJapaneseEraRules
->getNumberOfEras() - 1) { // <rdar://problem/49714633>
286 // TODO: Investigate what value should be used here - revisit after 4.0.
287 return handleGetLimit(UCAL_YEAR
, UCAL_LIMIT_MAXIMUM
);
289 int32_t nextEraStart
[3] = { 0,0,0 };
290 gJapaneseEraRules
->getStartDate(era
+ 1, nextEraStart
, status
);
291 int32_t nextEraYear
= nextEraStart
[0];
292 int32_t nextEraMonth
= nextEraStart
[1]; // 1-base
293 int32_t nextEraDate
= nextEraStart
[2];
295 int32_t eraStartYear
= gJapaneseEraRules
->getStartYear(era
, status
);
296 int32_t maxYear
= nextEraYear
- eraStartYear
+ 1; // 1-base
297 if (nextEraMonth
== 1 && nextEraDate
== 1) {
298 // Subtract 1, because the next era starts at Jan 1
304 return GregorianCalendar::getActualMaximum(field
, status
);