c11 safe string handling support
[vpp.git] / src / vppinfra / time_range.c
1 /*
2  * Copyright (c) 2018 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <vppinfra/time_range.h>
17
18 void
19 clib_timebase_init (clib_timebase_t * tb, i32 timezone_offset_in_hours,
20                     clib_timebase_daylight_time_t daylight_type)
21 {
22   clib_memset (tb, 0, sizeof (*tb));
23
24   clib_time_init (&tb->clib_time);
25   tb->time_zero = unix_time_now ();
26
27   tb->timezone_offset = ((f64) (timezone_offset_in_hours)) * 3600.0;
28   tb->daylight_time_type = daylight_type;
29   switch (tb->daylight_time_type)
30     {
31     case CLIB_TIMEBASE_DAYLIGHT_NONE:
32       tb->summer_offset = 0.0;
33       break;
34     case CLIB_TIMEBASE_DAYLIGHT_USA:
35       tb->summer_offset = 3600.0;
36       break;
37     default:
38       clib_warning ("unknown daylight type %d", tb->daylight_time_type);
39       tb->daylight_time_type = CLIB_TIMEBASE_DAYLIGHT_NONE;
40       tb->summer_offset = 0.0;
41     }
42 }
43
44 const static u32 days_per_month[] = {
45   31,                           /* Jan */
46   28,                           /* Feb */
47   31,                           /* Mar */
48   30,                           /* Apr */
49   31,                           /* May */
50   30,                           /* Jun */
51   31,                           /* Jul */
52   31,                           /* Aug */
53   30,                           /* Sep */
54   31,                           /* Oct */
55   30,                           /* Nov */
56   31,                           /* Dec */
57 };
58
59 const static char *month_short_names[] = {
60   "Jan",
61   "Feb",
62   "Mar",
63   "Apr",
64   "May",
65   "Jun",
66   "Jul",
67   "Aug",
68   "Sep",
69   "Oct",
70   "Nov",
71   "Dec",
72 };
73
74 const static char *day_names_epoch_order[] = {
75   "Thu",
76   "Fri",
77   "Sat",
78   "Sun",
79   "Mon",
80   "Tue",
81   "Wed",
82 };
83
84 const static char *day_names_calendar_order[] = {
85   "Sun",
86   "Mon",
87   "Tue",
88   "Wed",
89   "Thu",
90   "Fri",
91   "Sat",
92 };
93
94
95 void
96 clib_timebase_time_to_components (f64 now, clib_timebase_component_t * cp)
97 {
98   u32 year, month, hours, minutes, seconds, nanoseconds;
99   u32 days_in_year, days_in_month, day_of_month;
100   u32 days_since_epoch;
101   u32 day_name_index;
102
103   /* Unix epoch is 1/1/1970 00:00:00.00, a Thursday */
104
105   year = 1970;
106   days_since_epoch = 0;
107
108   do
109     {
110       days_in_year = clib_timebase_is_leap_year (year) ? 366 : 365;
111       days_since_epoch += days_in_year;
112       now = now - ((f64) days_in_year) * 86400.0;
113       year++;
114     }
115   while (now > 0.0);
116
117   days_since_epoch -= days_in_year;
118   now += ((f64) days_in_year) * 86400;
119   year--;
120
121   month = 0;
122
123   do
124     {
125       days_in_month = days_per_month[month];
126       if (month == 1 && clib_timebase_is_leap_year (year))
127         days_in_month++;
128
129       days_since_epoch += days_in_month;
130       now = now - ((f64) days_in_month) * 86400.0;
131       month++;
132     }
133   while (now > 0.0);
134
135   days_since_epoch -= days_in_month;
136   now += ((f64) days_in_month) * 86400;
137   month--;
138
139   day_of_month = 1;
140   do
141     {
142       now = now - 86400;
143       day_of_month++;
144       days_since_epoch++;
145     }
146   while (now > 0.0);
147
148   day_of_month--;
149   days_since_epoch--;
150   now += 86400.0;
151
152   day_name_index = days_since_epoch % 7;
153
154   hours = (u32) (now / (3600.0));
155   now -= (f64) (hours * 3600);
156
157   minutes = (u32) (now / 60.0);
158   now -= (f64) (minutes * 60);
159
160   seconds = (u32) (now);
161   now -= (f64) (seconds);
162
163   nanoseconds = (f64) (now * 1e9);
164
165   cp->year = year;
166   cp->month = month;
167   cp->day = day_of_month;
168   cp->day_name_index = day_name_index;
169   cp->hour = hours;
170   cp->minute = minutes;
171   cp->second = seconds;
172   cp->nanosecond = nanoseconds;
173   cp->fractional_seconds = now;
174 }
175
176 f64
177 clib_timebase_components_to_time (clib_timebase_component_t * cp)
178 {
179   f64 now = 0;
180   u32 year, days_in_year, month, days_in_month;
181
182   year = 1970;
183
184   while (year < cp->year)
185     {
186       days_in_year = clib_timebase_is_leap_year (year) ? 366 : 365;
187       now += ((f64) days_in_year) * 86400.0;
188       year++;
189     }
190
191   month = 0;
192
193   while (month < cp->month)
194     {
195       days_in_month = days_per_month[month];
196       if (month == 1 && clib_timebase_is_leap_year (year))
197         days_in_month++;
198
199       now += ((f64) days_in_month) * 86400.0;
200       month++;
201     }
202
203   now += ((f64) cp->day - 1) * 86400.0;
204   now += ((f64) cp->hour) * 3600.0;
205   now += ((f64) cp->minute) * 60.0;
206   now += ((f64) cp->second);
207   now += ((f64) cp->nanosecond) * 1e-9;
208
209   return (now);
210 }
211
212 f64
213 clib_timebase_find_sunday_midnight (f64 start_time)
214 {
215   clib_timebase_component_t _c, *cp = &_c;
216
217   clib_timebase_time_to_components (start_time, cp);
218
219   /* back up to midnight */
220   cp->hour = cp->minute = cp->second = 0;
221
222   start_time = clib_timebase_components_to_time (cp);
223
224   while (cp->day_name_index != 3 /* sunday */ )
225     {
226       /* Back up one day */
227       start_time -= 86400.0;
228       clib_timebase_time_to_components (start_time, cp);
229     }
230   /* Clean up residual fraction */
231   start_time -= cp->fractional_seconds;
232   start_time += 1e-6;           /* 1us inside Sunday  */
233
234   return (start_time);
235 }
236
237 f64
238 clib_timebase_offset_from_sunday (u8 * day)
239 {
240   int i;
241
242   for (i = 0; i < ARRAY_LEN (day_names_calendar_order); i++)
243     {
244       if (!strncmp ((char *) day, day_names_calendar_order[i], 3))
245         return ((f64) i) * 86400.0;
246     }
247   return 0.0;
248 }
249
250
251 u8 *
252 format_clib_timebase_time (u8 * s, va_list * args)
253 {
254   f64 now = va_arg (*args, f64);
255   clib_timebase_component_t _c, *cp = &_c;
256
257   clib_timebase_time_to_components (now, cp);
258
259   s = format (s, "%s, %u %s %u %u:%02u:%02u",
260               day_names_epoch_order[cp->day_name_index],
261               cp->day,
262               month_short_names[cp->month],
263               cp->year, cp->hour, cp->minute, cp->second);
264   return (s);
265 }
266
267 uword
268 unformat_clib_timebase_range_hms (unformat_input_t * input, va_list * args)
269 {
270   clib_timebase_range_t *rp = va_arg (*args, clib_timebase_range_t *);
271   clib_timebase_component_t _c, *cp = &_c;
272   u32 start_hour, start_minute, start_second;
273   u32 end_hour, end_minute, end_second;
274
275   start_hour = start_minute = start_second
276     = end_hour = end_minute = end_second = 0;
277
278   if (unformat (input, "%u:%u:%u - %u:%u:%u",
279                 &start_hour, &start_minute, &start_second,
280                 &end_hour, &end_minute, &end_second))
281     ;
282   else if (unformat (input, "%u:%u - %u:%u",
283                      &start_hour, &start_minute, &end_hour, &end_minute))
284     ;
285   else if (unformat (input, "%u - %u", &start_hour, &end_hour))
286     ;
287   else
288     return 0;
289
290   clib_timebase_time_to_components (1e-6, cp);
291
292   cp->hour = start_hour;
293   cp->minute = start_minute;
294   cp->second = start_second;
295
296   rp->start = clib_timebase_components_to_time (cp);
297
298   cp->hour = end_hour;
299   cp->minute = end_minute;
300   cp->second = end_second;
301
302   rp->end = clib_timebase_components_to_time (cp);
303
304   return 1;
305 }
306
307 uword
308 unformat_clib_timebase_range_vector (unformat_input_t * input, va_list * args)
309 {
310   clib_timebase_range_t **rpp = va_arg (*args, clib_timebase_range_t **);
311   clib_timebase_range_t _tmp, *tmp = &_tmp;
312   clib_timebase_range_t *rp, *new_rp;
313   int day_range_match = 0;
314   int time_range_match = 0;
315   f64 range_start_time_offset;
316   f64 range_end_time_offset;
317   f64 now;
318   u8 *start_day = 0, *end_day = 0;
319
320   rp = *rpp;
321
322   while (1)
323     {
324       if (!day_range_match
325           && unformat (input, "%s - %s", &start_day, &end_day))
326         {
327           range_start_time_offset
328             = clib_timebase_offset_from_sunday (start_day);
329           range_end_time_offset = clib_timebase_offset_from_sunday (end_day);
330           vec_free (start_day);
331           vec_free (end_day);
332           day_range_match = 1;
333           time_range_match = 0;
334         }
335       else if (!day_range_match && unformat (input, "%s", &start_day))
336         {
337           range_start_time_offset
338             = clib_timebase_offset_from_sunday (start_day);
339           range_end_time_offset = range_start_time_offset + 86399.0;
340           day_range_match = 1;
341           vec_free (start_day);
342           day_range_match = 1;
343           time_range_match = 0;
344         }
345       else if (day_range_match &&
346                unformat (input, "%U", unformat_clib_timebase_range_hms, tmp))
347         {
348           /* Across the week... */
349           for (now = range_start_time_offset; now <= range_end_time_offset;
350                now += 86400.0)
351             {
352               vec_add2 (rp, new_rp, 1);
353               new_rp->start = now + tmp->start;
354               new_rp->end = now + tmp->end;
355             }
356           day_range_match = 0;
357           time_range_match = 1;
358         }
359       else if (time_range_match)
360         break;
361       else
362         {
363           vec_free (rp);
364           *rpp = 0;
365           return 0;
366         }
367     }
368
369   if (time_range_match)
370     {
371       *rpp = rp;
372       return 1;
373     }
374   else
375     {
376       vec_free (rp);
377       *rpp = 0;
378       return 0;
379     }
380 }
381
382 f64
383 clib_timebase_summer_offset (clib_timebase_t * tb, f64 now)
384 {
385   clib_timebase_component_t _c, *cp = &_c;
386   f64 second_sunday_march_2am;
387   f64 first_sunday_november_2am;
388
389   if (PREDICT_TRUE
390       (now >= tb->cached_year_start && now <= tb->cached_year_end))
391     {
392       if (now >= tb->cached_summer_start && now <= tb->cached_summer_end)
393         return tb->summer_offset;
394       else
395         return (0.0);
396     }
397
398   clib_timebase_time_to_components (now, cp);
399
400   cp->month = 0;
401   cp->day = 1;
402   cp->hour = 0;
403   cp->minute = 0;
404   cp->second = 1;
405
406   tb->cached_year_start = clib_timebase_components_to_time (cp);
407
408   cp->year += 1;
409
410   tb->cached_year_end = clib_timebase_components_to_time (cp);
411
412   cp->year -= 1;
413
414   /* Search for the second sunday in march, 2am */
415   cp->month = 2;
416   cp->day = 1;
417   cp->hour = 2;
418   cp->second = 0;
419   cp->nanosecond = 1;
420
421   /* March 1st will never be the second sunday... */
422   second_sunday_march_2am = clib_timebase_components_to_time (cp);
423   cp->day_name_index = 0;
424
425   /* Find the first sunday */
426   do
427     {
428       clib_timebase_time_to_components (second_sunday_march_2am, cp);
429       second_sunday_march_2am += 86400.0;
430     }
431   while (cp->day_name_index != 3 /* sunday */ );
432
433   /* Find the second sunday */
434   do
435     {
436       clib_timebase_time_to_components (second_sunday_march_2am, cp);
437       second_sunday_march_2am += 86400.0;
438     }
439   while (cp->day_name_index != 3 /* sunday */ );
440
441   second_sunday_march_2am -= 86400.0;
442
443   tb->cached_summer_start = second_sunday_march_2am;
444
445   /* Find the first sunday in November, which can easily be 11/1 */
446   cp->month = 10;
447   cp->day = 1;
448
449   first_sunday_november_2am = clib_timebase_components_to_time (cp);
450   clib_timebase_time_to_components (first_sunday_november_2am, cp);
451
452   while (cp->day_name_index != 3 /* sunday */ )
453     {
454       first_sunday_november_2am += 86400.0;
455       clib_timebase_time_to_components (first_sunday_november_2am, cp);
456     }
457
458   tb->cached_summer_end = first_sunday_november_2am;
459
460   if (now >= tb->cached_summer_start && now <= tb->cached_summer_end)
461     return tb->summer_offset;
462   else
463     return (0.0);
464 }
465
466 /*
467  * fd.io coding-style-patch-verification: ON
468  *
469  * Local Variables:
470  * eval: (c-set-style "gnu")
471  * End:
472  */