/* * 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 __clib_export void clib_timebase_init (clib_timebase_t * tb, i32 timezone_offset_in_hours, clib_timebase_daylight_time_t daylight_type, clib_time_t * clib_time) { clib_memset (tb, 0, sizeof (*tb)); if (clib_time == 0) { tb->clib_time = clib_mem_alloc_aligned (sizeof (*clib_time), CLIB_CACHE_LINE_BYTES); memset (tb->clib_time, 0, sizeof (*clib_time)); clib_time_init (tb->clib_time); } else tb->clib_time = clib_time; 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", }; __clib_export 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; } __clib_export 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); } __clib_export 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; } __clib_export 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, %02u %s %u %02u:%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; } __clib_export 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; } } __clib_export 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: */