From a98c40305deedc58860722fb9710075028c8b9f8 Mon Sep 17 00:00:00 2001 From: Dave Barach Date: Wed, 6 Jun 2018 10:52:08 -0400 Subject: Time range support for vppinfra Change-Id: I2356b1e05fd868b46b4d26ade760900a5739ca4d Signed-off-by: Dave Barach --- src/vppinfra/time_range.c | 472 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 472 insertions(+) create mode 100644 src/vppinfra/time_range.c (limited to 'src/vppinfra/time_range.c') 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 + +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: + */ -- cgit 1.2.3-korg