Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package kopeninghours for openSUSE:Factory checked in at 2022-01-11 21:18:47 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/kopeninghours (Old) and /work/SRC/openSUSE:Factory/.kopeninghours.new.1892 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "kopeninghours" Tue Jan 11 21:18:47 2022 rev:10 rq:944375 version:21.12.1 Changes: -------- --- /work/SRC/openSUSE:Factory/kopeninghours/kopeninghours.changes 2021-12-13 20:48:52.844576744 +0100 +++ /work/SRC/openSUSE:Factory/.kopeninghours.new.1892/kopeninghours.changes 2022-01-11 21:22:41.065102940 +0100 @@ -1,0 +2,25 @@ +Tue Jan 4 10:26:05 UTC 2022 - Christophe Giboudeaux <[email protected]> + +- Update to 21.12.1 + * New bugfix release + * For more details please see: + * https://kde.org/announcements/gear/21.12.1/ +- Changes since 21.12.0: + * Turn nthMask into a vector of entries, to preserve intervals and ordering (kde#445963) + * Extended monthdays: remove ascending requirement + * Extend the extended syntax for month day sets + * Fix OpeningHours::simplifiedExpression not really being const + * Simplify "Feb 1-29" to "Feb" (kde#446252) + * Consider "through" as alternative range separator (kde#446136) + * Do not repeat months when unnecessary (kde#446224) + * Fix autocorrect for cases where the state differs (kde#445787) + * Add more Spanish state values (kde#446134) + * Support 4 digit times to the extend possible (kde#446137) + * Merge adjacent single weekday and time range selectors (kde#445784) + * Convert 24/7 rules to timespan selectors when used in a timespan context (kde#445962) + * Add Russian language support for states and sun-based events (kde#445785) + * Parse Russian long-form time ranges + * Add localized Russian month names + * Parse localized Russian day names + +------------------------------------------------------------------- Old: ---- kopeninghours-21.12.0.tar.xz kopeninghours-21.12.0.tar.xz.sig New: ---- kopeninghours-21.12.1.tar.xz kopeninghours-21.12.1.tar.xz.sig ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ kopeninghours.spec ++++++ --- /var/tmp/diff_new_pack.lqvZtQ/_old 2022-01-11 21:22:41.537103271 +0100 +++ /var/tmp/diff_new_pack.lqvZtQ/_new 2022-01-11 21:22:41.541103274 +0100 @@ -16,15 +16,15 @@ # -%bcond_without lang +%bcond_without released Name: kopeninghours -Version: 21.12.0 +Version: 21.12.1 Release: 0 Summary: OSM opening hours expression parser and evaluator License: LGPL-2.0-or-later URL: https://www.kde.org Source: https://download.kde.org/stable/release-service/%{version}/src/%{name}-%{version}.tar.xz -%if %{with lang} +%if %{with released} Source1: https://download.kde.org/stable/release-service/%{version}/src/%{name}-%{version}.tar.xz.sig Source2: applications.keyring %endif @@ -66,7 +66,7 @@ %install %kf5_makeinstall -C build -%if %{with lang} +%if %{with released} %find_lang %{name} --with-man %endif @@ -94,7 +94,7 @@ %{_kf5_cmakedir}/KOpeningHours/ %{_kf5_libdir}/libKOpeningHours.so -%if %{with lang} +%if %{with released} %files lang -f %{name}.lang %endif ++++++ kopeninghours-21.12.0.tar.xz -> kopeninghours-21.12.1.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kopeninghours-21.12.0/CMakeLists.txt new/kopeninghours-21.12.1/CMakeLists.txt --- old/kopeninghours-21.12.0/CMakeLists.txt 2021-12-02 23:01:28.000000000 +0100 +++ new/kopeninghours-21.12.1/CMakeLists.txt 2022-01-03 22:15:03.000000000 +0100 @@ -6,7 +6,7 @@ # KDE Application Version, managed by release script set (RELEASE_SERVICE_VERSION_MAJOR "21") set (RELEASE_SERVICE_VERSION_MINOR "12") -set (RELEASE_SERVICE_VERSION_MICRO "0") +set (RELEASE_SERVICE_VERSION_MICRO "1") set (RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}") project(KOpeningHours VERSION ${RELEASE_SERVICE_VERSION}) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kopeninghours-21.12.0/autotests/evaluatetest.cpp new/kopeninghours-21.12.1/autotests/evaluatetest.cpp --- old/kopeninghours-21.12.0/autotests/evaluatetest.cpp 2021-12-02 23:01:28.000000000 +0100 +++ new/kopeninghours-21.12.1/autotests/evaluatetest.cpp 2022-01-03 22:15:03.000000000 +0100 @@ -76,7 +76,7 @@ QTest::newRow("year open end") << QByteArray("2010+") << QDateTime({2010, 1, 1}, {0, 0}) << QDateTime(); QTest::newRow("year interval odd") << QByteArray("2011-2031/2") << QDateTime({2021, 1, 1}, {0, 0}) << QDateTime({2022, 1, 1}, {0, 0}); QTest::newRow("year interval even") << QByteArray("2010-2030/2") << QDateTime({2020, 1, 1}, {0, 0}) << QDateTime({2021, 1, 1}, {0, 0}); - QTest::newRow("year interval 4") << QByteArray("2000-2100/4") << QDateTime({2020, 1, 1}, {0, 0}) << QDateTime({2021, 1, 1}, {0, 0}); + QTest::newRow("year interval 4") << QByteArray("2004-2096/4") << QDateTime({2020, 1, 1}, {0, 0}) << QDateTime({2021, 1, 1}, {0, 0}); QTest::newRow("year time") << QByteArray("2021 10:00-20:00") << QDateTime({2021, 1, 1}, {10, 0}) << QDateTime({2021, 1, 1}, {20, 0}); QTest::newRow("year open end interval") << QByteArray("2016/5") << QDateTime({2021, 1, 1}, {0, 0}) << QDateTime({2022, 1, 1}, {0, 0}); @@ -195,7 +195,7 @@ void testNoMatch_data() { QTest::addColumn<QByteArray>("expression"); - QTest::newRow("year range") << QByteArray("1980-2000"); + QTest::newRow("year range") << QByteArray("1980-1999"); QTest::newRow("full date") << QByteArray("2020 Nov 6"); QTest::newRow("date range") << QByteArray("1980 Jan 1-2020 Nov 6"); QTest::newRow("year/month") << QByteArray("2020 Oct"); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kopeninghours-21.12.0/autotests/parsertest.cpp new/kopeninghours-21.12.1/autotests/parsertest.cpp --- old/kopeninghours-21.12.0/autotests/parsertest.cpp 2021-12-02 23:01:28.000000000 +0100 +++ new/kopeninghours-21.12.1/autotests/parsertest.cpp 2022-01-03 22:15:03.000000000 +0100 @@ -32,12 +32,12 @@ T("Dec off"); T("Dec 25 off"); T("Dec 25-26 off"); - T2("Dec 24-26,31 off", "Dec 24-26,Dec 31 off"); - T2("Jan 1,6 off", "Jan 01,Jan 06 off"); - T2("Dec 24,25,26", "Dec 24,Dec 25,Dec 26"); - T2("Jan 03,Dec 04,24 off", "Jan 03,Dec 04,Dec 24 off"); - T2("Jan 03, Dec 04, 24 off", "Jan 03,Dec 04,Dec 24 off"); - T2("07:30-20:00; Jan 03,13,23,Dec 04,14,24 off", "07:30-20:00; Jan 03,Jan 13,Jan 23,Dec 04,Dec 14,Dec 24 off"); + T("Dec 24-26,31 off"); + T2("Jan 1,6 off", "Jan 01,06 off"); + T("Dec 24,25,26"); + T2("Jan 03,Dec 04,24 off", "Jan 03,Dec 04,24 off"); + T2("Jan 03, Dec 04, 24 off", "Jan 03,Dec 04,24 off"); + T2("07:30-20:00; Jan 03,13,23,Dec 04,14,24 off", "07:30-20:00; Jan 03,13,23,Dec 04,14,24 off"); T("Dec 08:00"); T("Dec 08:00-14:00"); T("easter off"); @@ -95,6 +95,12 @@ T("Mar Su[1]-Oct Su[1]: 11:00-20:00; PH 11:00-20:00"); T2("Mo 20:00-26:00", "Mo 20:00-26:00"); // https://github.com/osm-fr/osmose-backend/issues/1344 + // https://bugs.kde.org/show_bug.cgi?id=445963 + T("Th[1-2] 09:30-11:45"); + T("Th[1,2] 09:30-11:45"); + T("Mo[1,3,-1]"); // order as desired, -1 means last + T("Mo[1,3,2]"); // currently not normalized + // from https://wiki.openstreetmap.org/wiki/Key:opening_hours#Simple_examples T("Mo-Fr 08:00-17:30"); T("Mo-Fr 08:00-12:00,13:00-17:30"); @@ -156,7 +162,7 @@ T("2012 easter -2 days-2012 easter +2 days: open \"Around easter\"; PH off"); T("24/7 closed \"always closed\""); T2("2013,2015,2050-2053,2055/2,2020-2029/3,2060+ Jan 1", "2013,2015,2050-2053,2055/2,2020-2029/3,2060+ Jan 01"); - T("Jan 23-Feb 11,Feb 12 00:00-24:00; PH off"); + T2("Jan 23-Feb 11,Feb 12 00:00-24:00; PH off", "Jan 23-Feb 11,Feb 12 00:00-24:00; PH off"); T("Apr-Oct Su[2] 14:00-18:00; Aug Su[-1] -1 day 10:00-18:00; Aug Su[-1] 10:00-18:00; PH off"); T("Mo-Fr 08:00-12:00, We 14:00-18:00; Su,PH off"); // open We morning too T("Mo-Fr 08:00-12:00; We 14:00-18:00; Su,PH off"); // closed We morning @@ -181,8 +187,9 @@ T("Oct: We[1]"); // from https://github.com/dfaure/DataNovaImportScripts/blob/master/saved_opening_hours - T3("Mo-Tu,Th-Fr 09:30-12:00; 2020 Dec 28 off; 2020 Dec 22,2020 Dec 29 off; We 15:00-17:00; 2020 Dec 23,2020 Dec 30 off; 2020 Dec 24,2020 Dec 31 off; Sa 10:00-12:00; 2020 Dec 26,2021 Jan 02 off; PH off", nullptr, - "Mo,Tu,Th,Fr 09:30-12:00; 2020 Dec 28 off; 2020 Dec 22,2020 Dec 29 off; We 15:00-17:00; 2020 Dec 23,2020 Dec 30 off; 2020 Dec 24,2020 Dec 31 off; Sa 10:00-12:00; 2020 Dec 26,2021 Jan 02 off; PH off"); + T3("Mo-Tu,Th-Fr 09:30-12:00; 2020 Dec 28 off; 2020 Dec 22,2020 Dec 29 off; We 15:00-17:00; 2020 Dec 23,2020 Dec 30 off; 2020 Dec 24,2020 Dec 31 off; Sa 10:00-12:00; 2020 Dec 26,2021 Jan 02 off; PH off", + "Mo-Tu,Th-Fr 09:30-12:00; 2020 Dec 28 off; 2020 Dec 22,29 off; We 15:00-17:00; 2020 Dec 23,30 off; 2020 Dec 24,31 off; Sa 10:00-12:00; 2020 Dec 26,2021 Jan 02 off; PH off", + "Mo,Tu,Th,Fr 09:30-12:00; 2020 Dec 28 off; 2020 Dec 22,29 off; We 15:00-17:00; 2020 Dec 23,30 off; 2020 Dec 24,31 off; Sa 10:00-12:00; 2020 Dec 26,2021 Jan 02 off; PH off"); // real-world tests from Osmose that we were handling wrongly T("Tu-Fr 11:30-14:30 open, 14:30-18:00 open \"pickup only\", 18:00-22:00 open"); @@ -204,7 +211,14 @@ T2("2020-2021 Mo, Tu-Fr, Sa [-1] 09:00-14:00", "2020-2021 Mo,Tu-Fr,Sa[-1] 09:00-14:00"); T2("week 1-3 Mo[2], Tu-Fr, Sa [-1] 09:00-14:00", "week 01-03 Mo[2],Tu-Fr,Sa[-1] 09:00-14:00"); T2("Mo Fr 09:30-12:30 13:30-18:30", "Mo,Fr 09:30-12:30,13:30-18:30"); + T2("Mo Fr 09:30-12:30, 13:30-18:30 off", "Mo,Fr 09:30-12:30,13:30-18:30 off"); T2("Mo, We, Fr 06:30-21:30; Tu, Th 09:00-21:30; Sa 09:00-17:00; Su 09:00-14:00", "Mo,We,Fr 06:30-21:30; Tu,Th 09:00-21:30; Sa 09:00-17:00; Su 09:00-14:00"); + T2("Lunes a s??bado, 9:30 AM-5:30 PM", "Mo-Sa 09:30-17:30"); // bug 445784 + + // lists of specific days + T("2021 Dec 26-28,30-31,2022 Jan 02-03 off"); + T2("2021 Dec 26-28,Dec 30-31,2022 Jan 02-03 off", "2021 Dec 26-28,30-31,2022 Jan 02-03 off"); + T2("2021 Dec 22, 26-28, 29, 31, 2022 Jan 02-03 off", "2021 Dec 22,26-28,29,31,2022 Jan 02-03 off"); // technically wrong but often found content in OSM for which we have error recovery T2("So", "Su"); @@ -221,9 +235,12 @@ T2("Friday 08:00-12:00", "Fr 08:00-12:00"); T2("Sat", "Sa"); T2("december", "Dec"); - T2("Dec 24,25,26, Jan 1,6 off", "Dec 24,Dec 25,Dec 26,Jan 01,Jan 06 off"); - T2("Dec 24,25,26 open, Jan 1,6 off", "Dec 24,Dec 25,Dec 26 open, Jan 01,Jan 06 off"); - T2("07:30-20:00; Jan 03, 13, 23, Feb 03, 13, 23, Mar 03, 13, 23, Apr 03, 13, 23, Jun 03, 13, 23, Jul 03, 13, 23, Aug 03, 13, 23, Sep 03, 13, 23, Oct 03, 13, 23, Nov 03, 13, 23, Dec 03, 13, 23 off", "07:30-20:00; Jan 03,Jan 13,Jan 23,Feb 03,Feb 13,Feb 23,Mar 03,Mar 13,Mar 23,Apr 03,Apr 13,Apr 23,Jun 03,Jun 13,Jun 23,Jul 03,Jul 13,Jul 23,Aug 03,Aug 13,Aug 23,Sep 03,Sep 13,Sep 23,Oct 03,Oct 13,Oct 23,Nov 03,Nov 13,Nov 23,Dec 03,Dec 13,Dec 23 off"); + T2("Dec 24,25,26, Jan 1,6 off", "Dec 24,25,26,Jan 01,06 off"); + T2("Dec 24,25,26 open, Jan 1,6 off", "Dec 24,25,26 open, Jan 01,06 off"); + T2("Dec 6,4", "Dec 06,04"); + T2("Dec 3,2,1", "Dec 03,02,01"); + T2("07:30-20:00; Jan 03, 13, 23, Feb 03, 13, 23, Mar 03, 13, 23, Apr 03, 13, 23, Jun 03, 13, 23, Jul 03, 13, 23, Aug 03, 13, 23, Sep 03, 13, 23, Oct 03, 13, 23, Nov 03, 13, 23, Dec 03, 13, 23 off", + "07:30-20:00; Jan 03,13,23,Feb 03,13,23,Mar 03,13,23,Apr 03,13,23,Jun 03,13,23,Jul 03,13,23,Aug 03,13,23,Sep 03,13,23,Oct 03,13,23,Nov 03,13,23,Dec 03,13,23 off"); T2("Apr, May, Oct, Nov, Dec: Mo-Su, 10:00-19:00; Jun-Sep: Mo-Su:10:00-20:00", "Apr,May,Oct,Nov,Dec: Mo-Su, 10:00-19:00; Jun-Sep: Mo-Su 10:00-20:00"); // Tolerance for incorrect casing @@ -272,6 +289,7 @@ T2("Mo-Th 11:00-20:00 Friday & Saturday 11:00-21:00 Sunday 12:00-19:00", "Mo-Th 11:00-20:00; Fr,Sa 11:00-21:00; Su 12:00-19:00"); T2("11:30-14:00???16:30-22:00", "11:30-14:00,16:30-22:00"); T2("Luned?? al Venerd?? 08:00-13:00", "Mo-Fr 08:00-13:00"); + T2("Monday through Friday 09:00 - 17:00", "Mo-Fr 09:00-17:00"); // (mis)use of colon as a small-range selector separator T2("Fr: 17:00-19:00", "Fr 17:00-19:00"); @@ -312,6 +330,18 @@ T3("Ma,Me,Je,Ve 8h-12h30, 14h-19h; Sa 8h-12h30, 14h-18h", "Tu,We,Th,Fr 08:00-12:30,14:00-19:00; Sa 08:00-12:30,14:00-18:00", "Tu-Fr 08:00-12:30,14:00-19:00; Sa 08:00-12:30,14:00-18:00"); T2("Mo-Fr: 10:00-18:30 Uhr Sa: 10:00-13:30 Uhr", "Mo-Fr 10:00-18:30; Sa 10:00-13:30"); T2("Montag & Dienstag Ruhetag", "Mo,Tu closed"); + T2("?????????????????????? - ?????????????? 09:00 - 21:00", "Mo-Sa 09:00-21:00"); + T2("??????-???? 08:00-20:00, ?????? 08:00-19:00", "Mo-Fr 08:00-20:00, Sa 08:00-19:00"); + T2("????????????-????????", "Nov-Mar"); + T2("?? 08:30 ???? 23:00", "08:30-23:00"); + T2("?????????????? - ??????????????", "dawn-dusk"); + T2("???? ???????????????? ???? ??????????????", "dawn-dusk"); + T2("???????????? - ??????????", "sunrise-sunset"); + T2("?? ?????????????? ??o ??????????", "sunrise-sunset"); + T2("???? ?????????????? ??o ????????????", "sunrise-sunset"); + T2("?????????? ??????????????; ?????????????? ??????????????; ?????????????? ????????????????????", "We open; Fr closed; Sa unknown"); + T2("???? ????????????????", "Tu closed"); + T2("Mo Cerrado; Tu Abierto, Fr libre", "Mo closed; Tu open, Fr closed"); // recovery from wrong rule separators T2("Fr,Sa 10:00-02:00,Su 10:00-20:00", "Fr,Sa 10:00-02:00, Su 10:00-20:00"); @@ -322,6 +352,10 @@ T2("Mo-Sa 12:00-15:00; 18:00-24:00", "Mo-Sa 12:00-15:00,18:00-24:00"); T2("Mo-Sa 12:00-15:00; Mo-Sa 18:00-24:00", "Mo-Sa 12:00-15:00,18:00-24:00"); T2("Mo 12:00-15:00; Mo 18:00-24:00", "Mo 12:00-15:00,18:00-24:00"); + T("Mo-Sa 12:00-15:00 off; 18:00-24:00"); + T("Mo-Sa 12:00-15:00; Mo-Sa 18:00-24:00 closed"); + T("Mo 12:00-15:00; Mo 18:00-24:00 \"comment\""); + T("Sa 08:00-12:00; 11:30-13:00 off"); // recovery from wrong time selector separators T3("Dimanche Ferm?? Lundi 08:00 ??? 12:30 14:00 ??? 19:00 Mardi 08:00 ??? 12:30 14:00 ??? 19:00 Mercredi 08:00 ??? 12:30 14:00 ??? 19:00 Jeudi 08:00 ??? 12:30 14:00 ??? 19:00 Vendredi 08:00 ??? 12:30 14:00 ??? 19:00 Samedi 08:00 ??? 12:30 14:30 ??? 18:00", @@ -345,6 +379,20 @@ T3("Sa-Mo 10:00-23:00, Th 10:00-23:00", "Sa-Mo 10:00-23:00, Th 10:00-23:00", "Sa-Mo,Th 10:00-23:00"); // beginDay > endDay T3("Sa-Mo 10:00-23:00, Fr 10:00-23:00", "Sa-Mo 10:00-23:00, Fr 10:00-23:00", "Fr-Mo 10:00-23:00"); // beginDay > endDay T3("Su-Th 10:00-23:00, Fr-Sa 10:00-23:00", "Su-Th 10:00-23:00, Fr-Sa 10:00-23:00", "Mo-Su 10:00-23:00"); // beginDay > endDay + T3("Feb 1-Feb 29 Mo-Su 10:30-20:30; Aug 1-Aug 31 Mo-Fr 10:30-12:00; PH closed", + "Feb 01-29 Mo-Su 10:30-20:30; Aug 01-31 Mo-Fr 10:30-12:00; PH closed", + "Feb Mo-Su 10:30-20:30; Aug Mo-Fr 10:30-12:00; PH closed"); + + // complex or creative 24/7 use + T("06:00-01:00 open \"Dining in\" || 24/7 \"Drive-through\""); + T2("Friday and Saturday 24/7 Sunday-Thursday 4:00 am to 12:00 am", "Fr,Sa 00:00-24:00; Su-Th 04:00-24:00"); // bug 445962 + // 24/7 as standalone small range selector not supported yet + //T2("Apr-Oct: 24/7; Nov-Mar: Mo-Su 06:00-22:00", "Apr-Oct: 00:00-24:00; Nov-Mar: Mo-Su 06:00-22:00"); + + // 4 digit times + T2("0600-1800", "06:00-18:00"); + T2("0700-2000", "07:00-20:00"); + T2("Tu-Th 8:30-17:30, Fr 8:30-1700", "Tu-Th 08:30-17:30, Fr 08:30-17:00"); #undef T #undef T2 #undef T3 @@ -359,6 +407,8 @@ QVERIFY(oh.error() != OpeningHours::SyntaxError); QCOMPARE(oh.normalizedExpression(), expectedOutput); QCOMPARE(oh.simplifiedExpression(), expectedSimplifiedOutput); + // verify that simplifiedExpression() doesn't alter `oh` + QCOMPARE(oh.normalizedExpression(), expectedOutput); // verify the expressions we generate are parsed correctly as well OpeningHours oh2(oh.normalizedExpression()); @@ -392,14 +442,11 @@ T("12:61"); T("60p"); - T("Dec 6,4"); T("Dec 24-Jan 1,6"); - T("Dec 3,2,1"); T("Mo, 1:100"); // from https://wiki.openstreetmap.org/wiki/Key:opening_hours#Common_mistakes T("7/8-23"); - T("0600-1800"); T("07;00-2;00pm"); T("08.00-16.00, public room till 03.00 a.m"); T("09:00-21:00 TEL/072(360)3200"); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kopeninghours-21.12.0/po/zh_CN/kopeninghours.po new/kopeninghours-21.12.1/po/zh_CN/kopeninghours.po --- old/kopeninghours-21.12.0/po/zh_CN/kopeninghours.po 2021-12-03 01:18:48.000000000 +0100 +++ new/kopeninghours-21.12.1/po/zh_CN/kopeninghours.po 2022-01-04 01:26:56.000000000 +0100 @@ -3,7 +3,7 @@ "Project-Id-Version: kdeorg\n" "Report-Msgid-Bugs-To: https://bugs.kde.org\n" "POT-Creation-Date: 2020-12-23 02:25+0100\n" -"PO-Revision-Date: 2021-11-30 15:24\n" +"PO-Revision-Date: 2021-12-22 14:11\n" "Last-Translator: \n" "Language-Team: Chinese Simplified\n" "Language: zh_CN\n" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kopeninghours-21.12.0/src/lib/consecutiveaccumulator_p.h new/kopeninghours-21.12.1/src/lib/consecutiveaccumulator_p.h --- old/kopeninghours-21.12.0/src/lib/consecutiveaccumulator_p.h 2021-12-02 23:01:28.000000000 +0100 +++ new/kopeninghours-21.12.1/src/lib/consecutiveaccumulator_p.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,69 +0,0 @@ -/* - SPDX-FileCopyrightText: 2020 David Faure <[email protected]> - - SPDX-License-Identifier: LGPL-2.0-or-later -*/ - -#ifndef KOPENINGHOURS_CONSECUTIVEACCUMULATOR_P_H -#define KOPENINGHOURS_CONSECUTIVEACCUMULATOR_P_H - -#include <functional> -#include <QByteArray> - -// Input: 1,2,4,5,6,9 -// Output: 1-2,4-6,9 -class ConsecutiveAccumulator -{ -public: - // The std::function is usually QByteArray::number - // but it could be also 1->Mo, 2->Tu etc. if we need that one day. - explicit ConsecutiveAccumulator(std::function<QByteArray(int)> &&f) - : func(std::move(f)) {} - - void add(int value) - { - if (!first && value == cur+1) { - ++cur; - } else { - flush(); - cur = value; - start = value; - first = false; - } - } - QByteArray result() - { - // Finalize - flush(); - if (!expr.isEmpty()) { - expr.chop(1); - } - return expr; - } - -private: - void flush() - { - if (!first) { - if (start < cur) { - if (cur >= 0) { - expr += func(start) + '-' + func(cur) + ','; - } else { // don't generate -2--1 - for (int i = start; i <= cur; ++i) { - expr += func(i) + ','; - } - } - } else { - expr += func(cur) + ','; - } - } - } - - bool first = true; - int cur = 0; - int start = 0; - QByteArray expr; - std::function<QByteArray(int)> func; -}; - -#endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kopeninghours-21.12.0/src/lib/evaluator.cpp new/kopeninghours-21.12.1/src/lib/evaluator.cpp --- old/kopeninghours-21.12.0/src/lib/evaluator.cpp 2021-12-02 23:01:28.000000000 +0100 +++ new/kopeninghours-21.12.1/src/lib/evaluator.cpp 2022-01-03 22:15:03.000000000 +0100 @@ -135,24 +135,27 @@ switch (holiday) { case NoHoliday: { - if (nthMask > 0) { - for (int i = 1; i <= 10; ++i) { - if ((nthMask & (1 << i)) == 0) { - continue; + if (nthSequence) { + qint64 smallestOffset = INT_MAX; + for (const NthEntry &entry : nthSequence->sequence) { + Q_ASSERT(entry.begin <= entry.end); + for (int n = entry.begin; n <= entry.end; ++n) { + const auto d = nthWeekdayInMonth(dt.date().addDays(-offset), beginDay, n); + if (!d.isValid() || d.addDays(offset) < dt.date()) { + continue; + } + if (d.addDays(offset) == dt.date()) { + auto i = interval; + i.setBegin(QDateTime(d.addDays(offset), {0, 0})); + i.setEnd(QDateTime(d.addDays(offset + 1), {0, 0})); + return i; + } + // d > dt.date() + smallestOffset = qMin(smallestOffset, dt.secsTo(QDateTime(d.addDays(offset), {0, 0}))); } - const auto n = (i % 2) ? (-5 + (i /2)) : (i / 2); - const auto d = nthWeekdayInMonth(dt.date().addDays(-offset), beginDay, n); - if (!d.isValid() || d.addDays(offset) < dt.date()) { - continue; - } - if (d.addDays(offset) == dt.date()) { - auto i = interval; - i.setBegin(QDateTime(d.addDays(offset), {0, 0})); - i.setEnd(QDateTime(d.addDays(offset + 1), {0, 0})); - return i; - } - // d > dt.date() - return dt.secsTo(QDateTime(d.addDays(offset), {0, 0})); + } + if (smallestOffset < INT_MAX) { + return smallestOffset; } // skip to next month diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kopeninghours-21.12.0/src/lib/openinghours.cpp new/kopeninghours-21.12.1/src/lib/openinghours.cpp --- old/kopeninghours-21.12.0/src/lib/openinghours.cpp 2021-12-02 23:01:28.000000000 +0100 +++ new/kopeninghours-21.12.1/src/lib/openinghours.cpp 2022-01-03 22:15:03.000000000 +0100 @@ -12,7 +12,6 @@ #include "interval.h" #include "rule_p.h" #include "logging.h" -#include "consecutiveaccumulator_p.h" #include <QDateTime> #include <QJsonArray> @@ -81,6 +80,13 @@ // the current rule only has a time selector, so we append that to the previous rule else if (curRuleSingleSelector && rule->m_timeSelector && prevRule->m_timeSelector) { appendSelector(prevRule->m_timeSelector.get(), std::move(rule->m_timeSelector)); + prevRule->copyStateFrom(*rule); + it = std::prev(m_rules.erase(it)); + } + + // previous is a single weekday selector and current is a single time selector + else if (curRuleSingleSelector && prevRuleSingleSelector && rule->m_timeSelector && prevRule->m_weekdaySelector) { + prevRule->m_timeSelector = std::move(rule->m_timeSelector); it = std::prev(m_rules.erase(it)); } @@ -93,12 +99,22 @@ std::swap(*it, *std::prev(it)); it = std::prev(m_rules.erase(it)); } + + // previous has no time selector and the current one is a misplaced 24/7 rule: + // convert the 24/7 to a 00:00-24:00 time selector + else if (rule->selectorCount() == 0 && rule->m_seen_24_7 && !prevRule->m_timeSelector) { + prevRule->m_timeSelector.reset(new Timespan); + prevRule->m_timeSelector->begin = { Time::NoEvent, 0, 0 }; + prevRule->m_timeSelector->end = { Time::NoEvent, 24, 0 }; + it = std::prev(m_rules.erase(it)); + } } else if (rule->m_ruleType == Rule::NormalRule) { // Previous rule has time and other selectors // Current rule is only a time selector // "Mo-Sa 12:00-15:00; 18:00-24:00" => "Mo-Sa 12:00-15:00,18:00-24:00" if (curRuleSingleSelector && rule->m_timeSelector - && prevRule->selectorCount() > 1 && prevRule->m_timeSelector) { + && prevRule->selectorCount() > 1 && prevRule->m_timeSelector + && rule->state() == prevRule->state()) { appendSelector(prevRule->m_timeSelector.get(), std::move(rule->m_timeSelector)); it = std::prev(m_rules.erase(it)); } @@ -113,6 +129,7 @@ && rule->selectorCount() == 2 && rule->m_weekdaySelector && prevRule->m_weekdaySelector // slower than writing an operator==, but so much easier to write :) && rule->m_weekdaySelector->toExpression() == prevRule->m_weekdaySelector->toExpression() + && rule->state() == prevRule->state() ) { appendSelector(prevRule->m_timeSelector.get(), std::move(rule->m_timeSelector)); it = std::prev(m_rules.erase(it)); @@ -176,6 +193,9 @@ if (rule->m_weekdaySelector) { rule->m_weekdaySelector->simplify(); } + if (rule->m_monthdaySelector) { + rule->m_monthdaySelector->simplify(); + } } } @@ -411,8 +431,9 @@ QByteArray OpeningHours::simplifiedExpression() const { - d->simplify(); - return normalizedExpression(); + OpeningHours copy(normalizedExpression()); + copy.d->simplify(); + return copy.normalizedExpression(); } QString OpeningHours::normalizedExpressionString() const diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kopeninghours-21.12.0/src/lib/openinghourslexer.l new/kopeninghours-21.12.1/src/lib/openinghourslexer.l --- old/kopeninghours-21.12.0/src/lib/openinghourslexer.l 2021-12-02 23:01:28.000000000 +0100 +++ new/kopeninghours-21.12.1/src/lib/openinghourslexer.l 2022-01-03 22:15:03.000000000 +0100 @@ -25,15 +25,24 @@ SPACE ([ \t\r\n]|??|???|???)+ -INTEGER [0-9]+ - -YEAR [1-2][019][0-9][0-9] +CYRILLIC (??|??|??|??|??|??|??|??|??|??|??|??|??|??|??|??|??|??|??|??|??|??|??|??|??|??|??|??|??|??|??|??|??) %% {SPACE} {} -{YEAR} { yylval->num = std::strtol(yytext, nullptr, 10); return T_YEAR; } +[0-9]+ { + yylval->num = std::strtol(yytext, nullptr, 10); + if (yyleng == 4) { + if ((yylval->num > 2000 && yylval->num < 2100) || (yylval->num >= 1000 && (yylval->num % 100) >= 60)) { + return T_YEAR; + } + if (yylval->num <= 2400 && (yylval->num % 100) < 60) { + return T_4DIGIT_TIME; + } + } + return T_INTEGER; +} ;/. { return T_NORMAL_RULE_SEPARATOR; } // technically this should have space after the semicolon, but that is not always followed in OSM data ", " { return T_ADDITIONAL_RULE_SEPARATOR; } @@ -77,8 +86,6 @@ [0-5]?[0-9](\ ?a\.?m\.?|a) { yylval->num = std::strtol(yytext, nullptr, 10); return T_ALT_TIME_AM; } [0-5]?[0-9](\ ?p\.?m\.?|p) { yylval->num = std::strtol(yytext, nullptr, 10); return T_ALT_TIME_PM; } -{INTEGER} { yylval->num = std::strtol(yytext, nullptr, 10); return T_INTEGER; } - /* technically weekday names should be two letter English abbreviations, but reality is more creative */ Mondays? { yylval->num = 1; return T_WEEKDAY; } Tuesdays? { yylval->num = 2; return T_WEEKDAY; } @@ -147,6 +154,21 @@ "Novembre" { yylval->num = 11; return T_MONTH; } "D??cembre" { yylval->num = 12; return T_MONTH; } + /* Month names in Russian */ +"????????????" { yylval->num = 1; return T_MONTH; } +"??????????????" { yylval->num = 2; return T_MONTH; } +"????????" { yylval->num = 3; return T_MONTH; } +"????????????" { yylval->num = 4; return T_MONTH; } +"??????" { yylval->num = 5; return T_MONTH; } +"????????" { yylval->num = 6; return T_MONTH; } +"????????" { yylval->num = 7; return T_MONTH; } +"????????????" { yylval->num = 8; return T_MONTH; } +"????????????????" { yylval->num = 9; return T_MONTH; } +"??????????????" { yylval->num = 10; return T_MONTH; } +"????????????" { yylval->num = 11; return T_MONTH; } +"??????????????" { yylval->num = 12; return T_MONTH; } + + /* different quote types are sometimes mixed and/or used nested, so this is a compromise to catch most of them */ ["][^"]*["] { yylval->strRef.str = yytext + 1; @@ -179,10 +201,12 @@ h|??? { return T_ALT_TIME_SEP_OR_SUFFIX; } /* alternative range separators */ -~|???|???|to|??|bis|a|??s|??s|as|au|al|???|???? { return T_ALT_RANGE_SEP; } +~|???|???|to|through|??|bis|a|??s|??s|as|au|al|???|????|??o|??o { return T_ALT_RANGE_SEP; } /* localized state names */ -ferm(e|??)|geschlossen|ruhetag|encerrado|chiuso { yylval->state = State::Closed; return T_STATE; } +ferm(e|??)|geschlossen|ruhetag|encerrado|chiuso|??????????{CYRILLIC}*|??????{CYRILLIC}*|cerrado|libre { yylval->state = State::Closed; return T_STATE; } +??????????{CYRILLIC}*|abierto { yylval->state = State::Open; return T_STATE; } +??????????{CYRILLIC}* { yylval->state = State::Unknown; return T_STATE; } /* German localized day names. */ Montags? { yylval->num = 1; return T_WEEKDAY; } @@ -268,11 +292,27 @@ Sabtu { yylval->num = 6; return T_WEEKDAY; } Minggu { yylval->num = 7; return T_WEEKDAY; } + /* Russian localized day names */ +??????????????????????|??????|???? { yylval->num = 1; return T_WEEKDAY; } +??????????????|??????|???? { yylval->num = 2; return T_WEEKDAY; } +??????????|??????|???? { yylval->num = 3; return T_WEEKDAY; } +??????????????|??????|???? { yylval->num = 4; return T_WEEKDAY; } +??????????????|??????|????|???? { yylval->num = 5; return T_WEEKDAY; } +??????????????|??????|????|???? { yylval->num = 6; return T_WEEKDAY; } +??????????????????????|??????|???? { yylval->num = 7; return T_WEEKDAY; } + /* creative rule separators */ ???|and|et|e|y|und|& { return T_ADDITIONAL_RULE_SEPARATOR; } /* skip filler words */ -from|von|du|de|le|das|???|uhr|"en continu" {} + /* Note: the '??' is not an ASCII 'c'! */ +from|von|du|de|le|das|???|uhr|"en continu"|??|???? {} + + /* localized time event names */ +??????????????{CYRILLIC}* { yylval->time = { Time::Dawn, 0, 0 }; return T_EVENT; } +??????????{CYRILLIC}???{CYRILLIC}* { yylval->time = { Time::Dusk, 0, 0 }; return T_EVENT; } +????????????{CYRILLIC}* { yylval->time = { Time::Sunrise, 0, 0 }; return T_EVENT; } +??????????{CYRILLIC}* { yylval->time = { Time::Sunset , 0, 0 }; return T_EVENT; } . { //printf("unexpected character: %s at %d:%d\n", yytext, yylloc->first_line, yylloc->first_column); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kopeninghours-21.12.0/src/lib/openinghoursparser.y new/kopeninghours-21.12.1/src/lib/openinghoursparser.y --- old/kopeninghours-21.12.0/src/lib/openinghoursparser.y 2021-12-02 23:01:28.000000000 +0100 +++ new/kopeninghours-21.12.1/src/lib/openinghoursparser.y 2022-01-03 22:15:03.000000000 +0100 @@ -5,8 +5,8 @@ */ #include "openinghours_p.h" -#include "openinghoursparser_p.h" -#include "openinghoursscanner_p.h" +#include "openinghoursparser_p.h" // generated +#include "openinghoursscanner_p.h" // generated #include "logging.h" using namespace KOpeningHours; @@ -43,17 +43,16 @@ rule->m_wideRangeSelectorComment = QString::fromUtf8(sels.wideRangeSelectorComment.str, sels.wideRangeSelectorComment.len); } -static bool extendMonthdaySelector(MonthdayRange *monthdaySelector, int day) +static bool extendMonthdaySelector(MonthdayRange *monthdaySelector, int beginDay, int endDay) { const auto prevSelector = lastSelector(monthdaySelector); if (prevSelector->begin.year == prevSelector->end.year - && prevSelector->begin.month == prevSelector->end.month - && prevSelector->begin.day < day - && prevSelector->end.day < day) + && prevSelector->begin.month == prevSelector->end.month) { auto sel = new MonthdayRange; sel->begin = sel->end = prevSelector->end; - sel->begin.day = sel->end.day = day; + sel->begin.day = beginDay; + sel->end.day = endDay; appendSelector(prevSelector, sel); return true; } @@ -114,6 +113,8 @@ Time time; Selectors selectors; Timespan *timespan; + NthEntry nthEntry; + NthSequence *nthSequence; WeekdayRange *weekdayRange; Week *week; Date date; @@ -141,6 +142,7 @@ %token T_ALT_TIME_SEP_OR_SUFFIX %token <num> T_ALT_TIME_AM %token <num> T_ALT_TIME_PM +%token <num> T_4DIGIT_TIME %token T_ALT_RANGE_SEP @@ -189,8 +191,8 @@ %type <weekdayRange> WeekdayRange %type <weekdayRange> HolidaySequence %type <weekdayRange> Holiday -%type <num> NthSequence -%type <num> NthEntry +%type <nthSequence> NthSequence +%type <nthEntry> NthEntry %type <num> DayOffset %type <dateOffset> DateOffset %type <week> Week @@ -212,6 +214,7 @@ delete $$.yearSelector; } <selectors> %destructor { delete $$; } <timespan> +%destructor { delete $$; } <nthSequence> %destructor { delete $$; } <weekdayRange> %destructor { delete $$; } <week> %destructor { delete $$; } <monthdayRange> @@ -502,12 +505,12 @@ | T_WEEKDAY[D] T_LBRACKET NthSequence[N] T_RBRACKET { $$ = new WeekdayRange; $$->beginDay = $$->endDay = $D; - $$->nthMask = $N; + $$->nthSequence.reset($N); } | T_WEEKDAY[D] T_LBRACKET NthSequence[N] T_RBRACKET DayOffset[O] { $$ = new WeekdayRange; $$->beginDay = $$->endDay = $D; - $$->nthMask = $N; + $$->nthSequence.reset($N); $$->offset = $O; } ; @@ -534,24 +537,27 @@ ; NthSequence: - NthEntry[N] { $$ = $N; } -| NthSequence[N1] T_COMMA NthEntry[N2] { $$ = $N1 | $N2; } + NthEntry[N] { + $$ = new NthSequence; + $$->add($N); + } +| NthSequence[N1] T_COMMA NthEntry[N2] { + $N1->add($N2); + $$ = $N1; + } NthEntry: T_INTEGER[N] { if ($N < 1 || $N > 5) { YYABORT; } - $$ = (1 << (2 * $N)); + $$ = {$N,$N}; } | T_INTEGER[N1] T_MINUS T_INTEGER[N2] { if ($N1 < 1 || $N1 > 5 || $N2 < 1 || $N2 > 5 || $N2 <= $N1) { YYABORT; } - $$ = 0; - for (int i = $N1; i <= $N2; ++i) { - $$ |= (1 << (2 * i)); - } + $$ = {$N1,$N2}; } | T_MINUS T_INTEGER[N] { if ($N < 1 || $N > 5) { YYABORT; } - $$ = (1 << ((2 * (6 - $N)) - 1)); + $$ = {-$N,-$N}; } ; @@ -606,7 +612,7 @@ // month day sets, not covered the official grammar but in the // description in https://wiki.openstreetmap.org/wiki/Key:opening_hours#Summary_syntax $$ = $S; - if (!extendMonthdaySelector($$.monthdaySelector, $D)) { + if (!extendMonthdaySelector($$.monthdaySelector, $D, $D)) { delete $$.monthdaySelector; YYABORT; } @@ -614,7 +620,23 @@ | MonthdaySelector[S] T_ADDITIONAL_RULE_SEPARATOR T_INTEGER[D] { // same as the above, just with the wrong ", " separator $$ = $S; - if (!extendMonthdaySelector($$.monthdaySelector, $D)) { + if (!extendMonthdaySelector($$.monthdaySelector, $D, $D)) { + delete $$.monthdaySelector; + YYABORT; + } + } +| MonthdaySelector[S] T_COMMA T_INTEGER[D1] T_MINUS T_INTEGER[D2] { + // same with a range of days + $$ = $S; + if (!extendMonthdaySelector($$.monthdaySelector, $D1, $D2)) { + delete $$.monthdaySelector; + YYABORT; + } + } +| MonthdaySelector[S] T_ADDITIONAL_RULE_SEPARATOR T_INTEGER[D1] T_MINUS T_INTEGER[D2] { + // same as the above, just with the wrong ", " separator + $$ = $S; + if (!extendMonthdaySelector($$.monthdaySelector, $D1, $D2)) { delete $$.monthdaySelector; YYABORT; } @@ -857,6 +879,9 @@ Time::convertFromPm($$); if (!Time::isValid($$)) { YYABORT; } } +| T_4DIGIT_TIME[T] { + $$ = { Time::NoEvent, $T / 100, $T % 100 }; // lexer ensures this is always a valid time +} ; RangeSeparator: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kopeninghours-21.12.0/src/lib/rule.cpp new/kopeninghours-21.12.1/src/lib/rule.cpp --- old/kopeninghours-21.12.0/src/lib/rule.cpp 2021-12-02 23:01:28.000000000 +0100 +++ new/kopeninghours-21.12.1/src/lib/rule.cpp 2022-01-03 22:15:03.000000000 +0100 @@ -35,6 +35,12 @@ } } +void Rule::copyStateFrom(const Rule &otherRule) +{ + m_state = otherRule.m_state; + m_stateFlags = otherRule.m_stateFlags; +} + bool Rule::hasComment() const { return !m_comment.isEmpty(); @@ -95,7 +101,7 @@ } if (m_monthdaySelector) { maybeSpace(); - expr += m_monthdaySelector->toExpression(); + expr += m_monthdaySelector->toExpression({}); } if (m_weekSelector) { maybeSpace(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kopeninghours-21.12.0/src/lib/rule_p.h new/kopeninghours-21.12.1/src/lib/rule_p.h --- old/kopeninghours-21.12.0/src/lib/rule_p.h 2021-12-02 23:01:28.000000000 +0100 +++ new/kopeninghours-21.12.1/src/lib/rule_p.h 2022-01-03 22:15:03.000000000 +0100 @@ -54,6 +54,7 @@ Interval::State state() const; bool hasImplicitState() const; void setState(State state); + void copyStateFrom(const Rule &otherRule); bool hasComment() const; void setComment(const char *str, int len); int requiredCapabilities() const; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kopeninghours-21.12.0/src/lib/selectors.cpp new/kopeninghours-21.12.1/src/lib/selectors.cpp --- old/kopeninghours-21.12.0/src/lib/selectors.cpp 2021-12-02 23:01:28.000000000 +0100 +++ new/kopeninghours-21.12.1/src/lib/selectors.cpp 2022-01-03 22:15:03.000000000 +0100 @@ -7,7 +7,6 @@ #include "selectors_p.h" #include "logging.h" #include "openinghours_p.h" -#include "consecutiveaccumulator_p.h" #include <cstdlib> #include <cassert> @@ -153,13 +152,13 @@ int WeekdayRange::requiredCapabilities() const { - // only ranges or nthMask are allowed, not both at the same time, enforced by parser - assert(beginDay == endDay || nthMask == 0); + // only ranges or nthSequence are allowed, not both at the same time, enforced by parser + assert(beginDay == endDay || !nthSequence); int c = Capability::None; switch (holiday) { case NoHoliday: - if ((offset > 0 && nthMask == 0)) { + if ((offset > 0 && !nthSequence)) { c |= Capability::NotImplemented; } break; @@ -201,21 +200,8 @@ expr = "SH"; break; } - if (nthMask > 0) { - ConsecutiveAccumulator accu([](int i) { return QByteArray::number(i); }); - // Negative numbers - for (int i = 1; i <= 10; i += 2) { - if ((nthMask & (1 << i)) != 0) { - accu.add(-5 + (i / 2)); - } - } - // Positive numbers - for (int i = 2; i <= 10; i += 2) { - if ((nthMask & (1 << i)) != 0) { - accu.add(i / 2); - } - } - expr += '[' + accu.result() + ']'; + if (nthSequence) { + expr += '[' + nthSequence->toExpression() + ']'; } if (offset > 0) { expr += " +" + QByteArray::number(offset) + ' ' + (offset > 1 ? "days" : "day"); @@ -237,7 +223,7 @@ std::fill(std::begin(seenDays), std::end(seenDays), false); for (WeekdayRange *selector = this; selector; selector = selector->next.get()) { // Ensure it's all just week days, no other features - if (selector->nthMask || selector->lhsAndSelector || selector->holiday != NoHoliday || selector->offset) { + if (selector->nthSequence || selector->lhsAndSelector || selector->holiday != NoHoliday || selector->offset) { return; } const bool wrap = selector->beginDay > selector->endDay; @@ -352,7 +338,7 @@ return expr; } -QByteArray Date::toExpression(Date refDate) const +QByteArray Date::toExpression(const Date &refDate, const MonthdayRange &prev) const { QByteArray expr; auto maybeSpace = [&]() { @@ -361,20 +347,26 @@ } }; switch (variableDate) { - case FixedDate: - if (year && year != refDate.year) { + case FixedDate: { + const bool needYear = year && year != refDate.year && year != prev.begin.year && year != prev.end.year; + if (needYear) { expr += QByteArray::number(year); } - if (month && ((year && year != refDate.year) || month != refDate.month || hasOffset())) { - static const char* s_monthName[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; - maybeSpace(); - expr += s_monthName[month-1]; + if (month) { + const bool combineWithPrev = prev.begin.month == prev.end.month && month == prev.begin.month; + const bool implicitMonth = month == refDate.month || (refDate.month == 0 && combineWithPrev); + if (needYear || !implicitMonth || hasOffset()) { + static const char* s_monthName[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + maybeSpace(); + expr += s_monthName[month-1]; + } } if (day && *this != refDate) { maybeSpace(); expr += twoDigits(day); } break; + } case Date::Easter: if (year) { expr += QByteArray::number(year) + ' '; @@ -436,18 +428,36 @@ return Capability::None; } -QByteArray MonthdayRange::toExpression() const +QByteArray MonthdayRange::toExpression(const MonthdayRange &prev) const { - QByteArray expr = begin.toExpression({}); + QByteArray expr = begin.toExpression({}, prev); if (end != begin) { - expr += '-' + end.toExpression(begin); + expr += '-' + end.toExpression(begin, prev); } if (next) { - expr += ',' + next->toExpression(); + expr += ',' + next->toExpression(*this); } return expr; } +void MonthdayRange::simplify() +{ + // "Feb 1-29" => "Feb" (#446252) + if (begin.variableDate == Date::FixedDate && + end.variableDate == Date::FixedDate && + begin.year == end.year && + begin.month && end.month && + begin.month == end.month && + begin.day && end.day) { + // The year doesn't matter, but take one with a leap day, for Feb 1-29 + const int lastDay = QDate{2004, end.month, end.day}.daysInMonth(); + if (begin.day == 1 && end.day == lastDay) { + begin.day = 0; + end.day = 0; + } + } +} + int YearRange::requiredCapabilities() const { return Capability::None; @@ -471,3 +481,26 @@ } return expr; } + +void NthSequence::add(NthEntry range) +{ + sequence.push_back(std::move(range)); +} + +QByteArray NthSequence::toExpression() const +{ + QByteArray ret; + for (const NthEntry &entry : sequence) { + if (!ret.isEmpty()) + ret += ','; + ret += entry.toExpression(); + } + return ret; +} + +QByteArray NthEntry::toExpression() const +{ + if (begin == end) + return QByteArray::number(begin); + return QByteArray::number(begin) + '-' + QByteArray::number(end); +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kopeninghours-21.12.0/src/lib/selectors_p.h new/kopeninghours-21.12.1/src/lib/selectors_p.h --- old/kopeninghours-21.12.0/src/lib/selectors_p.h 2021-12-02 23:01:28.000000000 +0100 +++ new/kopeninghours-21.12.1/src/lib/selectors_p.h 2022-01-03 22:15:03.000000000 +0100 @@ -135,6 +135,21 @@ std::unique_ptr<Timespan> next; }; +struct NthEntry { + int begin; + int end; + QByteArray toExpression() const; +}; + +/** Nth week days, like 1-2,4,6-8 */ +class NthSequence +{ +public: + void add(NthEntry range); + QByteArray toExpression() const; + std::vector<NthEntry> sequence; +}; + /** Weekday range. */ class WeekdayRange { @@ -147,7 +162,7 @@ uint8_t beginDay = 0; // Mo=1, Tu=2, ..., Su=7 uint8_t endDay = 0; - uint16_t nthMask = 0; + std::unique_ptr<NthSequence> nthSequence; int16_t offset = 0; enum Holiday : uint8_t { NoHoliday = 0, @@ -186,11 +201,13 @@ int8_t nthWeekday; }; +class MonthdayRange; + /** Date */ class Date { public: - QByteArray toExpression(Date refDate) const; + QByteArray toExpression(const Date &refDate, const MonthdayRange &prev) const; bool operator==(Date other) const; bool operator!=(Date other) const { return !operator==(other); } bool hasOffset() const; @@ -212,7 +229,8 @@ public: int requiredCapabilities() const; SelectorResult nextInterval(const Interval &interval, const QDateTime &dt, OpeningHoursPrivate *context) const; - QByteArray toExpression() const; + QByteArray toExpression(const MonthdayRange &prev) const; + void simplify(); Date begin = { 0, 0, 0, Date::FixedDate, { 0, 0, 0 } }; Date end = { 0, 0, 0, Date::FixedDate, { 0, 0, 0 } };
