Time range support for vppinfra 22/12922/6
authorDave Barach <dave@barachs.net>
Wed, 6 Jun 2018 14:52:08 +0000 (10:52 -0400)
committerFlorin Coras <florin.coras@gmail.com>
Fri, 8 Jun 2018 23:19:09 +0000 (23:19 +0000)
Change-Id: I2356b1e05fd868b46b4d26ade760900a5739ca4d
Signed-off-by: Dave Barach <dave@barachs.net>
src/vppinfra.am
src/vppinfra/test_time_range.c [new file with mode: 0644]
src/vppinfra/time_range.c [new file with mode: 0644]
src/vppinfra/time_range.h [new file with mode: 0644]

index d138a26..66c2ad2 100644 (file)
@@ -52,6 +52,7 @@ TESTS  +=  test_bihash_template \
           test_slist \
           test_socket \
           test_time \
+          test_time_range \
           test_timing_wheel \
           test_tw_timer \
           test_valloc \
@@ -87,6 +88,7 @@ test_serialize_SOURCES = vppinfra/test_serialize.c
 test_slist_SOURCES = vppinfra/test_slist.c
 test_socket_SOURCES = vppinfra/test_socket.c
 test_time_SOURCES = vppinfra/test_time.c
+test_time_range_SOURCES = vppinfra/test_time_range.c
 test_timing_wheel_SOURCES = vppinfra/test_timing_wheel.c
 test_tw_timer_SOURCES = vppinfra/test_tw_timer.c
 test_valloc_SOURCES = vppinfra/test_valloc.c
@@ -151,6 +153,7 @@ test_serialize_LDADD =      libvppinfra.la
 test_slist_LDADD =     libvppinfra.la
 test_socket_LDADD =    libvppinfra.la
 test_time_LDADD =      libvppinfra.la -lm
+test_time_range_LDADD =        libvppinfra.la -lm
 test_timing_wheel_LDADD =      libvppinfra.la -lm
 test_tw_timer_LDADD =  libvppinfra.la
 test_valloc_LDADD =    libvppinfra.la
@@ -182,6 +185,7 @@ test_serialize_LDFLAGS = -static
 test_slist_LDFLAGS = -static
 test_socket_LDFLAGS = -static
 test_time_LDFLAGS = -static
+test_time_range_LDFLAGS = -static
 test_timing_wheel_LDFLAGS = -static
 test_tw_timer_LDFLAGS = -static
 test_valloc_LDFLAGS = -static
@@ -258,6 +262,7 @@ nobase_include_HEADERS = \
   vppinfra/sparse_vec.h \
   vppinfra/string.h \
   vppinfra/time.h \
+  vppinfra/time_range.h \
   vppinfra/timing_wheel.h \
   vppinfra/timer.h \
   vppinfra/tw_timer_2t_1w_2048sl.h \
@@ -319,6 +324,7 @@ CLIB_CORE = \
   vppinfra/std-formats.c \
   vppinfra/string.c \
   vppinfra/time.c \
+  vppinfra/time_range.c \
   vppinfra/timing_wheel.c \
   vppinfra/tw_timer_template.h \
   vppinfra/tw_timer_2t_1w_2048sl.h \
diff --git a/src/vppinfra/test_time_range.c b/src/vppinfra/test_time_range.c
new file mode 100644 (file)
index 0000000..ccb63b2
--- /dev/null
@@ -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 (file)
index 0000000..e502ca3
--- /dev/null
@@ -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 (file)
index 0000000..6dcd82d
--- /dev/null
@@ -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:
+ */