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