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 <nativenumbersupplier.hxx>
25 #include <com/sun/star/i18n/CalendarDisplayCode.hpp>
26 #include <com/sun/star/i18n/CalendarDisplayIndex.hpp>
27 #include <com/sun/star/i18n/NativeNumberMode.hpp>
28 #include <com/sun/star/i18n/reservedWords.hpp>
29 #include <cppuhelper/supportsservice.hxx>
30 #include <rtl/math.hxx>
31 #include <sal/log.hxx>
32
33 #include <stdio.h>
34 #include <string.h>
35
36 #define erDUMP_ICU_CALENDAR 0
37 #define erDUMP_I18N_CALENDAR 0
38 #if erDUMP_ICU_CALENDAR || erDUMP_I18N_CALENDAR
39 // If both are used, DUMP_ICU_CAL_MSG() must be used before DUMP_I18N_CAL_MSG()
40 // to obtain internally set values from ICU, else Calendar::get() calls in
41 // DUMP_I18N_CAL_MSG() recalculate!
42
43 // These pieces of macro are shamelessly borrowed from icu's olsontz.cpp, the
44 // double parens'ed approach to pass multiple parameters as one macro parameter
45 // is appealing.
debug_cal_loc(const char * f,int32_t l)46 static void debug_cal_loc(const char *f, int32_t l)
47 {
48 fprintf(stderr, "%s:%d: ", f, l);
49 }
50 # include <stdarg.h>
debug_cal_msg(const char * pat,...)51 static void debug_cal_msg(const char *pat, ...)
52 {
53 va_list ap;
54 va_start(ap, pat);
55 vfprintf(stderr, pat, ap);
56 va_end(ap);
57 }
58
59 #if erDUMP_ICU_CALENDAR
60 // Make icu with
61 // DEFS = -DU_DEBUG_CALSVC -DUCAL_DEBUG_DUMP
62 // in workdir/UnpackedTarball/icu/source/icudefs.mk
63 // May need some patches to fix unmaintained things there.
64 extern void ucal_dump( const icu::Calendar & );
debug_icu_cal_dump(const::icu::Calendar & r)65 static void debug_icu_cal_dump( const ::icu::Calendar & r )
66 {
67 ucal_dump(r);
68 fflush(stderr);
69 // set a breakpoint here to pause display between dumps
70 }
71 // must use double parens, i.e.: DUMP_ICU_CAL_MSG(("four is: %d",4));
72 #define DUMP_ICU_CAL_MSG(x) {debug_cal_loc(__FILE__,__LINE__);debug_cal_msg x;debug_icu_cal_dump(*body);}
73 #else // erDUMP_ICU_CALENDAR
74 #define DUMP_ICU_CAL_MSG(x)
75 #endif // erDUMP_ICU_CALENDAR
76
77 #if erDUMP_I18N_CALENDAR
debug_cal_millis_to_time(long nMillis,long & h,long & m,long & s,long & f)78 static void debug_cal_millis_to_time( long nMillis, long & h, long & m, long & s, long & f )
79 {
80 int sign = (nMillis < 0 ? -1 : 1);
81 nMillis = ::std::abs(nMillis);
82 h = sign * nMillis / (60 * 60 * 1000);
83 nMillis -= sign * h * (60 * 60 * 1000);
84 m = nMillis / (60 * 1000);
85 nMillis -= m * (60 * 1000);
86 s = nMillis / (1000);
87 nMillis -= s * (1000);
88 f = nMillis;
89 }
debug_i18n_cal_dump(const::icu::Calendar & r)90 static void debug_i18n_cal_dump( const ::icu::Calendar & r )
91 {
92 UErrorCode status;
93 long nMillis, h, m, s, f;
94 fprintf( stderr, " %04ld", (long)r.get( UCAL_YEAR, status = U_ZERO_ERROR));
95 fprintf( stderr, "-%02ld", (long)r.get( UCAL_MONTH, status = U_ZERO_ERROR)+1);
96 fprintf( stderr, "-%02ld", (long)r.get( UCAL_DATE, status = U_ZERO_ERROR));
97 fprintf( stderr, " %02ld", (long)r.get( UCAL_HOUR_OF_DAY, status = U_ZERO_ERROR));
98 fprintf( stderr, ":%02ld", (long)r.get( UCAL_MINUTE, status = U_ZERO_ERROR));
99 fprintf( stderr, ":%02ld", (long)r.get( UCAL_SECOND, status = U_ZERO_ERROR));
100 fprintf( stderr, " zone: %ld", (long)(nMillis = r.get( UCAL_ZONE_OFFSET, status = U_ZERO_ERROR)));
101 fprintf( stderr, " (%f min)", (double)nMillis / 60000);
102 debug_cal_millis_to_time( nMillis, h, m, s, f);
103 fprintf( stderr, " (%ld:%02ld:%02ld.%ld)", h, m, s, f);
104 fprintf( stderr, " DST: %ld", (long)(nMillis = r.get( UCAL_DST_OFFSET, status = U_ZERO_ERROR)));
105 fprintf( stderr, " (%f min)", (double)nMillis / 60000);
106 debug_cal_millis_to_time( nMillis, h, m, s, f);
107 fprintf( stderr, " (%ld:%02ld:%02ld.%ld)", h, m, s, f);
108 fprintf( stderr, "\n");
109 fflush(stderr);
110 }
111 // must use double parens, i.e.: DUMP_I18N_CAL_MSG(("four is: %d",4));
112 #define DUMP_I18N_CAL_MSG(x) {debug_cal_loc(__FILE__,__LINE__);debug_cal_msg x;debug_i18n_cal_dump(*body);}
113 #else // erDUMP_I18N_CALENDAR
114 #define DUMP_I18N_CAL_MSG(x)
115 #endif // erDUMP_I18N_CALENDAR
116
117 #else // erDUMP_ICU_CALENDAR || erDUMP_I18N_CALENDAR
118 #define DUMP_ICU_CAL_MSG(x)
119 #define DUMP_I18N_CAL_MSG(x)
120 #endif // erDUMP_ICU_CALENDAR || erDUMP_I18N_CALENDAR
121
122
123 using namespace ::com::sun::star::uno;
124 using namespace ::com::sun::star::i18n;
125 using namespace ::com::sun::star::lang;
126
127
128 namespace i18npool {
129
130 #define ERROR RuntimeException()
131
Calendar_gregorian()132 Calendar_gregorian::Calendar_gregorian()
133 : mxNatNum(new NativeNumberSupplierService)
134 {
135 init(nullptr);
136 }
Calendar_gregorian(const Era * _earArray)137 Calendar_gregorian::Calendar_gregorian(const Era *_earArray)
138 : mxNatNum(new NativeNumberSupplierService)
139 {
140 init(_earArray);
141 }
142 void
init(const Era * _eraArray)143 Calendar_gregorian::init(const Era *_eraArray)
144 {
145 cCalendar = "com.sun.star.i18n.Calendar_gregorian";
146
147 fieldSet = 0;
148
149 // #i102356# With icu::Calendar::createInstance(UErrorCode) in a Thai
150 // th_TH system locale we accidentally used a Buddhist calendar. Though
151 // the ICU documentation says that should be the case only for
152 // th_TH_TRADITIONAL (and ja_JP_TRADITIONAL Gengou), a plain th_TH
153 // already triggers that behavior, ja_JP does not. Strange enough,
154 // passing a th_TH locale to the calendar creation doesn't trigger
155 // this.
156 // See also http://userguide.icu-project.org/datetime/calendar
157
158 // Whatever ICU offers as the default calendar for a locale, ensure we
159 // have a Gregorian calendar as requested.
160
161 /* XXX: with the current implementation the aLocale member variable is
162 * not set prior to loading a calendar from locale data. This
163 * creates an empty (root) locale for ICU, but at least the correct
164 * calendar is used. The language part must not be NULL (respectively
165 * not all, language and country and variant), otherwise the current
166 * default locale would be used again and the calendar keyword ignored.
167 * */
168 icu::Locale aIcuLocale( "", nullptr, nullptr, "calendar=gregorian");
169
170 /* XXX: not specifying a timezone when creating a calendar assigns the
171 * system's timezone with all DST quirks, invalid times when switching
172 * to/from DST and so on. The XCalendar* interfaces are defined to support
173 * local time and UTC time so we can not override that here.
174 */
175 UErrorCode status = U_ZERO_ERROR;
176 body.reset( icu::Calendar::createInstance( aIcuLocale, status) );
177 if (!body || !U_SUCCESS(status)) throw ERROR;
178 eraArray=_eraArray;
179 }
180
~Calendar_gregorian()181 Calendar_gregorian::~Calendar_gregorian()
182 {
183 }
184
Calendar_hanja()185 Calendar_hanja::Calendar_hanja()
186 {
187 cCalendar = "com.sun.star.i18n.Calendar_hanja";
188 }
189
190 OUString SAL_CALL
getDisplayName(sal_Int16 displayIndex,sal_Int16 idx,sal_Int16 nameType)191 Calendar_hanja::getDisplayName( sal_Int16 displayIndex, sal_Int16 idx, sal_Int16 nameType )
192 {
193 if ( displayIndex == CalendarDisplayIndex::AM_PM ) {
194 // Am/Pm string for Korean Hanja calendar will refer to Japanese locale
195 css::lang::Locale jaLocale("ja", OUString(), OUString());
196 if (idx == 0) return LocaleDataImpl::get()->getLocaleItem(jaLocale).timeAM;
197 else if (idx == 1) return LocaleDataImpl::get()->getLocaleItem(jaLocale).timePM;
198 else throw ERROR;
199 }
200 else
201 return Calendar_gregorian::getDisplayName( displayIndex, idx, nameType );
202 }
203
204
Calendar_hanja_yoil()205 Calendar_hanja_yoil::Calendar_hanja_yoil()
206 {
207 cCalendar = "com.sun.star.i18n.Calendar_Calendar_hanja_yoil";
208 }
209
210 OUString SAL_CALL
getDisplayName(sal_Int16 displayIndex,sal_Int16 idx,sal_Int16 nameType)211 Calendar_hanja_yoil::getDisplayName( sal_Int16 displayIndex, sal_Int16 idx, sal_Int16 nameType )
212 {
213 if ( displayIndex == CalendarDisplayIndex::AM_PM ) {
214 // Am/Pm string for Korean Hanja calendar will refer to Japanese locale
215 css::lang::Locale jaLocale("ja", OUString(), OUString());
216 if (idx == 0) return LocaleDataImpl::get()->getLocaleItem(jaLocale).timeAM;
217 else if (idx == 1) return LocaleDataImpl::get()->getLocaleItem(jaLocale).timePM;
218 else throw ERROR;
219 }
220 else
221 return Calendar_gregorian::getDisplayName( displayIndex, idx, nameType );
222 }
223
224
225 const Era gengou_eraArray[] = {
226 {1868, 1, 1, 0}, // Meiji
227 {1912, 7, 30, 0}, // Taisho
228 {1926, 12, 25, 0}, // Showa
229 {1989, 1, 8, 0}, // Heisei
230 {2019, 5, 1, 0}, // Reiwa
231 {0, 0, 0, 0}
232 };
Calendar_gengou()233 Calendar_gengou::Calendar_gengou() : Calendar_gregorian(gengou_eraArray)
234 {
235 cCalendar = "com.sun.star.i18n.Calendar_gengou";
236 }
237
238 const Era ROC_eraArray[] = {
239 {1912, 1, 1, kDisplayEraForcedLongYear}, // #i116701#
240 {0, 0, 0, 0}
241 };
Calendar_ROC()242 Calendar_ROC::Calendar_ROC() : Calendar_gregorian(ROC_eraArray)
243 {
244 cCalendar = "com.sun.star.i18n.Calendar_ROC";
245 }
246
247 /**
248 * The start year of the Korean traditional calendar (Dan-gi) is the inaugural
249 * year of Dan-gun (BC 2333).
250 */
251 const Era dangi_eraArray[] = {
252 {-2332, 1, 1, 0},
253 {0, 0, 0, 0}
254 };
Calendar_dangi()255 Calendar_dangi::Calendar_dangi() : Calendar_gregorian(dangi_eraArray)
256 {
257 cCalendar = "com.sun.star.i18n.Calendar_dangi";
258 }
259
260 const Era buddhist_eraArray[] = {
261 {-542, 1, 1, 0},
262 {0, 0, 0, 0}
263 };
Calendar_buddhist()264 Calendar_buddhist::Calendar_buddhist() : Calendar_gregorian(buddhist_eraArray)
265 {
266 cCalendar = "com.sun.star.i18n.Calendar_buddhist";
267 }
268
269 void SAL_CALL
loadCalendar(const OUString & uniqueID,const css::lang::Locale & rLocale)270 Calendar_gregorian::loadCalendar( const OUString& uniqueID, const css::lang::Locale& rLocale )
271 {
272 // init. fieldValue[]
273 getValue();
274
275 aLocale = rLocale;
276 const Sequence< Calendar2 > xC = LocaleDataImpl::get()->getAllCalendars2(rLocale);
277 for (const auto& rCal : xC)
278 {
279 if (uniqueID == rCal.Name)
280 {
281 aCalendar = rCal;
282 // setup minimalDaysInFirstWeek
283 setMinimumNumberOfDaysForFirstWeek(
284 aCalendar.MinimumNumberOfDaysForFirstWeek);
285 // setup first day of week
286 for (sal_Int16 day = sal::static_int_cast<sal_Int16>(
287 aCalendar.Days.getLength()-1); day>=0; day--)
288 {
289 if (aCalendar.StartOfWeek == aCalendar.Days[day].ID)
290 {
291 setFirstDayOfWeek( day);
292 return;
293 }
294 }
295 }
296 }
297 // Calendar is not for the locale
298 throw ERROR;
299 }
300
301
302 css::i18n::Calendar2 SAL_CALL
getLoadedCalendar2()303 Calendar_gregorian::getLoadedCalendar2()
304 {
305 return aCalendar;
306 }
307
308 css::i18n::Calendar SAL_CALL
getLoadedCalendar()309 Calendar_gregorian::getLoadedCalendar()
310 {
311 return LocaleDataImpl::downcastCalendar( aCalendar);
312 }
313
314 OUString SAL_CALL
getUniqueID()315 Calendar_gregorian::getUniqueID()
316 {
317 return aCalendar.Name;
318 }
319
320 void SAL_CALL
setDateTime(double fTimeInDays)321 Calendar_gregorian::setDateTime( double fTimeInDays )
322 {
323 // ICU handles dates in milliseconds as double values and uses floor()
324 // to obtain integer values, which may yield a date decremented by one
325 // for odd (historical) timezone values where the computed value due to
326 // rounding errors has a fractional part in milliseconds. Ensure we
327 // pass a value without fraction here. If not, that may lead to
328 // fdo#44286 or fdo#52619 and the like, e.g. when passing
329 // -2136315212000.000244 instead of -2136315212000.000000
330 double fM = fTimeInDays * U_MILLIS_PER_DAY;
331 double fR = rtl::math::round( fM );
332 SAL_INFO_IF( fM != fR, "i18npool",
333 "Calendar_gregorian::setDateTime: " << std::fixed << fM << " rounded to " << fR);
334 UErrorCode status = U_ZERO_ERROR;
335 body->setTime( fR, status);
336 if ( !U_SUCCESS(status) ) throw ERROR;
337 getValue();
338 }
339
340 double SAL_CALL
getDateTime()341 Calendar_gregorian::getDateTime()
342 {
343 if (fieldSet) {
344 setValue();
345 getValue();
346 }
347 UErrorCode status = U_ZERO_ERROR;
348 double fR = body->getTime(status);
349 if ( !U_SUCCESS(status) ) throw ERROR;
350 return fR / U_MILLIS_PER_DAY;
351 }
352
353 void SAL_CALL
setLocalDateTime(double fTimeInDays)354 Calendar_gregorian::setLocalDateTime( double fTimeInDays )
355 {
356 // See setDateTime() for why the rounding.
357 double fM = fTimeInDays * U_MILLIS_PER_DAY;
358 double fR = rtl::math::round( fM );
359 SAL_INFO_IF( fM != fR, "i18npool",
360 "Calendar_gregorian::setLocalDateTime: " << std::fixed << fM << " rounded to " << fR);
361 int32_t nZoneOffset, nDSTOffset;
362 UErrorCode status = U_ZERO_ERROR;
363 body->getTimeZone().getOffset( fR, true, nZoneOffset, nDSTOffset, status );
364
365 if ( !U_SUCCESS(status) ) throw ERROR;
366 status = U_ZERO_ERROR;
367 body->setTime( fR - (nZoneOffset + nDSTOffset), status );
368 if ( !U_SUCCESS(status) ) throw ERROR;
369 getValue();
370 }
371
372 double SAL_CALL
getLocalDateTime()373 Calendar_gregorian::getLocalDateTime()
374 {
375 if (fieldSet) {
376 setValue();
377 getValue();
378 }
379 UErrorCode status = U_ZERO_ERROR;
380 double fTime = body->getTime( status );
381 if ( !U_SUCCESS(status) ) throw ERROR;
382 status = U_ZERO_ERROR;
383 int32_t nZoneOffset = body->get( UCAL_ZONE_OFFSET, status );
384 if ( !U_SUCCESS(status) ) throw ERROR;
385 status = U_ZERO_ERROR;
386 int32_t nDSTOffset = body->get( UCAL_DST_OFFSET, status );
387 if ( !U_SUCCESS(status) ) throw ERROR;
388 return (fTime + (nZoneOffset + nDSTOffset)) / U_MILLIS_PER_DAY;
389 }
390
setTimeZone(const OUString & rTimeZone)391 bool Calendar_gregorian::setTimeZone( const OUString& rTimeZone )
392 {
393 if (fieldSet)
394 {
395 setValue();
396 getValue();
397 }
398 const icu::UnicodeString aID( reinterpret_cast<const UChar*>(rTimeZone.getStr()), rTimeZone.getLength());
399 const std::unique_ptr<const icu::TimeZone> pTZ( icu::TimeZone::createTimeZone(aID));
400 if (!pTZ)
401 return false;
402
403 body->setTimeZone(*pTZ);
404 return true;
405 }
406
407 // map field value from gregorian calendar to other calendar, it can be overwritten by derived class.
408 // By using eraArray, it can take care Japanese and Taiwan ROC calendar.
mapFromGregorian()409 void Calendar_gregorian::mapFromGregorian()
410 {
411 if (!eraArray)
412 return;
413
414 sal_Int16 e, y, m, d;
415
416 e = fieldValue[CalendarFieldIndex::ERA];
417 y = fieldValue[CalendarFieldIndex::YEAR];
418 m = fieldValue[CalendarFieldIndex::MONTH] + 1;
419 d = fieldValue[CalendarFieldIndex::DAY_OF_MONTH];
420
421 // since the year is reversed for first era, it is reversed again here for Era compare.
422 if (e == 0)
423 y = 1 - y;
424
425 for (e = 0; eraArray[e].year; e++)
426 if ((y != eraArray[e].year) ? y < eraArray[e].year :
427 (m != eraArray[e].month) ? m < eraArray[e].month : d < eraArray[e].day)
428 break;
429
430 fieldValue[CalendarFieldIndex::ERA] = e;
431 fieldValue[CalendarFieldIndex::YEAR] =
432 sal::static_int_cast<sal_Int16>( (e == 0) ? (eraArray[0].year - y) : (y - eraArray[e-1].year + 1) );
433 }
434
435 #define FIELDS ((1 << CalendarFieldIndex::ERA) | (1 << CalendarFieldIndex::YEAR))
436 // map field value from other calendar to gregorian calendar, it can be overwritten by derived class.
437 // By using eraArray, it can take care Japanese and Taiwan ROC calendar.
mapToGregorian()438 void Calendar_gregorian::mapToGregorian()
439 {
440 if (eraArray && (fieldSet & FIELDS)) {
441 sal_Int16 y, e = fieldValue[CalendarFieldIndex::ERA];
442 if (e == 0)
443 y = sal::static_int_cast<sal_Int16>( eraArray[0].year - fieldValue[CalendarFieldIndex::YEAR] );
444 else
445 y = sal::static_int_cast<sal_Int16>( eraArray[e-1].year + fieldValue[CalendarFieldIndex::YEAR] - 1 );
446
447 fieldSetValue[CalendarFieldIndex::ERA] = y <= 0 ? 0 : 1;
448 fieldSetValue[CalendarFieldIndex::YEAR] = (y <= 0 ? 1 - y : y);
449 fieldSet |= FIELDS;
450 }
451 }
452
453 /// @throws RuntimeException
fieldNameConverter(sal_Int16 fieldIndex)454 static UCalendarDateFields fieldNameConverter(sal_Int16 fieldIndex)
455 {
456 UCalendarDateFields f;
457
458 switch (fieldIndex) {
459 case CalendarFieldIndex::AM_PM: f = UCAL_AM_PM; break;
460 case CalendarFieldIndex::DAY_OF_MONTH: f = UCAL_DATE; break;
461 case CalendarFieldIndex::DAY_OF_WEEK: f = UCAL_DAY_OF_WEEK; break;
462 case CalendarFieldIndex::DAY_OF_YEAR: f = UCAL_DAY_OF_YEAR; break;
463 case CalendarFieldIndex::DST_OFFSET: f = UCAL_DST_OFFSET; break;
464 case CalendarFieldIndex::ZONE_OFFSET: f = UCAL_ZONE_OFFSET; break;
465 case CalendarFieldIndex::HOUR: f = UCAL_HOUR_OF_DAY; break;
466 case CalendarFieldIndex::MINUTE: f = UCAL_MINUTE; break;
467 case CalendarFieldIndex::SECOND: f = UCAL_SECOND; break;
468 case CalendarFieldIndex::MILLISECOND: f = UCAL_MILLISECOND; break;
469 case CalendarFieldIndex::WEEK_OF_MONTH: f = UCAL_WEEK_OF_MONTH; break;
470 case CalendarFieldIndex::WEEK_OF_YEAR: f = UCAL_WEEK_OF_YEAR; break;
471 case CalendarFieldIndex::YEAR: f = UCAL_YEAR; break;
472 case CalendarFieldIndex::MONTH: f = UCAL_MONTH; break;
473 case CalendarFieldIndex::ERA: f = UCAL_ERA; break;
474 default: throw ERROR;
475 }
476 return f;
477 }
478
479 void SAL_CALL
setValue(sal_Int16 fieldIndex,sal_Int16 value)480 Calendar_gregorian::setValue( sal_Int16 fieldIndex, sal_Int16 value )
481 {
482 if (fieldIndex < 0 || FIELD_INDEX_COUNT <= fieldIndex)
483 throw ERROR;
484 fieldSet |= (1 << fieldIndex);
485 fieldValue[fieldIndex] = value;
486 }
487
getCombinedOffset(sal_Int32 & o_nOffset,sal_Int16 nParentFieldIndex,sal_Int16 nChildFieldIndex) const488 bool Calendar_gregorian::getCombinedOffset( sal_Int32 & o_nOffset,
489 sal_Int16 nParentFieldIndex, sal_Int16 nChildFieldIndex ) const
490 {
491 o_nOffset = 0;
492 bool bFieldsSet = false;
493 if (fieldSet & (1 << nParentFieldIndex))
494 {
495 bFieldsSet = true;
496 o_nOffset = static_cast<sal_Int32>( fieldValue[nParentFieldIndex]) * 60000;
497 }
498 if (fieldSet & (1 << nChildFieldIndex))
499 {
500 bFieldsSet = true;
501 if (o_nOffset < 0)
502 o_nOffset -= static_cast<sal_uInt16>( fieldValue[nChildFieldIndex]);
503 else
504 o_nOffset += static_cast<sal_uInt16>( fieldValue[nChildFieldIndex]);
505 }
506 return bFieldsSet;
507 }
508
getZoneOffset(sal_Int32 & o_nOffset) const509 bool Calendar_gregorian::getZoneOffset( sal_Int32 & o_nOffset ) const
510 {
511 return getCombinedOffset( o_nOffset, CalendarFieldIndex::ZONE_OFFSET,
512 CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS);
513 }
514
getDSTOffset(sal_Int32 & o_nOffset) const515 bool Calendar_gregorian::getDSTOffset( sal_Int32 & o_nOffset ) const
516 {
517 return getCombinedOffset( o_nOffset, CalendarFieldIndex::DST_OFFSET,
518 CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS);
519 }
520
submitFields()521 void Calendar_gregorian::submitFields()
522 {
523 for (sal_Int16 fieldIndex = 0; fieldIndex < FIELD_INDEX_COUNT; fieldIndex++)
524 {
525 if (fieldSet & (1 << fieldIndex))
526 {
527 switch (fieldIndex)
528 {
529 default:
530 body->set(fieldNameConverter(fieldIndex), fieldSetValue[fieldIndex]);
531 break;
532 case CalendarFieldIndex::ZONE_OFFSET:
533 case CalendarFieldIndex::DST_OFFSET:
534 case CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS:
535 case CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS:
536 break; // nothing, extra handling
537 }
538 }
539 }
540 sal_Int32 nZoneOffset, nDSTOffset;
541 if (getZoneOffset( nZoneOffset))
542 body->set( fieldNameConverter( CalendarFieldIndex::ZONE_OFFSET), nZoneOffset);
543 if (getDSTOffset( nDSTOffset))
544 body->set( fieldNameConverter( CalendarFieldIndex::DST_OFFSET), nDSTOffset);
545 }
546
setValue()547 void Calendar_gregorian::setValue()
548 {
549 // Copy fields before calling submitFields() directly or indirectly below.
550 memcpy(fieldSetValue, fieldValue, sizeof(fieldSetValue));
551 // Possibly setup ERA and YEAR in fieldSetValue.
552 mapToGregorian();
553
554 DUMP_ICU_CAL_MSG(("%s\n","setValue() before submission"));
555 DUMP_I18N_CAL_MSG(("%s\n","setValue() before submission"));
556
557 submitFields();
558
559 DUMP_ICU_CAL_MSG(("%s\n","setValue() after submission"));
560 DUMP_I18N_CAL_MSG(("%s\n","setValue() after submission"));
561
562 #if erDUMP_ICU_CALENDAR || erDUMP_I18N_CALENDAR
563 {
564 // force icu::Calendar to recalculate
565 UErrorCode status;
566 sal_Int32 nTmp = body->get( UCAL_DATE, status = U_ZERO_ERROR);
567 DUMP_ICU_CAL_MSG(("%s: %d\n","setValue() result day",nTmp));
568 DUMP_I18N_CAL_MSG(("%s: %d\n","setValue() result day",nTmp));
569 }
570 #endif
571 }
572
getValue()573 void Calendar_gregorian::getValue()
574 {
575 DUMP_ICU_CAL_MSG(("%s\n","getValue()"));
576 DUMP_I18N_CAL_MSG(("%s\n","getValue()"));
577 for (sal_Int16 fieldIndex = 0; fieldIndex < FIELD_INDEX_COUNT; fieldIndex++)
578 {
579 if (fieldIndex == CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS ||
580 fieldIndex == CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS)
581 continue; // not ICU fields
582
583 UErrorCode status = U_ZERO_ERROR;
584 sal_Int32 value = body->get( fieldNameConverter(
585 fieldIndex), status);
586 if ( !U_SUCCESS(status) ) throw ERROR;
587
588 // Convert millisecond to minute for ZONE and DST and set remainder in
589 // second field.
590 if (fieldIndex == CalendarFieldIndex::ZONE_OFFSET)
591 {
592 sal_Int32 nMinutes = value / 60000;
593 sal_Int16 nMillis = static_cast<sal_Int16>( static_cast<sal_uInt16>(
594 abs( value - nMinutes * 60000)));
595 fieldValue[CalendarFieldIndex::ZONE_OFFSET] = static_cast<sal_Int16>( nMinutes);
596 fieldValue[CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS] = nMillis;
597 }
598 else if (fieldIndex == CalendarFieldIndex::DST_OFFSET)
599 {
600 sal_Int32 nMinutes = value / 60000;
601 sal_Int16 nMillis = static_cast<sal_Int16>( static_cast<sal_uInt16>(
602 abs( value - nMinutes * 60000)));
603 fieldValue[CalendarFieldIndex::DST_OFFSET] = static_cast<sal_Int16>( nMinutes);
604 fieldValue[CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS] = nMillis;
605 }
606 else
607 fieldValue[fieldIndex] = static_cast<sal_Int16>(value);
608
609 // offset 1 since the value for week start day SunDay is different between Calendar and Weekdays.
610 if ( fieldIndex == CalendarFieldIndex::DAY_OF_WEEK )
611 fieldValue[fieldIndex]--; // UCAL_SUNDAY:/* == 1 */ ==> Weekdays::SUNDAY /* ==0 */
612 }
613 mapFromGregorian();
614 fieldSet = 0;
615 }
616
617 sal_Int16 SAL_CALL
getValue(sal_Int16 fieldIndex)618 Calendar_gregorian::getValue( sal_Int16 fieldIndex )
619 {
620 if (fieldIndex < 0 || FIELD_INDEX_COUNT <= fieldIndex)
621 throw ERROR;
622
623 if (fieldSet) {
624 setValue();
625 getValue();
626 }
627
628 return fieldValue[fieldIndex];
629 }
630
631 void SAL_CALL
addValue(sal_Int16 fieldIndex,sal_Int32 value)632 Calendar_gregorian::addValue( sal_Int16 fieldIndex, sal_Int32 value )
633 {
634 // since ZONE and DST could not be add, we don't need to convert value here
635 UErrorCode status = U_ZERO_ERROR;
636 body->add(fieldNameConverter(fieldIndex), value, status);
637 if ( !U_SUCCESS(status) ) throw ERROR;
638 getValue();
639 }
640
641 sal_Bool SAL_CALL
isValid()642 Calendar_gregorian::isValid()
643 {
644 if (fieldSet) {
645 sal_Int32 tmp = fieldSet;
646 setValue();
647 memcpy(fieldSetValue, fieldValue, sizeof(fieldSetValue));
648 getValue();
649 for ( sal_Int16 fieldIndex = 0; fieldIndex < FIELD_INDEX_COUNT; fieldIndex++ ) {
650 // compare only with fields that are set and reset fieldSet[]
651 if (tmp & (1 << fieldIndex)) {
652 if (fieldSetValue[fieldIndex] != fieldValue[fieldIndex])
653 return false;
654 }
655 }
656 }
657 return true;
658 }
659
660 // NativeNumberMode has different meaning between Number and Calendar for Asian locales.
661 // Here is the mapping table
662 // calendar(q/y/m/d) zh_CN zh_TW ja ko
663 // NatNum1 NatNum1/1/7/7 NatNum1/1/7/7 NatNum1/1/4/4 NatNum1/1/7/7
664 // NatNum2 NatNum2/2/8/8 NatNum2/2/8/8 NatNum2/2/5/5 NatNum2/2/8/8
665 // NatNum3 NatNum3/3/3/3 NatNum3/3/3/3 NatNum3/3/3/3 NatNum3/3/3/3
666 // NatNum4 NatNum9/9/11/11
667
NatNumForCalendar(const css::lang::Locale & aLocale,sal_Int32 nCalendarDisplayCode,sal_Int16 nNativeNumberMode,sal_Int16 value)668 static sal_Int16 NatNumForCalendar(const css::lang::Locale& aLocale,
669 sal_Int32 nCalendarDisplayCode, sal_Int16 nNativeNumberMode, sal_Int16 value )
670 {
671 bool isShort = ((nCalendarDisplayCode == CalendarDisplayCode::SHORT_YEAR ||
672 nCalendarDisplayCode == CalendarDisplayCode::LONG_YEAR) && value >= 100) ||
673 nCalendarDisplayCode == CalendarDisplayCode::SHORT_QUARTER ||
674 nCalendarDisplayCode == CalendarDisplayCode::LONG_QUARTER;
675 bool isChinese = aLocale.Language == "zh";
676 bool isJapanese = aLocale.Language == "ja";
677 bool isKorean = aLocale.Language == "ko";
678
679 if (isChinese || isJapanese || isKorean) {
680 switch (nNativeNumberMode) {
681 case NativeNumberMode::NATNUM1:
682 if (!isShort)
683 nNativeNumberMode = isJapanese ? NativeNumberMode::NATNUM4 : NativeNumberMode::NATNUM7;
684 break;
685 case NativeNumberMode::NATNUM2:
686 if (!isShort)
687 nNativeNumberMode = isJapanese ? NativeNumberMode::NATNUM5 : NativeNumberMode::NATNUM8;
688 break;
689 case NativeNumberMode::NATNUM3:
690 break;
691 case NativeNumberMode::NATNUM4:
692 if (isKorean)
693 return isShort ? NativeNumberMode::NATNUM9 : NativeNumberMode::NATNUM11;
694 [[fallthrough]];
695 default: return 0;
696 }
697 }
698 return nNativeNumberMode;
699 }
700
DisplayCode2FieldIndex(sal_Int32 nCalendarDisplayCode)701 static sal_Int32 DisplayCode2FieldIndex(sal_Int32 nCalendarDisplayCode)
702 {
703 switch( nCalendarDisplayCode ) {
704 case CalendarDisplayCode::SHORT_DAY:
705 case CalendarDisplayCode::LONG_DAY:
706 return CalendarFieldIndex::DAY_OF_MONTH;
707 case CalendarDisplayCode::SHORT_DAY_NAME:
708 case CalendarDisplayCode::LONG_DAY_NAME:
709 case CalendarDisplayCode::NARROW_DAY_NAME:
710 return CalendarFieldIndex::DAY_OF_WEEK;
711 case CalendarDisplayCode::SHORT_QUARTER:
712 case CalendarDisplayCode::LONG_QUARTER:
713 case CalendarDisplayCode::SHORT_MONTH:
714 case CalendarDisplayCode::LONG_MONTH:
715 case CalendarDisplayCode::SHORT_MONTH_NAME:
716 case CalendarDisplayCode::LONG_MONTH_NAME:
717 case CalendarDisplayCode::NARROW_MONTH_NAME:
718 case CalendarDisplayCode::SHORT_GENITIVE_MONTH_NAME:
719 case CalendarDisplayCode::LONG_GENITIVE_MONTH_NAME:
720 case CalendarDisplayCode::NARROW_GENITIVE_MONTH_NAME:
721 case CalendarDisplayCode::SHORT_PARTITIVE_MONTH_NAME:
722 case CalendarDisplayCode::LONG_PARTITIVE_MONTH_NAME:
723 case CalendarDisplayCode::NARROW_PARTITIVE_MONTH_NAME:
724 return CalendarFieldIndex::MONTH;
725 case CalendarDisplayCode::SHORT_YEAR:
726 case CalendarDisplayCode::LONG_YEAR:
727 return CalendarFieldIndex::YEAR;
728 case CalendarDisplayCode::SHORT_ERA:
729 case CalendarDisplayCode::LONG_ERA:
730 return CalendarFieldIndex::ERA;
731 case CalendarDisplayCode::SHORT_YEAR_AND_ERA:
732 case CalendarDisplayCode::LONG_YEAR_AND_ERA:
733 return CalendarFieldIndex::YEAR;
734 default:
735 return 0;
736 }
737 }
738
739 sal_Int16 SAL_CALL
getFirstDayOfWeek()740 Calendar_gregorian::getFirstDayOfWeek()
741 {
742 // UCAL_SUNDAY == 1, Weekdays::SUNDAY == 0 => offset -1
743 // Check for underflow just in case we're called "out of sync".
744 return ::std::max( sal::static_int_cast<sal_Int16>(0),
745 sal::static_int_cast<sal_Int16>( static_cast<sal_Int16>(
746 body->getFirstDayOfWeek()) - 1));
747 }
748
749 void SAL_CALL
setFirstDayOfWeek(sal_Int16 day)750 Calendar_gregorian::setFirstDayOfWeek( sal_Int16 day )
751 {
752 // Weekdays::SUNDAY == 0, UCAL_SUNDAY == 1 => offset +1
753 body->setFirstDayOfWeek( static_cast<UCalendarDaysOfWeek>( day + 1));
754 }
755
756 void SAL_CALL
setMinimumNumberOfDaysForFirstWeek(sal_Int16 days)757 Calendar_gregorian::setMinimumNumberOfDaysForFirstWeek( sal_Int16 days )
758 {
759 aCalendar.MinimumNumberOfDaysForFirstWeek = days;
760 body->setMinimalDaysInFirstWeek( static_cast<uint8_t>( days));
761 }
762
763 sal_Int16 SAL_CALL
getMinimumNumberOfDaysForFirstWeek()764 Calendar_gregorian::getMinimumNumberOfDaysForFirstWeek()
765 {
766 return aCalendar.MinimumNumberOfDaysForFirstWeek;
767 }
768
769 sal_Int16 SAL_CALL
getNumberOfMonthsInYear()770 Calendar_gregorian::getNumberOfMonthsInYear()
771 {
772 return static_cast<sal_Int16>(aCalendar.Months.getLength());
773 }
774
775
776 sal_Int16 SAL_CALL
getNumberOfDaysInWeek()777 Calendar_gregorian::getNumberOfDaysInWeek()
778 {
779 return static_cast<sal_Int16>(aCalendar.Days.getLength());
780 }
781
782
783 Sequence< CalendarItem > SAL_CALL
getDays()784 Calendar_gregorian::getDays()
785 {
786 return LocaleDataImpl::downcastCalendarItems( aCalendar.Days);
787 }
788
789
790 Sequence< CalendarItem > SAL_CALL
getMonths()791 Calendar_gregorian::getMonths()
792 {
793 return LocaleDataImpl::downcastCalendarItems( aCalendar.Months);
794 }
795
796
797 Sequence< CalendarItem2 > SAL_CALL
getDays2()798 Calendar_gregorian::getDays2()
799 {
800 return aCalendar.Days;
801 }
802
803
804 Sequence< CalendarItem2 > SAL_CALL
getMonths2()805 Calendar_gregorian::getMonths2()
806 {
807 return aCalendar.Months;
808 }
809
810
811 Sequence< CalendarItem2 > SAL_CALL
getGenitiveMonths2()812 Calendar_gregorian::getGenitiveMonths2()
813 {
814 return aCalendar.GenitiveMonths;
815 }
816
817
818 Sequence< CalendarItem2 > SAL_CALL
getPartitiveMonths2()819 Calendar_gregorian::getPartitiveMonths2()
820 {
821 return aCalendar.PartitiveMonths;
822 }
823
824
825 OUString SAL_CALL
getDisplayName(sal_Int16 displayIndex,sal_Int16 idx,sal_Int16 nameType)826 Calendar_gregorian::getDisplayName( sal_Int16 displayIndex, sal_Int16 idx, sal_Int16 nameType )
827 {
828 OUString aStr;
829
830 switch( displayIndex ) {
831 case CalendarDisplayIndex::AM_PM:/* ==0 */
832 if (idx == 0) aStr = LocaleDataImpl::get()->getLocaleItem(aLocale).timeAM;
833 else if (idx == 1) aStr = LocaleDataImpl::get()->getLocaleItem(aLocale).timePM;
834 else throw ERROR;
835 break;
836 case CalendarDisplayIndex::DAY:
837 if( idx >= aCalendar.Days.getLength() ) throw ERROR;
838 if (nameType == 0) aStr = aCalendar.Days[idx].AbbrevName;
839 else if (nameType == 1) aStr = aCalendar.Days[idx].FullName;
840 else if (nameType == 2) aStr = aCalendar.Days[idx].NarrowName;
841 else throw ERROR;
842 break;
843 case CalendarDisplayIndex::MONTH:
844 if( idx >= aCalendar.Months.getLength() ) throw ERROR;
845 if (nameType == 0) aStr = aCalendar.Months[idx].AbbrevName;
846 else if (nameType == 1) aStr = aCalendar.Months[idx].FullName;
847 else if (nameType == 2) aStr = aCalendar.Months[idx].NarrowName;
848 else throw ERROR;
849 break;
850 case CalendarDisplayIndex::GENITIVE_MONTH:
851 if( idx >= aCalendar.GenitiveMonths.getLength() ) throw ERROR;
852 if (nameType == 0) aStr = aCalendar.GenitiveMonths[idx].AbbrevName;
853 else if (nameType == 1) aStr = aCalendar.GenitiveMonths[idx].FullName;
854 else if (nameType == 2) aStr = aCalendar.GenitiveMonths[idx].NarrowName;
855 else throw ERROR;
856 break;
857 case CalendarDisplayIndex::PARTITIVE_MONTH:
858 if( idx >= aCalendar.PartitiveMonths.getLength() ) throw ERROR;
859 if (nameType == 0) aStr = aCalendar.PartitiveMonths[idx].AbbrevName;
860 else if (nameType == 1) aStr = aCalendar.PartitiveMonths[idx].FullName;
861 else if (nameType == 2) aStr = aCalendar.PartitiveMonths[idx].NarrowName;
862 else throw ERROR;
863 break;
864 case CalendarDisplayIndex::ERA:
865 if( idx >= aCalendar.Eras.getLength() ) throw ERROR;
866 if (nameType == 0) aStr = aCalendar.Eras[idx].AbbrevName;
867 else if (nameType == 1) aStr = aCalendar.Eras[idx].FullName;
868 else throw ERROR;
869 break;
870 case CalendarDisplayIndex::YEAR:
871 break;
872 default:
873 throw ERROR;
874 }
875 return aStr;
876 }
877
878 // Methods in XExtendedCalendar
879 OUString SAL_CALL
getDisplayString(sal_Int32 nCalendarDisplayCode,sal_Int16 nNativeNumberMode)880 Calendar_gregorian::getDisplayString( sal_Int32 nCalendarDisplayCode, sal_Int16 nNativeNumberMode )
881 {
882 return getDisplayStringImpl( nCalendarDisplayCode, nNativeNumberMode, false);
883 }
884
885 OUString
getDisplayStringImpl(sal_Int32 nCalendarDisplayCode,sal_Int16 nNativeNumberMode,bool bEraMode)886 Calendar_gregorian::getDisplayStringImpl( sal_Int32 nCalendarDisplayCode, sal_Int16 nNativeNumberMode, bool bEraMode )
887 {
888 sal_Int16 value = getValue(sal::static_int_cast<sal_Int16>( DisplayCode2FieldIndex(nCalendarDisplayCode) ));
889 OUString aOUStr;
890
891 if (nCalendarDisplayCode == CalendarDisplayCode::SHORT_QUARTER ||
892 nCalendarDisplayCode == CalendarDisplayCode::LONG_QUARTER) {
893 Sequence< OUString> xR = LocaleDataImpl::get()->getReservedWord(aLocale);
894 sal_Int16 quarter = value / 3;
895 // Since this base class method may be called by derived calendar
896 // classes where a year consists of more than 12 months we need a check
897 // to not run out of bounds of reserved quarter words. Perhaps a more
898 // clean way (instead of dividing by 3) would be to first get the
899 // number of months, divide by 4 and then use that result to divide the
900 // actual month value.
901 if ( quarter > 3 )
902 quarter = 3;
903 quarter = sal::static_int_cast<sal_Int16>( quarter +
904 ((nCalendarDisplayCode == CalendarDisplayCode::SHORT_QUARTER) ?
905 reservedWords::QUARTER1_ABBREVIATION : reservedWords::QUARTER1_WORD) );
906 aOUStr = xR[quarter];
907 } else {
908 // The "#100211# - checked" comments serve for detection of "use of
909 // sprintf is safe here" conditions. An sprintf encountered without
910 // having that comment triggers alarm ;-)
911 char aStr[10];
912 switch( nCalendarDisplayCode ) {
913 case CalendarDisplayCode::SHORT_MONTH:
914 value += 1; // month is zero based
915 [[fallthrough]];
916 case CalendarDisplayCode::SHORT_DAY:
917 sprintf(aStr, "%d", value); // #100211# - checked
918 break;
919 case CalendarDisplayCode::LONG_YEAR:
920 if ( aCalendar.Name == "gengou" )
921 sprintf(aStr, "%02d", value); // #100211# - checked
922 else
923 sprintf(aStr, "%d", value); // #100211# - checked
924 break;
925 case CalendarDisplayCode::LONG_MONTH:
926 value += 1; // month is zero based
927 sprintf(aStr, "%02d", value); // #100211# - checked
928 break;
929 case CalendarDisplayCode::SHORT_YEAR:
930 // Take last 2 digits, or only one if value<10, for example,
931 // in case of the Gengou calendar. For combined era+year always
932 // the full year is displayed, without leading 0.
933 // Workaround for non-combined calls in certain calendars is
934 // the kDisplayEraForcedLongYear flag, but this also could get
935 // called for YY not only E format codes, no differentiation
936 // possible here; the good news is that usually the Gregorian
937 // calendar is the default and hence YY calls for Gregorian and
938 // E for the other calendar and currently (2013-02-28) ROC is
939 // the only calendar using this.
940 // See i#116701 and fdo#60915
941 if (value < 100 || bEraMode || (eraArray && (eraArray[0].flags & kDisplayEraForcedLongYear)))
942 sprintf(aStr, "%d", value); // #100211# - checked
943 else
944 sprintf(aStr, "%02d", value % 100); // #100211# - checked
945 break;
946 case CalendarDisplayCode::LONG_DAY:
947 sprintf(aStr, "%02d", value); // #100211# - checked
948 break;
949
950 case CalendarDisplayCode::SHORT_DAY_NAME:
951 return getDisplayName(CalendarDisplayIndex::DAY, value, 0);
952 case CalendarDisplayCode::LONG_DAY_NAME:
953 return getDisplayName(CalendarDisplayIndex::DAY, value, 1);
954 case CalendarDisplayCode::NARROW_DAY_NAME:
955 return getDisplayName(CalendarDisplayIndex::DAY, value, 2);
956 case CalendarDisplayCode::SHORT_MONTH_NAME:
957 return getDisplayName(CalendarDisplayIndex::MONTH, value, 0);
958 case CalendarDisplayCode::LONG_MONTH_NAME:
959 return getDisplayName(CalendarDisplayIndex::MONTH, value, 1);
960 case CalendarDisplayCode::NARROW_MONTH_NAME:
961 return getDisplayName(CalendarDisplayIndex::MONTH, value, 2);
962 case CalendarDisplayCode::SHORT_GENITIVE_MONTH_NAME:
963 return getDisplayName(CalendarDisplayIndex::GENITIVE_MONTH, value, 0);
964 case CalendarDisplayCode::LONG_GENITIVE_MONTH_NAME:
965 return getDisplayName(CalendarDisplayIndex::GENITIVE_MONTH, value, 1);
966 case CalendarDisplayCode::NARROW_GENITIVE_MONTH_NAME:
967 return getDisplayName(CalendarDisplayIndex::GENITIVE_MONTH, value, 2);
968 case CalendarDisplayCode::SHORT_PARTITIVE_MONTH_NAME:
969 return getDisplayName(CalendarDisplayIndex::PARTITIVE_MONTH, value, 0);
970 case CalendarDisplayCode::LONG_PARTITIVE_MONTH_NAME:
971 return getDisplayName(CalendarDisplayIndex::PARTITIVE_MONTH, value, 1);
972 case CalendarDisplayCode::NARROW_PARTITIVE_MONTH_NAME:
973 return getDisplayName(CalendarDisplayIndex::PARTITIVE_MONTH, value, 2);
974 case CalendarDisplayCode::SHORT_ERA:
975 return getDisplayName(CalendarDisplayIndex::ERA, value, 0);
976 case CalendarDisplayCode::LONG_ERA:
977 return getDisplayName(CalendarDisplayIndex::ERA, value, 1);
978
979 case CalendarDisplayCode::SHORT_YEAR_AND_ERA:
980 return getDisplayStringImpl( CalendarDisplayCode::SHORT_ERA, nNativeNumberMode, true ) +
981 getDisplayStringImpl( CalendarDisplayCode::SHORT_YEAR, nNativeNumberMode, true );
982
983 case CalendarDisplayCode::LONG_YEAR_AND_ERA:
984 return getDisplayStringImpl( CalendarDisplayCode::LONG_ERA, nNativeNumberMode, true ) +
985 getDisplayStringImpl( CalendarDisplayCode::LONG_YEAR, nNativeNumberMode, true );
986
987 default:
988 throw ERROR;
989 }
990 aOUStr = OUString::createFromAscii(aStr);
991 }
992 // NatNum12 used only for selected parts
993 if (nNativeNumberMode > 0 && nNativeNumberMode != 12) {
994 // For Japanese calendar, first year calls GAN, see bug 111668 for detail.
995 if (eraArray == gengou_eraArray && value == 1
996 && (nCalendarDisplayCode == CalendarDisplayCode::SHORT_YEAR ||
997 nCalendarDisplayCode == CalendarDisplayCode::LONG_YEAR)
998 && (nNativeNumberMode == NativeNumberMode::NATNUM1 ||
999 nNativeNumberMode == NativeNumberMode::NATNUM2)) {
1000 static sal_Unicode gan = 0x5143;
1001 return OUString(&gan, 1);
1002 }
1003 sal_Int16 nNatNum = NatNumForCalendar(aLocale, nCalendarDisplayCode, nNativeNumberMode, value);
1004 if (nNatNum > 0)
1005 return mxNatNum->getNativeNumberString(aOUStr, aLocale, nNatNum);
1006 }
1007 return aOUStr;
1008 }
1009
1010 // Methods in XExtendedCalendar
1011 OUString SAL_CALL
getDisplayString(sal_Int32 nCalendarDisplayCode,sal_Int16 nNativeNumberMode)1012 Calendar_buddhist::getDisplayString( sal_Int32 nCalendarDisplayCode, sal_Int16 nNativeNumberMode )
1013 {
1014 // make year and era in different order for year before and after 0.
1015 if ((nCalendarDisplayCode == CalendarDisplayCode::LONG_YEAR_AND_ERA ||
1016 nCalendarDisplayCode == CalendarDisplayCode::SHORT_YEAR_AND_ERA) &&
1017 getValue(CalendarFieldIndex::ERA) == 0) {
1018 if (nCalendarDisplayCode == CalendarDisplayCode::LONG_YEAR_AND_ERA)
1019 return getDisplayStringImpl( CalendarDisplayCode::SHORT_YEAR, nNativeNumberMode, true ) +
1020 getDisplayStringImpl( CalendarDisplayCode::SHORT_ERA, nNativeNumberMode, true );
1021 else
1022 return getDisplayStringImpl( CalendarDisplayCode::LONG_YEAR, nNativeNumberMode, true ) +
1023 getDisplayStringImpl( CalendarDisplayCode::LONG_ERA, nNativeNumberMode, true );
1024 }
1025 return Calendar_gregorian::getDisplayString(nCalendarDisplayCode, nNativeNumberMode);
1026 }
1027
1028 OUString SAL_CALL
getImplementationName()1029 Calendar_gregorian::getImplementationName()
1030 {
1031 return OUString::createFromAscii(cCalendar);
1032 }
1033
1034 sal_Bool SAL_CALL
supportsService(const OUString & rServiceName)1035 Calendar_gregorian::supportsService(const OUString& rServiceName)
1036 {
1037 return cppu::supportsService(this, rServiceName);
1038 }
1039
1040 Sequence< OUString > SAL_CALL
getSupportedServiceNames()1041 Calendar_gregorian::getSupportedServiceNames()
1042 {
1043 Sequence< OUString > aRet { OUString::createFromAscii(cCalendar) };
1044 return aRet;
1045 }
1046
1047 }
1048
1049 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1050