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 } };

Reply via email to