1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qglobal.h"
5#include "qjalalicalendar_p.h"
6#include "qjalalicalendar_data_p.h"
7#include "qcalendarmath_p.h"
8#include <QtCore/qmath.h>
9
10QT_BEGIN_NAMESPACE
11
12using namespace QRoundingDown;
13
14// Constants
15
16constexpr qint64 cycleDays = 1029983;
17constexpr int cycleYears = 2820;
18constexpr double yearLength = 365.24219858156028368; // 365 + 683 / 2820.
19constexpr qint64 jalaliEpoch = 2121446; // 475/01/01 AP, start of 2820 cycle
20// This appears to be based on Ahmad Birashk's algorithm.
21
22// Calendar implementation
23
24static inline int cycle(qint64 jdn)
25{
26 return qDiv<cycleDays>(a: jdn - jalaliEpoch);
27}
28
29qint64 cycleStart(int cycleNo)
30{
31 return jalaliEpoch + cycleNo * cycleDays;
32}
33
34qint64 firstDayOfYear(int year, int cycleNo)
35{
36 qint64 firstDOYinEra = static_cast<qint64>(qFloor(v: year * yearLength));
37 return jalaliEpoch + cycleNo * cycleDays + firstDOYinEra;
38}
39
40/*!
41 \since 5.14
42
43 \class QJalaliCalendar
44 \inmodule QtCore
45 \brief The QJalaliCalendar class provides Jalali (Hijri Shamsi) calendar
46 system implementation.
47
48 \section1 Solar Hijri Calendar System
49
50 The Solar Hijri calendar, also called the Solar Hejri calendar, Shamsi
51 Hijri calendar or Jalali calendar, is the official calendar of Iran and
52 Afghanistan. It begins on the vernal equinox (Nowruz) as determined by
53 astronomical calculation for the Iran Standard Time meridian
54 (52.5°E or GMT+3.5h). This determination of starting moment is more accurate
55 than the Gregorian calendar for predicting the date of the vernal equinox,
56 because it uses astronomical observations rather than mathematical rules.
57
58 \section2 Calendar Organization
59
60 Each of the twelve months corresponds with a zodiac sign. The first six
61 months have 31 days, the next five have 30 days, and the last month has 29
62 days in usual years but 30 days in leap years. The New Year's Day always
63 falls on the March equinox.
64
65 \section2 Leap Year Rules
66
67 The Solar Hijri calendar produces a five-year leap year interval after about
68 every seven four-year leap year intervals. It usually follows a 33-year
69 cycle with occasional interruptions by single 29-year or 37-year subcycles.
70 The reason for this behavior is that it tracks the observed vernal equinox.
71 By contrast, some less accurate predictive algorithms are in use based
72 on confusion between the average tropical year (365.2422 days, approximated
73 with near 128-year cycles or 2820-year great cycles) and the mean interval
74 between spring equinoxes (365.2424 days, approximated with a near 33-year
75 cycle).
76
77 Source: \l {https://en.wikipedia.org/wiki/Solar_Hijri_calendar}{Wikipedia
78 page on Solar Hijri Calendar}
79*/
80
81QString QJalaliCalendar::name() const
82{
83 return QStringLiteral("Jalali");
84}
85
86QStringList QJalaliCalendar::nameList()
87{
88 return {
89 QStringLiteral("Jalali"),
90 QStringLiteral("Persian"),
91 };
92}
93
94bool QJalaliCalendar::isLeapYear(int year) const
95{
96 if (year == QCalendar::Unspecified)
97 return false;
98 if (year < 0)
99 year++;
100 return qMod<2820>(a: (year + 2346) * 683) < 683;
101}
102
103bool QJalaliCalendar::isLunar() const
104{
105 return false;
106}
107
108bool QJalaliCalendar::isLuniSolar() const
109{
110 return false;
111}
112
113bool QJalaliCalendar::isSolar() const
114{
115 return true;
116}
117
118bool QJalaliCalendar::dateToJulianDay(int year, int month, int day, qint64 *jd) const
119{
120 Q_ASSERT(jd);
121 if (!isDateValid(year, month, day))
122 return false;
123
124 const int y = year - (year < 0 ? 474 : 475);
125 const int c = qDiv<cycleYears>(a: y);
126 const int yearInCycle = y - c * cycleYears;
127 int dayInYear = day;
128 for (int i = 1; i < month; ++i)
129 dayInYear += daysInMonth(month: i, year);
130 *jd = firstDayOfYear(year: yearInCycle, cycleNo: c) + dayInYear - 1;
131 return true;
132}
133
134QCalendar::YearMonthDay QJalaliCalendar::julianDayToDate(qint64 jd) const
135{
136 const int c = cycle(jdn: jd);
137 int yearInCycle = qFloor(v: (jd - cycleStart(cycleNo: c)) / yearLength);
138 int year = yearInCycle + 475 + c * cycleYears;
139 int day = jd - firstDayOfYear(year: yearInCycle, cycleNo: c) + 1;
140 if (day > daysInYear(year: year <= 0 ? year - 1 : year)) {
141 year++;
142 day = 1;
143 }
144 if (year <= 0)
145 year--;
146 int month;
147 for (month = 1; month < 12; ++month) {
148 const int last = daysInMonth(month, year);
149 if (day <= last)
150 break;
151 day -= last;
152 }
153 return QCalendar::YearMonthDay(year, month, day);
154}
155
156int QJalaliCalendar::daysInMonth(int month, int year) const
157{
158 if (year && month > 0 && month <= 12)
159 return month < 7 ? 31 : month < 12 || isLeapYear(year) ? 30 : 29;
160
161 return 0;
162}
163
164const QCalendarLocale *QJalaliCalendar::localeMonthIndexData() const
165{
166 return QtPrivate::Jalali::locale_data;
167}
168
169const char16_t *QJalaliCalendar::localeMonthData() const
170{
171 return QtPrivate::Jalali::months_data;
172}
173
174QT_END_NAMESPACE
175

source code of qtbase/src/corelib/time/qjalalicalendar.cpp