Add support for the Islamic Civil calendar

This has its own locale data, extracted from CLDR. This data may
potentially be shared with other variants on the Islamic calendar, so
is handled by a separate base-class, QHijriCalendar, on which such
variants may base their implementations.

[ChangeLog][QtCore][QCalendar] Added support for the Islamic Civil
calendar, controlled by feature islamiccivilcalendar, with locale data
that can be shared with other implementations, controlled by feature
hijricalendar.

Fixes: QTBUG-56675
Change-Id: Idf32d3da7034baa8ec5e66ef847e59a8a2f31cbd
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
Soroush Rabiei 2019-08-08 20:35:13 +02:00 committed by Volker Hilsheimer
parent a8cc4cea92
commit 7026645712
14 changed files with 1626 additions and 6 deletions

View File

@ -1047,6 +1047,19 @@
"section": "Utilities",
"output": [ "publicFeature" ]
},
"hijricalendar": {
"label": "QHijriCalendar",
"purpose": "Generic basis for Islamic calendars, providing shared locale data",
"section": "Utilities",
"output": [ "privateFeature" ]
},
"islamiccivilcalendar": {
"label": "QIslamicCivilCalendar",
"purpose": "Support the Islamic Civil calendar",
"section": "Utilities",
"condition": "features.hijricalendar",
"output": [ "publicFeature" ]
},
"timezone": {
"label": "QTimeZone",
"purpose": "Provides support for time-zone handling.",

View File

@ -84,8 +84,10 @@
#define QT_FEATURE_getauxval (QT_HAS_INCLUDE(<sys/auxv.h>) ? 1 : -1)
#define QT_FEATURE_getentropy -1
#define QT_NO_GEOM_VARIANT
#define QT_FEATURE_hijricalendar -1
#define QT_FEATURE_iconv -1
#define QT_FEATURE_icu -1
#define QT_FEATURE_islamiccivilcalendar -1
#define QT_FEATURE_jalalicalendar -1
#define QT_FEATURE_journald -1
#define QT_FEATURE_futimens -1

View File

@ -36,6 +36,9 @@
#if QT_CONFIG(jalalicalendar)
#include "qjalalicalendar_p.h"
#endif
#if QT_CONFIG(islamiccivilcalendar)
#include "qislamiccivilcalendar_p.h"
#endif
#include "qdatetime.h"
#include "qcalendarmath_p.h"
@ -624,6 +627,10 @@ const QCalendarBackend *QCalendarBackend::fromEnum(QCalendar::System system)
#if QT_CONFIG(jalalicalendar)
case QCalendar::System::Jalali:
return new QJalaliCalendar;
#endif
#if QT_CONFIG(islamiccivilcalendar)
case QCalendar::System::IslamicCivil:
return new QIslamicCivilCalendar;
#else // When highest-numbered system isn't enabled, ensure we have a case for Last:
case QCalendar::System::Last:
#endif
@ -667,6 +674,7 @@ const QCalendarBackend *QCalendarBackend::fromEnum(QCalendar::System system)
\value Julian An ancient Roman calendar with too few leap years.
\value Milankovic A revised Julian calendar used by some Orthodox churches.
\value Jalali The Solar Hijri calendar (also called Persian).
\value IslamicCivil The (tabular) Islamic Civil calendar.
\sa QCalendar
*/

View File

@ -62,8 +62,10 @@
* Indian -- National
* Islamic -- Based on astronomical observations, not predictions, so hard to
implement. CLDR's data for type="islamic" apply, unless overridden, to the
other Islamic calendar variants.
* IslamicTabular -- tabular, astronomical epoch, CLDR type="islamic-tbla"
other Islamic calendar variants, i.e. IslamicCivil, above, and the three
following. See QHijriCalendar, a common base to provide that data.
* IslamicTabular -- tabular, astronomical epoch (same as IslamicCivil, except
for epoch), CLDR type="islamic-tbla"
* Saudi -- Saudi Arabia, sighting; CLDR type="islamic-rgsa"
* UmmAlQura -- Umm al-Qura, Saudi Arabia, calculated; CLDR type="islamic-umalqura"
* Iso8601 -- as Gregorian, but treating ISO 8601 weeks as "months"
@ -115,8 +117,14 @@ public:
#if QT_CONFIG(jalalicalendar) // type="persian"
Jalali = 10,
#endif
#if QT_CONFIG(islamiccivilcalendar) // type="islamic-civil", uses data from type="islamic"
IslamicCivil = 11,
// tabular, civil epoch
// 30 year cycle, leap on 2, 5, 7, 10, 13, 16, 18, 21, 24, 26 and 29
// (Other variants: 2, 5, 8, (10|11), 13, 16, 19, 21, 24, 27 and 29.)
#endif
Last = 10, // Highest number of any above
Last = 11, // Highest number of any above
User = -1
};
// New entries must be added to the \enum doc in qcalendar.cpp and

View File

@ -0,0 +1,125 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qglobal.h"
#include "qhijricalendar_p.h"
#include "qhijricalendar_data_p.h"
QT_BEGIN_NAMESPACE
/*!
\since 5.14
\class QHijriCalendar
\inmodule QtCore
\brief The QHijriCalendar class supports Islamic (Hijri) calendar implementations.
\section1 Islamic Calendar System
The Islamic, Muslim, or Hijri calendar is a lunar calendar consisting of 12
months in a year of 354 or 355 days. It is used (often alongside the
Gregorian calendar) to date events in many Muslim countries. It is also used
by Muslims to determine the proper days of Islamic holidays and rituals,
such as the annual period of fasting and the proper time for the pilgrimage
to Mecca.
Source: \l {https://en.wikipedia.org/wiki/Islamic_calendar}{Wikipedia page on
Hijri Calendar}
\section1 Support for variants
This base class provides the common details shared by all variants on the
Islamic calendar. Each year comprises 12 months of 29 or 30 days each; most
years have as many of 29 as of 30, but leap years extend one 29-day month to
30 days. In tabular versions of the calendar (where mathematical rules are
used to determine the details), odd-numbered months have 30 days, as does the
last (twelfth) month of a leap year; all other months have 29 days. Other
versions are based on actual astronomical observations of the moon's phase at
sunset, which vary from place to place.
\sa QIslamicCivilCalendar, QCalendar
*/
bool QHijriCalendar::isLunar() const
{
return true;
}
bool QHijriCalendar::isLuniSolar() const
{
return false;
}
bool QHijriCalendar::isSolar() const
{
return false;
}
int QHijriCalendar::daysInMonth(int month, int year) const
{
if (year == 0 || month < 1 || month > 12)
return 0;
if (month == 12 && isLeapYear(year))
return 30;
return month % 2 == 0 ? 29 : 30;
}
int QHijriCalendar::maxDaysInMonth() const
{
return 30;
}
int QHijriCalendar::daysInYear(int year) const
{
return monthsInYear(year) ? isLeapYear(year) ? 355 : 354 : 0;
}
const QCalendarLocale *QHijriCalendar::localeMonthIndexData() const
{
return locale_data;
}
const ushort *QHijriCalendar::localeMonthData() const
{
return months_data;
}
QT_END_NAMESPACE

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,83 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QHIJRI_CALENDAR_P_H
#define QHIJRI_CALENDAR_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists for the convenience
// of calendar implementations. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <QtCore/private/qglobal_p.h>
#include "qcalendarbackend_p.h"
QT_REQUIRE_CONFIG(hijricalendar);
QT_BEGIN_NAMESPACE
// Base for sharing with other variants on the Islamic calendar, as needed:
class Q_CORE_EXPORT QHijriCalendar : public QCalendarBackend
{
public:
int daysInMonth(int month, int year = QCalendar::Unspecified) const override;
int maxDaysInMonth() const override;
int daysInYear(int year) const override;
bool isLunar() const override;
bool isLuniSolar() const override;
bool isSolar() const override;
protected:
const QCalendarLocale *localeMonthIndexData() const override;
const ushort *localeMonthData() const override;
// (The INTEGRITY compiler got upset at: using QCalendarBackend:QCalendarBackend;)
QHijriCalendar(const QString &name, QCalendar::System id = QCalendar::System::User)
: QCalendarBackend(name, id) {}
};
QT_END_NAMESPACE
#endif // QHIJRI_CALENDAR_P_H

View File

@ -0,0 +1,125 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qglobal.h"
#include "qislamiccivilcalendar_p.h"
#include "qcalendarmath_p.h"
#include <QtCore/qmath.h>
QT_BEGIN_NAMESPACE
using namespace QRoundingDown;
/*!
\since 5.14
\class QIslamicCivilCalendar
\inmodule QtCore
\brief Implements a commonly-used computed version of the Islamic calendar.
\section1 Civil Islamic Calendar
QIslamicCivilCalendar implements a tabular version of the Hijri calendar which
is known as the Islamic Civil Calendar. It has the same numbering of years and
months, but the months are determined by arithmetical rules rather than by
observation or astronomical calculations.
\section2 Calendar Organization
The civil calendar follows the usual tabular scheme of odd-numbered months and
the last month of each leap year being 30 days long, the rest being 29 days
long. Its determination of leap years follows a 30-year cycle, in each of
which the years 2, 5, 7, 10, 13, 16, 18, 21, 24, 26 and 29 are leap years.
\sa QJijriCalendar, QCalendar
*/
QIslamicCivilCalendar::QIslamicCivilCalendar()
: QHijriCalendar(QStringLiteral("Islamic Civil"), QCalendar::System::IslamicCivil)
{
registerAlias(QStringLiteral("islamic-civil")); // CLDR name
registerAlias(QStringLiteral("islamicc")); // old CLDR name, still (2018) used by Mozilla
// Until we have a oncrete implementation that knows all the needed ephemerides:
registerAlias(QStringLiteral("Islamic"));
}
QString QIslamicCivilCalendar::name() const
{
return QStringLiteral("Islamic Civil");
}
QCalendar::System QIslamicCivilCalendar::calendarSystem() const
{
return QCalendar::System::IslamicCivil;
}
bool QIslamicCivilCalendar::isLeapYear(int year) const
{
if (year == QCalendar::Unspecified)
return false;
if (year < 0)
++year;
return qMod(year * 11 + 14, 30) < 11;
}
bool QIslamicCivilCalendar::dateToJulianDay(int year, int month, int day, qint64 *jd) const
{
Q_ASSERT(jd);
if (!isDateValid(year, month, day))
return false;
if (year <= 0)
++year;
*jd = qDiv(10631 * year - 10617, 30)
+ qDiv(325 * month - 320, 11)
+ day + 1948439;
return true;
}
QCalendar::YearMonthDay QIslamicCivilCalendar::julianDayToDate(qint64 jd) const
{
const qint64 epoch = 1948440;
const int32_t k2 = 30 * (jd - epoch) + 15;
const int32_t k1 = 11 * qDiv(qMod(k2, 10631), 30) + 5;
int y = qDiv(k2, 10631) + 1;
const int month = qDiv(k1, 325) + 1;
const int day = qDiv(qMod(k1, 325), 11) + 1;
return QCalendar::YearMonthDay(y > 0 ? y : y - 1, month, day);
}
QT_END_NAMESPACE

View File

@ -0,0 +1,76 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QISLAMIC_CIVIL_CALENDAR_P_H
#define QISLAMIC_CIVIL_CALENDAR_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists for the convenience
// of calendar implementations. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include "qhijricalendar_p.h"
QT_REQUIRE_CONFIG(islamiccivilcalendar);
QT_BEGIN_NAMESPACE
class Q_CORE_EXPORT QIslamicCivilCalendar : public QHijriCalendar
{
public:
QIslamicCivilCalendar();
// Calendar properties:
QString name() const override;
QCalendar::System calendarSystem() const override;
// Date queries:
bool isLeapYear(int year) const override;
// Julian Day conversions:
bool dateToJulianDay(int year, int month, int day, qint64 *jd) const override;
QCalendar::YearMonthDay julianDayToDate(qint64 jd) const override;
};
QT_END_NAMESPACE
#endif // QISLAMIC_CIVIL_CALENDAR_P_H

View File

@ -20,6 +20,21 @@ SOURCES += \
time/qmilankoviccalendar.cpp \
time/qromancalendar.cpp
qtConfig(hijricalendar) {
SOURCES += \
time/qhijricalendar.cpp
HEADERS += \
time/qhijricalendar_p.h \
time/qhijricalendar_data_p.h
}
qtConfig(islamiccivilcalendar) {
SOURCES += \
time/qislamiccivilcalendar.cpp
HEADERS += \
time/qislamiccivilcalendar_p.h
}
qtConfig(jalalicalendar) {
SOURCES += \
time/qjalalicalendar.cpp

View File

@ -179,6 +179,10 @@ void tst_QCalendar::specific_data()
// Jalali year 1355 started on Gregorian 1976-3-21:
ADDROW(Jalali, 1355, 1, 1, 1976, 3, 21);
#endif // jalali
#if QT_CONFIG(islamiccivilcalendar)
// TODO: confirm this is correct
ADDROW(IslamicCivil, 1, 1, 1, 622, 7, 19);
#endif
#undef ADDROW
}

View File

@ -64,7 +64,7 @@ from dateconverter import convert_date
from localexml import Locale
# TODO: make calendars a command-line option
calendars = ['gregorian', 'persian'] # 'islamic', 'hebrew'
calendars = ['gregorian', 'persian', 'islamic'] # 'hebrew'
findEntryInFile = xpathlite._findEntryInFile
def wrappedwarn(prefix, tokens):
return sys.stderr.write(

View File

@ -225,6 +225,15 @@ class Locale:
def firstThree(i, name): return name[:3]
def initial(i, name): return name[:1]
def number(i, name): return str(i + 1)
def islamicShort(i, name):
if not name: return name
if name == 'Shawwal': return 'Shaw.'
words = name.split()
if words[0].startswith('Dhu'):
words[0] = words[0][:7] + '.'
elif len(words[0]) > 3:
words[0] = words[0][:3] + '.'
return ' '.join(words)
@staticmethod
def __monthNames(calendars,
known={ # Map calendar to (names, extractors...):
@ -239,6 +248,12 @@ class Locale:
(fullName, fullName),
(firstThree, firstThree),
(number, initial)),
'islamic': ((u'Muharram', u'Safar', u'Rabiʻ I', u'Rabiʻ II', u'Jumada I',
u'Jumada II', u'Rajab', u'Shaʻban', u'Ramadan', u'Shawwal',
u'Dhuʻl-Qiʻdah', u'Dhuʻl-Hijjah'),
(fullName, fullName),
(islamicShort, islamicShort),
(number, number)),
'hebrew': (('Tishri', 'Heshvan', 'Kislev', 'Tevet', 'Shevat', 'Adar I',
'Adar', 'Nisan', 'Iyar', 'Sivan', 'Tamuz', 'Av'),
(fullName, fullName),
@ -258,7 +273,7 @@ class Locale:
';'.join(get[n][0](i, x) for i, x in enumerate(names)))
yield ('_'.join((camelCase(('standalone', size, 'months')), cal)),
';'.join(get[n][1](i, x) for i, x in enumerate(names)))
del fullName, firstThree, initial, number
del fullName, firstThree, initial, number, islamicShort
@classmethod
def C(cls, calendars=('gregorian',),

View File

@ -44,7 +44,7 @@ from localexml import Locale
# TODO: Make calendars a command-line parameter
# map { CLDR name: Qt file name }
calendars = {'gregorian': 'roman', 'persian': 'jalali',} # 'islamic': 'hijri', 'hebrew': 'hebrew',
calendars = {'gregorian': 'roman', 'persian': 'jalali', 'islamic': 'hijri',} # 'hebrew': 'hebrew',
generated_template = """
/*