aboutsummaryrefslogtreecommitdiffstats
path: root/src/vppinfra
diff options
context:
space:
mode:
Diffstat (limited to 'src/vppinfra')
-rw-r--r--src/vppinfra/test_time_range.c178
-rw-r--r--src/vppinfra/time_range.c472
-rw-r--r--src/vppinfra/time_range.h131
3 files changed, 781 insertions, 0 deletions
diff --git a/src/vppinfra/test_time_range.c b/src/vppinfra/test_time_range.c
new file mode 100644
index 00000000000..ccb63b20873
--- /dev/null
+++ b/src/vppinfra/test_time_range.c
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vppinfra/time_range.h>
+
+static int
+test_time_range_main (unformat_input_t * input)
+{
+ clib_timebase_t _tb, *tb = &_tb;
+ clib_timebase_component_t _c, *cp = &_c;
+ clib_timebase_range_t *rp = 0;
+ clib_timebase_range_t *this_rp;
+ unformat_input_t _input2, *input2 = &_input2;
+ char *test_range_string;
+ f64 sunday_midnight;
+ f64 now, then;
+ f64 start_time, end_time;
+ f64 timezone_offset;
+
+ /* Init time base */
+ clib_timebase_init (tb, -5 /* EST */ , CLIB_TIMEBASE_DAYLIGHT_USA);
+
+ /* Set up summer time cache */
+ now = clib_timebase_now (tb);
+
+ /* Test it */
+ now = clib_timebase_now (tb);
+
+ /* show current time */
+ fformat (stdout, "Current time in UTC%f, US daylight time rules:\n",
+ tb->timezone_offset / 3600.0);
+ fformat (stdout, "%U", format_clib_timebase_time, now);
+
+ /* Test conversion to component structure */
+ clib_timebase_time_to_components (now, cp);
+ now = clib_timebase_components_to_time (cp);
+ fformat (stdout, " -> %U\n", format_clib_timebase_time, now);
+
+ /*
+ * test a few other dates, to verify summer time operation
+ * 2011: started sunday 3/13, ended sunday 11/6
+ */
+
+ fformat (stdout, "Test daylight time rules:\n");
+
+ memset (cp, 0, sizeof (*cp));
+
+ /* Just before DST starts */
+ cp->year = 2011;
+ cp->month = 2;
+ cp->day = 13;
+ cp->hour = 1;
+ cp->minute = 59;
+ cp->second = 59;
+ then = clib_timebase_components_to_time (cp);
+
+ timezone_offset = clib_timebase_summer_offset_fastpath (tb, then);
+
+ fformat (stdout, "%U should not be in DST, and it %s\n",
+ format_clib_timebase_time, then,
+ (timezone_offset != 0.0) ? "is" : "is not");
+
+ /* add two seconds */
+
+ then += 2.0;
+
+ timezone_offset = clib_timebase_summer_offset_fastpath (tb, then);
+
+ fformat (stdout, "%U should be in DST, and it %s\n",
+ format_clib_timebase_time, then,
+ (timezone_offset != 0.0) ? "is" : "is not");
+
+ /* Just before DST ends */
+ cp->year = 2011;
+ cp->month = 10;
+ cp->day = 6;
+ cp->hour = 1;
+ cp->minute = 59;
+ cp->second = 59;
+ then = clib_timebase_components_to_time (cp);
+
+ timezone_offset = clib_timebase_summer_offset_fastpath (tb, then);
+
+ fformat (stdout, "%U should be in DST, and it %s\n",
+ format_clib_timebase_time, then,
+ (timezone_offset != 0.0) ? "is" : "is not");
+
+ /* add two seconds. */
+
+ then += 2.0;
+
+ timezone_offset = clib_timebase_summer_offset_fastpath (tb, then);
+
+ fformat (stdout, "%U should not be in DST, and it %s\n",
+ format_clib_timebase_time, then,
+ (timezone_offset != 0.0) ? "is" : "is not");
+
+ /* Back to the future... */
+ clib_timebase_time_to_components (now, cp);
+
+ fformat (stdout, "Test time range calculations:\n");
+
+ /* Find previous Sunday midnight */
+ sunday_midnight = now = clib_timebase_find_sunday_midnight (now);
+
+ clib_timebase_time_to_components (now, cp);
+
+ fformat (stdout, "Sunday midnight: %U\n", format_clib_timebase_time, now);
+
+ test_range_string = "Mon 11 - 17 Tue 7 - 11 Wed - Fri 8 - 18";
+
+ unformat_init_string (input2, test_range_string,
+ strlen (test_range_string));
+
+ if (unformat (input2, "%U", unformat_clib_timebase_range_vector, &rp))
+ {
+ vec_foreach (this_rp, rp)
+ {
+ start_time = sunday_midnight + this_rp->start;
+ end_time = sunday_midnight + this_rp->end;
+ fformat (stdout, "range: %U - %U\n",
+ format_clib_timebase_time, start_time,
+ format_clib_timebase_time, end_time);
+ }
+ vec_free (rp);
+ }
+ else
+ {
+ fformat (stdout, "Time convert fail!\n");
+ }
+
+ unformat_free (input2);
+
+ return 0;
+}
+
+/*
+ * GDB callable function: vl - Return vector length of vector
+ */
+u32
+vl (void *p)
+{
+ return vec_len (p);
+}
+
+#ifdef CLIB_UNIX
+int
+main (int argc, char *argv[])
+{
+ unformat_input_t i;
+ int ret;
+
+ unformat_init_command_line (&i, argv);
+ ret = test_time_range_main (&i);
+ unformat_free (&i);
+
+ return ret;
+}
+#endif /* CLIB_UNIX */
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vppinfra/time_range.c b/src/vppinfra/time_range.c
new file mode 100644
index 00000000000..e502ca358db
--- /dev/null
+++ b/src/vppinfra/time_range.c
@@ -0,0 +1,472 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vppinfra/time_range.h>
+
+void
+clib_timebase_init (clib_timebase_t * tb, i32 timezone_offset_in_hours,
+ clib_timebase_daylight_time_t daylight_type)
+{
+ memset (tb, 0, sizeof (*tb));
+
+ clib_time_init (&tb->clib_time);
+ tb->time_zero = unix_time_now ();
+
+ tb->timezone_offset = ((f64) (timezone_offset_in_hours)) * 3600.0;
+ tb->daylight_time_type = daylight_type;
+ switch (tb->daylight_time_type)
+ {
+ case CLIB_TIMEBASE_DAYLIGHT_NONE:
+ tb->summer_offset = 0.0;
+ break;
+ case CLIB_TIMEBASE_DAYLIGHT_USA:
+ tb->summer_offset = 3600.0;
+ break;
+ default:
+ clib_warning ("unknown daylight type %d", tb->daylight_time_type);
+ tb->daylight_time_type = CLIB_TIMEBASE_DAYLIGHT_NONE;
+ tb->summer_offset = 0.0;
+ }
+}
+
+const static u32 days_per_month[] = {
+ 31, /* Jan */
+ 28, /* Feb */
+ 31, /* Mar */
+ 30, /* Apr */
+ 31, /* May */
+ 30, /* Jun */
+ 31, /* Jul */
+ 31, /* Aug */
+ 30, /* Sep */
+ 31, /* Oct */
+ 30, /* Nov */
+ 31, /* Dec */
+};
+
+const static char *month_short_names[] = {
+ "Jan",
+ "Feb",
+ "Mar",
+ "Apr",
+ "May",
+ "Jun",
+ "Jul",
+ "Aug",
+ "Sep",
+ "Oct",
+ "Nov",
+ "Dec",
+};
+
+const static char *day_names_epoch_order[] = {
+ "Thu",
+ "Fri",
+ "Sat",
+ "Sun",
+ "Mon",
+ "Tue",
+ "Wed",
+};
+
+const static char *day_names_calendar_order[] = {
+ "Sun",
+ "Mon",
+ "Tue",
+ "Wed",
+ "Thu",
+ "Fri",
+ "Sat",
+};
+
+
+void
+clib_timebase_time_to_components (f64 now, clib_timebase_component_t * cp)
+{
+ u32 year, month, hours, minutes, seconds, nanoseconds;
+ u32 days_in_year, days_in_month, day_of_month;
+ u32 days_since_epoch;
+ u32 day_name_index;
+
+ /* Unix epoch is 1/1/1970 00:00:00.00, a Thursday */
+
+ year = 1970;
+ days_since_epoch = 0;
+
+ do
+ {
+ days_in_year = clib_timebase_is_leap_year (year) ? 366 : 365;
+ days_since_epoch += days_in_year;
+ now = now - ((f64) days_in_year) * 86400.0;
+ year++;
+ }
+ while (now > 0.0);
+
+ days_since_epoch -= days_in_year;
+ now += ((f64) days_in_year) * 86400;
+ year--;
+
+ month = 0;
+
+ do
+ {
+ days_in_month = days_per_month[month];
+ if (month == 1 && clib_timebase_is_leap_year (year))
+ days_in_month++;
+
+ days_since_epoch += days_in_month;
+ now = now - ((f64) days_in_month) * 86400.0;
+ month++;
+ }
+ while (now > 0.0);
+
+ days_since_epoch -= days_in_month;
+ now += ((f64) days_in_month) * 86400;
+ month--;
+
+ day_of_month = 1;
+ do
+ {
+ now = now - 86400;
+ day_of_month++;
+ days_since_epoch++;
+ }
+ while (now > 0.0);
+
+ day_of_month--;
+ days_since_epoch--;
+ now += 86400.0;
+
+ day_name_index = days_since_epoch % 7;
+
+ hours = (u32) (now / (3600.0));
+ now -= (f64) (hours * 3600);
+
+ minutes = (u32) (now / 60.0);
+ now -= (f64) (minutes * 60);
+
+ seconds = (u32) (now);
+ now -= (f64) (seconds);
+
+ nanoseconds = (f64) (now * 1e9);
+
+ cp->year = year;
+ cp->month = month;
+ cp->day = day_of_month;
+ cp->day_name_index = day_name_index;
+ cp->hour = hours;
+ cp->minute = minutes;
+ cp->second = seconds;
+ cp->nanosecond = nanoseconds;
+ cp->fractional_seconds = now;
+}
+
+f64
+clib_timebase_components_to_time (clib_timebase_component_t * cp)
+{
+ f64 now = 0;
+ u32 year, days_in_year, month, days_in_month;
+
+ year = 1970;
+
+ while (year < cp->year)
+ {
+ days_in_year = clib_timebase_is_leap_year (year) ? 366 : 365;
+ now += ((f64) days_in_year) * 86400.0;
+ year++;
+ }
+
+ month = 0;
+
+ while (month < cp->month)
+ {
+ days_in_month = days_per_month[month];
+ if (month == 1 && clib_timebase_is_leap_year (year))
+ days_in_month++;
+
+ now += ((f64) days_in_month) * 86400.0;
+ month++;
+ }
+
+ now += ((f64) cp->day - 1) * 86400.0;
+ now += ((f64) cp->hour) * 3600.0;
+ now += ((f64) cp->minute) * 60.0;
+ now += ((f64) cp->second);
+ now += ((f64) cp->nanosecond) * 1e-9;
+
+ return (now);
+}
+
+f64
+clib_timebase_find_sunday_midnight (f64 start_time)
+{
+ clib_timebase_component_t _c, *cp = &_c;
+
+ clib_timebase_time_to_components (start_time, cp);
+
+ /* back up to midnight */
+ cp->hour = cp->minute = cp->second = 0;
+
+ start_time = clib_timebase_components_to_time (cp);
+
+ while (cp->day_name_index != 3 /* sunday */ )
+ {
+ /* Back up one day */
+ start_time -= 86400.0;
+ clib_timebase_time_to_components (start_time, cp);
+ }
+ /* Clean up residual fraction */
+ start_time -= cp->fractional_seconds;
+ start_time += 1e-6; /* 1us inside Sunday */
+
+ return (start_time);
+}
+
+f64
+clib_timebase_offset_from_sunday (u8 * day)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_LEN (day_names_calendar_order); i++)
+ {
+ if (!strncmp ((char *) day, day_names_calendar_order[i], 3))
+ return ((f64) i) * 86400.0;
+ }
+ return 0.0;
+}
+
+
+u8 *
+format_clib_timebase_time (u8 * s, va_list * args)
+{
+ f64 now = va_arg (*args, f64);
+ clib_timebase_component_t _c, *cp = &_c;
+
+ clib_timebase_time_to_components (now, cp);
+
+ s = format (s, "%s, %u %s %u %u:%02u:%02u",
+ day_names_epoch_order[cp->day_name_index],
+ cp->day,
+ month_short_names[cp->month],
+ cp->year, cp->hour, cp->minute, cp->second);
+ return (s);
+}
+
+uword
+unformat_clib_timebase_range_hms (unformat_input_t * input, va_list * args)
+{
+ clib_timebase_range_t *rp = va_arg (*args, clib_timebase_range_t *);
+ clib_timebase_component_t _c, *cp = &_c;
+ u32 start_hour, start_minute, start_second;
+ u32 end_hour, end_minute, end_second;
+
+ start_hour = start_minute = start_second
+ = end_hour = end_minute = end_second = 0;
+
+ if (unformat (input, "%u:%u:%u - %u:%u:%u",
+ &start_hour, &start_minute, &start_second,
+ &end_hour, &end_minute, &end_second))
+ ;
+ else if (unformat (input, "%u:%u - %u:%u",
+ &start_hour, &start_minute, &end_hour, &end_minute))
+ ;
+ else if (unformat (input, "%u - %u", &start_hour, &end_hour))
+ ;
+ else
+ return 0;
+
+ clib_timebase_time_to_components (1e-6, cp);
+
+ cp->hour = start_hour;
+ cp->minute = start_minute;
+ cp->second = start_second;
+
+ rp->start = clib_timebase_components_to_time (cp);
+
+ cp->hour = end_hour;
+ cp->minute = end_minute;
+ cp->second = end_second;
+
+ rp->end = clib_timebase_components_to_time (cp);
+
+ return 1;
+}
+
+uword
+unformat_clib_timebase_range_vector (unformat_input_t * input, va_list * args)
+{
+ clib_timebase_range_t **rpp = va_arg (*args, clib_timebase_range_t **);
+ clib_timebase_range_t _tmp, *tmp = &_tmp;
+ clib_timebase_range_t *rp, *new_rp;
+ int day_range_match = 0;
+ int time_range_match = 0;
+ f64 range_start_time_offset;
+ f64 range_end_time_offset;
+ f64 now;
+ u8 *start_day = 0, *end_day = 0;
+
+ rp = *rpp;
+
+ while (1)
+ {
+ if (!day_range_match
+ && unformat (input, "%s - %s", &start_day, &end_day))
+ {
+ range_start_time_offset
+ = clib_timebase_offset_from_sunday (start_day);
+ range_end_time_offset = clib_timebase_offset_from_sunday (end_day);
+ vec_free (start_day);
+ vec_free (end_day);
+ day_range_match = 1;
+ time_range_match = 0;
+ }
+ else if (!day_range_match && unformat (input, "%s", &start_day))
+ {
+ range_start_time_offset
+ = clib_timebase_offset_from_sunday (start_day);
+ range_end_time_offset = range_start_time_offset + 86399.0;
+ day_range_match = 1;
+ vec_free (start_day);
+ day_range_match = 1;
+ time_range_match = 0;
+ }
+ else if (day_range_match &&
+ unformat (input, "%U", unformat_clib_timebase_range_hms, tmp))
+ {
+ /* Across the week... */
+ for (now = range_start_time_offset; now <= range_end_time_offset;
+ now += 86400.0)
+ {
+ vec_add2 (rp, new_rp, 1);
+ new_rp->start = now + tmp->start;
+ new_rp->end = now + tmp->end;
+ }
+ day_range_match = 0;
+ time_range_match = 1;
+ }
+ else if (time_range_match)
+ break;
+ else
+ {
+ vec_free (rp);
+ *rpp = 0;
+ return 0;
+ }
+ }
+
+ if (time_range_match)
+ {
+ *rpp = rp;
+ return 1;
+ }
+ else
+ {
+ vec_free (rp);
+ *rpp = 0;
+ return 0;
+ }
+}
+
+f64
+clib_timebase_summer_offset (clib_timebase_t * tb, f64 now)
+{
+ clib_timebase_component_t _c, *cp = &_c;
+ f64 second_sunday_march_2am;
+ f64 first_sunday_november_2am;
+
+ if (PREDICT_TRUE
+ (now >= tb->cached_year_start && now <= tb->cached_year_end))
+ {
+ if (now >= tb->cached_summer_start && now <= tb->cached_summer_end)
+ return tb->summer_offset;
+ else
+ return (0.0);
+ }
+
+ clib_timebase_time_to_components (now, cp);
+
+ cp->month = 0;
+ cp->day = 1;
+ cp->hour = 0;
+ cp->minute = 0;
+ cp->second = 1;
+
+ tb->cached_year_start = clib_timebase_components_to_time (cp);
+
+ cp->year += 1;
+
+ tb->cached_year_end = clib_timebase_components_to_time (cp);
+
+ cp->year -= 1;
+
+ /* Search for the second sunday in march, 2am */
+ cp->month = 2;
+ cp->day = 1;
+ cp->hour = 2;
+ cp->second = 0;
+ cp->nanosecond = 1;
+
+ /* March 1st will never be the second sunday... */
+ second_sunday_march_2am = clib_timebase_components_to_time (cp);
+ cp->day_name_index = 0;
+
+ /* Find the first sunday */
+ do
+ {
+ clib_timebase_time_to_components (second_sunday_march_2am, cp);
+ second_sunday_march_2am += 86400.0;
+ }
+ while (cp->day_name_index != 3 /* sunday */ );
+
+ /* Find the second sunday */
+ do
+ {
+ clib_timebase_time_to_components (second_sunday_march_2am, cp);
+ second_sunday_march_2am += 86400.0;
+ }
+ while (cp->day_name_index != 3 /* sunday */ );
+
+ second_sunday_march_2am -= 86400.0;
+
+ tb->cached_summer_start = second_sunday_march_2am;
+
+ /* Find the first sunday in November, which can easily be 11/1 */
+ cp->month = 10;
+ cp->day = 1;
+
+ first_sunday_november_2am = clib_timebase_components_to_time (cp);
+ clib_timebase_time_to_components (first_sunday_november_2am, cp);
+
+ while (cp->day_name_index != 3 /* sunday */ )
+ {
+ first_sunday_november_2am += 86400.0;
+ clib_timebase_time_to_components (first_sunday_november_2am, cp);
+ }
+
+ tb->cached_summer_end = first_sunday_november_2am;
+
+ if (now >= tb->cached_summer_start && now <= tb->cached_summer_end)
+ return tb->summer_offset;
+ else
+ return (0.0);
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vppinfra/time_range.h b/src/vppinfra/time_range.h
new file mode 100644
index 00000000000..6dcd82d7deb
--- /dev/null
+++ b/src/vppinfra/time_range.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef included_time_range_h
+#define included_time_range_h
+
+#include <vppinfra/format.h>
+#include <vppinfra/time.h>
+
+typedef enum
+{
+ CLIB_TIMEBASE_DAYLIGHT_NONE = 0,
+ CLIB_TIMEBASE_DAYLIGHT_USA,
+} clib_timebase_daylight_time_t;
+
+typedef struct
+{
+ /* provides f64 seconds since clib_time_init was called */
+ clib_time_t clib_time;
+ /*
+ * time in f64 seconds since Thursday 1 Jan 1970 00:00:00 UTC
+ * when clib_time_init was called
+ */
+ f64 time_zero;
+ f64 timezone_offset;
+ f64 summer_offset;
+ clib_timebase_daylight_time_t daylight_time_type;
+ f64 cached_year_start;
+ f64 cached_year_end;
+ f64 cached_summer_start;
+ f64 cached_summer_end;
+} clib_timebase_t;
+
+typedef struct
+{
+ u32 year, month, day, hour, minute, second, nanosecond;
+ /* 0 => Thursday */
+ u32 day_name_index;
+ f64 fractional_seconds;
+} clib_timebase_component_t;
+
+typedef struct
+{
+ f64 start, end;
+} clib_timebase_range_t;
+
+void clib_timebase_init (clib_timebase_t * tb, i32 timezone_offset_in_hours,
+ clib_timebase_daylight_time_t daylight_type);
+
+void clib_timebase_time_to_components (f64 now,
+ clib_timebase_component_t * cp);
+
+f64 clib_timebase_components_to_time (clib_timebase_component_t * cp);
+
+f64 clib_timebase_find_sunday_midnight (f64 start_time);
+f64 clib_timebase_offset_from_sunday (u8 * day);
+f64 clib_timebase_summer_offset (clib_timebase_t * tb, f64 now);
+
+unformat_function_t unformat_clib_timebase_range_hms;
+unformat_function_t unformat_clib_timebase_range_vector;
+
+format_function_t format_clib_timebase_time;
+
+static inline f64 clib_timebase_summer_offset_fastpath
+ (clib_timebase_t * tb, f64 now)
+{
+ if (PREDICT_TRUE
+ (now >= tb->cached_year_start && now <= tb->cached_year_end))
+ {
+ if (now >= tb->cached_summer_start && now <= tb->cached_summer_end)
+ return tb->summer_offset;
+ else
+ return 0.0;
+ }
+ else
+ return clib_timebase_summer_offset (tb, now);
+}
+
+static inline f64
+clib_timebase_now (clib_timebase_t * tb)
+{
+ f64 now;
+
+ now = tb->time_zero + clib_time_now (&tb->clib_time);
+ now += tb->timezone_offset;
+ now += clib_timebase_summer_offset_fastpath (tb, now);
+
+ return now;
+}
+
+static inline int
+clib_timebase_is_leap_year (u32 year)
+{
+ int rv;
+
+ if (PREDICT_TRUE ((year % 4) != 0))
+ return 0;
+
+ rv = 0;
+
+ if ((year % 4) == 0)
+ rv = 1;
+ if ((year % 100) == 0)
+ rv = 0;
+ if ((year % 400) == 0)
+ rv = 1;
+ return rv;
+}
+
+#endif /* included_time_range_h */
+
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */