calendarspec: allow ranges in date and time specifications
authorDouglas Christman <DouglasChristman@gmail.com>
Fri, 1 Jul 2016 00:16:05 +0000 (20:16 -0400)
committerDouglas Christman <DouglasChristman@gmail.com>
Sat, 2 Jul 2016 03:13:58 +0000 (23:13 -0400)
Resolves #3042

TODO
man/systemd.time.xml
src/basic/calendarspec.c
src/test/test-calendarspec.c

diff --git a/TODO b/TODO
index 3af3126..5208bdb 100644 (file)
--- a/TODO
+++ b/TODO
@@ -565,7 +565,6 @@ Features:
     o CLOCK_REALTIME makes jumps (TFD_TIMER_CANCEL_ON_SET)
     o DST changes
   - Support 2012-02~4 as syntax for specifying the fourth to last day of the month.
-  - calendarspec: support value ranges with ".." notation. Example: 2013-4..8-1
   - Modulate timer frequency based on battery state
 
 * add libsystemd-password or so to query passwords during boot using the password agent logic
index ffcac82..6f9e884 100644 (file)
     values separated by commas. Values may also be suffixed with
     <literal>/</literal> and a repetition value, which indicates that
     the value and all values plus multiples of the repetition value
-    are matched.</para>
+    are matched.  Each component may also contain a range of values
+    separated by <literal>..</literal>.</para>
 
     <para>The seconds component may contain decimal fractions both in
     the value and the repetition. All fractions are rounded to 6
@@ -273,6 +274,7 @@ Wed-Sat,Tue 12-10-15 1:2:3 → Tue-Sat 2012-10-15 01:02:03
        monday *-12-* 17:00 → Mon *-12-* 17:00:00
  Mon,Fri *-*-3,1,2 *:30:45 → Mon,Fri *-*-01,02,03 *:30:45
       12,14,13,12:20,10,30 → *-*-* 12,13,14:10,20,30:00
+           12..14:10,20,30 → *-*-* 12,13,14:10,20,30:00
  mon,fri *-1/2-1,3 *:30:45 → Mon,Fri *-01/2-01,03 *:30:45
             03-05 08:05:40 → *-03-05 08:05:40
                   08:05:40 → *-*-* 08:05:40
@@ -281,6 +283,7 @@ Wed-Sat,Tue 12-10-15 1:2:3 → Tue-Sat 2012-10-15 01:02:03
           Sat,Sun 08:05:40 → Sat,Sun *-*-* 08:05:40
           2003-03-05 05:40 → 2003-03-05 05:40:00
 05:40:23.4200004/3.1700005 → 05:40:23.420000/3.170001
+            2003-02..04-05 → 2003-02,03,04-05 00:00:00
       2003-03-05 05:40 UTC → 2003-03-05 05:40:00 UTC
                 2003-03-05 → 2003-03-05 00:00:00
                      03-05 → *-03-05 00:00:00
index 6e0bab9..54ab909 100644 (file)
@@ -32,6 +32,8 @@
 #include "parse-util.h"
 #include "string-util.h"
 
+/* Longest valid date/time range is 1970..2199 */
+#define MAX_RANGE_LEN   230
 #define BITS_WEEKDAYS   127
 
 static void free_chain(CalendarComponent *c) {
@@ -448,8 +450,26 @@ static int parse_component_decimal(const char **p, bool usec, unsigned long *res
         return 0;
 }
 
+static int const_chain(int value, CalendarComponent **c) {
+        CalendarComponent *cc = NULL;
+
+        assert(c);
+
+        cc = new0(CalendarComponent, 1);
+        if (!cc)
+                return -ENOMEM;
+
+        cc->value = value;
+        cc->repeat = 0;
+        cc->next = *c;
+
+        *c = cc;
+
+        return 0;
+}
+
 static int prepend_component(const char **p, bool usec, CalendarComponent **c) {
-        unsigned long value, repeat = 0;
+        unsigned long i, value, range_end, range_inc, repeat = 0;
         CalendarComponent *cc;
         int r;
         const char *e;
@@ -471,6 +491,30 @@ static int prepend_component(const char **p, bool usec, CalendarComponent **c) {
 
                 if (repeat == 0)
                         return -ERANGE;
+        } else if (e[0] == '.' && e[1] == '.') {
+                e += 2;
+                r = parse_component_decimal(&e, usec, &range_end);
+                if (r < 0)
+                        return r;
+
+                if (value >= range_end)
+                        return -EINVAL;
+
+                range_inc = usec ? USEC_PER_SEC : 1;
+
+                /* Don't allow impossibly large ranges... */
+                if (range_end - value >= MAX_RANGE_LEN * range_inc)
+                        return -EINVAL;
+
+                /* ...or ranges with only a single element */
+                if (range_end - value < range_inc)
+                        return -EINVAL;
+
+                for (i = value; i <= range_end; i += range_inc) {
+                        r = const_chain(i, c);
+                        if (r < 0)
+                                return r;
+                }
         }
 
         if (*e != 0 && *e != ' ' && *e != ',' && *e != '-' && *e != ':')
@@ -495,24 +539,6 @@ static int prepend_component(const char **p, bool usec, CalendarComponent **c) {
         return 0;
 }
 
-static int const_chain(int value, CalendarComponent **c) {
-        CalendarComponent *cc = NULL;
-
-        assert(c);
-
-        cc = new0(CalendarComponent, 1);
-        if (!cc)
-                return -ENOMEM;
-
-        cc->value = value;
-        cc->repeat = 0;
-        cc->next = *c;
-
-        *c = cc;
-
-        return 0;
-}
-
 static int parse_chain(const char **p, bool usec, CalendarComponent **c) {
         const char *t;
         CalendarComponent *cc = NULL;
index 5a8c6cb..cb2538e 100644 (file)
@@ -124,6 +124,10 @@ int main(int argc, char* argv[]) {
         test_one("2016-03-27 03:17:00.4200005", "2016-03-27 03:17:00.420001");
         test_one("2016-03-27 03:17:00/0.42", "2016-03-27 03:17:00/0.420000");
         test_one("2016-03-27 03:17:00/0.42", "2016-03-27 03:17:00/0.420000");
+        test_one("9..11,13:00,30", "*-*-* 09,10,11,13:00,30:00");
+        test_one("1..3-1..3 1..3:1..3", "*-01,02,03-01,02,03 01,02,03:01,02,03:00");
+        test_one("00:00:1.125..2.125", "*-*-* 00:00:01.125000,02.125000");
+        test_one("00:00:1.0..3.8", "*-*-* 00:00:01,02,03");
 
         test_next("2016-03-27 03:17:00", "", 12345, 1459048620000000);
         test_next("2016-03-27 03:17:00", "CET", 12345, 1459041420000000);
@@ -146,6 +150,7 @@ int main(int argc, char* argv[]) {
         assert_se(calendar_spec_from_string("2000-03-05.23 00:00:00", &c) < 0);
         assert_se(calendar_spec_from_string("2000-03-05 00:00.1:00", &c) < 0);
         assert_se(calendar_spec_from_string("00:00:00/0.00000001", &c) < 0);
+        assert_se(calendar_spec_from_string("00:00:00.0..00.9", &c) < 0);
 
         return 0;
 }