Discussion:
[Development] Calendar Systems proposal
Soroush Rabiei
2016-12-15 10:30:33 UTC
Permalink
1. Purpose

Nowadays, almost all major programming frameworks support calendar
globalization. We are a small group of developers working with
non-Gregorian
calendars and we believe Qt must support this too. This proposal discusses
details of our plan (and early implementation) to add support for multiple
calendar systems in Qt library.

2.History

Originally there was four plans described in [1] based on ideas discussed
in
[2]. These talks (dated back to 2011) were never implemented and eventually
abandoned.

3. Current Status

Currently there is no support of any calendar system other than Gregorian.
Gregorian calendar is widely used in western countries. However most
countries
in Middle-east, Asia and Africa use other calendar systems. Unfortunately
there
is no support for them.

4. Requirements

Users of calendar systems expect same functionality provided by default
calendar for their local calendar (at least we do so). Following list
describes
requirements of date/time API.

4.1. Such a feature must provide an API same as QDate, and all related
classes.
Adding convenient classed for each calendar type (QJalaliDate, QHebrewDate,
QIslamicDate, etc.) will be discussed, Tough we believe any API without
QDate
integration will not satisfy our needs.

4.2. Backward compatibility must be kept in both public API and semantics
of
QDate, QDateTime and related classes. Such a feature must not affect any
program that uses date, but has nothing to do with the calendar systems. A
program compiled with Qt 5.9 must behave the way it do with 5.8, whether it
utilizes calendar systems or not. There will be no assumption on user
locale,
nor the host system’s default calendar (as there is not any now).

4.3. The Public API and semantics of other parts of Qt that work with
QDate,
should not be changed. For instance, regardless of the calendar system that
a
QDate object uses, a QSqlQuery must be able to use it. And QVarient must be
able to store/read it the way it did before. We prefer not to change
private
API and implementation as well, but if we had have to, (and it’s an
affordable
change) we will do it. For example we may need to change QtSql and QtCore
classes to overwrite calendar system of QDate object to work properly. We
hope
we will find a way to avoid this. These examples are based on the
assumption
that QDate API will change, and calendar system support will be added
directly
to QDate (See S5 below)

4.4. Calendar systems and Locales, are irrelevant except month naming.
There is
nothing to do with the locales while implementing calendar system: Adding a
new
calendar is not about naming months for some locale. Some calendars are
different in the math behind. For example, the year in Jalali calendar
starts
in 21st March. First 6 months are 31 days and next 6 months are 30 except
12th.

Changing locale should not change calendar system. And there will be no
information on calendar system in supported locales. (There is no default
calendar in locale definition).

It’s necessary to have multiple calendars at the same time in an
application,
and it’s necessary to have calendar system in all locales. Also calendar
System
and Time Zones are irrelevant. There were assumptions on previous plans
(described in [1]) that are totally misunderstanding of the calendar
systems.
A particular plan was about adding calendar integration to QLocale, which
we do
believe is wrong in all aspects. Because calendar system has nothing to do
with
culture. A calendar is an abstract astronomical concept.

5. API Candidates

The plan is to implement support for at least five most-used calendar
systems.
Each calendar’s implementation will be added into a new class named
`QCalendarSystem`. This class will provide information on calendar system
details, like how many days are in each month and if a year is leap year or
normal.

This class will also contain the calculation code needed for QDate. Most
importantly, how to convert between julian day and calendar date. Currently
these calculations are implemented in QDate class
(qtbase/src/corelib/tools/qdatetime.cpp).

There are several candidates for date object APIs. Following list discusses
these APIs.

5.1. Integrate calendar system semantics into QDate

This is what we plan to do. There are several ways to do this. All of
course
will preserve backward compatibility. First three options are based on John
Layt’s ideas described at [3]

5.1.1. Add a calculator class, add convenience methods to QDate

QDate myDate = QDate::gregorianDate(2000, 1, 1);
QDateCalculator myCalc;
int localYear = myCalc.year(myDate);
QString localString = QLocale::system().toString(myDate);
QDateTimeEdit myEdit(myDate);
myCalc.setCalendar(QLocale::HebrewCalendar);
int hebrewYear = myCalc.year();

There are several issues with this API. Most importantly it does not
preserve
backward-compatibility of QDate API. This also does not meet the
requirement of
having QDate-like API.

5.1.2. Add new methods to QDate

QDate myDate = QDate::gregorianDate(2000,1,1);
int localYear = myDate.localYear();
QString localString = QLocale::system().toString(myDate);
QDateTimeEdit myEdit(myDate);
int hebrewYear = myDate.localYear(QLocale::HebrewCalendar);

This option has previous one’s problems. And there is something not clear
with
this option: How QLocale knows about calendar system of a given date
object?
Is there a member added to QDate?

5.1.3. Add `calendar()` method to QDate, and return a QLocalDate

QDate myDate = QDate::gregorianDate(2000,1,1);
int localYear = myDate.calendar().year();
QString localString = myDate.calendar().toString();
QDateTimeEdit myEdit(myDate);
int hebrewYear = myDate.calendar(QLocale::HebrewCalendar).year();

This is the best of above three, yet does not meet the requirements:
myDate.calendar(someCalendar).year() is not a good way to obtain year
number of
a date object. We prefer QDate::year().

5.1.4. Using an enumeration to perform the math

This option is complicated, and backward-compatible. We plan to implement
this
one if there is no problem.

Needed API changes are:

* Adding an enum to the QLocale class:

enum Calendar{Default, Gregorian=Default, Hebrew, Jalali,
Islamic,...}

* Adding arguments of this enum with default values to `Gregorian` to
these
member functions:

QString monthName(int, FormatType format = LongFormat,
Calendar calendar = Default) const;
QString standaloneMonthName(int, FormatType format = LongFormat,
Calendar calendar = Default) const;
* Adding `QCalendarSystem` class
* Adding three member functions to the QDate:
setCalendarSystem, calendarSyste and a constructor

The QCalendarSystem will contain information on calendar systems and the
math.
This will help us to move calculations out of QDate, by providing a handle
(a
private member) to a calendar system object. This object will have the
dirty
code of calendar system, and will keep QDate implementation cleaner.

So we will have something like:

// qdatetime.cpp:
void QDate::setCalendarSystem(QCalendarSystem::Type t){
// This is the new API
d_calendar.setType(t);
}

int QDate::year() const {
#ifndef QT_NOCALENDARSYSTEM
// Previous implementation
#else
return d_calendar.year();
#endif
}

// qcalendar_system.cpp:
QCalendarSystem::year(quint64 jd){
switch(m_type){
case CalendarSyste::Gregorian:
return q_gregorianYearFromJulianDay(jd);
case CalendarSyste::Jalali:
return q_jalaliYearFromJulianDay(jd);
}
}

// the calendar math (not optimized):
QCalendarSystemPrivate::ParsedDate
jalaliDateFromJulianDay(quint64 julianDay) {
quint64 depoch = julianDay - PERSIAN_EPOCH;
quint64 cycle = depoch / 1029983;
quint64 cyear = depoch % 1029983;
quint64 ycycle ;
quint64 aux1,aux2;
if(cyear==1029982){
ycycle=2820;
} else{
aux1 = cyear / 366;
aux2 = cyear % 366;
ycycle = (((2134 * aux1) + (2816 * aux2) + 2815) / 1028522)
+ aux1 + 1;
}
int year = ycycle + (2820 * cycle) + 474;
int month;
if(year <= 0){
--year;
}
quint64 yday = (julianDay - persian_jdn(year, 1, 1)) + 1;
if(yday <= 186){
month = ::ceil(static_cast<float>(yday)/31.0);
} else {
month = ::ceil(static_cast<float>(yday-6.0)/30.0);
}
int day = julianDay-persian_jdn(year, 1, 1)+1;
const QCalendarSystemPrivate::ParsedDate result = {year,month,day};
return result;
}

5.2. Provide separated date classes for each calendar system

So there will be QGregorianDate, QJalaliDate, QIslamicDate, QHebrewDate...
Possibly all inherited a common base. This solution is not reasonable. This
will not meet any of our requirements. And will not satisfy the need to
have
calendar system integrated into QDate (So that we can change the calnedar
easily). We prefer transparent API which is integrated to Qt itself, not
many
irrelevant classes. We already have them in several libraries, and our own
hand-made APIs that we currently use. However this API is open for
discussion.
If you think we must go with this option, feel free to discuss about your
reasons.

6. Known Problems

There are several issues with option 5.1.4 that are discussed here. I will
not
discuss other options (5.1.1 to 5.1.3) as I don't want to implement them.
If
you think they may fit the requirements, please let me know about the
reason
and issues. And also if you see any other issues not mentioned here, please
let
me know about.

6.1. Missing calendar localization in CLDR

Currently, month names are provided by QLocale and being read from the CLDR
embedded in Qt source code. Unfortunately there is no data in CLDR for
non-Gregorian calendars. This problem is submited as a proposal in 2012 [4]
which is not added to CLDR yet. So how do we provide data for month names?
I
have implemented a workaround for this:

QString
QLocale::monthName(int month, FormatType type, Calendar calendar) const
{
if (month < 1 || month > 12)
return QString();

switch (calendar) {
case Gregorian:
{
#ifndef QT_NO_SYSTEMLOCALE
if (d->m_data == systemData()) {
QVariant res =
systemLocale()->query(type == LongFormat
? QSystemLocale::MonthNameLong:
QSystemLocale::MonthNameShort,
month);
if (!res.isNull())
return res.toString();
}
#endif

quint32 idx, size;
switch (type) {
case QLocale::LongFormat:
idx = d->m_data->m_long_month_names_idx;
size = d->m_data->m_long_month_names_size;
break;
case QLocale::ShortFormat:
idx = d->m_data->m_short_month_names_idx;
size = d->m_data->m_short_month_names_size;
break;
case QLocale::NarrowFormat:
idx = d->m_data->m_narrow_month_names_idx;
size = d->m_data->m_narrow_month_names_size;
break;
default:
return QString();
}
return getLocaleListData(months_data + idx, size, month - 1);
}
case Jalali:
switch (type) {
case QLocale::LongFormat:
// TODO: Add local month names at least for
// Persian, Afghani, Pashtoo, English and Arabic
return jalaliLongMonthNames[month];
break;
case QLocale::ShortFormat:
case QLocale::NarrowFormat:
// TODO: Add local month names at least for
// Persian, Afghani, Pashtoo, English and Arabic
return jalaliShortMonthNames[month];
break;
default:
return QString();
}
default:
return QString();
}

}

It works, but it's not good enough. Can we add month names to the CLDR and
pass
the calnedar type to query?

6.2. Calendar system support must be optional. Adding calendars following
option 5.1.4, will add a dependency to QDate. And qmake will also need to
compile that. I'm thinking of adding options to configure script to enable
calendar support. And let qmake be compiled without calendar object in it.
This
requires a lot of #ifdef preprocessors in QDate class, to keep previous
implementation. That will cause a dirty code in QDate...

6.3. ICU support must be in place when ICU is available. We can do calendar
math, conversions using ICU. Though not having ICU, we must have calendars
available. That also requires a lot of math in QCalendarSystem messed up
with
loads of #ifdefs.

7. References

[1] https://wiki.qt.io/Locale_Support_in_Qt_5
[2] https://wiki.qt.io/Qt_5_ICU#QCalendarSystem_Design
[3]
http://lists.qt-project.org/pipermail/qt5-feedback/2011-September/001126.html
[4]
http://cldr.unicode.org/development/development-process/design-proposals/generic
-calendar-data

Cheers,
soroush
Edward Welbourne
2016-12-15 12:23:20 UTC
Permalink
Soroush Rabiei wrote:
> Nowadays, almost all major programming frameworks support calendar
> globalization. We are a small group of developers working with
> non-Gregorian calendars and we believe Qt must support this too. This
> proposal discusses details of our plan (and early implementation) to
> add support for multiple calendar systems in Qt library.

Excellent initiative. I've only had time for a cursory review (I'm
running away for mid-winter after today, not back until January, and
have a few other irons in the fire to get into a sensible state before I
go) so shall have to read in greater depth next year. However, one
thing did cross my mind in reading:

How about having the QCalendarSystem object be an optional parameter to
various methods of QDate, that configures how it behaves, with the
default behaviour being that of the Gregorian system ? This has the
advantage that client code might be able to supply a custom
QCalendarSystem object, where an enum-based solution can only know about
the ones that the Qt project has chosen to support.

Presumably every calendar system can be referred back to the Julian date
[0], so most of the QCalendarSystem API would just implement methods
mapping Julian date to the chosen calendar's year, month, day &c.

[0] which, lest anyone be confused, has nothing to do with the Julian
calendar - which *is* still in use ...


For the sake of anyone who hasn't understood why calendar system isn't
related to locale (or time-zone, or anything else particularly), note
that members of a culture that traditionally uses another calendar may
want to deal with a government-imposed (probably Gregorian) calendar
for all their work planning while using their culture's traditional
calendar when organising family and community events. A conference
centre organiser, furthermore, may want to be able to switch freely
between calendars to get a view of their diverse guests' perspectives in
order to avoid cultural gaffes and be ready to accommodate
complications. Even if there's nothing religious about the conference,
knowing that it happens to fall in Ramadan will prime the conference
centre staff to be ready to accommodate any attendees who won't be
eating during the hours of daylight,

Eddy.
Soroush Rabiei
2016-12-15 23:24:39 UTC
Permalink
On Thu, Dec 15, 2016 at 3:53 PM, Edward Welbourne <***@qt.io>
wrote:

> Soroush Rabiei wrote:
> > Nowadays, almost all major programming frameworks support calendar
> > globalization. We are a small group of developers working with
> > non-Gregorian calendars and we believe Qt must support this too. This
> > proposal discusses details of our plan (and early implementation) to
> > add support for multiple calendar systems in Qt library.
>
> Excellent initiative. I've only had time for a cursory review (I'm
> running away for mid-winter after today, not back until January, and
> have a few other irons in the fire to get into a sensible state before I
> go) so shall have to read in greater depth next year. However, one
> thing did cross my mind in reading:
>
> How about having the QCalendarSystem object be an optional parameter to
> various methods of QDate, that configures how it behaves, with the
> default behaviour being that of the Gregorian system ? This has the
> advantage that client code might be able to supply a custom
> QCalendarSystem object, where an enum-based solution can only know about
> the ones that the Qt project has chosen to support.
>

That's interesting. Please tell me more about your idea when you're back. I
suppose adding new calendars by users, requires subclassing QDate? Or maybe
somehow extend the enum that contains calendar systems[?]. I think adding
the information on which calendar system is current date is on, can be
added as a member (handlre/enum) to QDate.


>
> Presumably every calendar system can be referred back to the Julian date
> [0], so most of the QCalendarSystem API would just implement methods
> mapping Julian date to the chosen calendar's year, month, day &c.
>
> [0] which, lest anyone be confused, has nothing to do with the Julian
> calendar - which *is* still in use ...
>
>
That's correct for Persian, Islamic and Hebrew calendar AFAIK. There math
behind is a more complex compared to Gregorian, but it will do it. I'm
learning about other calendar systems, and it seems there will be no
problem with having ant reference day as a date start point, for any
calendar.


> For the sake of anyone who hasn't understood why calendar system isn't
> related to locale (or time-zone, or anything else particularly), note
> that members of a culture that traditionally uses another calendar may
> want to deal with a government-imposed (probably Gregorian) calendar
> for all their work planning while using their culture's traditional
> calendar when organising family and community events. A conference
> centre organiser, furthermore, may want to be able to switch freely
> between calendars to get a view of their diverse guests' perspectives in
> order to avoid cultural gaffes and be ready to accommodate
> complications. Even if there's nothing religious about the conference,
> knowing that it happens to fall in Ramadan will prime the conference
> centre staff to be ready to accommodate any attendees who won't be
> eating during the hours of daylight,
>
> Eddy.
>

Exactly! Locale, Time Zone and Calendars can be used in any combination. We
may want to have date and time in Persian calendar, written in German, on
on CET +01:00 to celebrate Nowrooz in Berlin. And we must be able to have
multiple calendars in one application.
Sune Vuorela
2016-12-15 21:10:19 UTC
Permalink
On 2016-12-15, Soroush Rabiei <***@gmail.com> wrote:
> 2.History
>

Hi

I would welcome more calendar systems. Personally I hope for french
revolutionary calendar. Because it is funny.

I think you need to touch quite some of the 'inner bits' of date / time,
and while you are there, I'd love if the design could make it easier to
implement my two missing pet features:
- Partial dates
- Date/time intervals/delta's.

You might need the latter part for the implementation.

(By partial dates, I mean e.g. my friend has birthday on november 1st.
every year. or unknown year.)

(by date/time deltas, I e.g. I started working somewhere on november 1st
2014. and stopped january 3rd 2015. How long did I work there)

/Sune
André Somers
2016-12-15 21:33:11 UTC
Permalink
Op 15/12/2016 om 22:10 schreef Sune Vuorela:
> On 2016-12-15, Soroush Rabiei <***@gmail.com> wrote:
>> 2.History
>>
> Hi
>
> I would welcome more calendar systems. Personally I hope for french
> revolutionary calendar. Because it is funny.
>
> I think you need to touch quite some of the 'inner bits' of date / time,
> and while you are there, I'd love if the design could make it easier to
> implement my two missing pet features:
> - Partial dates
> - Date/time intervals/delta's.
>
> You might need the latter part for the implementation.
>
> (By partial dates, I mean e.g. my friend has birthday on november 1st.
> every year. or unknown year.)
>
> (by date/time deltas, I e.g. I started working somewhere on november 1st
> 2014. and stopped january 3rd 2015. How long did I work there)
If memory serves me right: When, years ago, I tried to get the latter
in, the work was a bit blocked because somebody else what working on...
calendar support. :-)

André
Ch'Gans
2016-12-16 02:44:00 UTC
Permalink
On 16 December 2016 at 10:10, Sune Vuorela <***@vuorela.dk> wrote:
> On 2016-12-15, Soroush Rabiei <***@gmail.com> wrote:
>> 2.History
>>
>
> Hi
>
> I would welcome more calendar systems. Personally I hope for french
> revolutionary calendar. Because it is funny.
>
> I think you need to touch quite some of the 'inner bits' of date / time,
> and while you are there, I'd love if the design could make it easier to
> implement my two missing pet features:
> - Partial dates
> - Date/time intervals/delta's.
>
> You might need the latter part for the implementation.
>
> (By partial dates, I mean e.g. my friend has birthday on november 1st.
> every year. or unknown year.)
>
> (by date/time deltas, I e.g. I started working somewhere on november 1st
> 2014. and stopped january 3rd 2015. How long did I work there)

Boost have them all: date/time, calendar, time zone, time period and
time duration
Date/Time is a very tricky subject, why not rely on boost implementation?

http://www.boost.org/doc/libs/1_51_0/libs/locale/doc/html/group__date__time.html


Chris


>
> /Sune
>
> _______________________________________________
> Development mailing list
> ***@qt-project.org
> http://lists.qt-project.org/mailman/listinfo/development
Kevin Kofler
2016-12-16 15:33:40 UTC
Permalink
Ch'Gans wrote:
> Boost have them all: date/time, calendar, time zone, time period and
> time duration
> Date/Time is a very tricky subject, why not rely on boost implementation?
>
> http://www.boost.org/doc/libs/1_51_0/libs/locale/doc/html/group__date__time.html

Because it would suck for Qt to depend on Boost. And because just using
Boost directly in the applications (without a Qt wrapper) would mean dealing
with a horrible API.

Kevin Kofler
Thiago Macieira
2016-12-16 16:45:49 UTC
Permalink
Em quinta-feira, 15 de dezembro de 2016, às 21:10:19 PST, Sune Vuorela
escreveu:
> I think you need to touch quite some of the 'inner bits' of date / time,
> and while you are there, I'd love if the design could make it easier to
> implement my two missing pet features:
> - Partial dates
> - Date/time intervals/delta's.

I don't expect the calendaring system to require any changes to QDate
internals. It stores a Julian day, that's all.

--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Soroush Rabiei
2016-12-17 13:43:38 UTC
Permalink
>
> I don't expect the calendaring system to require any changes to QDate
> internals. It stores a Julian day, that's all.
>

That's why we need to change QDate. The idea is to remove all calendar
calculation code out of the QDate (into QCalendarSystem possibly). I think
QDate already has been bloated and knows more that it needs. Consequently,
there is no chance to add other calendaring API into QDate, and I think
it's wrong to add such implementation to QDate. My view of QDate is this:
QDate represents a day in time. So it only needs to know what day it is
(how many days are to the day 0).

For example, QDate knows how many days are in a month, what are month names
(in case locale is not present), how to find leap years, and how to convert
a JulianDay to year, month and day in Gregorian calendar. Well, I think it
should only keep a Julian day, and (possibly) a handle to some external
resource (may be QCalendarSystem) that knows about calendar math. This
external resource will tell the QDate how to convert its Julian day to
current calendar. Please let me know if there is anything wrong with this.

Some of my current changes to QDate are:

int QDate::year() const
{
if (isNull())
return 0;
int year = 0;
// Ask from QCalendarSystem what year we are in
year = this->cs.yearFromJulianDay(jd);
return year;
}

And also added two methods. This way I'm getting something like:

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QDate date;
date = QDate::currentDate();
date.setCalendar(QCalendarSystem::Jalali);
qDebug() << date.toString("yyyy/MM/dd");
qDebug() << date.toString("dd MMMM yyyy");
qDebug() << date.toString();
return 0;
}

That prints this for me:

Starting
/home/soroush/workspace/build-test-jalali-date-Qt_5_8_0_qt5-Debug/test-jalali-date...
"1395/09/26"
"26 Azar 1395"
"Sat Aza 26 1395"
Thiago Macieira
2016-12-17 19:16:24 UTC
Permalink
On sábado, 17 de dezembro de 2016 17:13:38 PST Soroush Rabiei wrote:
> > I don't expect the calendaring system to require any changes to QDate
> > internals. It stores a Julian day, that's all.
>
> That's why we need to change QDate.

If you change QDate's internals, you have to wait for Qt 6.

You can add to its API before then, though.

> The idea is to remove all calendar
> calculation code out of the QDate (into QCalendarSystem possibly). I think
> QDate already has been bloated and knows more that it needs. Consequently,
> there is no chance to add other calendaring API into QDate, and I think
> it's wrong to add such implementation to QDate. My view of QDate is this:
> QDate represents a day in time. So it only needs to know what day it is
> (how many days are to the day 0).

No chance of that ever happening. QDate will continue to support Gregorian
day, month, year, as well as ISO weeks. Support for other calendaring systems
(or maybe even other week systems) can be provided by a different class,
accessible from QDate and QLocale.

> Some of my current changes to QDate are:
>
> int QDate::year() const
> {
> if (isNull())
> return 0;
> int year = 0;
> // Ask from QCalendarSystem what year we are in
> year = this->cs.yearFromJulianDay(jd);
> return year;
> }

There's no "cs" member and you cannot add one, so the above would be:

return QCalendarSystem::gregorian(*this).year();

> And also added two methods. This way I'm getting something like:
>
> int main(int argc, char *argv[])
> {
> QCoreApplication a(argc, argv);
> QDate date;
> date = QDate::currentDate();
> date.setCalendar(QCalendarSystem::Jalali);

You can't add this method.

--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Soroush Rabiei
2016-12-19 08:20:47 UTC
Permalink
>
> If you change QDate's internals, you have to wait for Qt 6.
>
> You can add to its API before then, though.
>

Hmm... I'll think of something else then.

I thought these changes are backward compatible (API only), though it seems
I'm mistaken. Maybe missing something about ABI compatibility: Adding a
member will change memory layout and eventually client apps will be forced
to recompile I suppose...


> No chance of that ever happening. QDate will continue to support Gregorian
> day, month, year, as well as ISO weeks. Support for other calendaring
> systems
> (or maybe even other week systems) can be provided by a different class,
> accessible from QDate and QLocale.
>
>
It will support Gregorian day, month, year of course, adding calendaring
system will not change default behavior. But it will change if calendar is
set to something else. Nevertheless, it seems not possible for QDate to
change in semantics in anyway, not for Qt6 nor any release.

Following these rules, we need a new design, without changing QDate
interface nor its internals. I'm thinking of some changes in viewer widgets
and view classes to change their current calendar (utilizing calendar
classes and QLocale) without breaking compatibility.

Cheers,
Soroush
Soroush Rabiei
2016-12-19 13:18:27 UTC
Permalink
>
> Can you elaborate on the reasons that prevent any change of that kind in
> QDate? Maybe they can be worked around?
>
>
According to
https://community.kde.org/Policies/Binary_Compatibility_Issues_With_C%2B%2B#The_Do.27s_and_Don.27ts
Adding a member to struct or class is not possible without breaking ABI. On
the other hand https://wiki.qt.io/Qt-Version-Compatibility suggests that Qt
minor releases are backwards binary and source compatible. So there is no
chance of touching QDate in Qt5 series.

I'm working on a solution to provide calendar functionality without
breaking ABI, while considering possibilities for Qt6 (keep minimum effort
for converting current, temporary solution to futures Qt6 one). This may
fail of course, There are too many details that need to be discussed. How
we are supposed to change underlying calendar without adding information to
QDate? Can we add arguments to all methods with a default value? Something
like:


QDate d;
qDebug() << d.year(); // prints 2016
qDebug() << d.year(QCalendar::Jalali); // prints 1395

And then force relevant widgets/views to show/edit date and time on a
specific calendar system:

QDateEdit de;
de.setDate(d);
de.setCalendarSystem(QCalendar::Hebrew); // Is this possible?
Lars Knoll
2017-01-02 09:21:25 UTC
Permalink
I wonder whether we can't keep handling of different calendars completely outside of QDate. Something similar to what we've done with QString/QLocale. So QDate would continue unchanged and only support the standard Gregorian calendar. In addition, we have a QCalendar class, that can be constructed with a different calendar system, and can then return 'localized' date strings, days, months and years for this calendar system.

Something like:

QDate date;
QCalendar c(QCalendar::Hebrew);
QString hebrewDateString = c.toString(date);
int hebrewYear = c.year(date);

Maybe one could even integrate this into QLocale, that already provides support for localized month and day names?

Cheers,
Lars



On 19/12/16 14:18, "Development on behalf of Soroush Rabiei" <development-bounces+lars.knoll=***@qt-project.org on behalf of ***@gmail.com> wrote:


Can you elaborate on the reasons that prevent any change of that kind in
QDate? Maybe they can be worked around?





According to https://community.kde.org/Policies/Binary_Compatibility_Issues_With_C%2B%2B#The_Do.27s_and_Don.27ts Adding a member to struct
or class is not possible without breaking ABI. On the other hand https://wiki.qt.io/Qt-Version-Compatibility suggests that Qt minor releases are backwards binary and source compatible. So there is no
chance of touching QDate in Qt5 series.


I'm working on a solution to provide calendar functionality without breaking ABI, while considering possibilities for Qt6 (keep minimum effort for converting current, temporary solution to futures Qt6 one). This may fail of course, There are too many details
that need to be discussed. How we are supposed to change underlying calendar without adding information to QDate? Can we add arguments to all methods with a default value? Something like:




QDate d;
qDebug() << d.year(); // prints 2016
qDebug() << d.year(QCalendar::Jalali); // prints 1395



And then force relevant widgets/views to show/edit date and time on a specific calendar system:


QDateEdit de;
de.setDate(d);
de.setCalendarSystem(QCalendar::Hebrew); // Is this possible?
Frédéric Marchal
2017-01-02 11:01:49 UTC
Permalink
On Monday 02 January 2017 09:21:25 Lars Knoll wrote:
> I wonder whether we can't keep handling of different calendars completely
> outside of QDate. Something similar to what we've done with
> QString/QLocale. So QDate would continue unchanged and only support the
> standard Gregorian calendar. In addition, we have a QCalendar class, that
> can be constructed with a different calendar system, and can then return
> 'localized' date strings, days, months and years for this calendar system.
>
> Something like:
>
> QDate date;
> QCalendar c(QCalendar::Hebrew);
> QString hebrewDateString = c.toString(date);
> int hebrewYear = c.year(date);
>
> Maybe one could even integrate this into QLocale, that already provides
> support for localized month and day names?

There is more to it than converting a date to a string:

* Add N days to a date.
* Find the number of days in a month.
* Compare two dates.
* Count the number of days between two dates.

For instance a program wishing a happy new year to its users should do it with
as little modifications as possible.

Using a plain QDate would have been the easiest way to reach more users
because it doesn't require to replace lots of QDate with a new, very similar,
class. As it is not possible to change QDate for now, Soroush is looking for a
temporary solution that would bridge the gap until Qt6 is out.

BTW, happy new year to those using the Gregorian calendar :-)

Frederic
Lars Knoll
2017-01-02 11:39:58 UTC
Permalink
Sure, that there’s more to do than just the examples I listed. Still, design wise it might be a good idea to have this functionality in a class separate from QDate. We’ve done the same design decision for QString (having no locale specific functionality in QString), and this worked out rather nicely. So I would encourage you to have a look whether and how a similar design could be done for calendar system support.

Cheers,
Lars

On 02/01/17 12:01, "Frédéric Marchal" <***@wowtechnology.com> wrote:

On Monday 02 January 2017 09:21:25 Lars Knoll wrote:
> I wonder whether we can't keep handling of different calendars completely
> outside of QDate. Something similar to what we've done with
> QString/QLocale. So QDate would continue unchanged and only support the
> standard Gregorian calendar. In addition, we have a QCalendar class, that
> can be constructed with a different calendar system, and can then return
> 'localized' date strings, days, months and years for this calendar system.
>
> Something like:
>
> QDate date;
> QCalendar c(QCalendar::Hebrew);
> QString hebrewDateString = c.toString(date);
> int hebrewYear = c.year(date);
>
> Maybe one could even integrate this into QLocale, that already provides
> support for localized month and day names?

There is more to it than converting a date to a string:

* Add N days to a date.
* Find the number of days in a month.
* Compare two dates.
* Count the number of days between two dates.

For instance a program wishing a happy new year to its users should do it with
as little modifications as possible.

Using a plain QDate would have been the easiest way to reach more users
because it doesn't require to replace lots of QDate with a new, very similar,
class. As it is not possible to change QDate for now, Soroush is looking for a
temporary solution that would bridge the gap until Qt6 is out.

BTW, happy new year to those using the Gregorian calendar :-)

Frederic

_______________________________________________
Development mailing list
***@qt-project.org
http://lists.qt-project.org/mailman/listinfo/development
Edward Welbourne
2017-02-10 09:51:55 UTC
Permalink
On Monday 02 January 2017 09:21:25 Lars Knoll wrote:
>>> I wonder whether we can't keep handling of different calendars
>>> completely outside of QDate. Something similar to what we've done
>>> with QString/QLocale. So QDate would continue unchanged and only
>>> support the standard Gregorian calendar. In addition, we have a
>>> QCalendar class, that can be constructed with a different calendar
>>> system, and can then return 'localized' date strings, days, months
>>> and years for this calendar system.
>>>
>>> Something like:
>>>
>>> QDate date;
>>> QCalendar c(QCalendar::Hebrew);
>>> QString hebrewDateString = c.toString(date);
>>> int hebrewYear = c.year(date);
>>>
>>> Maybe one could even integrate this into QLocale, that already provides
>>> support for localized month and day names?

The current work actually moves QLocale's calendar-related data *out* to
the calendar system classes - a step towards the dismembering of QLocle
that was contemplated some years ago, but on which no progress appears
to have been made. This separation is one of the particularly
satisfying results of Soroush's work. Choice of calendar system is
(kinda) orthogonal to choice of locale, anyway, so the calendar data
doesn't belong in the QLocale (which only knows about language, script
and country, although we *could* teach QLocale(const QString &) to parse
more complications out of a string - I doubt that'd be a win).

Note, by the way, that the locale data is the *only* data in most
calendar system classes, handled as globals (as in QLocale) of the
class's compilation unit. Calendar objects themselves are usually
entirely algorithmic - they have no data members, just a vtable -
although one can conceive of eccentric ones (that we aren't about to
implement, but client code might) that could conceivably have member
data (e.g. a historian might use a hybrid Julian/Gregorian calendar with
one datum, the Julian day number at which to make the transition between
the two; the class for that could be instantiated with different
transition dates for the various jurisdictions that switched at
different dates).

On 02/01/17 12:01, "Frédéric Marchal" <***@wowtechnology.com> wrote:
>> There is more to it than converting a date to a string:
>>
>> * Add N days to a date.
>> * Find the number of days in a month.
>> * Compare two dates.
>> * Count the number of days between two dates.
>>
>> For instance a program wishing a happy new year to its users should
>> do it with as little modifications as possible.
>>
>> Using a plain QDate would have been the easiest way to reach more
>> users because it doesn't require to replace lots of QDate with a new,
>> very similar, class. As it is not possible to change QDate for now,
>> Soroush is looking for a temporary solution that would bridge the gap
>> until Qt6 is out.

Lars Knoll (2 January 2017 12:39) followed up with:
> Sure, that there’s more to do than just the examples I listed. Still,
> design wise it might be a good idea to have this functionality in a
> class separate from QDate. We’ve done the same design decision for
> QString (having no locale specific functionality in QString), and this
> worked out rather nicely. So I would encourage you to have a look
> whether and how a similar design could be done for calendar system
> support.

Actually, I do indeed want to move calendaring information - notably
that of the Gregorian calendar - out of QDate; even though QDate must
surely use Gregorian by default, moving the Gregorian logic (already
largely separated into static functions) out makes the QDate class more
straightforward. That actually leaves little in QDate itself; and what
it does leave can readily be made calendar-system agnostic, although it
manipulates a calendar-system object to do the work. So making
Gregorian the default in APIs that take a calendar system is in fact a
simplification of QDate, where I suppose locale-support complicated
QString and its removal simplified.

One big reason for wanting to integrate calendar into QDate is that,
without it, the overhead of supporting alternate calendars in any app
involves re-writing the app, reducing all use of QDate to a mere carrier
for its ->toJulianDay(). With QDate taking a calendar object (that
defaults Gregorian), it's fairly easy and painless to integrate calendar
support into an app. This is rather well illustrated by Soroush's most
recent patch-set (11), which extends QCalendarWidget to have
QAbstractCalendar member (that, of course, shall default Gregorian),
which it duly passes to each QDate method:

https://codereview.qt-project.org#/c/182341/11/src/widgets/widgets/qcalendarwidget.cpp,unified

This is easy, straightforward and natural; one can readily see that it's
correct. (The one wrinkle, QCalendarModel::referenceDate(), being due
to the existing comment looking suspiciously bogus, see my comments in
Gerrit; search for "Oct 1582" on PS 11.)

Now contrast this with what happens if we keep calendar systems out of
QDate. There's a basic level of conversion that would go just as
straightforwardly - transforming date->method(args) into
cal->method(date, args) in most cases - but then there are places where
the code needs to add days or jump to the start of the week, month or
year, e.g. QCalendarView::moveCursor(). The calendar API can surely
have the methods to do that - but, if it does so, the calendar systems
would all be doing the same thing as QDate, so we'd end up with concrete
methods of QAbstractCalendar (to avoid duplicating *between* systems),
each of which duplicates code in a matching QDate method. We could
eliminate this duplication by rewriting each QDate method as

ret QDate::method(args)
{
return QGregorianCalendar().method(this, args);
}

or, indeed, for get-ish things

ret QDate::method(args)
{
return QGregorianCalendar().method(this->toJulianDay(), args);
}

and, for set-ish things,

void QDate::setThing(args)
{
this->fromJulianDay(QGregorianCalendar()->julianDayFromThing(args));
}

possibly with a this->toJulianDay() added to the args, as needed. What
bothers me about this is that, at this point, I see no value left in
QDate. It would just be a cumbersome way to package a Julian day number
- as the last two illustrate - that code using calendar systems other
than Gregorian would be obliged to go via in order to interact with the
rest of Qt. That's actually worse than no value: it's an obstacle to
adding support for other calendars in apps using Qt.

QDate is the natural place for the algorithms that would, in this
scenario, be carried by QAbstractCalendar's non-abstract methods - they
are *date* manipulations, though they need to consult a calendar to work
out what to do, not *calendar* manipulations or conversions.

Now, as it happens, QDate's relevant methods at present mostly call out
to local static functions that would inevitably become methods of
QGregorianCalendar (and the exceptions inline relevant code in a QDate
method, duplicating what would be in QGregorianCalendar), making it
natural to implement them by having a local instance of that calendar
class in each method, so as to call its methods. Once we do that,
though, the stop to having that instance come in as a parameter (that
defaults to a QGregorianCalendar instance) is very low indeed.

As a result, integrating calendar support into QDate would actually be
more natural than separating it - it would intrude scarcely at all into
the implementations (once re-written to use a QGregorianCalendar, to
save duplication) while being the natural way to share date-manipulation
code that would otherwise have to live in QAbstractCalendar, either as a
duplicate of QDate's code or as what QDate code ends up calling to do a
job that more naturally belongs to QDate,

Eddy.
Soroush Rabiei
2017-08-05 12:08:23 UTC
Permalink
I believe our proposed change (containing locale backend , date and time
classes and related widgets) is ready to be merged (maybe after some minor
improvements)

I would like to ask someone to review this change:

https://codereview.qt-project.org/#/c/182341/

Cheers,
Soroush
Thiago Macieira
2017-08-05 16:14:43 UTC
Permalink
On Saturday, 5 August 2017 05:08:23 PDT Soroush Rabiei wrote:
> I believe our proposed change (containing locale backend , date and time
> classes and related widgets) is ready to be merged (maybe after some minor
> improvements)
>
> I would like to ask someone to review this change:
>
> https://codereview.qt-project.org/#/c/182341/

Will do, but it's a bit too late for the feature freeze in 3 days...

--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Thiago Macieira
2017-08-07 04:14:02 UTC
Permalink
On Saturday, 5 August 2017 05:08:23 PDT Soroush Rabiei wrote:
> I believe our proposed change (containing locale backend , date and time
> classes and related widgets) is ready to be merged (maybe after some minor
> improvements)
>
> I would like to ask someone to review this change:
>
> https://codereview.qt-project.org/#/c/182341/

Ok, a quick review's quick conclusions:

I mostly like your API. It looks simple and well integrated into QDateTime and
QLocale. There are a couple of minor problems in the API itself, like
QCalendarWidget::calendar() returning a reference. That's just wrong and needs
work.

But there are bigger problems with the implementation, starting with
QAbstractCalendar having a static protected QMap member. The big problem is
how you've implemented the new API in QDateTime and QLocale. There's code
duplication that cannot be there in QLocale, but the way you've removed the
duplication in QDateTime also needs changing for performance reasons.

int QDate::year() const
{
return year(QGregorianCalendar());
}

This creates a polymorphic object and makes a call that ends up delegating to
it in

if (cal.julianDayToDate(jd, y, m, d))

The commit also includes changes that look like unrelated clean-ups and will
need to be split into different commits. It's at this point lacking
documentation and there are a couple of coding style mistakes.

In all, good work and I like how this is coming along, but it's not "ready for
beta" right now, so it shouldn't be included in dev until after 5.11 opens for
new features.

--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Edward Welbourne
2017-08-08 15:49:36 UTC
Permalink
Thiago Macieira (7 August 2017 06:14)
> ... there are bigger problems with the implementation, starting with
> QAbstractCalendar having a static protected QMap member.

That's my fault. We're going to need some way for QML/V4 to get at
calendars; and I want to ensure our design leaves scope for client code
to provide a calendar of its choice - e.g. so that a developer whose
target market uses a relatively obscure calendar can support them with a
minimum of pain, without Qt itself needing to contain code for every
calendar that ever has been or will be invented. (Conversely, of
course, it's also worth making it easy to configure which, of the
calendar implementations present in Qt, actually get compiled into any
given build.) Each calendar implementation needs a way to make itself
visible to QML/V4.

My naive idea for how to solve that was to let each calendar
implementation register a factory for its objects under a name
representing its calendar. Then QML/V4 (and, for that matter, C++ APIs)
can look up a calendar by name and get a calendar object to use.
Indeed, we can even consult the map for a list of supported calendars,
to populate a UI's drop-down to select which to use.

Please suggest a better solution (and explain the problem with the
present solution), either here or in the review.

> The big problem is how you've implemented the new API in QDateTime and
> QLocale. There's code duplication that cannot be there in QLocale,

That's probably best addressed by you commenting on the review; I'm not
sure what duplication you're referring to ("cannot be there" is strong
language), although I do know about dateTimeToString(). There are a few
places I expect to find myself doing clean-up in the aftermath of
getting this all in, but I don't mind doing some of it before-hand.

Note, also, that this moves calendar-related data out of QLocale's
CLDR-derived data blob into calendar-specific data blobs - a step in the
general direction of making QLocale less monolithic.

> but the way you've removed the duplication in QDateTime also needs
> changing for performance reasons.
>
> int QDate::year() const
> {
> return year(QGregorianCalendar());
> }
>
> This creates a polymorphic object and makes a call that ends up delegating to
> it in
>
> if (cal.julianDayToDate(jd, y, m, d))

Please elaborate: why is this a problem ? The Gregorian arithmetic
naturally belongs in a calendar implementation. The date-time code
should naturally call it, rather than duplicating it in static functions
of its own (which have, naturally, been moved to become its methods).

> The commit also includes changes that look like unrelated clean-ups and will
> need to be split into different commits.

Please point these out on the review. Some of them might not be as
unrelated as you think - I did a fair bit of pulling separable changes
out already, rebasing Soroush's work onto the results. I may well have
missed some, but there were bits that couldn't be separated.

> It's at this point lacking documentation

Indeed, there remains work to be done. On the other hand, deciding what
shape the API should be is worth doing before taking pains over
documenting every detail - that would all change if we decide we need to
change the API.

> and there are a couple of coding style mistakes.

Please note in Gerrit.

Eddy.
Thiago Macieira
2017-08-08 18:10:09 UTC
Permalink
On Tuesday, 8 August 2017 08:49:36 PDT Edward Welbourne wrote:
> Thiago Macieira (7 August 2017 06:14)
>
> > ... there are bigger problems with the implementation, starting with
> > QAbstractCalendar having a static protected QMap member.
>
> That's my fault. We're going to need some way for QML/V4 to get at
> calendars; and I want to ensure our design leaves scope for client code
[cut]
>
> Please suggest a better solution (and explain the problem with the
> present solution), either here or in the review.

The problem is the presence of a protected, static, non-POD member. Remove it.

Store the data in a Q_GLOBAL_STATIC in qabstractcalendar.cpp. If you need to
get a listing or do any kind of searching, add static functions to
QAbstractCalendar. See QTextCodec for inspiration.

> > The big problem is how you've implemented the new API in QDateTime and
> > QLocale. There's code duplication that cannot be there in QLocale,
>
> That's probably best addressed by you commenting on the review; I'm not
> sure what duplication you're referring to ("cannot be there" is strong
> language), although I do know about dateTimeToString(). There are a few
> places I expect to find myself doing clean-up in the aftermath of
> getting this all in, but I don't mind doing some of it before-hand.

The big code block that was added in the commit to qlocale.cpp looks like a
copy & paste of existing code.

> Note, also, that this moves calendar-related data out of QLocale's
> CLDR-derived data blob into calendar-specific data blobs - a step in the
> general direction of making QLocale less monolithic.

That's good.

> > but the way you've removed the duplication in QDateTime also needs
> > changing for performance reasons.
> >
> > int QDate::year() const
> > {
> > return year(QGregorianCalendar());
> > }
> >
> > This creates a polymorphic object and makes a call that ends up delegating
> > to it in
> >
> > if (cal.julianDayToDate(jd, y, m, d))
>
> Please elaborate: why is this a problem ? The Gregorian arithmetic
> naturally belongs in a calendar implementation. The date-time code
> should naturally call it, rather than duplicating it in static functions
> of its own (which have, naturally, been moved to become its methods).

The problems are:
- creating a polymorphic type (QGregorianCalendar) [performance]
- passing it by reference instead of a pointer [coding style]
- calling a non-inline function to do the calculation [performance]

The performance problems are due to performance regressions. I'd rather you
inverted the deduplication: let QDate have the calculation and call that from
QGregorianCalendar.

> > The commit also includes changes that look like unrelated clean-ups and
> > will need to be split into different commits.
>
> Please point these out on the review. Some of them might not be as
> unrelated as you think - I did a fair bit of pulling separable changes
> out already, rebasing Soroush's work onto the results. I may well have
> missed some, but there were bits that couldn't be separated.

I will.

> > It's at this point lacking documentation
>
> Indeed, there remains work to be done. On the other hand, deciding what
> shape the API should be is worth doing before taking pains over
> documenting every detail - that would all change if we decide we need to
> change the API.

Sure. I just replied here because it looked like it was a request to make it
before the FF.

>
> > and there are a couple of coding style mistakes.
>
> Please note in Gerrit.

s/\w& / &/g

--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Sergio Martins
2017-01-02 12:44:44 UTC
Permalink
On 2017-01-02 11:01, Frédéric Marchal wrote:
(...)
> There is more to it than converting a date to a string:
>
> * Add N days to a date.
> * Find the number of days in a month.
> * Compare two dates.
> * Count the number of days between two dates.

Hi,


Have you seen KCalendarSystem ?
https://api.kde.org/4.x-api/kdelibs-apidocs/kdecore/html/classKCalendarSystem.html
It can do all the above and doesn't require disruptive changes such as
modifying QDate.
It's also stable and well tested, as it's been used in Kontact for many
years.


Regards,
--
Sérgio Martins | ***@kdab.com | Senior Software Engineer
Klarälvdalens Datakonsult AB, a KDAB Group company
Tel: Sweden (HQ) +46-563-540090, USA +1-866-777-KDAB(5322)
KDAB - The Qt, C++ and OpenGL Experts
Thiago Macieira
2017-01-02 13:29:18 UTC
Permalink
On segunda-feira, 2 de janeiro de 2017 12:01:49 BRST Frédéric Marchal wrote:
> There is more to it than converting a date to a string:
>
> * Add N days to a date.
> * Find the number of days in a month.
> * Compare two dates.
> * Count the number of days between two dates.

All but the second one are possible already with QDate, since they don't
depend on the calendaring system in use.

> For instance a program wishing a happy new year to its users should do it
> with as little modifications as possible.
>
> Using a plain QDate would have been the easiest way to reach more users
> because it doesn't require to replace lots of QDate with a new, very
> similar, class. As it is not possible to change QDate for now, Soroush is
> looking for a temporary solution that would bridge the gap until Qt6 is
> out.

I don't think I'll ever allow making those deep changes to QDate. Just like
QString being able to format numbers in one particular locale (the "C"
locale), we ought to support the C locale's calendaring system in QDate too.
If nothing else, because people are using that code right now and have been
for 20 years.

> BTW, happy new year to those using the Gregorian calendar :-)

Happy Gregorian New Year. 15 days until the Julian New Year.

--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Edward Welbourne
2017-01-05 16:12:16 UTC
Permalink
This post might be inappropriate. Click to display it.
Christoph Feck
2017-01-05 17:19:35 UTC
Permalink
On 05.01.2017 17:12, Edward Welbourne wrote:
> Sérgio Martins very helpfully linked to KCalendarSystem - thank you.
> One of the things we should clearly aim for is to make it easy for
> KCalendarSystem to (when its developers care to and can find the time,
> with no pressure to do so) adapt to use QCalendarSystem and the adapted
> QDate. TODO: I also need to find and talk to its maintainers.

Maintainer for KCalenderSystem was John Layt.
Soroush Rabiei
2017-01-07 07:51:39 UTC
Permalink
Greetings all, and happy new year


> Issue: Q(Date|Time)+ think day-changes happen at midnight. Some
>
calendar systems think they happen at sunset or sunrise; these are both
> rather tricky, as their time depends on date and latitude [3] - and I'm
> not sure what they do about days when the sun doesn't set or rise at
> all. I don't see any hint of coping with this in KCalendarSystem and
> I'm fairly sure I don't want to solve that problem, so I think we stick
> with midnight and leave apps using the result to fix up after the fact
> if they really care. Indeed, what *is* common practice in The Middle
> East, as regards how evenings get handled - as the tail of one day or
> the start of the next ? Has secular society shifted the day boundary to
> midnight in practice ?
>

Indeed they do the simplification needed to adopt modern lifestyle. As far
as I know, modern lunar calendars being used in ME as official calendars,
have loads of simplifications applied compared to the original calendars.
The start of a month in Islamic calendar in Fiqh (regional calendar) is
based on observing new moon (by unaided eye) in the sky. That's why Saudi
Arabia sometimes celebrate end of Ramadan one day after Turkey, and one day
before Iran. That's why number of days differ from a year to another.
Though, the start of the day is 00:00 in modern calendars. And number of
days in months are fixed.

Anyway, the only country using lunar calendar (Saudi Arabia) just switched
to solar system (Gregorian). But we have to support Islamic calendar
(modern version) of course.

My current implementation looks like KCalendarSystem with some differences.
Calendar classes are subclassed from QAbstractCalendar, and have to
implement several methods. I tried to make this class as generic as
possible, there is no assumption on month days and number of months. I just
finished adding new methods to QDate and it's working for Jalali Calendar.

QDate d = QDate::currentDate();
int gyear1 = d.year(); // 2017
int gyear2 = d.year(QGregorianCalendar); // 2017
int jyear1 = d.year(QJalaliCalendar()); // 1395
int jyear2 = d.year(QAbstractCalendar::fromName("jalali")); // 1395

I didn't touch Gregorian calculations in QDate, just added more functions.
Does removing static members count as an ABI change? I'm planning to move
all the math out of QDate into QGregorianCalendar.

About calendaring widgets, The widget obviously needs to keep information
on what calendar system it uses. I think it's possible to add members to
private API classes? After all it's **private** and there's only a pointer
to private API class in main class. So it would be A[BP]I compatible I
suppose. And also we need to add some functions to main class:

void QDateEdit::setCalendar(QAbstractCalendar* cal); // maybe a QString?
QAbstractCalendar* QDateEdit::calendar() const; // meh...

There is another issue: How do we add calendar localization to QLocale?

Cheers,
Soroush
Edward Welbourne
2017-01-09 12:43:42 UTC
Permalink
I observed:
>> Issue: Q(Date|Time)+ think day-changes happen at midnight. [...] Has
>> secular society shifted the day boundary to midnight in practice ?

Soroush replied:
> Indeed they do the simplification needed to adopt modern lifestyle.

OK, good - then we have an excuse to keep the simple model !

> As far as I know, modern lunar calendars being used in ME as official
> calendars, have loads of simplifications applied compared to the
> original calendars. The start of a month in Islamic calendar in Fiqh
> (regional calendar) is based on observing new moon (by unaided eye) in
> the sky. That's why Saudi Arabia sometimes celebrate end of Ramadan
> one day after Turkey, and one day before Iran. That's why number of
> days differ from a year to another.

All of which argues for us to support the simplified official version,
while making it possible for apps and user-communities to add in their
own implementations taking account of local quirks.

> Though, the start of the day is 00:00 in modern calendars. And number
> of days in months are fixed.

That, at least, is reassuring ;-)

> Anyway, the only country using lunar calendar (Saudi Arabia) just
> switched to solar system (Gregorian). But we have to support Islamic
> calendar (modern version) of course.

Indeed - even if it's not the official state calendar, there are folk
still using it who shall want their software to respect that. We can
make that *possible*, even where we aren't going to do all of the
work ourselves ...

> My current implementation looks like KCalendarSystem with some
> differences. Calendar classes are subclassed from QAbstractCalendar,
> and have to implement several methods. I tried to make this class as
> generic as possible, there is no assumption on month days and number
> of months.

Sounds promising. If you can push it to gerrit as a work in progress
(start the subject line of the commit message with "WIP: " and the
system shall know what you mean; you can also push to refs/drafts/dev to
make it a draft review, until you eventually push to refs/for/dev to
turn it into a "real" review), I'll take a look at it and we can discuss
more there (taking traffic off this list).

I was tidying up in our Wiki after wearing out my brain on Friday and
stumbled upon [0], a 2011 write-up of relevant ideas - written by John
Layt, who is the author of KCalendarSystem. (Speaking of: if anyone is
in touch with him and can reach him other than via his @KDE e-mail,
which I've pinged, please do bring this discussion to his attention.)
It may be a source of some fruitful insights.

[0] https://wiki.qt.io/Qt-contributors-summit-2011-QDateTime

> I just finished adding new methods to QDate and it's working for
> Jalali Calendar.
>
> QDate d = QDate::currentDate();
> int gyear1 = d.year(); // 2017
> int gyear2 = d.year(QGregorianCalendar); // 2017
> int jyear1 = d.year(QJalaliCalendar()); // 1395
> int jyear2 = d.year(QAbstractCalendar::fromName("jalali")); // 1395

Nice. I interpolate a missing () on the end of QGregorianCalendar().

> I didn't touch Gregorian calculations in QDate, just added more
> functions. Does removing static members count as an ABI change? I'm
> planning to move all the math out of QDate into QGregorianCalendar.

A static function is invisible to anything outside its own compilation
unit, so turning assorted static functions in qdatetime.cpp into methods
of QGregorianCalendar is entirely feasible. You can even mess with
QDateTimePrivate - see the warning comment in qdatetime_p.h ;^>

Static members of a public class, however, have to stay (I suppose).
Not that you have to continue *using* them, if something else makes them
redundant ...

> About calendaring widgets, The widget obviously needs to keep
> information on what calendar system it uses. I think it's possible to
> add members to private API classes? After all it's **private** and
> there's only a pointer to private API class in main class. So it would
> be A[BP]I compatible I suppose.

Check for that warning comment, or a header-name ending _p.h - you can
get away with plenty of change there.

> And also we need to add some functions to main class:
>
> void QDateEdit::setCalendar(QAbstractCalendar* cal); // maybe a QString?
> QAbstractCalendar* QDateEdit::calendar() const; // meh...

Those make sense. Definitely pass QAbstractCalendar * to the setter;
otherwise, client code can't supply a custom calendar that's not known
to our abstract base class's method. The advantage of an overload
taking QString is minor - it'll package a call to fromName - and I fear
it would add confusions, with folk expecting too much of it, or a
matching getter returning QString.

Please keep QDateEdit changes in a separate commit from QDateTime
changes; ideally, even separate the creation of QCalendarSystem's base
class (at least) from re-working QDateTime to break out
QGregorianCalendar.

> There is another issue: How do we add calendar localization to QLocale?

I don't know. However, [1] may offer some insight; albeit [2] is
similar and says the plan outlined there was abandoned - two more finds
from Friday evening. The latter links to a blog post, by John, from
2012 - as a hint to how old these are.

[1] https://wiki.qt.io/Locale_Support_in_Qt_5#QCalendar
[2] https://wiki.qt.io/Qt_5_ICU

Eddy.
Soroush Rabiei
2017-01-13 14:07:32 UTC
Permalink
Sorry for being noisy on this list, but I think we have several issues
needed discussion before going further.

First we have a design decision to make, the minimal set of
assumptions on calendaring systems. According to my minute research we
can assume following facts on every calendar that is in-use:

1. All calendaring systems, show dates in three levels: Year, Month and Day
2. The week is 7 days. It may start at any of these days.
3. Number of months in all years are fixed.

Please let me know about any problem.

Second issue is on CLDR data. I was all mistaken about CLDR. The new
versions (including v29) include month name data for many calendars
(Gregorian, Buddhist, Chinese, Coptic, Dangi, Ethiopic, Hebrew,
Indian, Islamic, Japanese and Persian). I managed to modify the
scripts (cldr2qlocalexml.py and qlocalexml2cpp.py) to prepare data for
Jalali calendar (named as Persian in CLDR terminology). On the other
hand, QLocale integration is completed. Now there is `months_data_per'
array in qlocale_data_p.h and it's working well.

The problem however, is that this data is bigger than what I had in
mind. Apparently there are many communities using Jalali calendars.
There are strange month names for Korean, Russian, Turkish and several
more. (I was expecting data only for Persian and Afghani languages,
maybe Tajik too...) The array containing month names is 2419 bytes.
There is also another 6*2=12 bytes per locale in locale's index table.
considering number of supported locales in qt, we will have 552 * 12 =
6624 bytes there and as a result the overhead in Qt's binary, will be
9.043 KB only for Jalali calendar. Islamic civil calendar is almost
three times bigger if I'm not much mistaken, and I have no idea about
other calendars. I guess adding five calendars will blow QtCore up to
5~7 Megabytes.

I have changed qlocalexml2cpp.py such that it generates data for each
calendar in a separate array. So it can be excluded by preprocessor
directives. My suggestion is to make calendaring support,
configurable, and provide data only for Gregorian calendar by default.
And also we can give users the option to choose among locales they
want to include in their builds:

./configure -developer-build -opensource -confirm-license -debug
-calendars gregorian,persian,islamic_civil

Cheers,
Soroush
Edward Welbourne
2017-01-13 14:46:20 UTC
Permalink
Soroush Rabiei
> Sorry for being noisy on this list, but I think we have several issues
> needed discussion before going further.

I should note that Lars still holds to the view that we should keep this
out of QDate: which seems to imply roughly just upstreaming
KCalendarSystem, albeit with the opportunity to make any improvements we
want in the process.

> First we have a design decision to make, the minimal set of
> assumptions on calendaring systems. According to my minute research we
> can assume following facts on every calendar that is in-use:
>
> 1. All calendaring systems, show dates in three levels: Year, Month and Day
> 2. The week is 7 days. It may start at any of these days.

French revolutionary calendar:
https://en.wikipedia.org/wiki/French_Republican_Calendar
Not that I propose supporting it !

However, it would be best to design a system which doesn't preclude
someone else implementing it. If we can avoid hard-coding the
assumption of a seven-day week, so much the better.

> 3. Number of months in all years are fixed.

Is there no lunar calendar with occasional extra months ?

> Please let me know about any problem.
>
> Second issue is on CLDR data. I was all mistaken about CLDR. The new
> versions (including v29) include month name data for many calendars
> (Gregorian, Buddhist, Chinese, Coptic, Dangi, Ethiopic, Hebrew,
> Indian, Islamic, Japanese and Persian). I managed to modify the
> scripts (cldr2qlocalexml.py and qlocalexml2cpp.py) to prepare data for
> Jalali calendar (named as Persian in CLDR terminology). On the other
> hand, QLocale integration is completed. Now there is `months_data_per'
> array in qlocale_data_p.h and it's working well.
>
> The problem however, is that this data is bigger than what I had in
> mind. Apparently there are many communities using Jalali calendars.
> There are strange month names for Korean, Russian, Turkish and several
> more. (I was expecting data only for Persian and Afghani languages,
> maybe Tajik too...) The array containing month names is 2419 bytes.
> There is also another 6*2=12 bytes per locale in locale's index table.
> considering number of supported locales in qt, we will have 552 * 12 =
> 6624 bytes there and as a result the overhead in Qt's binary, will be
> 9.043 KB only for Jalali calendar. Islamic civil calendar is almost
> three times bigger if I'm not much mistaken, and I have no idea about
> other calendars. I guess adding five calendars will blow QtCore up to
> 5~7 Megabytes.

Not something we want to impose on all users of Qt.

> I have changed qlocalexml2cpp.py such that it generates data for each
> calendar in a separate array. So it can be excluded by preprocessor
> directives. My suggestion is to make calendaring support,
> configurable, and provide data only for Gregorian calendar by default.
> And also we can give users the option to choose among locales they
> want to include in their builds:
>
> ./configure -developer-build -opensource -confirm-license -debug -calendars gregorian,persian,islamic_civil

That sounds reasonable. Another approach might be to design a
plugin-based architecture - with similar config, but controlling which
plugins get built. This would make life easier for anyone who wants to
add a calendar system we don't support: they'd just need to add their
plugin to wherever we put ours.

Eddy.
Kevin Kofler
2017-01-14 00:09:26 UTC
Permalink
Edward Welbourne wrote:

> Soroush Rabiei
>> 3. Number of months in all years are fixed.
>
> Is there no lunar calendar with occasional extra months ?

Of course there is. Any lunar calendar adjusted for consistency with the
solar year necessarily has occasional intercalar months. The Jewish calendar
is the most prominent example. (The Islamic calendar, on the other hand, is
the most prominent example of a lunar calendar that is NOT adjusted for the
solar year, which explains why e.g. Ramadan rotates all over the solar year
over the years.)

Kevin Kofler
Grégoire Barbier
2017-01-14 12:33:32 UTC
Permalink
Le 13/01/2017 à 15:46, Edward Welbourne a écrit :
> Soroush Rabiei
>> 1. All calendaring systems, show dates in three levels: Year, Month and Day
>> 2. The week is 7 days. It may start at any of these days.
> French revolutionary calendar:
> https://en.wikipedia.org/wiki/French_Republican_Calendar
> Not that I propose supporting it !

Don't forget that in addition to weeks being 10 days, days are also
divided into 10 decimal hours, each of them counting 100 decimal minutes.

It would be a lot of fun to support it. But not sure anyone actually
needs that. ;-)

--
Grégoire Barbier :: g à g76r.eu :: +33 6 21 35 73 49
Sune Vuorela
2016-12-21 21:05:20 UTC
Permalink
On 2016-12-17, Soroush Rabiei <***@gmail.com> wrote:
> it's wrong to add such implementation to QDate. My view of QDate is this:
> QDate represents a day in time. So it only needs to know what day it is
> (how many days are to the day 0).

And it is exactly things like that that would prevent partial date
support in QDate (which is why I brought it up)

/Sune
Soroush Rabiei
2017-01-15 11:09:49 UTC
Permalink
Just submitted first change set:

https://codereview.qt-project.org/#/c/182341/

Planning to add three more calendars and separate index array (to
reduce overhead on locales) this week. Persian calendar is still one
day behind. Seems to be related to julian day base. The original
algorithm counts day 1 as a base date.
Frédéric Marchal
2017-01-16 08:13:20 UTC
Permalink
On Sunday 15 January 2017 14:39:49 Soroush Rabiei wrote:
> Just submitted first change set:
>
> https://codereview.qt-project.org/#/c/182341/

I'm seeing an error: "The page you requested was not found, or you do not have
permission to view this page."

Frederic
Edward Welbourne
2017-01-16 13:07:21 UTC
Permalink
On Sunday 15 January 2017 14:39:49 Soroush Rabiei wrote:
>> Just submitted first change set:
>>
>> https://codereview.qt-project.org/#/c/182341/

Frédéric Marchal replied:
> I'm seeing an error: "The page you requested was not found, or you do
> not have permission to view this page."

I've just added you to the list of reviewers - does that help ?

Eddy.
Jake Petroules
2017-01-16 19:10:59 UTC
Permalink
Eddy, "draft" does not do what you think it does. This is why no one can see the change.

Please remove "draft" status and add "WIP: " at the front of the commit message instead so we can all take a look.

Thanks,

> On Jan 16, 2017, at 5:07 AM, Edward Welbourne <***@qt.io> wrote:
>
> On Sunday 15 January 2017 14:39:49 Soroush Rabiei wrote:
>>> Just submitted first change set:
>>>
>>> https://codereview.qt-project.org/#/c/182341/
>
> Frédéric Marchal replied:
>> I'm seeing an error: "The page you requested was not found, or you do
>> not have permission to view this page."
>
> I've just added you to the list of reviewers - does that help ?
>
> Eddy.
> _______________________________________________
> Development mailing list
> ***@qt-project.org
> http://lists.qt-project.org/mailman/listinfo/development

--
Jake Petroules - ***@qt.io
The Qt Company - Silicon Valley
Qbs build tool evangelist - qbs.io
Edward Welbourne
2017-01-17 10:22:57 UTC
Permalink
Jake Petroules
> Eddy, "draft" does not do what you think it does. This is why no one can see the change.

I think you are addressing the wrong person.
Soroush created the review (as a draft) and added me as a reviewer.
That enabled me to add Frederic.

> Please remove "draft" status and add "WIP: " at the front of the commit message instead so we can all take a look.

It already has WIP: on its commit message.

Soroush: please push your next patch set to refs/for/dev, to make the review public.
Subsequent pushes can be to refs/drafts/dev if you like, to make clear it's all still a draft.

Eddy.
Thiago Macieira
2017-01-17 17:14:19 UTC
Permalink
Em terça-feira, 17 de janeiro de 2017, às 10:22:57 PST, Edward Welbourne
escreveu:
> Jake Petroules
>
> > Eddy, "draft" does not do what you think it does. This is why no one can
> > see the change.
> I think you are addressing the wrong person.
> Soroush created the review (as a draft) and added me as a reviewer.
> That enabled me to add Frederic.
>
> > Please remove "draft" status and add "WIP: " at the front of the commit
> > message instead so we can all take a look.
> It already has WIP: on its commit message.
>
> Soroush: please push your next patch set to refs/for/dev, to make the review
> public. Subsequent pushes can be to refs/drafts/dev if you like, to make
> clear it's all still a draft.

People not added to the reviews will not see them.

Don't use refs/drafts for anything you want other people to see.

--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Allan Sandfeld Jensen
2017-01-16 22:07:54 UTC
Permalink
On Thursday 15 December 2016, Soroush Rabiei wrote:
> Currently there is no support of any calendar system other than Gregorian.
> Gregorian calendar is widely used in western countries. However most
> countries
> in Middle-east, Asia and Africa use other calendar systems. Unfortunately
> there
> is no support for them.
>
Are you aware of KCalenderSystem? It appears to have most of the calenders,
and is under LGPL, not sure if it has been ported to KDE Frameworks though,
and it could probably be useful to reimplement for Qt inclusion, but it might
be worth checking out if you plan to implement something similar.

Best regards
`Allan
Edward Welbourne
2017-01-17 10:30:19 UTC
Permalink
Allan Sandfeld Jensen asked:
> Are you aware of KCalenderSystem?

Yes - Sergio Martins helpfully brought it up a couple of weeks ago:
http://lists.qt-project.org/pipermail/development/2017-January/028241.html

Current plan is roughly to upstream it. Debate remains as to whether it
should sit outside QDate (Lars) or be taken by overloads of QDate
methods to tweak their action (Soroush and I).

Eddy.
Soroush Rabiei
2017-01-30 12:13:30 UTC
Permalink
Can we have calendars for 5.9 ? It's not FF yet I suppose. And there's
not much to do. Either we implement calendars as factory classes
operating on QDate, or adding to QDate's API, there is not much work
left to do.
Thiago Macieira
2017-01-30 16:37:42 UTC
Permalink
On segunda-feira, 30 de janeiro de 2017 15:43:30 PST Soroush Rabiei wrote:
> Can we have calendars for 5.9 ? It's not FF yet I suppose. And there's
> not much to do. Either we implement calendars as factory classes
> operating on QDate, or adding to QDate's API, there is not much work
> left to do.

I'll review your change proposal to QDate, but don't hold your hopes up that
it'll pass in time for the feature freeze.

--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Loading...