summaryrefslogtreecommitdiffstats
path: root/intl/icu/source/i18n/japancal.cpp
blob: 75a248f406ffe1c74699e77d24a7f48273a05848 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
// © 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 <stdlib.h> // getenv() is not available in UWP env
#else
#ifndef WIN32_LEAN_AND_MEAN
#   define WIN32_LEAN_AND_MEAN
#endif
#   define VC_EXTRALEAN
#   define NOUSER
#   define NOSERVICE
#   define NOIME
#   define NOMCX
#include <windows.h>
#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";


// Export the following for use by test code.
UBool JapaneseCalendar::enableTentativeEra() {
    // 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<int32_t>(uprv_strlen(TENTATIVE_ERA_VAR_NAME)));
    WCHAR varValue[5] = {};
    DWORD ret = GetEnvironmentVariableW(reinterpret_cast<WCHAR*>(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
    return includeTentativeEra;
}


// Initialize global Japanese era data
static void U_CALLCONV initializeEras(UErrorCode &status) {
    gJapaneseEraRules = EraRules::createInstance("japanese", JapaneseCalendar::enableTentativeEra(), 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;
}

JapaneseCalendar* JapaneseCalendar::clone() 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 gJapaneseEraRules->getNumberOfEras() - 1; // max known era, not 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 == gJapaneseEraRules->getNumberOfEras() - 1) { // max known era, not 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