File indexing completed on 2024-06-16 05:03:55

0001 /*
0002     SPDX-FileCopyrightText: 2023 Fushan Wen <qydwhotmail@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "islamiccalendar.h"
0008 
0009 #include "icucalendar_p.h"
0010 
0011 class IslamicCalendarProviderPrivate : public ICUCalendarPrivate
0012 {
0013 public:
0014     explicit IslamicCalendarProviderPrivate(CalendarSystem::System calendarSystem);
0015 
0016     /**
0017      * For formatting, see the documentation of SimpleDateFormat:
0018      * https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classicu_1_1SimpleDateFormat.html#details
0019      */
0020     QString formattedDateString(const icu::UnicodeString &str) const;
0021     QString formattedDateStringInNativeLanguage(const icu::UnicodeString &str) const;
0022 
0023     QCalendar::YearMonthDay fromGregorian(const QDate &_date);
0024     CalendarEvents::CalendarEventsPlugin::SubLabel subLabel(const QDate &date);
0025 
0026 private:
0027     // See https://unicode-org.github.io/icu/userguide/locale/#keywords for available keywords
0028     icu::Locale m_arabicLocale;
0029     icu::Locale m_nativeLocale;
0030 };
0031 
0032 IslamicCalendarProviderPrivate::IslamicCalendarProviderPrivate(CalendarSystem::System calendarSystem)
0033     : ICUCalendarPrivate()
0034 {
0035     if (U_FAILURE(m_errorCode)) {
0036         return; // Failed to create m_GregorianCalendar
0037     }
0038 
0039     // See https://github.com/unicode-org/cldr/blob/main/common/bcp47/number.xml for available number systems
0040     // See https://cldr.unicode.org/development/development-process/design-proposals/islamic-calendar-types for available Islamic caneldar types
0041     switch (calendarSystem) {
0042     case CalendarSystem::Jalali: {
0043         m_arabicLocale = icu::Locale("ar_IR", 0, 0, "calendar=persian;numbers=arab");
0044         m_nativeLocale = icu::Locale(QLocale::system().name().toLatin1(), 0, 0, "calendar=persian;");
0045         break;
0046     }
0047     case CalendarSystem::Islamic: {
0048         m_arabicLocale = icu::Locale("ar_SA", 0, 0, "calendar=islamic;numbers=arab");
0049         m_nativeLocale = icu::Locale(QLocale::system().name().toLatin1(), 0, 0, "calendar=islamic;");
0050         break;
0051     }
0052     case CalendarSystem::IslamicCivil: {
0053         m_arabicLocale = icu::Locale("ar_SA", 0, 0, "calendar=islamic-civil;numbers=arab");
0054         m_nativeLocale = icu::Locale(QLocale::system().name().toLatin1(), 0, 0, "calendar=islamic-civil;");
0055         break;
0056     }
0057     case CalendarSystem::IslamicUmalqura: {
0058         m_arabicLocale = icu::Locale("ar_SA", 0, 0, "calendar=islamic-umalqura;numbers=arab");
0059         m_nativeLocale = icu::Locale(QLocale::system().name().toLatin1(), 0, 0, "calendar=islamic-umalqura;");
0060         break;
0061     }
0062     default:
0063         Q_UNREACHABLE();
0064     }
0065 
0066     m_calendar.reset(icu::Calendar::createInstance(m_arabicLocale, m_errorCode));
0067 }
0068 
0069 QString IslamicCalendarProviderPrivate::formattedDateString(const icu::UnicodeString &str) const
0070 {
0071     UErrorCode errorCode = U_ZERO_ERROR;
0072     icu::UnicodeString dateString;
0073     icu::SimpleDateFormat formatter(str, m_arabicLocale, errorCode);
0074     formatter.setCalendar(*m_calendar);
0075     formatter.format(m_calendar->getTime(errorCode), dateString);
0076 
0077     return QString::fromUtf16(dateString.getBuffer(), dateString.length());
0078 }
0079 
0080 QString IslamicCalendarProviderPrivate::formattedDateStringInNativeLanguage(const icu::UnicodeString &str) const
0081 {
0082     UErrorCode errorCode = U_ZERO_ERROR;
0083     icu::UnicodeString dateString;
0084     icu::SimpleDateFormat formatter(str, m_nativeLocale, errorCode);
0085     formatter.setCalendar(*m_calendar);
0086     formatter.format(m_calendar->getTime(errorCode), dateString);
0087 
0088     return QString::fromUtf16(dateString.getBuffer(), dateString.length());
0089 }
0090 
0091 QCalendar::YearMonthDay IslamicCalendarProviderPrivate::fromGregorian(const QDate &_date)
0092 {
0093     if (U_FAILURE(m_errorCode) || !_date.isValid() || !setDate(_date)) {
0094         return {};
0095     }
0096 
0097     return date();
0098 }
0099 
0100 CalendarEvents::CalendarEventsPlugin::SubLabel IslamicCalendarProviderPrivate::subLabel(const QDate &date)
0101 {
0102     auto sublabel = CalendarEvents::CalendarEventsPlugin::SubLabel{};
0103 
0104     if (U_FAILURE(m_errorCode) || !date.isValid() || !setDate(date)) {
0105         return sublabel;
0106     }
0107 
0108     const bool isLocaleArabicOrPersian = QLocale::system().language() == QLocale::Arabic || QLocale::system().language() == QLocale::Persian;
0109 
0110     sublabel.dayLabel = isLocaleArabicOrPersian ? formattedDateString("d") : QString::number(day());
0111     // From QLocale(QLocale::Arabic).dateFormat() and QLocale(QLocale::Persian).dateFormat()
0112     const QString arabicDateString = formattedDateString("d MMMM yyyy");
0113     // Translated month names are available in https://github.com/unicode-org/icu/tree/main/icu4c/source/data/locales
0114     sublabel.label = isLocaleArabicOrPersian
0115         ? arabicDateString
0116         : i18ndc("plasma_calendar_alternatecalendar",
0117                  "@label %1 Day number %2 Month name in Islamic Calendar %3 Year number %4 Islamic calendar date in Arabic",
0118                  "%1 %2, %3 (%4)",
0119                  QString::number(day()),
0120                  formattedDateStringInNativeLanguage("MMMM"),
0121                  QString::number(year()),
0122                  arabicDateString);
0123     sublabel.priority = CalendarEvents::CalendarEventsPlugin::SubLabelPriority::Low;
0124 
0125     return sublabel;
0126 }
0127 
0128 IslamicCalendarProvider::IslamicCalendarProvider(QObject *parent,
0129                                                  CalendarSystem::System calendarSystem,
0130                                                  const QDate &startDate,
0131                                                  const QDate &endDate,
0132                                                  int dateOffset)
0133     : AbstractCalendarProvider(parent, calendarSystem, startDate, endDate, dateOffset)
0134     , d(std::make_unique<IslamicCalendarProviderPrivate>(calendarSystem))
0135 {
0136     Q_ASSERT(m_calendarSystem == CalendarSystem::Jalali || m_calendarSystem == CalendarSystem::Islamic || m_calendarSystem == CalendarSystem::IslamicCivil
0137              || m_calendarSystem == CalendarSystem::IslamicUmalqura);
0138 }
0139 
0140 IslamicCalendarProvider::~IslamicCalendarProvider()
0141 {
0142 }
0143 
0144 QCalendar::YearMonthDay IslamicCalendarProvider::fromGregorian(const QDate &date) const
0145 {
0146     return d->fromGregorian(date);
0147 }
0148 
0149 CalendarEvents::CalendarEventsPlugin::SubLabel IslamicCalendarProvider::subLabel(const QDate &date) const
0150 {
0151     return d->subLabel(date);
0152 }