summaryrefslogtreecommitdiffstats
path: root/intl/icu/source/i18n/dangical.cpp
blob: 2b340ee4b4545c847365cb76cfa19ff44484c0f8 (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
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
 ******************************************************************************
 * Copyright (C) 2013, International Business Machines Corporation
 * and others. All Rights Reserved.
 ******************************************************************************
 *
 * File DANGICAL.CPP
 *****************************************************************************
 */

#include "chnsecal.h"
#include "dangical.h"

#if !UCONFIG_NO_FORMATTING

#include "gregoimp.h" // Math
#include "uassert.h"
#include "ucln_in.h"
#include "umutex.h"
#include "unicode/rbtz.h"
#include "unicode/tzrule.h"

// --- The cache --
static icu::TimeZone *gDangiCalendarZoneAstroCalc = nullptr;
static icu::UInitOnce gDangiCalendarInitOnce {};

/**
 * The start year of the Korean traditional calendar (Dan-gi) is the inaugural
 * year of Dan-gun (BC 2333).
 */
static const int32_t DANGI_EPOCH_YEAR = -2332; // Gregorian year

U_CDECL_BEGIN
static UBool calendar_dangi_cleanup() {
    if (gDangiCalendarZoneAstroCalc) {
        delete gDangiCalendarZoneAstroCalc;
        gDangiCalendarZoneAstroCalc = nullptr;
    }
    gDangiCalendarInitOnce.reset();
    return true;
}
U_CDECL_END

U_NAMESPACE_BEGIN

// Implementation of the DangiCalendar class

//-------------------------------------------------------------------------
// Constructors...
//-------------------------------------------------------------------------

DangiCalendar::DangiCalendar(const Locale& aLocale, UErrorCode& success)
:   ChineseCalendar(aLocale, DANGI_EPOCH_YEAR, getDangiCalZoneAstroCalc(success), success)
{
}

DangiCalendar::DangiCalendar (const DangiCalendar& other) 
: ChineseCalendar(other)
{
}

DangiCalendar::~DangiCalendar()
{
}

DangiCalendar*
DangiCalendar::clone() const
{
    return new DangiCalendar(*this);
}

const char *DangiCalendar::getType() const { 
    return "dangi";
}

/**
 * The time zone used for performing astronomical computations for
 * Dangi calendar. In Korea various timezones have been used historically 
 * (cf. http://www.math.snu.ac.kr/~kye/others/lunar.html): 
 *  
 *            - 1908/04/01: GMT+8 
 * 1908/04/01 - 1911/12/31: GMT+8.5 
 * 1912/01/01 - 1954/03/20: GMT+9 
 * 1954/03/21 - 1961/08/09: GMT+8.5 
 * 1961/08/10 -           : GMT+9 
 *  
 * Note that, in 1908-1911, the government did not apply the timezone change 
 * but used GMT+8. In addition, 1954-1961's timezone change does not affect 
 * the lunar date calculation. Therefore, the following simpler rule works: 
 *   
 * -1911: GMT+8 
 * 1912-: GMT+9 
 *  
 * Unfortunately, our astronomer's approximation doesn't agree with the 
 * references (http://www.math.snu.ac.kr/~kye/others/lunar.html and 
 * http://astro.kasi.re.kr/Life/ConvertSolarLunarForm.aspx?MenuID=115) 
 * in 1897/7/30. So the following ad hoc fix is used here: 
 *  
 *     -1896: GMT+8 
 *      1897: GMT+7 
 * 1898-1911: GMT+8 
 * 1912-    : GMT+9 
 */
static void U_CALLCONV initDangiCalZoneAstroCalc(UErrorCode &status) {
    U_ASSERT(gDangiCalendarZoneAstroCalc == nullptr);
    const UDate millis1897[] = { (UDate)((1897 - 1970) * 365 * kOneDay) }; // some days of error is not a problem here
    const UDate millis1898[] = { (UDate)((1898 - 1970) * 365 * kOneDay) }; // some days of error is not a problem here
    const UDate millis1912[] = { (UDate)((1912 - 1970) * 365 * kOneDay) }; // this doesn't create an issue for 1911/12/20
    LocalPointer<InitialTimeZoneRule> initialTimeZone(new InitialTimeZoneRule(
        UnicodeString(u"GMT+8"), 8*kOneHour, 0), status);

    LocalPointer<TimeZoneRule> rule1897(new TimeArrayTimeZoneRule(
        UnicodeString(u"Korean 1897"), 7*kOneHour, 0, millis1897, 1, DateTimeRule::STANDARD_TIME), status);

    LocalPointer<TimeZoneRule> rule1898to1911(new TimeArrayTimeZoneRule(
        UnicodeString(u"Korean 1898-1911"), 8*kOneHour, 0, millis1898, 1, DateTimeRule::STANDARD_TIME), status);

    LocalPointer<TimeZoneRule> ruleFrom1912(new TimeArrayTimeZoneRule(
        UnicodeString(u"Korean 1912-"), 9*kOneHour, 0, millis1912, 1, DateTimeRule::STANDARD_TIME), status);

    LocalPointer<RuleBasedTimeZone> dangiCalZoneAstroCalc(new RuleBasedTimeZone(
        UnicodeString(u"KOREA_ZONE"), initialTimeZone.orphan()), status); // adopts initialTimeZone

    if (U_FAILURE(status)) {
        return;
    }
    dangiCalZoneAstroCalc->addTransitionRule(rule1897.orphan(), status); // adopts rule1897
    dangiCalZoneAstroCalc->addTransitionRule(rule1898to1911.orphan(), status);
    dangiCalZoneAstroCalc->addTransitionRule(ruleFrom1912.orphan(), status);
    dangiCalZoneAstroCalc->complete(status);
    if (U_SUCCESS(status)) {
        gDangiCalendarZoneAstroCalc = dangiCalZoneAstroCalc.orphan();
    }
    ucln_i18n_registerCleanup(UCLN_I18N_DANGI_CALENDAR, calendar_dangi_cleanup);
}

const TimeZone* DangiCalendar::getDangiCalZoneAstroCalc(UErrorCode &status) const {
    umtx_initOnce(gDangiCalendarInitOnce, &initDangiCalZoneAstroCalc, status);
    return gDangiCalendarZoneAstroCalc;
}

constexpr uint32_t kDangiRelatedYearDiff = -2333;

int32_t DangiCalendar::getRelatedYear(UErrorCode &status) const
{
    int32_t year = get(UCAL_EXTENDED_YEAR, status);
    if (U_FAILURE(status)) {
        return 0;
    }
    return year + kDangiRelatedYearDiff;
}

void DangiCalendar::setRelatedYear(int32_t year)
{
    // set extended year
    set(UCAL_EXTENDED_YEAR, year - kDangiRelatedYearDiff);
}


UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DangiCalendar)

U_NAMESPACE_END

#endif