// © 2016 and later: Unicode, Inc. and others. // License & terms of use: http://www.unicode.org/copyright.html /* ******************************************************************************* * Copyright (C) 2003-2009,2012,2016 International Business Machines Corporation and * others. All Rights Reserved. ******************************************************************************* * * File JAPANCAL.CPP * * Modification History: * 05/16/2003 srl copied from buddhcal.cpp * */ #include "unicode/utypes.h" #if !UCONFIG_NO_FORMATTING #if U_PLATFORM_HAS_WINUWP_API == 0 #include // getenv() is not available in UWP env #endif #include "cmemory.h" #include "erarules.h" #include "japancal.h" #include "unicode/gregocal.h" #include "umutex.h" #include "uassert.h" #include "ucln_in.h" #include "cstring.h" static icu::EraRules * gJapaneseEraRules = nullptr; static icu::UInitOnce gJapaneseEraRulesInitOnce = U_INITONCE_INITIALIZER; static int32_t gCurrentEra = 0; U_CDECL_BEGIN static UBool japanese_calendar_cleanup(void) { if (gJapaneseEraRules) { delete gJapaneseEraRules; gJapaneseEraRules = nullptr; } gCurrentEra = 0; gJapaneseEraRulesInitOnce.reset(); return TRUE; } U_CDECL_END U_NAMESPACE_BEGIN UOBJECT_DEFINE_RTTI_IMPLEMENTATION(JapaneseCalendar) static const int32_t kGregorianEpoch = 1970; // used as the default value of EXTENDED_YEAR static const char* TENTATIVE_ERA_VAR_NAME = "ICU_ENABLE_TENTATIVE_ERA"; // Initialize global Japanese era data static void U_CALLCONV initializeEras(UErrorCode &status) { // Although start date of next Japanese era is planned ahead, a name of // new era might not be available. This implementation allows tester to // check a new era without era names by settings below (in priority order). // By default, such tentative era is disabled. // 1. Environment variable ICU_ENABLE_TENTATIVE_ERA=true or false UBool includeTentativeEra = FALSE; #if U_PLATFORM_HAS_WINUWP_API == 1 // UWP doesn't allow access to getenv(), but we can call GetEnvironmentVariableW to do the same thing. UChar varName[26] = {}; u_charsToUChars(TENTATIVE_ERA_VAR_NAME, varName, static_cast(uprv_strlen(TENTATIVE_ERA_VAR_NAME))); WCHAR varValue[5] = {}; DWORD ret = GetEnvironmentVariableW(reinterpret_cast(varName), varValue, UPRV_LENGTHOF(varValue)); if ((ret == 4) && (_wcsicmp(varValue, L"true") == 0)) { includeTentativeEra = TRUE; } #else char *envVarVal = getenv(TENTATIVE_ERA_VAR_NAME); if (envVarVal != NULL && uprv_stricmp(envVarVal, "true") == 0) { includeTentativeEra = TRUE; } #endif gJapaneseEraRules = EraRules::createInstance("japanese", includeTentativeEra, status); if (U_FAILURE(status)) { return; } gCurrentEra = gJapaneseEraRules->getCurrentEraIndex(); } static void init(UErrorCode &status) { umtx_initOnce(gJapaneseEraRulesInitOnce, &initializeEras, status); ucln_i18n_registerCleanup(UCLN_I18N_JAPANESE_CALENDAR, japanese_calendar_cleanup); } /* Some platforms don't like to export constants, like old Palm OS and some z/OS configurations. */ uint32_t JapaneseCalendar::getCurrentEra() { return gCurrentEra; } JapaneseCalendar::JapaneseCalendar(const Locale& aLocale, UErrorCode& success) : GregorianCalendar(aLocale, success) { init(success); setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly. } JapaneseCalendar::~JapaneseCalendar() { } JapaneseCalendar::JapaneseCalendar(const JapaneseCalendar& source) : GregorianCalendar(source) { UErrorCode status = U_ZERO_ERROR; init(status); U_ASSERT(U_SUCCESS(status)); } JapaneseCalendar& JapaneseCalendar::operator= ( const JapaneseCalendar& right) { GregorianCalendar::operator=(right); return *this; } Calendar* JapaneseCalendar::clone(void) const { return new JapaneseCalendar(*this); } const char *JapaneseCalendar::getType() const { return "japanese"; } int32_t JapaneseCalendar::getDefaultMonthInYear(int32_t eyear) { int32_t era = internalGetEra(); // TODO do we assume we can trust 'era'? What if it is denormalized? int32_t month = 0; // Find out if we are at the edge of an era int32_t eraStart[3] = { 0,0,0 }; UErrorCode status = U_ZERO_ERROR; gJapaneseEraRules->getStartDate(era, eraStart, status); U_ASSERT(U_SUCCESS(status)); if(eyear == eraStart[0]) { // Yes, we're in the first year of this era. return eraStart[1] // month -1; // return 0-based month } return month; } int32_t JapaneseCalendar::getDefaultDayInMonth(int32_t eyear, int32_t month) { int32_t era = internalGetEra(); int32_t day = 1; int32_t eraStart[3] = { 0,0,0 }; UErrorCode status = U_ZERO_ERROR; gJapaneseEraRules->getStartDate(era, eraStart, status); U_ASSERT(U_SUCCESS(status)); if(eyear == eraStart[0]) { if(month == eraStart[1] - 1) { return eraStart[2]; } } return day; } int32_t JapaneseCalendar::internalGetEra() const { return internalGet(UCAL_ERA, gCurrentEra); } int32_t JapaneseCalendar::handleGetExtendedYear() { // EXTENDED_YEAR in JapaneseCalendar is a Gregorian year // The default value of EXTENDED_YEAR is 1970 (Showa 45) int32_t year; if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR && newerField(UCAL_EXTENDED_YEAR, UCAL_ERA) == UCAL_EXTENDED_YEAR) { year = internalGet(UCAL_EXTENDED_YEAR, kGregorianEpoch); } else { UErrorCode status = U_ZERO_ERROR; int32_t eraStartYear = gJapaneseEraRules->getStartYear(internalGet(UCAL_ERA, gCurrentEra), status); U_ASSERT(U_SUCCESS(status)); // extended year is a gregorian year, where 1 = 1AD, 0 = 1BC, -1 = 2BC, etc year = internalGet(UCAL_YEAR, 1) // pin to minimum of year 1 (first year) + eraStartYear // add gregorian starting year - 1; // Subtract one because year starts at 1 } return year; } void JapaneseCalendar::handleComputeFields(int32_t julianDay, UErrorCode& status) { //Calendar::timeToFields(theTime, quick, status); GregorianCalendar::handleComputeFields(julianDay, status); int32_t year = internalGet(UCAL_EXTENDED_YEAR); // Gregorian year int32_t eraIdx = gJapaneseEraRules->getEraIndex(year, internalGet(UCAL_MONTH) + 1, internalGet(UCAL_DAY_OF_MONTH), status); internalSet(UCAL_ERA, eraIdx); internalSet(UCAL_YEAR, year - gJapaneseEraRules->getStartYear(eraIdx, status) + 1); } /* Disable pivoting */ UBool JapaneseCalendar::haveDefaultCentury() const { return FALSE; } UDate JapaneseCalendar::defaultCenturyStart() const { return 0;// WRONG } int32_t JapaneseCalendar::defaultCenturyStartYear() const { return 0; } int32_t JapaneseCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const { switch(field) { case UCAL_ERA: if (limitType == UCAL_LIMIT_MINIMUM || limitType == UCAL_LIMIT_GREATEST_MINIMUM) { return 0; } return gCurrentEra; case UCAL_YEAR: { switch (limitType) { case UCAL_LIMIT_MINIMUM: case UCAL_LIMIT_GREATEST_MINIMUM: return 1; case UCAL_LIMIT_LEAST_MAXIMUM: return 1; case UCAL_LIMIT_COUNT: //added to avoid warning case UCAL_LIMIT_MAXIMUM: { UErrorCode status = U_ZERO_ERROR; int32_t eraStartYear = gJapaneseEraRules->getStartYear(gCurrentEra, status); U_ASSERT(U_SUCCESS(status)); return GregorianCalendar::handleGetLimit(UCAL_YEAR, UCAL_LIMIT_MAXIMUM) - eraStartYear; } default: return 1; // Error condition, invalid limitType } } default: return GregorianCalendar::handleGetLimit(field,limitType); } } int32_t JapaneseCalendar::getActualMaximum(UCalendarDateFields field, UErrorCode& status) const { if (field == UCAL_YEAR) { int32_t era = get(UCAL_ERA, status); if (U_FAILURE(status)) { return 0; // error case... any value } if (era == gCurrentEra) { // TODO: Investigate what value should be used here - revisit after 4.0. return handleGetLimit(UCAL_YEAR, UCAL_LIMIT_MAXIMUM); } else { int32_t nextEraStart[3] = { 0,0,0 }; gJapaneseEraRules->getStartDate(era + 1, nextEraStart, status); int32_t nextEraYear = nextEraStart[0]; int32_t nextEraMonth = nextEraStart[1]; // 1-base int32_t nextEraDate = nextEraStart[2]; int32_t eraStartYear = gJapaneseEraRules->getStartYear(era, status); int32_t maxYear = nextEraYear - eraStartYear + 1; // 1-base if (nextEraMonth == 1 && nextEraDate == 1) { // Subtract 1, because the next era starts at Jan 1 maxYear--; } return maxYear; } } return GregorianCalendar::getActualMaximum(field, status); } U_NAMESPACE_END #endif