1/*
2*******************************************************************************
3* Copyright (C) 2003 - 2009, International Business Machines Corporation and  *
4* others. All Rights Reserved.                                                *
5*******************************************************************************
6*/
7
8#include "unicode/utypes.h"
9
10#if !UCONFIG_NO_FORMATTING
11
12#include "umutex.h"
13#include "ethpccal.h"
14#include "cecal.h"
15#include <float.h>
16
17U_NAMESPACE_BEGIN
18
19UOBJECT_DEFINE_RTTI_IMPLEMENTATION(EthiopicCalendar)
20
21//static const int32_t JD_EPOCH_OFFSET_AMETE_ALEM = -285019;
22static const int32_t JD_EPOCH_OFFSET_AMETE_MIHRET = 1723856;
23static const int32_t AMETE_MIHRET_DELTA = 5500; // 5501 - 1 (Amete Alem 5501 = Amete Mihret 1)
24
25//-------------------------------------------------------------------------
26// Constructors...
27//-------------------------------------------------------------------------
28
29EthiopicCalendar::EthiopicCalendar(const Locale& aLocale,
30                                   UErrorCode& success,
31                                   EEraType type /*= AMETE_MIHRET_ERA*/)
32:   CECalendar(aLocale, success),
33    eraType(type)
34{
35}
36
37EthiopicCalendar::EthiopicCalendar(const EthiopicCalendar& other)
38:   CECalendar(other),
39    eraType(other.eraType)
40{
41}
42
43EthiopicCalendar::~EthiopicCalendar()
44{
45}
46
47Calendar*
48EthiopicCalendar::clone() const
49{
50    return new EthiopicCalendar(*this);
51}
52
53const char *
54EthiopicCalendar::getType() const
55{
56    if (isAmeteAlemEra()) {
57        return "ethiopic-amete-alem";
58    }
59    return "ethiopic";
60}
61
62void
63EthiopicCalendar::setAmeteAlemEra(UBool onOff)
64{
65    eraType = onOff ? AMETE_ALEM_ERA : AMETE_MIHRET_ERA;
66}
67
68UBool
69EthiopicCalendar::isAmeteAlemEra() const
70{
71    return (eraType == AMETE_ALEM_ERA);
72}
73
74//-------------------------------------------------------------------------
75// Calendar framework
76//-------------------------------------------------------------------------
77
78int32_t
79EthiopicCalendar::handleGetExtendedYear()
80{
81    // Ethiopic calendar uses EXTENDED_YEAR aligned to
82    // Amelete Hihret year always.
83    int32_t eyear;
84    if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) {
85        eyear = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1
86    } else if (isAmeteAlemEra()) {
87        eyear = internalGet(UCAL_YEAR, 1 + AMETE_MIHRET_DELTA)
88            - AMETE_MIHRET_DELTA; // Default to year 1 of Amelete Mihret
89    } else {
90        // The year defaults to the epoch start, the era to AMETE_MIHRET
91        int32_t era = internalGet(UCAL_ERA, AMETE_MIHRET);
92        if (era == AMETE_MIHRET) {
93            eyear = internalGet(UCAL_YEAR, 1); // Default to year 1
94        } else {
95            eyear = internalGet(UCAL_YEAR, 1) - AMETE_MIHRET_DELTA;
96        }
97    }
98    return eyear;
99}
100
101void
102EthiopicCalendar::handleComputeFields(int32_t julianDay, UErrorCode &/*status*/)
103{
104    int32_t eyear, month, day, era, year;
105    jdToCE(julianDay, getJDEpochOffset(), eyear, month, day);
106
107    if (isAmeteAlemEra()) {
108        era = AMETE_ALEM;
109        year = eyear + AMETE_MIHRET_DELTA;
110    } else {
111        if (eyear > 0) {
112            era = AMETE_MIHRET;
113            year = eyear;
114        } else {
115            era = AMETE_ALEM;
116            year = eyear + AMETE_MIHRET_DELTA;
117        }
118    }
119
120    internalSet(UCAL_EXTENDED_YEAR, eyear);
121    internalSet(UCAL_ERA, era);
122    internalSet(UCAL_YEAR, year);
123    internalSet(UCAL_MONTH, month);
124    internalSet(UCAL_DATE, day);
125    internalSet(UCAL_DAY_OF_YEAR, (30 * month) + day);
126}
127
128int32_t
129EthiopicCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const
130{
131    if (isAmeteAlemEra() && field == UCAL_ERA) {
132        return 0; // Only one era in this mode, era is always 0
133    }
134    return CECalendar::handleGetLimit(field, limitType);
135}
136
137const UDate     EthiopicCalendar::fgSystemDefaultCentury        = DBL_MIN;
138const int32_t   EthiopicCalendar::fgSystemDefaultCenturyYear    = -1;
139
140UDate           EthiopicCalendar::fgSystemDefaultCenturyStart       = DBL_MIN;
141int32_t         EthiopicCalendar::fgSystemDefaultCenturyStartYear   = -1;
142
143UDate
144EthiopicCalendar::defaultCenturyStart() const
145{
146    initializeSystemDefaultCentury();
147    return fgSystemDefaultCenturyStart;
148}
149
150int32_t
151EthiopicCalendar::defaultCenturyStartYear() const
152{
153    initializeSystemDefaultCentury();
154    if (isAmeteAlemEra()) {
155        return fgSystemDefaultCenturyStartYear + AMETE_MIHRET_DELTA;
156    }
157    return fgSystemDefaultCenturyStartYear;
158}
159
160void
161EthiopicCalendar::initializeSystemDefaultCentury()
162{
163    // lazy-evaluate systemDefaultCenturyStart
164    UBool needsUpdate;
165    UMTX_CHECK(NULL, (fgSystemDefaultCenturyStart == fgSystemDefaultCentury), needsUpdate);
166
167    if (!needsUpdate) {
168        return;
169    }
170
171    UErrorCode status = U_ZERO_ERROR;
172
173    EthiopicCalendar calendar(Locale("@calendar=ethiopic"), status);
174    if (U_SUCCESS(status)) {
175        calendar.setTime(Calendar::getNow(), status);
176        calendar.add(UCAL_YEAR, -80, status);
177        UDate    newStart = calendar.getTime(status);
178        int32_t  newYear  = calendar.get(UCAL_YEAR, status);
179        {
180            umtx_lock(NULL);
181            fgSystemDefaultCenturyStartYear = newYear;
182            fgSystemDefaultCenturyStart = newStart;
183            umtx_unlock(NULL);
184        }
185    }
186    // We have no recourse upon failure unless we want to propagate the failure
187    // out.
188}
189
190int32_t
191EthiopicCalendar::getJDEpochOffset() const
192{
193    return JD_EPOCH_OFFSET_AMETE_MIHRET;
194}
195
196
197#if 0
198// We do not want to introduce this API in ICU4C.
199// It was accidentally introduced in ICU4J as a public API.
200
201//-------------------------------------------------------------------------
202// Calendar system Conversion methods...
203//-------------------------------------------------------------------------
204
205int32_t
206EthiopicCalendar::ethiopicToJD(int32_t year, int32_t month, int32_t date)
207{
208    return ceToJD(year, month, date, JD_EPOCH_OFFSET_AMETE_MIHRET);
209}
210#endif
211
212U_NAMESPACE_END
213
214#endif
215