1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 *******************************************************************************
5 * Copyright (C) 2003-2009,2012,2016 International Business Machines Corporation and
6 * others. All Rights Reserved.
7 *******************************************************************************
8 *
9 * File JAPANCAL.CPP
10 *
11 * Modification History:
12 * 05/16/2003 srl copied from buddhcal.cpp
13 *
14 */
15
16 #include "unicode/utypes.h"
17
18 #if !UCONFIG_NO_FORMATTING
19 #if U_PLATFORM_HAS_WINUWP_API == 0
20 #include <stdlib.h> // getenv() is not available in UWP env
21 #endif
22 #include "cmemory.h"
23 #include "erarules.h"
24 #include "japancal.h"
25 #include "unicode/gregocal.h"
26 #include "umutex.h"
27 #include "uassert.h"
28 #include "ucln_in.h"
29 #include "cstring.h"
30
31 static icu::EraRules * gJapaneseEraRules = nullptr;
32 static icu::UInitOnce gJapaneseEraRulesInitOnce = U_INITONCE_INITIALIZER;
33 static int32_t gCurrentEra = 0;
34
35 U_CDECL_BEGIN
japanese_calendar_cleanup(void)36 static UBool japanese_calendar_cleanup(void) {
37 if (gJapaneseEraRules) {
38 delete gJapaneseEraRules;
39 gJapaneseEraRules = nullptr;
40 }
41 gCurrentEra = 0;
42 gJapaneseEraRulesInitOnce.reset();
43 return TRUE;
44 }
45 U_CDECL_END
46
47 U_NAMESPACE_BEGIN
48
49 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(JapaneseCalendar)
50
51 static const int32_t kGregorianEpoch = 1970; // used as the default value of EXTENDED_YEAR
52 static const char* TENTATIVE_ERA_VAR_NAME = "ICU_ENABLE_TENTATIVE_ERA";
53
54 // Initialize global Japanese era data
initializeEras(UErrorCode & status)55 static void U_CALLCONV initializeEras(UErrorCode &status) {
56 // Although start date of next Japanese era is planned ahead, a name of
57 // new era might not be available. This implementation allows tester to
58 // check a new era without era names by settings below (in priority order).
59 // By default, such tentative era is disabled.
60
61 // 1. Environment variable ICU_ENABLE_TENTATIVE_ERA=true or false
62
63 UBool includeTentativeEra = FALSE;
64
65 #if U_PLATFORM_HAS_WINUWP_API == 1
66 // UWP doesn't allow access to getenv(), but we can call GetEnvironmentVariableW to do the same thing.
67 UChar varName[26] = {};
68 u_charsToUChars(TENTATIVE_ERA_VAR_NAME, varName, static_cast<int32_t>(uprv_strlen(TENTATIVE_ERA_VAR_NAME)));
69 WCHAR varValue[5] = {};
70 DWORD ret = GetEnvironmentVariableW(reinterpret_cast<WCHAR*>(varName), varValue, UPRV_LENGTHOF(varValue));
71 if ((ret == 4) && (_wcsicmp(varValue, L"true") == 0)) {
72 includeTentativeEra = TRUE;
73 }
74 #else
75 char *envVarVal = getenv(TENTATIVE_ERA_VAR_NAME);
76 if (envVarVal != NULL && uprv_stricmp(envVarVal, "true") == 0) {
77 includeTentativeEra = TRUE;
78 }
79 #endif
80 gJapaneseEraRules = EraRules::createInstance("japanese", includeTentativeEra, status);
81 if (U_FAILURE(status)) {
82 return;
83 }
84 gCurrentEra = gJapaneseEraRules->getCurrentEraIndex();
85 }
86
init(UErrorCode & status)87 static void init(UErrorCode &status) {
88 umtx_initOnce(gJapaneseEraRulesInitOnce, &initializeEras, status);
89 ucln_i18n_registerCleanup(UCLN_I18N_JAPANESE_CALENDAR, japanese_calendar_cleanup);
90 }
91
92 /* Some platforms don't like to export constants, like old Palm OS and some z/OS configurations. */
getCurrentEra()93 uint32_t JapaneseCalendar::getCurrentEra() {
94 return gCurrentEra;
95 }
96
JapaneseCalendar(const Locale & aLocale,UErrorCode & success)97 JapaneseCalendar::JapaneseCalendar(const Locale& aLocale, UErrorCode& success)
98 : GregorianCalendar(aLocale, success)
99 {
100 init(success);
101 setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly.
102 }
103
~JapaneseCalendar()104 JapaneseCalendar::~JapaneseCalendar()
105 {
106 }
107
JapaneseCalendar(const JapaneseCalendar & source)108 JapaneseCalendar::JapaneseCalendar(const JapaneseCalendar& source)
109 : GregorianCalendar(source)
110 {
111 UErrorCode status = U_ZERO_ERROR;
112 init(status);
113 U_ASSERT(U_SUCCESS(status));
114 }
115
operator =(const JapaneseCalendar & right)116 JapaneseCalendar& JapaneseCalendar::operator= ( const JapaneseCalendar& right)
117 {
118 GregorianCalendar::operator=(right);
119 return *this;
120 }
121
clone(void) const122 Calendar* JapaneseCalendar::clone(void) const
123 {
124 return new JapaneseCalendar(*this);
125 }
126
getType() const127 const char *JapaneseCalendar::getType() const
128 {
129 return "japanese";
130 }
131
getDefaultMonthInYear(int32_t eyear)132 int32_t JapaneseCalendar::getDefaultMonthInYear(int32_t eyear)
133 {
134 int32_t era = internalGetEra();
135 // TODO do we assume we can trust 'era'? What if it is denormalized?
136
137 int32_t month = 0;
138
139 // Find out if we are at the edge of an era
140 int32_t eraStart[3] = { 0,0,0 };
141 UErrorCode status = U_ZERO_ERROR;
142 gJapaneseEraRules->getStartDate(era, eraStart, status);
143 U_ASSERT(U_SUCCESS(status));
144 if(eyear == eraStart[0]) {
145 // Yes, we're in the first year of this era.
146 return eraStart[1] // month
147 -1; // return 0-based month
148 }
149
150 return month;
151 }
152
getDefaultDayInMonth(int32_t eyear,int32_t month)153 int32_t JapaneseCalendar::getDefaultDayInMonth(int32_t eyear, int32_t month)
154 {
155 int32_t era = internalGetEra();
156 int32_t day = 1;
157
158 int32_t eraStart[3] = { 0,0,0 };
159 UErrorCode status = U_ZERO_ERROR;
160 gJapaneseEraRules->getStartDate(era, eraStart, status);
161 U_ASSERT(U_SUCCESS(status));
162 if(eyear == eraStart[0]) {
163 if(month == eraStart[1] - 1) {
164 return eraStart[2];
165 }
166 }
167
168 return day;
169 }
170
171
internalGetEra() const172 int32_t JapaneseCalendar::internalGetEra() const
173 {
174 return internalGet(UCAL_ERA, gCurrentEra);
175 }
176
handleGetExtendedYear()177 int32_t JapaneseCalendar::handleGetExtendedYear()
178 {
179 // EXTENDED_YEAR in JapaneseCalendar is a Gregorian year
180 // The default value of EXTENDED_YEAR is 1970 (Showa 45)
181 int32_t year;
182
183 if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR &&
184 newerField(UCAL_EXTENDED_YEAR, UCAL_ERA) == UCAL_EXTENDED_YEAR) {
185 year = internalGet(UCAL_EXTENDED_YEAR, kGregorianEpoch);
186 } else {
187 UErrorCode status = U_ZERO_ERROR;
188 int32_t eraStartYear = gJapaneseEraRules->getStartYear(internalGet(UCAL_ERA, gCurrentEra), status);
189 U_ASSERT(U_SUCCESS(status));
190
191 // extended year is a gregorian year, where 1 = 1AD, 0 = 1BC, -1 = 2BC, etc
192 year = internalGet(UCAL_YEAR, 1) // pin to minimum of year 1 (first year)
193 + eraStartYear // add gregorian starting year
194 - 1; // Subtract one because year starts at 1
195 }
196 return year;
197 }
198
199
handleComputeFields(int32_t julianDay,UErrorCode & status)200 void JapaneseCalendar::handleComputeFields(int32_t julianDay, UErrorCode& status)
201 {
202 //Calendar::timeToFields(theTime, quick, status);
203 GregorianCalendar::handleComputeFields(julianDay, status);
204 int32_t year = internalGet(UCAL_EXTENDED_YEAR); // Gregorian year
205 int32_t eraIdx = gJapaneseEraRules->getEraIndex(year, internalGet(UCAL_MONTH) + 1, internalGet(UCAL_DAY_OF_MONTH), status);
206
207 internalSet(UCAL_ERA, eraIdx);
208 internalSet(UCAL_YEAR, year - gJapaneseEraRules->getStartYear(eraIdx, status) + 1);
209 }
210
211 /*
212 Disable pivoting
213 */
haveDefaultCentury() const214 UBool JapaneseCalendar::haveDefaultCentury() const
215 {
216 return FALSE;
217 }
218
defaultCenturyStart() const219 UDate JapaneseCalendar::defaultCenturyStart() const
220 {
221 return 0;// WRONG
222 }
223
defaultCenturyStartYear() const224 int32_t JapaneseCalendar::defaultCenturyStartYear() const
225 {
226 return 0;
227 }
228
handleGetLimit(UCalendarDateFields field,ELimitType limitType) const229 int32_t JapaneseCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const
230 {
231 switch(field) {
232 case UCAL_ERA:
233 if (limitType == UCAL_LIMIT_MINIMUM || limitType == UCAL_LIMIT_GREATEST_MINIMUM) {
234 return 0;
235 }
236 return gCurrentEra;
237 case UCAL_YEAR:
238 {
239 switch (limitType) {
240 case UCAL_LIMIT_MINIMUM:
241 case UCAL_LIMIT_GREATEST_MINIMUM:
242 return 1;
243 case UCAL_LIMIT_LEAST_MAXIMUM:
244 return 1;
245 case UCAL_LIMIT_COUNT: //added to avoid warning
246 case UCAL_LIMIT_MAXIMUM:
247 {
248 UErrorCode status = U_ZERO_ERROR;
249 int32_t eraStartYear = gJapaneseEraRules->getStartYear(gCurrentEra, status);
250 U_ASSERT(U_SUCCESS(status));
251 return GregorianCalendar::handleGetLimit(UCAL_YEAR, UCAL_LIMIT_MAXIMUM) - eraStartYear;
252 }
253 default:
254 return 1; // Error condition, invalid limitType
255 }
256 }
257 default:
258 return GregorianCalendar::handleGetLimit(field,limitType);
259 }
260 }
261
getActualMaximum(UCalendarDateFields field,UErrorCode & status) const262 int32_t JapaneseCalendar::getActualMaximum(UCalendarDateFields field, UErrorCode& status) const {
263 if (field == UCAL_YEAR) {
264 int32_t era = get(UCAL_ERA, status);
265 if (U_FAILURE(status)) {
266 return 0; // error case... any value
267 }
268 if (era == gCurrentEra) {
269 // TODO: Investigate what value should be used here - revisit after 4.0.
270 return handleGetLimit(UCAL_YEAR, UCAL_LIMIT_MAXIMUM);
271 } else {
272 int32_t nextEraStart[3] = { 0,0,0 };
273 gJapaneseEraRules->getStartDate(era + 1, nextEraStart, status);
274 int32_t nextEraYear = nextEraStart[0];
275 int32_t nextEraMonth = nextEraStart[1]; // 1-base
276 int32_t nextEraDate = nextEraStart[2];
277
278 int32_t eraStartYear = gJapaneseEraRules->getStartYear(era, status);
279 int32_t maxYear = nextEraYear - eraStartYear + 1; // 1-base
280 if (nextEraMonth == 1 && nextEraDate == 1) {
281 // Subtract 1, because the next era starts at Jan 1
282 maxYear--;
283 }
284 return maxYear;
285 }
286 }
287 return GregorianCalendar::getActualMaximum(field, status);
288 }
289
290 U_NAMESPACE_END
291
292 #endif
293