1*cdf0e10cSrcweir /*************************************************************************
2*cdf0e10cSrcweir  *
3*cdf0e10cSrcweir  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4*cdf0e10cSrcweir  *
5*cdf0e10cSrcweir  * Copyright 2000, 2010 Oracle and/or its affiliates.
6*cdf0e10cSrcweir  *
7*cdf0e10cSrcweir  * OpenOffice.org - a multi-platform office productivity suite
8*cdf0e10cSrcweir  *
9*cdf0e10cSrcweir  * This file is part of OpenOffice.org.
10*cdf0e10cSrcweir  *
11*cdf0e10cSrcweir  * OpenOffice.org is free software: you can redistribute it and/or modify
12*cdf0e10cSrcweir  * it under the terms of the GNU Lesser General Public License version 3
13*cdf0e10cSrcweir  * only, as published by the Free Software Foundation.
14*cdf0e10cSrcweir  *
15*cdf0e10cSrcweir  * OpenOffice.org is distributed in the hope that it will be useful,
16*cdf0e10cSrcweir  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17*cdf0e10cSrcweir  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18*cdf0e10cSrcweir  * GNU Lesser General Public License version 3 for more details
19*cdf0e10cSrcweir  * (a copy is included in the LICENSE file that accompanied this code).
20*cdf0e10cSrcweir  *
21*cdf0e10cSrcweir  * You should have received a copy of the GNU Lesser General Public License
22*cdf0e10cSrcweir  * version 3 along with OpenOffice.org.  If not, see
23*cdf0e10cSrcweir  * <http://www.openoffice.org/license.html>
24*cdf0e10cSrcweir  * for a copy of the LGPLv3 License.
25*cdf0e10cSrcweir  *
26*cdf0e10cSrcweir  ************************************************************************/
27*cdf0e10cSrcweir 
28*cdf0e10cSrcweir // MARKER(update_precomp.py): autogen include statement, do not remove
29*cdf0e10cSrcweir #include "precompiled_i18npool.hxx"
30*cdf0e10cSrcweir 
31*cdf0e10cSrcweir #include "calendar_gregorian.hxx"
32*cdf0e10cSrcweir #include "localedata.hxx"
33*cdf0e10cSrcweir #include <com/sun/star/i18n/AmPmValue.hpp>
34*cdf0e10cSrcweir #include <com/sun/star/i18n/Months.hpp>
35*cdf0e10cSrcweir #include <com/sun/star/i18n/Weekdays.hpp>
36*cdf0e10cSrcweir #include <com/sun/star/i18n/reservedWords.hpp>
37*cdf0e10cSrcweir #include <com/sun/star/lang/XMultiServiceFactory.hpp>
38*cdf0e10cSrcweir #include <comphelper/processfactory.hxx>
39*cdf0e10cSrcweir 
40*cdf0e10cSrcweir #include <stdio.h>
41*cdf0e10cSrcweir #include <string.h>
42*cdf0e10cSrcweir 
43*cdf0e10cSrcweir #define erDUMP_ICU_CALENDAR 0
44*cdf0e10cSrcweir #define erDUMP_I18N_CALENDAR 0
45*cdf0e10cSrcweir #if erDUMP_ICU_CALENDAR || erDUMP_I18N_CALENDAR
46*cdf0e10cSrcweir // If both are used, DUMP_ICU_CAL_MSG() must be used before DUMP_I18N_CAL_MSG()
47*cdf0e10cSrcweir // to obtain internally set values from ICU, else Calendar::get() calls in
48*cdf0e10cSrcweir // DUMP_I18N_CAL_MSG() recalculate!
49*cdf0e10cSrcweir 
50*cdf0e10cSrcweir // These pieces of macro are shamelessly borrowed from icu's olsontz.cpp, the
51*cdf0e10cSrcweir // double parens'ed approach to pass multiple parameters as one macro parameter
52*cdf0e10cSrcweir // is appealing.
53*cdf0e10cSrcweir static void debug_cal_loc(const char *f, int32_t l)
54*cdf0e10cSrcweir {
55*cdf0e10cSrcweir     fprintf(stderr, "%s:%d: ", f, l);
56*cdf0e10cSrcweir }
57*cdf0e10cSrcweir # include <stdarg.h>
58*cdf0e10cSrcweir static void debug_cal_msg(const char *pat, ...)
59*cdf0e10cSrcweir {
60*cdf0e10cSrcweir     va_list ap;
61*cdf0e10cSrcweir     va_start(ap, pat);
62*cdf0e10cSrcweir     vfprintf(stderr, pat, ap);
63*cdf0e10cSrcweir }
64*cdf0e10cSrcweir 
65*cdf0e10cSrcweir #if erDUMP_ICU_CALENDAR
66*cdf0e10cSrcweir // Make icu with
67*cdf0e10cSrcweir // DEFS = -DU_DEBUG_CALSVC -DUCAL_DEBUG_DUMP
68*cdf0e10cSrcweir // in icu/$(INPATH)/misc/build/icu/source/icudefs.mk
69*cdf0e10cSrcweir // May need some patches to fix unmaintained things there.
70*cdf0e10cSrcweir extern void ucal_dump( const icu::Calendar & );
71*cdf0e10cSrcweir static void debug_icu_cal_dump( const ::icu::Calendar & r )
72*cdf0e10cSrcweir {
73*cdf0e10cSrcweir     ucal_dump(r);
74*cdf0e10cSrcweir     fflush(stderr);
75*cdf0e10cSrcweir     // set a breakpoint here to pause display between dumps
76*cdf0e10cSrcweir }
77*cdf0e10cSrcweir // must use double parens, i.e.:  DUMP_ICU_CAL_MSG(("four is: %d",4));
78*cdf0e10cSrcweir #define DUMP_ICU_CAL_MSG(x) {debug_cal_loc(__FILE__,__LINE__);debug_cal_msg x;debug_icu_cal_dump(*body);}
79*cdf0e10cSrcweir #else   // erDUMP_ICU_CALENDAR
80*cdf0e10cSrcweir #define DUMP_ICU_CAL_MSG(x)
81*cdf0e10cSrcweir #endif  // erDUMP_ICU_CALENDAR
82*cdf0e10cSrcweir 
83*cdf0e10cSrcweir #if erDUMP_I18N_CALENDAR
84*cdf0e10cSrcweir static void debug_cal_millis_to_time( long nMillis, long & h, long & m, long & s, long & f )
85*cdf0e10cSrcweir {
86*cdf0e10cSrcweir     int sign = (nMillis < 0 ? -1 : 1);
87*cdf0e10cSrcweir     nMillis = ::std::abs(nMillis);
88*cdf0e10cSrcweir     h = sign * nMillis / (60 * 60 * 1000);
89*cdf0e10cSrcweir     nMillis -= sign * h * (60 * 60 * 1000);
90*cdf0e10cSrcweir     m = nMillis / (60 * 1000);
91*cdf0e10cSrcweir     nMillis -= m * (60 * 1000);
92*cdf0e10cSrcweir     s = nMillis / (1000);
93*cdf0e10cSrcweir     nMillis -= s * (1000);
94*cdf0e10cSrcweir     f = nMillis;
95*cdf0e10cSrcweir }
96*cdf0e10cSrcweir static void debug_i18n_cal_dump( const ::icu::Calendar & r )
97*cdf0e10cSrcweir {
98*cdf0e10cSrcweir     UErrorCode status;
99*cdf0e10cSrcweir     long nMillis, h, m, s, f;
100*cdf0e10cSrcweir     fprintf( stderr, " %04ld", (long)r.get( UCAL_YEAR, status = U_ZERO_ERROR));
101*cdf0e10cSrcweir     fprintf( stderr, "-%02ld", (long)r.get( UCAL_MONTH, status = U_ZERO_ERROR)+1);
102*cdf0e10cSrcweir     fprintf( stderr, "-%02ld", (long)r.get( UCAL_DATE, status = U_ZERO_ERROR));
103*cdf0e10cSrcweir     fprintf( stderr, " %02ld", (long)r.get( UCAL_HOUR_OF_DAY, status = U_ZERO_ERROR));
104*cdf0e10cSrcweir     fprintf( stderr, ":%02ld", (long)r.get( UCAL_MINUTE, status = U_ZERO_ERROR));
105*cdf0e10cSrcweir     fprintf( stderr, ":%02ld", (long)r.get( UCAL_SECOND, status = U_ZERO_ERROR));
106*cdf0e10cSrcweir     fprintf( stderr, "  zone: %ld", (long)(nMillis = r.get( UCAL_ZONE_OFFSET, status = U_ZERO_ERROR)));
107*cdf0e10cSrcweir     fprintf( stderr, " (%f min)", (double)nMillis / 60000);
108*cdf0e10cSrcweir     debug_cal_millis_to_time( nMillis, h, m, s, f);
109*cdf0e10cSrcweir     fprintf( stderr, " (%ld:%02ld:%02ld.%ld)", h, m, s, f);
110*cdf0e10cSrcweir     fprintf( stderr, "  DST: %ld", (long)(nMillis = r.get( UCAL_DST_OFFSET, status = U_ZERO_ERROR)));
111*cdf0e10cSrcweir     fprintf( stderr, " (%f min)", (double)nMillis / 60000);
112*cdf0e10cSrcweir     debug_cal_millis_to_time( nMillis, h, m, s, f);
113*cdf0e10cSrcweir     fprintf( stderr, " (%ld:%02ld:%02ld.%ld)", h, m, s, f);
114*cdf0e10cSrcweir     fprintf( stderr, "\n");
115*cdf0e10cSrcweir     fflush(stderr);
116*cdf0e10cSrcweir }
117*cdf0e10cSrcweir // must use double parens, i.e.:  DUMP_I18N_CAL_MSG(("four is: %d",4));
118*cdf0e10cSrcweir #define DUMP_I18N_CAL_MSG(x) {debug_cal_loc(__FILE__,__LINE__);debug_cal_msg x;debug_i18n_cal_dump(*body);}
119*cdf0e10cSrcweir #else   // erDUMP_I18N_CALENDAR
120*cdf0e10cSrcweir #define DUMP_I18N_CAL_MSG(x)
121*cdf0e10cSrcweir #endif  // erDUMP_I18N_CALENDAR
122*cdf0e10cSrcweir 
123*cdf0e10cSrcweir #else   // erDUMP_ICU_CALENDAR || erDUMP_I18N_CALENDAR
124*cdf0e10cSrcweir #define DUMP_ICU_CAL_MSG(x)
125*cdf0e10cSrcweir #define DUMP_I18N_CAL_MSG(x)
126*cdf0e10cSrcweir #endif  // erDUMP_ICU_CALENDAR || erDUMP_I18N_CALENDAR
127*cdf0e10cSrcweir 
128*cdf0e10cSrcweir 
129*cdf0e10cSrcweir using namespace ::com::sun::star::uno;
130*cdf0e10cSrcweir using namespace ::com::sun::star::lang;
131*cdf0e10cSrcweir using namespace ::com::sun::star::i18n;
132*cdf0e10cSrcweir using namespace ::rtl;
133*cdf0e10cSrcweir 
134*cdf0e10cSrcweir #define ERROR RuntimeException()
135*cdf0e10cSrcweir 
136*cdf0e10cSrcweir Calendar_gregorian::Calendar_gregorian()
137*cdf0e10cSrcweir {
138*cdf0e10cSrcweir     init(NULL);
139*cdf0e10cSrcweir }
140*cdf0e10cSrcweir Calendar_gregorian::Calendar_gregorian(Era *_earArray)
141*cdf0e10cSrcweir {
142*cdf0e10cSrcweir     init(_earArray);
143*cdf0e10cSrcweir }
144*cdf0e10cSrcweir void SAL_CALL
145*cdf0e10cSrcweir Calendar_gregorian::init(Era *_eraArray)
146*cdf0e10cSrcweir {
147*cdf0e10cSrcweir         cCalendar = "com.sun.star.i18n.Calendar_gregorian";
148*cdf0e10cSrcweir 
149*cdf0e10cSrcweir         // #i102356# With icu::Calendar::createInstance(UErrorCode) in a Thai
150*cdf0e10cSrcweir         // th_TH system locale we accidentally used a Buddhist calendar. Though
151*cdf0e10cSrcweir         // the ICU documentation says that should be the case only for
152*cdf0e10cSrcweir         // th_TH_TRADITIONAL (and ja_JP_TRADITIONAL Gengou), a plain th_TH
153*cdf0e10cSrcweir         // already triggers that behavior, ja_JP does not. Strange enough,
154*cdf0e10cSrcweir         // passing a th_TH locale to the calendar creation doesn't trigger
155*cdf0e10cSrcweir         // this.
156*cdf0e10cSrcweir         // See also http://userguide.icu-project.org/datetime/calendar
157*cdf0e10cSrcweir 
158*cdf0e10cSrcweir         // Whatever ICU offers as the default calendar for a locale, ensure we
159*cdf0e10cSrcweir         // have a Gregorian calendar as requested.
160*cdf0e10cSrcweir 
161*cdf0e10cSrcweir         /* XXX: with the current implementation the aLocale member variable is
162*cdf0e10cSrcweir          * not set prior to loading a calendar from locale data. This
163*cdf0e10cSrcweir          * creates an empty (root) locale for ICU, but at least the correct
164*cdf0e10cSrcweir          * calendar is used. The language part must not be NULL (respectively
165*cdf0e10cSrcweir          * not all, language and country and variant), otherwise the current
166*cdf0e10cSrcweir          * default locale would be used again and the calendar keyword ignored.
167*cdf0e10cSrcweir          * */
168*cdf0e10cSrcweir         icu::Locale aIcuLocale( "", NULL, NULL, "calendar=gregorian");
169*cdf0e10cSrcweir 
170*cdf0e10cSrcweir         UErrorCode status;
171*cdf0e10cSrcweir         body = icu::Calendar::createInstance( aIcuLocale, status = U_ZERO_ERROR);
172*cdf0e10cSrcweir         if (!body || !U_SUCCESS(status)) throw ERROR;
173*cdf0e10cSrcweir 
174*cdf0e10cSrcweir #if 0
175*cdf0e10cSrcweir         {
176*cdf0e10cSrcweir             icu::Locale loc;
177*cdf0e10cSrcweir             loc = body->getLocale( ULOC_ACTUAL_LOCALE, status = U_ZERO_ERROR);
178*cdf0e10cSrcweir             fprintf( stderr, "\nICU calendar actual locale: %s\n", loc.getName());
179*cdf0e10cSrcweir             loc = body->getLocale( ULOC_VALID_LOCALE, status = U_ZERO_ERROR);
180*cdf0e10cSrcweir             fprintf( stderr,   "ICU calendar valid  locale: %s\n", loc.getName());
181*cdf0e10cSrcweir         }
182*cdf0e10cSrcweir #endif
183*cdf0e10cSrcweir 
184*cdf0e10cSrcweir         eraArray=_eraArray;
185*cdf0e10cSrcweir }
186*cdf0e10cSrcweir 
187*cdf0e10cSrcweir Calendar_gregorian::~Calendar_gregorian()
188*cdf0e10cSrcweir {
189*cdf0e10cSrcweir         delete body;
190*cdf0e10cSrcweir }
191*cdf0e10cSrcweir 
192*cdf0e10cSrcweir Calendar_hanja::Calendar_hanja()
193*cdf0e10cSrcweir {
194*cdf0e10cSrcweir         cCalendar = "com.sun.star.i18n.Calendar_hanja";
195*cdf0e10cSrcweir }
196*cdf0e10cSrcweir 
197*cdf0e10cSrcweir OUString SAL_CALL
198*cdf0e10cSrcweir Calendar_hanja::getDisplayName( sal_Int16 displayIndex, sal_Int16 idx, sal_Int16 nameType ) throw(RuntimeException)
199*cdf0e10cSrcweir {
200*cdf0e10cSrcweir         if ( displayIndex == CalendarDisplayIndex::AM_PM ) {
201*cdf0e10cSrcweir             // Am/Pm string for Korean Hanja calendar will refer to Japanese locale
202*cdf0e10cSrcweir             com::sun::star::lang::Locale jaLocale =
203*cdf0e10cSrcweir                 com::sun::star::lang::Locale(OUString::createFromAscii("ja"), OUString(), OUString());
204*cdf0e10cSrcweir             if (idx == 0) return LocaleData().getLocaleItem(jaLocale).timeAM;
205*cdf0e10cSrcweir             else if (idx == 1) return LocaleData().getLocaleItem(jaLocale).timePM;
206*cdf0e10cSrcweir             else throw ERROR;
207*cdf0e10cSrcweir         }
208*cdf0e10cSrcweir         else
209*cdf0e10cSrcweir             return Calendar_gregorian::getDisplayName( displayIndex, idx, nameType );
210*cdf0e10cSrcweir }
211*cdf0e10cSrcweir 
212*cdf0e10cSrcweir void SAL_CALL
213*cdf0e10cSrcweir Calendar_hanja::loadCalendar( const OUString& /*uniqueID*/, const com::sun::star::lang::Locale& rLocale ) throw(RuntimeException)
214*cdf0e10cSrcweir {
215*cdf0e10cSrcweir         // Since this class could be called by service name 'hanja_yoil', we have to
216*cdf0e10cSrcweir         // rename uniqueID to get right calendar defined in locale data.
217*cdf0e10cSrcweir         Calendar_gregorian::loadCalendar(OUString::createFromAscii("hanja"), rLocale);
218*cdf0e10cSrcweir }
219*cdf0e10cSrcweir 
220*cdf0e10cSrcweir static Era gengou_eraArray[] = {
221*cdf0e10cSrcweir     {1868,  1,  1},
222*cdf0e10cSrcweir     {1912,  7, 30},
223*cdf0e10cSrcweir     {1926, 12, 25},
224*cdf0e10cSrcweir     {1989,  1,  8},
225*cdf0e10cSrcweir     {0, 0,  0}
226*cdf0e10cSrcweir };
227*cdf0e10cSrcweir Calendar_gengou::Calendar_gengou() : Calendar_gregorian(gengou_eraArray)
228*cdf0e10cSrcweir {
229*cdf0e10cSrcweir         cCalendar = "com.sun.star.i18n.Calendar_gengou";
230*cdf0e10cSrcweir }
231*cdf0e10cSrcweir 
232*cdf0e10cSrcweir static Era ROC_eraArray[] = {
233*cdf0e10cSrcweir     {1912, 1, 1},
234*cdf0e10cSrcweir     {0, 0,  0}
235*cdf0e10cSrcweir };
236*cdf0e10cSrcweir Calendar_ROC::Calendar_ROC() : Calendar_gregorian(ROC_eraArray)
237*cdf0e10cSrcweir {
238*cdf0e10cSrcweir         cCalendar = "com.sun.star.i18n.Calendar_ROC";
239*cdf0e10cSrcweir }
240*cdf0e10cSrcweir 
241*cdf0e10cSrcweir static Era buddhist_eraArray[] = {
242*cdf0e10cSrcweir     {-542, 1, 1},
243*cdf0e10cSrcweir     {0, 0,  0}
244*cdf0e10cSrcweir };
245*cdf0e10cSrcweir Calendar_buddhist::Calendar_buddhist() : Calendar_gregorian(buddhist_eraArray)
246*cdf0e10cSrcweir {
247*cdf0e10cSrcweir         cCalendar = "com.sun.star.i18n.Calendar_buddhist";
248*cdf0e10cSrcweir }
249*cdf0e10cSrcweir 
250*cdf0e10cSrcweir void SAL_CALL
251*cdf0e10cSrcweir Calendar_gregorian::loadCalendar( const OUString& uniqueID, const com::sun::star::lang::Locale& rLocale ) throw(RuntimeException)
252*cdf0e10cSrcweir {
253*cdf0e10cSrcweir         // init. fieldValue[]
254*cdf0e10cSrcweir         getValue();
255*cdf0e10cSrcweir 
256*cdf0e10cSrcweir         aLocale = rLocale;
257*cdf0e10cSrcweir         Sequence< Calendar> xC = LocaleData().getAllCalendars(rLocale);
258*cdf0e10cSrcweir         for (sal_Int32 i = 0; i < xC.getLength(); i++)
259*cdf0e10cSrcweir         {
260*cdf0e10cSrcweir             if (uniqueID == xC[i].Name)
261*cdf0e10cSrcweir             {
262*cdf0e10cSrcweir                 aCalendar = xC[i];
263*cdf0e10cSrcweir                 // setup minimalDaysInFirstWeek
264*cdf0e10cSrcweir                 setMinimumNumberOfDaysForFirstWeek(
265*cdf0e10cSrcweir                         aCalendar.MinimumNumberOfDaysForFirstWeek);
266*cdf0e10cSrcweir                 // setup first day of week
267*cdf0e10cSrcweir                 for (sal_Int16 day = sal::static_int_cast<sal_Int16>(
268*cdf0e10cSrcweir                             aCalendar.Days.getLength()-1); day>=0; day--)
269*cdf0e10cSrcweir                 {
270*cdf0e10cSrcweir                     if (aCalendar.StartOfWeek == aCalendar.Days[day].ID)
271*cdf0e10cSrcweir                     {
272*cdf0e10cSrcweir                         setFirstDayOfWeek( day);
273*cdf0e10cSrcweir                         return;
274*cdf0e10cSrcweir                     }
275*cdf0e10cSrcweir                 }
276*cdf0e10cSrcweir             }
277*cdf0e10cSrcweir         }
278*cdf0e10cSrcweir         // Calendar is not for the locale
279*cdf0e10cSrcweir         throw ERROR;
280*cdf0e10cSrcweir }
281*cdf0e10cSrcweir 
282*cdf0e10cSrcweir 
283*cdf0e10cSrcweir com::sun::star::i18n::Calendar SAL_CALL
284*cdf0e10cSrcweir Calendar_gregorian::getLoadedCalendar() throw(RuntimeException)
285*cdf0e10cSrcweir {
286*cdf0e10cSrcweir         return aCalendar;
287*cdf0e10cSrcweir }
288*cdf0e10cSrcweir 
289*cdf0e10cSrcweir OUString SAL_CALL
290*cdf0e10cSrcweir Calendar_gregorian::getUniqueID() throw(RuntimeException)
291*cdf0e10cSrcweir {
292*cdf0e10cSrcweir         return aCalendar.Name;
293*cdf0e10cSrcweir }
294*cdf0e10cSrcweir 
295*cdf0e10cSrcweir void SAL_CALL
296*cdf0e10cSrcweir Calendar_gregorian::setDateTime( double timeInDays ) throw(RuntimeException)
297*cdf0e10cSrcweir {
298*cdf0e10cSrcweir         UErrorCode status;
299*cdf0e10cSrcweir         body->setTime(timeInDays * U_MILLIS_PER_DAY, status = U_ZERO_ERROR);
300*cdf0e10cSrcweir         if ( !U_SUCCESS(status) ) throw ERROR;
301*cdf0e10cSrcweir         getValue();
302*cdf0e10cSrcweir }
303*cdf0e10cSrcweir 
304*cdf0e10cSrcweir double SAL_CALL
305*cdf0e10cSrcweir Calendar_gregorian::getDateTime() throw(RuntimeException)
306*cdf0e10cSrcweir {
307*cdf0e10cSrcweir         if (fieldSet) {
308*cdf0e10cSrcweir             setValue();
309*cdf0e10cSrcweir             getValue();
310*cdf0e10cSrcweir         }
311*cdf0e10cSrcweir         UErrorCode status;
312*cdf0e10cSrcweir         double r = body->getTime(status = U_ZERO_ERROR);
313*cdf0e10cSrcweir         if ( !U_SUCCESS(status) ) throw ERROR;
314*cdf0e10cSrcweir         return r / U_MILLIS_PER_DAY;
315*cdf0e10cSrcweir }
316*cdf0e10cSrcweir 
317*cdf0e10cSrcweir // map field value from gregorian calendar to other calendar, it can be overwritten by derived class.
318*cdf0e10cSrcweir // By using eraArray, it can take care Japanese and Taiwan ROC calendar.
319*cdf0e10cSrcweir void Calendar_gregorian::mapFromGregorian() throw(RuntimeException)
320*cdf0e10cSrcweir {
321*cdf0e10cSrcweir         if (eraArray) {
322*cdf0e10cSrcweir             sal_Int16 e, y, m, d;
323*cdf0e10cSrcweir 
324*cdf0e10cSrcweir             e = fieldValue[CalendarFieldIndex::ERA];
325*cdf0e10cSrcweir             y = fieldValue[CalendarFieldIndex::YEAR];
326*cdf0e10cSrcweir             m = fieldValue[CalendarFieldIndex::MONTH] + 1;
327*cdf0e10cSrcweir             d = fieldValue[CalendarFieldIndex::DAY_OF_MONTH];
328*cdf0e10cSrcweir 
329*cdf0e10cSrcweir             // since the year is reversed for first era, it is reversed again here for Era compare.
330*cdf0e10cSrcweir             if (e == 0)
331*cdf0e10cSrcweir                 y = 1 - y;
332*cdf0e10cSrcweir 
333*cdf0e10cSrcweir             for (e = 0; eraArray[e].year; e++)
334*cdf0e10cSrcweir                 if ((y != eraArray[e].year) ? y < eraArray[e].year :
335*cdf0e10cSrcweir                     (m != eraArray[e].month) ? m < eraArray[e].month : d < eraArray[e].day)
336*cdf0e10cSrcweir                     break;
337*cdf0e10cSrcweir 
338*cdf0e10cSrcweir             fieldValue[CalendarFieldIndex::ERA] = e;
339*cdf0e10cSrcweir             fieldValue[CalendarFieldIndex::YEAR] =
340*cdf0e10cSrcweir                 sal::static_int_cast<sal_Int16>( (e == 0) ? (eraArray[0].year - y) : (y - eraArray[e-1].year + 1) );
341*cdf0e10cSrcweir         }
342*cdf0e10cSrcweir }
343*cdf0e10cSrcweir 
344*cdf0e10cSrcweir #define FIELDS  ((1 << CalendarFieldIndex::ERA) | (1 << CalendarFieldIndex::YEAR))
345*cdf0e10cSrcweir // map field value from other calendar to gregorian calendar, it can be overwritten by derived class.
346*cdf0e10cSrcweir // By using eraArray, it can take care Japanese and Taiwan ROC calendar.
347*cdf0e10cSrcweir void Calendar_gregorian::mapToGregorian() throw(RuntimeException)
348*cdf0e10cSrcweir {
349*cdf0e10cSrcweir         if (eraArray && (fieldSet & FIELDS)) {
350*cdf0e10cSrcweir             sal_Int16 y, e = fieldValue[CalendarFieldIndex::ERA];
351*cdf0e10cSrcweir             if (e == 0)
352*cdf0e10cSrcweir                 y = sal::static_int_cast<sal_Int16>( eraArray[0].year - fieldValue[CalendarFieldIndex::YEAR] );
353*cdf0e10cSrcweir             else
354*cdf0e10cSrcweir                 y = sal::static_int_cast<sal_Int16>( eraArray[e-1].year + fieldValue[CalendarFieldIndex::YEAR] - 1 );
355*cdf0e10cSrcweir 
356*cdf0e10cSrcweir             fieldSetValue[CalendarFieldIndex::ERA] = y <= 0 ? 0 : 1;
357*cdf0e10cSrcweir             fieldSetValue[CalendarFieldIndex::YEAR] = (y <= 0 ? 1 - y : y);
358*cdf0e10cSrcweir             fieldSet |= FIELDS;
359*cdf0e10cSrcweir         }
360*cdf0e10cSrcweir }
361*cdf0e10cSrcweir 
362*cdf0e10cSrcweir static UCalendarDateFields fieldNameConverter(sal_Int16 fieldIndex) throw(RuntimeException)
363*cdf0e10cSrcweir {
364*cdf0e10cSrcweir         UCalendarDateFields f;
365*cdf0e10cSrcweir 
366*cdf0e10cSrcweir         switch (fieldIndex) {
367*cdf0e10cSrcweir             case CalendarFieldIndex::AM_PM:             f = UCAL_AM_PM; break;
368*cdf0e10cSrcweir             case CalendarFieldIndex::DAY_OF_MONTH:      f = UCAL_DATE; break;
369*cdf0e10cSrcweir             case CalendarFieldIndex::DAY_OF_WEEK:       f = UCAL_DAY_OF_WEEK; break;
370*cdf0e10cSrcweir             case CalendarFieldIndex::DAY_OF_YEAR:       f = UCAL_DAY_OF_YEAR; break;
371*cdf0e10cSrcweir             case CalendarFieldIndex::DST_OFFSET:        f = UCAL_DST_OFFSET; break;
372*cdf0e10cSrcweir             case CalendarFieldIndex::ZONE_OFFSET:       f = UCAL_ZONE_OFFSET; break;
373*cdf0e10cSrcweir             case CalendarFieldIndex::HOUR:              f = UCAL_HOUR_OF_DAY; break;
374*cdf0e10cSrcweir             case CalendarFieldIndex::MINUTE:            f = UCAL_MINUTE; break;
375*cdf0e10cSrcweir             case CalendarFieldIndex::SECOND:            f = UCAL_SECOND; break;
376*cdf0e10cSrcweir             case CalendarFieldIndex::MILLISECOND:       f = UCAL_MILLISECOND; break;
377*cdf0e10cSrcweir             case CalendarFieldIndex::WEEK_OF_MONTH:     f = UCAL_WEEK_OF_MONTH; break;
378*cdf0e10cSrcweir             case CalendarFieldIndex::WEEK_OF_YEAR:      f = UCAL_WEEK_OF_YEAR; break;
379*cdf0e10cSrcweir             case CalendarFieldIndex::YEAR:              f = UCAL_YEAR; break;
380*cdf0e10cSrcweir             case CalendarFieldIndex::MONTH:             f = UCAL_MONTH; break;
381*cdf0e10cSrcweir             case CalendarFieldIndex::ERA:               f = UCAL_ERA; break;
382*cdf0e10cSrcweir             default: throw ERROR;
383*cdf0e10cSrcweir         }
384*cdf0e10cSrcweir         return f;
385*cdf0e10cSrcweir }
386*cdf0e10cSrcweir 
387*cdf0e10cSrcweir void SAL_CALL
388*cdf0e10cSrcweir Calendar_gregorian::setValue( sal_Int16 fieldIndex, sal_Int16 value ) throw(RuntimeException)
389*cdf0e10cSrcweir {
390*cdf0e10cSrcweir     if (fieldIndex < 0 || FIELD_INDEX_COUNT <= fieldIndex)
391*cdf0e10cSrcweir         throw ERROR;
392*cdf0e10cSrcweir     fieldSet |= (1 << fieldIndex);
393*cdf0e10cSrcweir     fieldValue[fieldIndex] = value;
394*cdf0e10cSrcweir }
395*cdf0e10cSrcweir 
396*cdf0e10cSrcweir bool Calendar_gregorian::getCombinedOffset( sal_Int32 & o_nOffset,
397*cdf0e10cSrcweir         sal_Int16 nParentFieldIndex, sal_Int16 nChildFieldIndex ) const
398*cdf0e10cSrcweir {
399*cdf0e10cSrcweir     o_nOffset = 0;
400*cdf0e10cSrcweir     bool bFieldsSet = false;
401*cdf0e10cSrcweir     if (fieldSet & (1 << nParentFieldIndex))
402*cdf0e10cSrcweir     {
403*cdf0e10cSrcweir         bFieldsSet = true;
404*cdf0e10cSrcweir         o_nOffset = static_cast<sal_Int32>( fieldValue[nParentFieldIndex]) * 60000;
405*cdf0e10cSrcweir     }
406*cdf0e10cSrcweir     if (fieldSet & (1 << nChildFieldIndex))
407*cdf0e10cSrcweir     {
408*cdf0e10cSrcweir         bFieldsSet = true;
409*cdf0e10cSrcweir         if (o_nOffset < 0)
410*cdf0e10cSrcweir             o_nOffset -= static_cast<sal_uInt16>( fieldValue[nChildFieldIndex]);
411*cdf0e10cSrcweir         else
412*cdf0e10cSrcweir             o_nOffset += static_cast<sal_uInt16>( fieldValue[nChildFieldIndex]);
413*cdf0e10cSrcweir     }
414*cdf0e10cSrcweir     return bFieldsSet;
415*cdf0e10cSrcweir }
416*cdf0e10cSrcweir 
417*cdf0e10cSrcweir bool Calendar_gregorian::getZoneOffset( sal_Int32 & o_nOffset ) const
418*cdf0e10cSrcweir {
419*cdf0e10cSrcweir     return getCombinedOffset( o_nOffset, CalendarFieldIndex::ZONE_OFFSET,
420*cdf0e10cSrcweir             CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS);
421*cdf0e10cSrcweir }
422*cdf0e10cSrcweir 
423*cdf0e10cSrcweir bool Calendar_gregorian::getDSTOffset( sal_Int32 & o_nOffset ) const
424*cdf0e10cSrcweir {
425*cdf0e10cSrcweir     return getCombinedOffset( o_nOffset, CalendarFieldIndex::DST_OFFSET,
426*cdf0e10cSrcweir             CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS);
427*cdf0e10cSrcweir }
428*cdf0e10cSrcweir 
429*cdf0e10cSrcweir void Calendar_gregorian::submitFields() throw(com::sun::star::uno::RuntimeException)
430*cdf0e10cSrcweir {
431*cdf0e10cSrcweir     for (sal_Int16 fieldIndex = 0; fieldIndex < FIELD_INDEX_COUNT; fieldIndex++)
432*cdf0e10cSrcweir     {
433*cdf0e10cSrcweir         if (fieldSet & (1 << fieldIndex))
434*cdf0e10cSrcweir         {
435*cdf0e10cSrcweir             switch (fieldIndex)
436*cdf0e10cSrcweir             {
437*cdf0e10cSrcweir                 default:
438*cdf0e10cSrcweir                     body->set(fieldNameConverter(fieldIndex), fieldSetValue[fieldIndex]);
439*cdf0e10cSrcweir                     break;
440*cdf0e10cSrcweir                 case CalendarFieldIndex::ZONE_OFFSET:
441*cdf0e10cSrcweir                 case CalendarFieldIndex::DST_OFFSET:
442*cdf0e10cSrcweir                 case CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS:
443*cdf0e10cSrcweir                 case CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS:
444*cdf0e10cSrcweir                     break;  // nothing, extra handling
445*cdf0e10cSrcweir             }
446*cdf0e10cSrcweir         }
447*cdf0e10cSrcweir     }
448*cdf0e10cSrcweir     sal_Int32 nZoneOffset, nDSTOffset;
449*cdf0e10cSrcweir     if (getZoneOffset( nZoneOffset))
450*cdf0e10cSrcweir         body->set( fieldNameConverter( CalendarFieldIndex::ZONE_OFFSET), nZoneOffset);
451*cdf0e10cSrcweir     if (getDSTOffset( nDSTOffset))
452*cdf0e10cSrcweir         body->set( fieldNameConverter( CalendarFieldIndex::DST_OFFSET), nDSTOffset);
453*cdf0e10cSrcweir }
454*cdf0e10cSrcweir 
455*cdf0e10cSrcweir void Calendar_gregorian::submitValues( sal_Int32 nYear,
456*cdf0e10cSrcweir         sal_Int32 nMonth, sal_Int32 nDay, sal_Int32 nHour, sal_Int32 nMinute,
457*cdf0e10cSrcweir         sal_Int32 nSecond, sal_Int32 nMilliSecond, sal_Int32 nZone, sal_Int32 nDST )
458*cdf0e10cSrcweir             throw(com::sun::star::uno::RuntimeException)
459*cdf0e10cSrcweir {
460*cdf0e10cSrcweir     submitFields();
461*cdf0e10cSrcweir     if (nYear >= 0)
462*cdf0e10cSrcweir         body->set( UCAL_YEAR, nYear);
463*cdf0e10cSrcweir     if (nMonth >= 0)
464*cdf0e10cSrcweir         body->set( UCAL_MONTH, nMonth);
465*cdf0e10cSrcweir     if (nDay >= 0)
466*cdf0e10cSrcweir         body->set( UCAL_DATE, nDay);
467*cdf0e10cSrcweir     if (nHour >= 0)
468*cdf0e10cSrcweir         body->set( UCAL_HOUR_OF_DAY, nHour);
469*cdf0e10cSrcweir     if (nMinute >= 0)
470*cdf0e10cSrcweir         body->set( UCAL_MINUTE, nMinute);
471*cdf0e10cSrcweir     if (nSecond >= 0)
472*cdf0e10cSrcweir         body->set( UCAL_SECOND, nSecond);
473*cdf0e10cSrcweir     if (nMilliSecond >= 0)
474*cdf0e10cSrcweir         body->set( UCAL_MILLISECOND, nMilliSecond);
475*cdf0e10cSrcweir     if (nZone != 0)
476*cdf0e10cSrcweir         body->set( UCAL_ZONE_OFFSET, nZone);
477*cdf0e10cSrcweir     if (nDST != 0)
478*cdf0e10cSrcweir         body->set( UCAL_DST_OFFSET, nDST);
479*cdf0e10cSrcweir }
480*cdf0e10cSrcweir 
481*cdf0e10cSrcweir static void lcl_setCombinedOffsetFieldValues( sal_Int32 nValue,
482*cdf0e10cSrcweir         sal_Int16 rFieldSetValue[], sal_Int16 rFieldValue[],
483*cdf0e10cSrcweir         sal_Int16 nParentFieldIndex, sal_Int16 nChildFieldIndex )
484*cdf0e10cSrcweir {
485*cdf0e10cSrcweir     sal_Int32 nTrunc = nValue / 60000;
486*cdf0e10cSrcweir     rFieldSetValue[nParentFieldIndex] = rFieldValue[nParentFieldIndex] =
487*cdf0e10cSrcweir         static_cast<sal_Int16>( nTrunc);
488*cdf0e10cSrcweir     sal_uInt16 nMillis = static_cast<sal_uInt16>( abs( nValue - nTrunc * 60000));
489*cdf0e10cSrcweir     rFieldSetValue[nChildFieldIndex] = rFieldValue[nChildFieldIndex] =
490*cdf0e10cSrcweir         static_cast<sal_Int16>( nMillis);
491*cdf0e10cSrcweir }
492*cdf0e10cSrcweir 
493*cdf0e10cSrcweir void Calendar_gregorian::setValue() throw(RuntimeException)
494*cdf0e10cSrcweir {
495*cdf0e10cSrcweir         // Correct DST glitch, see also localtime/gmtime conversion pitfalls at
496*cdf0e10cSrcweir         // http://www.erack.de/download/timetest.c
497*cdf0e10cSrcweir         //
498*cdf0e10cSrcweir         // #i24082# in order to make the DST correction work in all
499*cdf0e10cSrcweir         // circumstances, the time values have to be always resubmitted,
500*cdf0e10cSrcweir         // regardless whether specified by the caller or not. It is not
501*cdf0e10cSrcweir         // sufficient to rely on the ICU internal values previously set, as the
502*cdf0e10cSrcweir         // following may happen:
503*cdf0e10cSrcweir         // - Let 2004-03-28T02:00 be the onsetRule.
504*cdf0e10cSrcweir         // - On 2004-03-29 (calendar initialized with 2004-03-29T00:00 DST) set
505*cdf0e10cSrcweir         //   a date of 2004-03-28 => calendar results in 2004-03-27T23:00 no DST.
506*cdf0e10cSrcweir         // - Correcting this with simply "2004-03-28 no DST" and no time
507*cdf0e10cSrcweir         //   specified results in 2004-03-29T00:00, the ICU internal 23:00 time
508*cdf0e10cSrcweir         //   being adjusted to 24:00 in this case, switching one day further.
509*cdf0e10cSrcweir         // => submit 2004-03-28T00:00 no DST.
510*cdf0e10cSrcweir 
511*cdf0e10cSrcweir         // This got even weirder since ICU incorporated also historical data,
512*cdf0e10cSrcweir         // even the timezone may differ for different dates! It is necessary to
513*cdf0e10cSrcweir         // let ICU choose the corresponding OlsonTimeZone transitions and adapt
514*cdf0e10cSrcweir         // values.
515*cdf0e10cSrcweir         // #i86094# gives examples where that went wrong:
516*cdf0e10cSrcweir         // TZ=Europe/Moscow date <= 1919-07-01
517*cdf0e10cSrcweir         //      zone +2:30:48 (!) instead of +3h, DST +2h instead of +1h
518*cdf0e10cSrcweir         // TZ=America/St_Johns date <= 1935-03-30
519*cdf0e10cSrcweir         //      zone -3:30:52 (!) instead of -3:30
520*cdf0e10cSrcweir 
521*cdf0e10cSrcweir         // Copy fields before calling submitFields() directly or indirectly below.
522*cdf0e10cSrcweir         memcpy(fieldSetValue, fieldValue, sizeof(fieldSetValue));
523*cdf0e10cSrcweir         // Possibly setup ERA and YEAR in fieldSetValue.
524*cdf0e10cSrcweir         mapToGregorian();
525*cdf0e10cSrcweir 
526*cdf0e10cSrcweir         DUMP_ICU_CAL_MSG(("%s\n","setValue() before any submission"));
527*cdf0e10cSrcweir         DUMP_I18N_CAL_MSG(("%s\n","setValue() before any submission"));
528*cdf0e10cSrcweir 
529*cdf0e10cSrcweir         bool bNeedZone = !(fieldSet & (1 << CalendarFieldIndex::ZONE_OFFSET));
530*cdf0e10cSrcweir         bool bNeedDST  = !(fieldSet & (1 << CalendarFieldIndex::DST_OFFSET));
531*cdf0e10cSrcweir         sal_Int32 nZone1, nDST1, nYear, nMonth, nDay, nHour, nMinute, nSecond, nMilliSecond, nZone0, nDST0;
532*cdf0e10cSrcweir         nZone1 = nDST1 = nZone0 = nDST0 = 0;
533*cdf0e10cSrcweir         nYear = nMonth = nDay = nHour = nMinute = nSecond = nMilliSecond = -1;
534*cdf0e10cSrcweir         if ( bNeedZone || bNeedDST )
535*cdf0e10cSrcweir         {
536*cdf0e10cSrcweir             UErrorCode status;
537*cdf0e10cSrcweir             if ( !(fieldSet & (1 << CalendarFieldIndex::YEAR)) )
538*cdf0e10cSrcweir             {
539*cdf0e10cSrcweir                 nYear = body->get( UCAL_YEAR, status = U_ZERO_ERROR);
540*cdf0e10cSrcweir                 if ( !U_SUCCESS(status) )
541*cdf0e10cSrcweir                     nYear = -1;
542*cdf0e10cSrcweir             }
543*cdf0e10cSrcweir             if ( !(fieldSet & (1 << CalendarFieldIndex::MONTH)) )
544*cdf0e10cSrcweir             {
545*cdf0e10cSrcweir                 nMonth = body->get( UCAL_MONTH, status = U_ZERO_ERROR);
546*cdf0e10cSrcweir                 if ( !U_SUCCESS(status) )
547*cdf0e10cSrcweir                     nMonth = -1;
548*cdf0e10cSrcweir             }
549*cdf0e10cSrcweir             if ( !(fieldSet & (1 << CalendarFieldIndex::DAY_OF_MONTH)) )
550*cdf0e10cSrcweir             {
551*cdf0e10cSrcweir                 nDay = body->get( UCAL_DATE, status = U_ZERO_ERROR);
552*cdf0e10cSrcweir                 if ( !U_SUCCESS(status) )
553*cdf0e10cSrcweir                     nDay = -1;
554*cdf0e10cSrcweir             }
555*cdf0e10cSrcweir             if ( !(fieldSet & (1 << CalendarFieldIndex::HOUR)) )
556*cdf0e10cSrcweir             {
557*cdf0e10cSrcweir                 nHour = body->get( UCAL_HOUR_OF_DAY, status = U_ZERO_ERROR);
558*cdf0e10cSrcweir                 if ( !U_SUCCESS(status) )
559*cdf0e10cSrcweir                     nHour = -1;
560*cdf0e10cSrcweir             }
561*cdf0e10cSrcweir             if ( !(fieldSet & (1 << CalendarFieldIndex::MINUTE)) )
562*cdf0e10cSrcweir             {
563*cdf0e10cSrcweir                 nMinute = body->get( UCAL_MINUTE, status = U_ZERO_ERROR);
564*cdf0e10cSrcweir                 if ( !U_SUCCESS(status) )
565*cdf0e10cSrcweir                     nMinute = -1;
566*cdf0e10cSrcweir             }
567*cdf0e10cSrcweir             if ( !(fieldSet & (1 << CalendarFieldIndex::SECOND)) )
568*cdf0e10cSrcweir             {
569*cdf0e10cSrcweir                 nSecond = body->get( UCAL_SECOND, status = U_ZERO_ERROR);
570*cdf0e10cSrcweir                 if ( !U_SUCCESS(status) )
571*cdf0e10cSrcweir                     nSecond = -1;
572*cdf0e10cSrcweir             }
573*cdf0e10cSrcweir             if ( !(fieldSet & (1 << CalendarFieldIndex::MILLISECOND)) )
574*cdf0e10cSrcweir             {
575*cdf0e10cSrcweir                 nMilliSecond = body->get( UCAL_MILLISECOND, status = U_ZERO_ERROR);
576*cdf0e10cSrcweir                 if ( !U_SUCCESS(status) )
577*cdf0e10cSrcweir                     nMilliSecond = -1;
578*cdf0e10cSrcweir             }
579*cdf0e10cSrcweir             if ( !(fieldSet & (1 << CalendarFieldIndex::ZONE_OFFSET)) )
580*cdf0e10cSrcweir             {
581*cdf0e10cSrcweir                 nZone0 = body->get( UCAL_ZONE_OFFSET, status = U_ZERO_ERROR);
582*cdf0e10cSrcweir                 if ( !U_SUCCESS(status) )
583*cdf0e10cSrcweir                     nZone0 = 0;
584*cdf0e10cSrcweir             }
585*cdf0e10cSrcweir             if ( !(fieldSet & (1 << CalendarFieldIndex::DST_OFFSET)) )
586*cdf0e10cSrcweir             {
587*cdf0e10cSrcweir                 nDST0 = body->get( UCAL_DST_OFFSET, status = U_ZERO_ERROR);
588*cdf0e10cSrcweir                 if ( !U_SUCCESS(status) )
589*cdf0e10cSrcweir                     nDST0 = 0;
590*cdf0e10cSrcweir             }
591*cdf0e10cSrcweir 
592*cdf0e10cSrcweir             // Submit values to obtain a time zone and DST corresponding to the date/time.
593*cdf0e10cSrcweir             submitValues( nYear, nMonth, nDay, nHour, nMinute, nSecond, nMilliSecond, nZone0, nDST0);
594*cdf0e10cSrcweir 
595*cdf0e10cSrcweir             DUMP_ICU_CAL_MSG(("%s\n","setValue() in bNeedZone||bNeedDST after submitValues()"));
596*cdf0e10cSrcweir             DUMP_I18N_CAL_MSG(("%s\n","setValue() in bNeedZone||bNeedDST after submitValues()"));
597*cdf0e10cSrcweir             nZone1 = body->get( UCAL_ZONE_OFFSET, status = U_ZERO_ERROR);
598*cdf0e10cSrcweir             if ( !U_SUCCESS(status) )
599*cdf0e10cSrcweir                 nZone1 = 0;
600*cdf0e10cSrcweir             nDST1 = body->get( UCAL_DST_OFFSET, status = U_ZERO_ERROR);
601*cdf0e10cSrcweir             if ( !U_SUCCESS(status) )
602*cdf0e10cSrcweir                 nDST1 = 0;
603*cdf0e10cSrcweir         }
604*cdf0e10cSrcweir 
605*cdf0e10cSrcweir         // The original submission, may lead to a different zone/DST and
606*cdf0e10cSrcweir         // different date.
607*cdf0e10cSrcweir         submitFields();
608*cdf0e10cSrcweir         DUMP_ICU_CAL_MSG(("%s\n","setValue() after original submission"));
609*cdf0e10cSrcweir         DUMP_I18N_CAL_MSG(("%s\n","setValue() after original submission"));
610*cdf0e10cSrcweir 
611*cdf0e10cSrcweir         if ( bNeedZone || bNeedDST )
612*cdf0e10cSrcweir         {
613*cdf0e10cSrcweir             UErrorCode status;
614*cdf0e10cSrcweir             sal_Int32 nZone2 = body->get( UCAL_ZONE_OFFSET, status = U_ZERO_ERROR);
615*cdf0e10cSrcweir             if ( !U_SUCCESS(status) )
616*cdf0e10cSrcweir                 nZone2 = nZone1;
617*cdf0e10cSrcweir             sal_Int32 nDST2 = body->get( UCAL_DST_OFFSET, status = U_ZERO_ERROR);
618*cdf0e10cSrcweir             if ( !U_SUCCESS(status) )
619*cdf0e10cSrcweir                 nDST2 = nDST1;
620*cdf0e10cSrcweir             if ( nZone0 != nZone1 || nZone2 != nZone1 || nDST0 != nDST1 || nDST2 != nDST1 )
621*cdf0e10cSrcweir             {
622*cdf0e10cSrcweir                 // Due to different DSTs, resulting date values may differ if
623*cdf0e10cSrcweir                 // DST is onset at 00:00 and the very onsetRule date was
624*cdf0e10cSrcweir                 // submitted with DST off => date-1 23:00, for example, which
625*cdf0e10cSrcweir                 // is not what we want.
626*cdf0e10cSrcweir                 // Resubmit all values, this time including DST => date 01:00
627*cdf0e10cSrcweir                 // Similar for zone differences.
628*cdf0e10cSrcweir                 // If already the first full submission with nZone0 and nDST0
629*cdf0e10cSrcweir                 // lead to date-1 23:00, the original submission was based on
630*cdf0e10cSrcweir                 // that date if it wasn't a full date (nDST0 set, nDST1 not
631*cdf0e10cSrcweir                 // set, nDST2==nDST1). If it was January 1st without year we're
632*cdf0e10cSrcweir                 // even off by one year now. Resubmit all values including new
633*cdf0e10cSrcweir                 // DST => date 00:00.
634*cdf0e10cSrcweir 
635*cdf0e10cSrcweir                 // Set field values accordingly in case they were used.
636*cdf0e10cSrcweir                 if (!bNeedZone)
637*cdf0e10cSrcweir                     lcl_setCombinedOffsetFieldValues( nZone2, fieldSetValue,
638*cdf0e10cSrcweir                             fieldValue, CalendarFieldIndex::ZONE_OFFSET,
639*cdf0e10cSrcweir                             CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS);
640*cdf0e10cSrcweir                 if (!bNeedDST)
641*cdf0e10cSrcweir                     lcl_setCombinedOffsetFieldValues( nDST2, fieldSetValue,
642*cdf0e10cSrcweir                             fieldValue, CalendarFieldIndex::DST_OFFSET,
643*cdf0e10cSrcweir                             CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS);
644*cdf0e10cSrcweir                 submitValues( nYear, nMonth, nDay, nHour, nMinute, nSecond, nMilliSecond, nZone2, nDST2);
645*cdf0e10cSrcweir                 DUMP_ICU_CAL_MSG(("%s\n","setValue() after Zone/DST glitch resubmit"));
646*cdf0e10cSrcweir                 DUMP_I18N_CAL_MSG(("%s\n","setValue() after Zone/DST glitch resubmit"));
647*cdf0e10cSrcweir 
648*cdf0e10cSrcweir                 // Time zone transition => resubmit.
649*cdf0e10cSrcweir                 // TZ=America/St_Johns date <= 1935-03-30
650*cdf0e10cSrcweir                 //      -3:30:52 (!) instead of -3:30
651*cdf0e10cSrcweir                 //      if first submission included time zone -3:30 that would be wrong.
652*cdf0e10cSrcweir                 bool bResubmit = false;
653*cdf0e10cSrcweir                 sal_Int32 nZone3 = body->get( UCAL_ZONE_OFFSET, status = U_ZERO_ERROR);
654*cdf0e10cSrcweir                 if ( !U_SUCCESS(status) )
655*cdf0e10cSrcweir                     nZone3 = nZone2;
656*cdf0e10cSrcweir                 if (nZone3 != nZone2)
657*cdf0e10cSrcweir                 {
658*cdf0e10cSrcweir                     bResubmit = true;
659*cdf0e10cSrcweir                     if (!bNeedZone)
660*cdf0e10cSrcweir                         lcl_setCombinedOffsetFieldValues( nZone3, fieldSetValue,
661*cdf0e10cSrcweir                                 fieldValue, CalendarFieldIndex::ZONE_OFFSET,
662*cdf0e10cSrcweir                                 CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS);
663*cdf0e10cSrcweir                 }
664*cdf0e10cSrcweir 
665*cdf0e10cSrcweir                 // If the DST onset rule says to switch from 00:00 to 01:00 and
666*cdf0e10cSrcweir                 // we tried to set onsetDay 00:00 with DST, the result was
667*cdf0e10cSrcweir                 // onsetDay-1 23:00 and no DST, which is not what we want. So
668*cdf0e10cSrcweir                 // once again without DST, resulting in onsetDay 01:00 and DST.
669*cdf0e10cSrcweir                 // Yes, this seems to be weird, but logically correct.
670*cdf0e10cSrcweir                 // It doesn't even have to be on an onsetDay as the DST is
671*cdf0e10cSrcweir                 // factored in all days by ICU and there seems to be some
672*cdf0e10cSrcweir                 // unknown behavior.
673*cdf0e10cSrcweir                 // TZ=Asia/Tehran 1999-03-22 exposes this, for example.
674*cdf0e10cSrcweir                 sal_Int32 nDST3 = body->get( UCAL_DST_OFFSET, status = U_ZERO_ERROR);
675*cdf0e10cSrcweir                 if ( !U_SUCCESS(status) )
676*cdf0e10cSrcweir                     nDST3 = nDST2;
677*cdf0e10cSrcweir                 if (nDST2 != nDST3 && !nDST3)
678*cdf0e10cSrcweir                 {
679*cdf0e10cSrcweir                     bResubmit = true;
680*cdf0e10cSrcweir                     if (!bNeedDST)
681*cdf0e10cSrcweir                     {
682*cdf0e10cSrcweir                         fieldSetValue[CalendarFieldIndex::DST_OFFSET] =
683*cdf0e10cSrcweir                             fieldValue[CalendarFieldIndex::DST_OFFSET] = 0;
684*cdf0e10cSrcweir                         fieldSetValue[CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS] =
685*cdf0e10cSrcweir                             fieldValue[CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS] = 0;
686*cdf0e10cSrcweir                     }
687*cdf0e10cSrcweir                 }
688*cdf0e10cSrcweir                 if (bResubmit)
689*cdf0e10cSrcweir                 {
690*cdf0e10cSrcweir                     submitValues( nYear, nMonth, nDay, nHour, nMinute, nSecond, nMilliSecond, nZone3, nDST3);
691*cdf0e10cSrcweir                     DUMP_ICU_CAL_MSG(("%s\n","setValue() after Zone/DST glitch 2nd resubmit"));
692*cdf0e10cSrcweir                     DUMP_I18N_CAL_MSG(("%s\n","setValue() after Zone/DST glitch 2nd resubmit"));
693*cdf0e10cSrcweir                 }
694*cdf0e10cSrcweir             }
695*cdf0e10cSrcweir         }
696*cdf0e10cSrcweir #if erDUMP_ICU_CALENDAR || erDUMP_I18N_CALENDAR
697*cdf0e10cSrcweir         {
698*cdf0e10cSrcweir             // force icu::Calendar to recalculate
699*cdf0e10cSrcweir             UErrorCode status;
700*cdf0e10cSrcweir             sal_Int32 nTmp = body->get( UCAL_DATE, status = U_ZERO_ERROR);
701*cdf0e10cSrcweir             DUMP_ICU_CAL_MSG(("%s: %d\n","setValue() result day",nTmp));
702*cdf0e10cSrcweir             DUMP_I18N_CAL_MSG(("%s: %d\n","setValue() result day",nTmp));
703*cdf0e10cSrcweir         }
704*cdf0e10cSrcweir #endif
705*cdf0e10cSrcweir }
706*cdf0e10cSrcweir 
707*cdf0e10cSrcweir void Calendar_gregorian::getValue() throw(RuntimeException)
708*cdf0e10cSrcweir {
709*cdf0e10cSrcweir     DUMP_ICU_CAL_MSG(("%s\n","getValue()"));
710*cdf0e10cSrcweir     DUMP_I18N_CAL_MSG(("%s\n","getValue()"));
711*cdf0e10cSrcweir     for (sal_Int16 fieldIndex = 0; fieldIndex < FIELD_INDEX_COUNT; fieldIndex++)
712*cdf0e10cSrcweir     {
713*cdf0e10cSrcweir         if (fieldIndex == CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS ||
714*cdf0e10cSrcweir                 fieldIndex == CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS)
715*cdf0e10cSrcweir             continue;   // not ICU fields
716*cdf0e10cSrcweir 
717*cdf0e10cSrcweir         UErrorCode status; sal_Int32 value = body->get( fieldNameConverter(
718*cdf0e10cSrcweir                     fieldIndex), status = U_ZERO_ERROR);
719*cdf0e10cSrcweir         if ( !U_SUCCESS(status) ) throw ERROR;
720*cdf0e10cSrcweir 
721*cdf0e10cSrcweir         // Convert millisecond to minute for ZONE and DST and set remainder in
722*cdf0e10cSrcweir         // second field.
723*cdf0e10cSrcweir         if (fieldIndex == CalendarFieldIndex::ZONE_OFFSET)
724*cdf0e10cSrcweir         {
725*cdf0e10cSrcweir             sal_Int32 nMinutes = value / 60000;
726*cdf0e10cSrcweir             sal_Int16 nMillis = static_cast<sal_Int16>( static_cast<sal_uInt16>(
727*cdf0e10cSrcweir                         abs( value - nMinutes * 60000)));
728*cdf0e10cSrcweir             fieldValue[CalendarFieldIndex::ZONE_OFFSET] = static_cast<sal_Int16>( nMinutes);
729*cdf0e10cSrcweir             fieldValue[CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS] = nMillis;
730*cdf0e10cSrcweir         }
731*cdf0e10cSrcweir         else if (fieldIndex == CalendarFieldIndex::DST_OFFSET)
732*cdf0e10cSrcweir         {
733*cdf0e10cSrcweir             sal_Int32 nMinutes = value / 60000;
734*cdf0e10cSrcweir             sal_Int16 nMillis = static_cast<sal_Int16>( static_cast<sal_uInt16>(
735*cdf0e10cSrcweir                         abs( value - nMinutes * 60000)));
736*cdf0e10cSrcweir             fieldValue[CalendarFieldIndex::DST_OFFSET] = static_cast<sal_Int16>( nMinutes);
737*cdf0e10cSrcweir             fieldValue[CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS] = nMillis;
738*cdf0e10cSrcweir         }
739*cdf0e10cSrcweir         else
740*cdf0e10cSrcweir             fieldValue[fieldIndex] = (sal_Int16) value;
741*cdf0e10cSrcweir 
742*cdf0e10cSrcweir         // offset 1 since the value for week start day SunDay is different between Calendar and Weekdays.
743*cdf0e10cSrcweir         if ( fieldIndex == CalendarFieldIndex::DAY_OF_WEEK )
744*cdf0e10cSrcweir             fieldValue[fieldIndex]--;   // UCAL_SUNDAY:/* == 1 */ ==> Weekdays::SUNDAY /* ==0 */
745*cdf0e10cSrcweir     }
746*cdf0e10cSrcweir     mapFromGregorian();
747*cdf0e10cSrcweir     fieldSet = 0;
748*cdf0e10cSrcweir }
749*cdf0e10cSrcweir 
750*cdf0e10cSrcweir sal_Int16 SAL_CALL
751*cdf0e10cSrcweir Calendar_gregorian::getValue( sal_Int16 fieldIndex ) throw(RuntimeException)
752*cdf0e10cSrcweir {
753*cdf0e10cSrcweir     if (fieldIndex < 0 || FIELD_INDEX_COUNT <= fieldIndex)
754*cdf0e10cSrcweir         throw ERROR;
755*cdf0e10cSrcweir 
756*cdf0e10cSrcweir     if (fieldSet)  {
757*cdf0e10cSrcweir         setValue();
758*cdf0e10cSrcweir         getValue();
759*cdf0e10cSrcweir     }
760*cdf0e10cSrcweir 
761*cdf0e10cSrcweir     return fieldValue[fieldIndex];
762*cdf0e10cSrcweir }
763*cdf0e10cSrcweir 
764*cdf0e10cSrcweir void SAL_CALL
765*cdf0e10cSrcweir Calendar_gregorian::addValue( sal_Int16 fieldIndex, sal_Int32 value ) throw(RuntimeException)
766*cdf0e10cSrcweir {
767*cdf0e10cSrcweir         // since ZONE and DST could not be add, we don't need to convert value here
768*cdf0e10cSrcweir         UErrorCode status;
769*cdf0e10cSrcweir         body->add(fieldNameConverter(fieldIndex), value, status = U_ZERO_ERROR);
770*cdf0e10cSrcweir         if ( !U_SUCCESS(status) ) throw ERROR;
771*cdf0e10cSrcweir         getValue();
772*cdf0e10cSrcweir }
773*cdf0e10cSrcweir 
774*cdf0e10cSrcweir sal_Bool SAL_CALL
775*cdf0e10cSrcweir Calendar_gregorian::isValid() throw(RuntimeException)
776*cdf0e10cSrcweir {
777*cdf0e10cSrcweir         if (fieldSet) {
778*cdf0e10cSrcweir             sal_Int32 tmp = fieldSet;
779*cdf0e10cSrcweir             setValue();
780*cdf0e10cSrcweir             memcpy(fieldSetValue, fieldValue, sizeof(fieldSetValue));
781*cdf0e10cSrcweir             getValue();
782*cdf0e10cSrcweir             for ( sal_Int16 fieldIndex = 0; fieldIndex < FIELD_INDEX_COUNT; fieldIndex++ ) {
783*cdf0e10cSrcweir                 // compare only with fields that are set and reset fieldSet[]
784*cdf0e10cSrcweir                 if (tmp & (1 << fieldIndex)) {
785*cdf0e10cSrcweir                     if (fieldSetValue[fieldIndex] != fieldValue[fieldIndex])
786*cdf0e10cSrcweir                         return sal_False;
787*cdf0e10cSrcweir                 }
788*cdf0e10cSrcweir             }
789*cdf0e10cSrcweir         }
790*cdf0e10cSrcweir         return true;
791*cdf0e10cSrcweir }
792*cdf0e10cSrcweir 
793*cdf0e10cSrcweir // NativeNumberMode has different meaning between Number and Calendar for Asian locales.
794*cdf0e10cSrcweir // Here is the mapping table
795*cdf0e10cSrcweir // calendar(q/y/m/d)    zh_CN           zh_TW           ja              ko
796*cdf0e10cSrcweir // NatNum1              NatNum1/1/7/7   NatNum1/1/7/7   NatNum1/1/4/4   NatNum1/1/7/7
797*cdf0e10cSrcweir // NatNum2              NatNum2/2/8/8   NatNum2/2/8/8   NatNum2/2/5/5   NatNum2/2/8/8
798*cdf0e10cSrcweir // NatNum3              NatNum3/3/3/3   NatNum3/3/3/3   NatNum3/3/3/3   NatNum3/3/3/3
799*cdf0e10cSrcweir // NatNum4                                                              NatNum9/9/11/11
800*cdf0e10cSrcweir 
801*cdf0e10cSrcweir static sal_Int16 SAL_CALL NatNumForCalendar(const com::sun::star::lang::Locale& aLocale,
802*cdf0e10cSrcweir         sal_Int32 nCalendarDisplayCode, sal_Int16 nNativeNumberMode, sal_Int16 value )
803*cdf0e10cSrcweir {
804*cdf0e10cSrcweir     sal_Bool isShort = ((nCalendarDisplayCode == CalendarDisplayCode::SHORT_YEAR ||
805*cdf0e10cSrcweir         nCalendarDisplayCode == CalendarDisplayCode::LONG_YEAR) && value >= 100) ||
806*cdf0e10cSrcweir         nCalendarDisplayCode == CalendarDisplayCode::SHORT_QUARTER ||
807*cdf0e10cSrcweir         nCalendarDisplayCode == CalendarDisplayCode::LONG_QUARTER;
808*cdf0e10cSrcweir     sal_Bool isChinese = aLocale.Language.equalsAscii("zh");
809*cdf0e10cSrcweir     sal_Bool isJapanese = aLocale.Language.equalsAscii("ja");
810*cdf0e10cSrcweir     sal_Bool isKorean = aLocale.Language.equalsAscii("ko");
811*cdf0e10cSrcweir 
812*cdf0e10cSrcweir     if (isChinese || isJapanese || isKorean) {
813*cdf0e10cSrcweir         switch (nNativeNumberMode) {
814*cdf0e10cSrcweir             case NativeNumberMode::NATNUM1:
815*cdf0e10cSrcweir                 if (!isShort)
816*cdf0e10cSrcweir                     nNativeNumberMode = isJapanese ? NativeNumberMode::NATNUM4 : NativeNumberMode::NATNUM7;
817*cdf0e10cSrcweir                 break;
818*cdf0e10cSrcweir             case NativeNumberMode::NATNUM2:
819*cdf0e10cSrcweir                 if (!isShort)
820*cdf0e10cSrcweir                     nNativeNumberMode = isJapanese ? NativeNumberMode::NATNUM5 : NativeNumberMode::NATNUM8;
821*cdf0e10cSrcweir                 break;
822*cdf0e10cSrcweir             case NativeNumberMode::NATNUM3:
823*cdf0e10cSrcweir                 break;
824*cdf0e10cSrcweir             case NativeNumberMode::NATNUM4:
825*cdf0e10cSrcweir                 if (isKorean)
826*cdf0e10cSrcweir                     return isShort ? NativeNumberMode::NATNUM9 : NativeNumberMode::NATNUM11;
827*cdf0e10cSrcweir                 // fall through
828*cdf0e10cSrcweir             default: return 0;
829*cdf0e10cSrcweir         }
830*cdf0e10cSrcweir     }
831*cdf0e10cSrcweir     return nNativeNumberMode;
832*cdf0e10cSrcweir }
833*cdf0e10cSrcweir 
834*cdf0e10cSrcweir static sal_Int32 SAL_CALL DisplayCode2FieldIndex(sal_Int32 nCalendarDisplayCode)
835*cdf0e10cSrcweir {
836*cdf0e10cSrcweir     switch( nCalendarDisplayCode ) {
837*cdf0e10cSrcweir         case CalendarDisplayCode::SHORT_DAY:
838*cdf0e10cSrcweir         case CalendarDisplayCode::LONG_DAY:
839*cdf0e10cSrcweir             return CalendarFieldIndex::DAY_OF_MONTH;
840*cdf0e10cSrcweir         case CalendarDisplayCode::SHORT_DAY_NAME:
841*cdf0e10cSrcweir         case CalendarDisplayCode::LONG_DAY_NAME:
842*cdf0e10cSrcweir             return CalendarFieldIndex::DAY_OF_WEEK;
843*cdf0e10cSrcweir         case CalendarDisplayCode::SHORT_QUARTER:
844*cdf0e10cSrcweir         case CalendarDisplayCode::LONG_QUARTER:
845*cdf0e10cSrcweir         case CalendarDisplayCode::SHORT_MONTH:
846*cdf0e10cSrcweir         case CalendarDisplayCode::LONG_MONTH:
847*cdf0e10cSrcweir         case CalendarDisplayCode::SHORT_MONTH_NAME:
848*cdf0e10cSrcweir         case CalendarDisplayCode::LONG_MONTH_NAME:
849*cdf0e10cSrcweir             return CalendarFieldIndex::MONTH;
850*cdf0e10cSrcweir         case CalendarDisplayCode::SHORT_YEAR:
851*cdf0e10cSrcweir         case CalendarDisplayCode::LONG_YEAR:
852*cdf0e10cSrcweir             return CalendarFieldIndex::YEAR;
853*cdf0e10cSrcweir         case CalendarDisplayCode::SHORT_ERA:
854*cdf0e10cSrcweir         case CalendarDisplayCode::LONG_ERA:
855*cdf0e10cSrcweir             return CalendarFieldIndex::ERA;
856*cdf0e10cSrcweir         case CalendarDisplayCode::SHORT_YEAR_AND_ERA:
857*cdf0e10cSrcweir         case CalendarDisplayCode::LONG_YEAR_AND_ERA:
858*cdf0e10cSrcweir             return CalendarFieldIndex::YEAR;
859*cdf0e10cSrcweir         default:
860*cdf0e10cSrcweir             return 0;
861*cdf0e10cSrcweir     }
862*cdf0e10cSrcweir }
863*cdf0e10cSrcweir 
864*cdf0e10cSrcweir sal_Int16 SAL_CALL
865*cdf0e10cSrcweir Calendar_gregorian::getFirstDayOfWeek() throw(RuntimeException)
866*cdf0e10cSrcweir {
867*cdf0e10cSrcweir     // UCAL_SUNDAY == 1, Weekdays::SUNDAY == 0 => offset -1
868*cdf0e10cSrcweir     // Check for underflow just in case we're called "out of sync".
869*cdf0e10cSrcweir     return ::std::max( sal::static_int_cast<sal_Int16>(0),
870*cdf0e10cSrcweir             sal::static_int_cast<sal_Int16>( static_cast<sal_Int16>(
871*cdf0e10cSrcweir                     body->getFirstDayOfWeek()) - 1));
872*cdf0e10cSrcweir }
873*cdf0e10cSrcweir 
874*cdf0e10cSrcweir void SAL_CALL
875*cdf0e10cSrcweir Calendar_gregorian::setFirstDayOfWeek( sal_Int16 day )
876*cdf0e10cSrcweir throw(RuntimeException)
877*cdf0e10cSrcweir {
878*cdf0e10cSrcweir     // Weekdays::SUNDAY == 0, UCAL_SUNDAY == 1 => offset +1
879*cdf0e10cSrcweir     body->setFirstDayOfWeek( static_cast<UCalendarDaysOfWeek>( day + 1));
880*cdf0e10cSrcweir }
881*cdf0e10cSrcweir 
882*cdf0e10cSrcweir void SAL_CALL
883*cdf0e10cSrcweir Calendar_gregorian::setMinimumNumberOfDaysForFirstWeek( sal_Int16 days ) throw(RuntimeException)
884*cdf0e10cSrcweir {
885*cdf0e10cSrcweir         aCalendar.MinimumNumberOfDaysForFirstWeek = days;
886*cdf0e10cSrcweir         body->setMinimalDaysInFirstWeek( static_cast<uint8_t>( days));
887*cdf0e10cSrcweir }
888*cdf0e10cSrcweir 
889*cdf0e10cSrcweir sal_Int16 SAL_CALL
890*cdf0e10cSrcweir Calendar_gregorian::getMinimumNumberOfDaysForFirstWeek() throw(RuntimeException)
891*cdf0e10cSrcweir {
892*cdf0e10cSrcweir         return aCalendar.MinimumNumberOfDaysForFirstWeek;
893*cdf0e10cSrcweir }
894*cdf0e10cSrcweir 
895*cdf0e10cSrcweir sal_Int16 SAL_CALL
896*cdf0e10cSrcweir Calendar_gregorian::getNumberOfMonthsInYear() throw(RuntimeException)
897*cdf0e10cSrcweir {
898*cdf0e10cSrcweir         return (sal_Int16) aCalendar.Months.getLength();
899*cdf0e10cSrcweir }
900*cdf0e10cSrcweir 
901*cdf0e10cSrcweir 
902*cdf0e10cSrcweir sal_Int16 SAL_CALL
903*cdf0e10cSrcweir Calendar_gregorian::getNumberOfDaysInWeek() throw(RuntimeException)
904*cdf0e10cSrcweir {
905*cdf0e10cSrcweir         return (sal_Int16) aCalendar.Days.getLength();
906*cdf0e10cSrcweir }
907*cdf0e10cSrcweir 
908*cdf0e10cSrcweir 
909*cdf0e10cSrcweir Sequence< CalendarItem > SAL_CALL
910*cdf0e10cSrcweir Calendar_gregorian::getMonths() throw(RuntimeException)
911*cdf0e10cSrcweir {
912*cdf0e10cSrcweir         return aCalendar.Months;
913*cdf0e10cSrcweir }
914*cdf0e10cSrcweir 
915*cdf0e10cSrcweir 
916*cdf0e10cSrcweir Sequence< CalendarItem > SAL_CALL
917*cdf0e10cSrcweir Calendar_gregorian::getDays() throw(RuntimeException)
918*cdf0e10cSrcweir {
919*cdf0e10cSrcweir         return aCalendar.Days;
920*cdf0e10cSrcweir }
921*cdf0e10cSrcweir 
922*cdf0e10cSrcweir OUString SAL_CALL
923*cdf0e10cSrcweir Calendar_gregorian::getDisplayName( sal_Int16 displayIndex, sal_Int16 idx, sal_Int16 nameType ) throw(RuntimeException)
924*cdf0e10cSrcweir {
925*cdf0e10cSrcweir         OUString aStr;
926*cdf0e10cSrcweir 
927*cdf0e10cSrcweir         switch( displayIndex ) {
928*cdf0e10cSrcweir             case CalendarDisplayIndex::AM_PM:/* ==0 */
929*cdf0e10cSrcweir                 if (idx == 0) aStr = LocaleData().getLocaleItem(aLocale).timeAM;
930*cdf0e10cSrcweir                 else if (idx == 1) aStr = LocaleData().getLocaleItem(aLocale).timePM;
931*cdf0e10cSrcweir                 else throw ERROR;
932*cdf0e10cSrcweir                 break;
933*cdf0e10cSrcweir             case CalendarDisplayIndex::DAY:
934*cdf0e10cSrcweir                 if( idx >= aCalendar.Days.getLength() ) throw ERROR;
935*cdf0e10cSrcweir                 if (nameType == 0) aStr = aCalendar.Days[idx].AbbrevName;
936*cdf0e10cSrcweir                 else if (nameType == 1) aStr = aCalendar.Days[idx].FullName;
937*cdf0e10cSrcweir                 else throw ERROR;
938*cdf0e10cSrcweir                 break;
939*cdf0e10cSrcweir             case CalendarDisplayIndex::MONTH:
940*cdf0e10cSrcweir                 if( idx >= aCalendar.Months.getLength() ) throw ERROR;
941*cdf0e10cSrcweir                 if (nameType == 0) aStr = aCalendar.Months[idx].AbbrevName;
942*cdf0e10cSrcweir                 else if (nameType == 1) aStr = aCalendar.Months[idx].FullName;
943*cdf0e10cSrcweir                 else throw ERROR;
944*cdf0e10cSrcweir                 break;
945*cdf0e10cSrcweir             case CalendarDisplayIndex::ERA:
946*cdf0e10cSrcweir                 if( idx >= aCalendar.Eras.getLength() ) throw ERROR;
947*cdf0e10cSrcweir                 if (nameType == 0) aStr = aCalendar.Eras[idx].AbbrevName;
948*cdf0e10cSrcweir                 else if (nameType == 1) aStr = aCalendar.Eras[idx].FullName;
949*cdf0e10cSrcweir                 else throw ERROR;
950*cdf0e10cSrcweir                 break;
951*cdf0e10cSrcweir             case CalendarDisplayIndex::YEAR:
952*cdf0e10cSrcweir                 break;
953*cdf0e10cSrcweir             default:
954*cdf0e10cSrcweir                 throw ERROR;
955*cdf0e10cSrcweir         }
956*cdf0e10cSrcweir         return aStr;
957*cdf0e10cSrcweir }
958*cdf0e10cSrcweir 
959*cdf0e10cSrcweir // Methods in XExtendedCalendar
960*cdf0e10cSrcweir OUString SAL_CALL
961*cdf0e10cSrcweir Calendar_gregorian::getDisplayString( sal_Int32 nCalendarDisplayCode, sal_Int16 nNativeNumberMode )
962*cdf0e10cSrcweir         throw (RuntimeException)
963*cdf0e10cSrcweir {
964*cdf0e10cSrcweir     sal_Int16 value = getValue(sal::static_int_cast<sal_Int16>( DisplayCode2FieldIndex(nCalendarDisplayCode) ));
965*cdf0e10cSrcweir     OUString aOUStr;
966*cdf0e10cSrcweir 
967*cdf0e10cSrcweir     if (nCalendarDisplayCode == CalendarDisplayCode::SHORT_QUARTER ||
968*cdf0e10cSrcweir             nCalendarDisplayCode == CalendarDisplayCode::LONG_QUARTER) {
969*cdf0e10cSrcweir         Sequence< OUString> xR = LocaleData().getReservedWord(aLocale);
970*cdf0e10cSrcweir         sal_Int16 quarter = value / 3;
971*cdf0e10cSrcweir         // Since this base class method may be called by derived calendar
972*cdf0e10cSrcweir         // classes where a year consists of more than 12 months we need a check
973*cdf0e10cSrcweir         // to not run out of bounds of reserved quarter words. Perhaps a more
974*cdf0e10cSrcweir         // clean way (instead of dividing by 3) would be to first get the
975*cdf0e10cSrcweir         // number of months, divide by 4 and then use that result to divide the
976*cdf0e10cSrcweir         // actual month value.
977*cdf0e10cSrcweir         if ( quarter > 3 )
978*cdf0e10cSrcweir             quarter = 3;
979*cdf0e10cSrcweir         quarter = sal::static_int_cast<sal_Int16>( quarter +
980*cdf0e10cSrcweir             ((nCalendarDisplayCode == CalendarDisplayCode::SHORT_QUARTER) ?
981*cdf0e10cSrcweir             reservedWords::QUARTER1_ABBREVIATION : reservedWords::QUARTER1_WORD) );
982*cdf0e10cSrcweir         aOUStr = xR[quarter];
983*cdf0e10cSrcweir     } else {
984*cdf0e10cSrcweir         // The "#100211# - checked" comments serve for detection of "use of
985*cdf0e10cSrcweir         // sprintf is safe here" conditions. An sprintf encountered without
986*cdf0e10cSrcweir         // having that comment triggers alarm ;-)
987*cdf0e10cSrcweir         sal_Char aStr[10];
988*cdf0e10cSrcweir         switch( nCalendarDisplayCode ) {
989*cdf0e10cSrcweir             case CalendarDisplayCode::SHORT_MONTH:
990*cdf0e10cSrcweir                 value += 1;     // month is zero based
991*cdf0e10cSrcweir                 // fall thru
992*cdf0e10cSrcweir             case CalendarDisplayCode::SHORT_DAY:
993*cdf0e10cSrcweir                 sprintf(aStr, "%d", value);     // #100211# - checked
994*cdf0e10cSrcweir                 break;
995*cdf0e10cSrcweir             case CalendarDisplayCode::LONG_YEAR:
996*cdf0e10cSrcweir                 if (aCalendar.Name.equalsAscii("gengou"))
997*cdf0e10cSrcweir                     sprintf(aStr, "%02d", value);     // #100211# - checked
998*cdf0e10cSrcweir                 else
999*cdf0e10cSrcweir                     sprintf(aStr, "%d", value);     // #100211# - checked
1000*cdf0e10cSrcweir                 break;
1001*cdf0e10cSrcweir             case CalendarDisplayCode::LONG_MONTH:
1002*cdf0e10cSrcweir                 value += 1;     // month is zero based
1003*cdf0e10cSrcweir                 sprintf(aStr, "%02d", value);   // #100211# - checked
1004*cdf0e10cSrcweir                 break;
1005*cdf0e10cSrcweir             case CalendarDisplayCode::SHORT_YEAR:
1006*cdf0e10cSrcweir                 // Take last 2 digits, or only one if value<10, for example,
1007*cdf0e10cSrcweir                 // in case of the Gengou calendar.
1008*cdf0e10cSrcweir                 // #i116701# For values in non-Gregorian era years use all
1009*cdf0e10cSrcweir                 // digits.
1010*cdf0e10cSrcweir                 if (value < 100 || eraArray)
1011*cdf0e10cSrcweir                     sprintf(aStr, "%d", value); // #100211# - checked
1012*cdf0e10cSrcweir                 else
1013*cdf0e10cSrcweir                     sprintf(aStr, "%02d", value % 100); // #100211# - checked
1014*cdf0e10cSrcweir                 break;
1015*cdf0e10cSrcweir             case CalendarDisplayCode::LONG_DAY:
1016*cdf0e10cSrcweir                 sprintf(aStr, "%02d", value);   // #100211# - checked
1017*cdf0e10cSrcweir                 break;
1018*cdf0e10cSrcweir 
1019*cdf0e10cSrcweir             case CalendarDisplayCode::SHORT_DAY_NAME:
1020*cdf0e10cSrcweir                 return getDisplayName(CalendarDisplayIndex::DAY, value, 0);
1021*cdf0e10cSrcweir             case CalendarDisplayCode::LONG_DAY_NAME:
1022*cdf0e10cSrcweir                 return getDisplayName(CalendarDisplayIndex::DAY, value, 1);
1023*cdf0e10cSrcweir             case CalendarDisplayCode::SHORT_MONTH_NAME:
1024*cdf0e10cSrcweir                 return getDisplayName(CalendarDisplayIndex::MONTH, value, 0);
1025*cdf0e10cSrcweir             case CalendarDisplayCode::LONG_MONTH_NAME:
1026*cdf0e10cSrcweir                 return getDisplayName(CalendarDisplayIndex::MONTH, value, 1);
1027*cdf0e10cSrcweir             case CalendarDisplayCode::SHORT_ERA:
1028*cdf0e10cSrcweir                 return getDisplayName(CalendarDisplayIndex::ERA, value, 0);
1029*cdf0e10cSrcweir             case CalendarDisplayCode::LONG_ERA:
1030*cdf0e10cSrcweir                 return getDisplayName(CalendarDisplayIndex::ERA, value, 1);
1031*cdf0e10cSrcweir 
1032*cdf0e10cSrcweir             case CalendarDisplayCode::SHORT_YEAR_AND_ERA:
1033*cdf0e10cSrcweir                 return  getDisplayString( CalendarDisplayCode::SHORT_ERA, nNativeNumberMode ) +
1034*cdf0e10cSrcweir                     getDisplayString( CalendarDisplayCode::SHORT_YEAR, nNativeNumberMode );
1035*cdf0e10cSrcweir 
1036*cdf0e10cSrcweir             case CalendarDisplayCode::LONG_YEAR_AND_ERA:
1037*cdf0e10cSrcweir                 return  getDisplayString( CalendarDisplayCode::LONG_ERA, nNativeNumberMode ) +
1038*cdf0e10cSrcweir                     getDisplayString( CalendarDisplayCode::LONG_YEAR, nNativeNumberMode );
1039*cdf0e10cSrcweir 
1040*cdf0e10cSrcweir             default:
1041*cdf0e10cSrcweir                 throw ERROR;
1042*cdf0e10cSrcweir         }
1043*cdf0e10cSrcweir         aOUStr = OUString::createFromAscii(aStr);
1044*cdf0e10cSrcweir     }
1045*cdf0e10cSrcweir     if (nNativeNumberMode > 0) {
1046*cdf0e10cSrcweir         // For Japanese calendar, first year calls GAN, see bug 111668 for detail.
1047*cdf0e10cSrcweir 		if (eraArray == gengou_eraArray && value == 1
1048*cdf0e10cSrcweir 			&& (nCalendarDisplayCode == CalendarDisplayCode::SHORT_YEAR ||
1049*cdf0e10cSrcweir 				nCalendarDisplayCode == CalendarDisplayCode::LONG_YEAR)
1050*cdf0e10cSrcweir 			&& (nNativeNumberMode == NativeNumberMode::NATNUM1 ||
1051*cdf0e10cSrcweir 				nNativeNumberMode == NativeNumberMode::NATNUM2)) {
1052*cdf0e10cSrcweir 			static sal_Unicode gan = 0x5143;
1053*cdf0e10cSrcweir 			return OUString(&gan, 1);
1054*cdf0e10cSrcweir 		}
1055*cdf0e10cSrcweir         sal_Int16 nNatNum = NatNumForCalendar(aLocale, nCalendarDisplayCode, nNativeNumberMode, value);
1056*cdf0e10cSrcweir         if (nNatNum > 0)
1057*cdf0e10cSrcweir             return aNatNum.getNativeNumberString(aOUStr, aLocale, nNatNum);
1058*cdf0e10cSrcweir     }
1059*cdf0e10cSrcweir     return aOUStr;
1060*cdf0e10cSrcweir }
1061*cdf0e10cSrcweir 
1062*cdf0e10cSrcweir // Methods in XExtendedCalendar
1063*cdf0e10cSrcweir OUString SAL_CALL
1064*cdf0e10cSrcweir Calendar_buddhist::getDisplayString( sal_Int32 nCalendarDisplayCode, sal_Int16 nNativeNumberMode )
1065*cdf0e10cSrcweir         throw (RuntimeException)
1066*cdf0e10cSrcweir {
1067*cdf0e10cSrcweir     // make year and era in different order for year before and after 0.
1068*cdf0e10cSrcweir     if ((nCalendarDisplayCode == CalendarDisplayCode::LONG_YEAR_AND_ERA ||
1069*cdf0e10cSrcweir                 nCalendarDisplayCode == CalendarDisplayCode::SHORT_YEAR_AND_ERA) &&
1070*cdf0e10cSrcweir             getValue(CalendarFieldIndex::ERA) == 0) {
1071*cdf0e10cSrcweir         if (nCalendarDisplayCode == CalendarDisplayCode::LONG_YEAR_AND_ERA)
1072*cdf0e10cSrcweir             return  getDisplayString( CalendarDisplayCode::SHORT_YEAR, nNativeNumberMode ) +
1073*cdf0e10cSrcweir                 getDisplayString( CalendarDisplayCode::SHORT_ERA, nNativeNumberMode );
1074*cdf0e10cSrcweir         else
1075*cdf0e10cSrcweir             return  getDisplayString( CalendarDisplayCode::LONG_YEAR, nNativeNumberMode ) +
1076*cdf0e10cSrcweir                 getDisplayString( CalendarDisplayCode::LONG_ERA, nNativeNumberMode );
1077*cdf0e10cSrcweir     }
1078*cdf0e10cSrcweir     return Calendar_gregorian::getDisplayString(nCalendarDisplayCode, nNativeNumberMode);
1079*cdf0e10cSrcweir }
1080*cdf0e10cSrcweir 
1081*cdf0e10cSrcweir OUString SAL_CALL
1082*cdf0e10cSrcweir Calendar_gregorian::getImplementationName(void) throw( RuntimeException )
1083*cdf0e10cSrcweir {
1084*cdf0e10cSrcweir         return OUString::createFromAscii(cCalendar);
1085*cdf0e10cSrcweir }
1086*cdf0e10cSrcweir 
1087*cdf0e10cSrcweir sal_Bool SAL_CALL
1088*cdf0e10cSrcweir Calendar_gregorian::supportsService(const rtl::OUString& rServiceName) throw( RuntimeException )
1089*cdf0e10cSrcweir {
1090*cdf0e10cSrcweir         return !rServiceName.compareToAscii(cCalendar);
1091*cdf0e10cSrcweir }
1092*cdf0e10cSrcweir 
1093*cdf0e10cSrcweir Sequence< OUString > SAL_CALL
1094*cdf0e10cSrcweir Calendar_gregorian::getSupportedServiceNames(void) throw( RuntimeException )
1095*cdf0e10cSrcweir {
1096*cdf0e10cSrcweir         Sequence< OUString > aRet(1);
1097*cdf0e10cSrcweir         aRet[0] = OUString::createFromAscii(cCalendar);
1098*cdf0e10cSrcweir         return aRet;
1099*cdf0e10cSrcweir }
1100*cdf0e10cSrcweir 
1101