BRL-CAD
time64.c
Go to the documentation of this file.
1 /*
2 
3 Copyright (c) 2007-2010 Michael G Schwern
4 
5 This software originally derived from Paul Sheer's pivotal_gmtime_r.c.
6 
7 The MIT License:
8 
9 Permission is hereby granted, free of charge, to any person obtaining a copy
10 of this software and associated documentation files (the "Software"), to deal
11 in the Software without restriction, including without limitation the rights
12 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 copies of the Software, and to permit persons to whom the Software is
14 furnished to do so, subject to the following conditions:
15 
16 The above copyright notice and this permission notice shall be included in
17 all copies or substantial portions of the Software.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 THE SOFTWARE.
26 
27 */
28 
29 /*
30 
31 Programmers who have available to them 64-bit time values as a 'long
32 long' type can use localtime64_r() and gmtime64_r() which correctly
33 converts the time even on 32-bit systems. Whether you have 64-bit time
34 values will depend on the operating system.
35 
36 localtime64_r() is a 64-bit equivalent of localtime_r().
37 
38 gmtime64_r() is a 64-bit equivalent of gmtime_r().
39 
40 */
41 
42 #include "common.h"
43 
44 #include <assert.h>
45 #include <stdlib.h>
46 #include <stdio.h>
47 #include <string.h>
48 #include <time.h>
49 #include <errno.h>
50 #include "time64.h"
51 #include "time64_limits.h"
52 
53 /* Max/min for mktime() */
54 /* MODIFIED: Assign to each field so as not to depend on system field
55  ordering
56 */
57 static struct tm SYSTEM_MKTIME_MAX;
58 static struct tm SYSTEM_MKTIME_MIN;
59 
60 static int mktime_maxmin_initialized = 0;
61 
63 {
64  SYSTEM_MKTIME_MAX.tm_sec = 7;
65  SYSTEM_MKTIME_MAX.tm_min = 14;
66  SYSTEM_MKTIME_MAX.tm_hour = 19;
67  SYSTEM_MKTIME_MAX.tm_mday = 18;
68  SYSTEM_MKTIME_MAX.tm_mon = 0;
69  SYSTEM_MKTIME_MAX.tm_year = 138;
70  SYSTEM_MKTIME_MAX.tm_wday = 1;
71  SYSTEM_MKTIME_MAX.tm_yday = 17;
72  SYSTEM_MKTIME_MAX.tm_isdst = 0;
73 #ifdef HAS_TM_TM_GMTOFF
74  SYSTEM_MKTIME_MAX.tm_gmtoff = -28800;
75 #endif
76 #ifdef HAS_TM_TM_ZONE
77  SYSTEM_MKTIME_MAX.tm_zone = "PST";
78 #endif
79 
80  SYSTEM_MKTIME_MIN.tm_sec = 52;
81  SYSTEM_MKTIME_MIN.tm_min = 45;
82  SYSTEM_MKTIME_MIN.tm_hour = 12;
83  SYSTEM_MKTIME_MIN.tm_mday = 13;
84  SYSTEM_MKTIME_MIN.tm_mon = 11;
85  SYSTEM_MKTIME_MIN.tm_year = 1;
86  SYSTEM_MKTIME_MIN.tm_wday = 5;
87  SYSTEM_MKTIME_MIN.tm_yday = 346;
88  SYSTEM_MKTIME_MIN.tm_isdst = 0;
89 #ifdef HAS_TM_TM_GMTOFF
90  SYSTEM_MKTIME_MIN.tm_gmtoff = 2880;
91 #endif
92 #ifdef HAS_TM_TM_ZONE
93  SYSTEM_MKTIME_MIN.tm_zone = "PST";
94 #endif
95 
96  mktime_maxmin_initialized = 1;
97 }
98 
99 /* Spec says except for stftime() and the _r() functions, these
100  all return static memory. Stabbings! */
101 static struct TM Static_Return_Date;
102 static char Static_Return_String[35];
103 
104 static const char days_in_month[2][12] = {
105  {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
106  {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
107 };
108 
109 static const short julian_days_by_month[2][12] = {
110  {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
111  {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335},
112 };
113 
114 static char wday_name[7][4] = {
115  "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
116 };
117 
118 static char mon_name[12][4] = {
119  "Jan", "Feb", "Mar", "Apr", "May", "Jun",
120  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
121 };
122 
123 static const short length_of_year[2] = { 365, 366 };
124 
125 /* Some numbers relating to the gregorian cycle */
126 static const Year years_in_gregorian_cycle = 400;
127 #define days_in_gregorian_cycle ((365 * 400) + 100 - 4 + 1)
128 static const Time64_T seconds_in_gregorian_cycle = days_in_gregorian_cycle * 60LL * 60LL * 24LL;
129 
130 /* Year range we can trust the time functions with */
131 #define MAX_SAFE_YEAR 2037
132 #define MIN_SAFE_YEAR 1971
133 
134 /* 28 year Julian calendar cycle */
135 #define SOLAR_CYCLE_LENGTH 28
136 
137 /* Year cycle from MAX_SAFE_YEAR down. */
138 static const short safe_years_high[SOLAR_CYCLE_LENGTH] = {
139  2016, 2017, 2018, 2019,
140  2020, 2021, 2022, 2023,
141  2024, 2025, 2026, 2027,
142  2028, 2029, 2030, 2031,
143  2032, 2033, 2034, 2035,
144  2036, 2037, 2010, 2011,
145  2012, 2013, 2014, 2015
146 };
147 
148 /* Year cycle from MIN_SAFE_YEAR up */
149 static const int safe_years_low[SOLAR_CYCLE_LENGTH] = {
150  1996, 1997, 1998, 1971,
151  1972, 1973, 1974, 1975,
152  1976, 1977, 1978, 1979,
153  1980, 1981, 1982, 1983,
154  1984, 1985, 1986, 1987,
155  1988, 1989, 1990, 1991,
156  1992, 1993, 1994, 1995,
157 };
158 
159 /* /\* This isn't used, but it's handy to look at *\/ */
160 /* static const char dow_year_start[SOLAR_CYCLE_LENGTH] = { */
161 /* 5, 0, 1, 2, /\* 0 2016 - 2019 *\/ */
162 /* 3, 5, 6, 0, /\* 4 *\/ */
163 /* 1, 3, 4, 5, /\* 8 1996 - 1998, 1971*\/ */
164 /* 6, 1, 2, 3, /\* 12 1972 - 1975 *\/ */
165 /* 4, 6, 0, 1, /\* 16 *\/ */
166 /* 2, 4, 5, 6, /\* 20 2036, 2037, 2010, 2011 *\/ */
167 /* 0, 2, 3, 4 /\* 24 2012, 2013, 2014, 2015 *\/ */
168 /* }; */
169 
170 /* Let's assume people are going to be looking for dates in the future.
171  Let's provide some cheats so you can skip ahead.
172  This has a 4x speed boost when near 2008.
173 */
174 /* Number of days since epoch on Jan 1st, 2008 GMT */
175 #define CHEAT_DAYS (1199145600 / 24 / 60 / 60)
176 #define CHEAT_YEARS 108
177 
178 #define IS_LEAP(n) ((!(((n) + 1900) % 400) || (!(((n) + 1900) % 4) && (((n) + 1900) % 100))) != 0)
179 #define WRAP(a,b,m) ((a) = ((a) < 0 ) ? ((b)--, (a) + (m)) : (a))
180 
181 #ifdef USE_SYSTEM_LOCALTIME
182 # define SHOULD_USE_SYSTEM_LOCALTIME(a) ( \
183  (a) <= SYSTEM_LOCALTIME_MAX && \
184  (a) >= SYSTEM_LOCALTIME_MIN \
185 )
186 #else
187 # define SHOULD_USE_SYSTEM_LOCALTIME(a) (0)
188 #endif
189 
190 #ifdef USE_SYSTEM_GMTIME
191 # define SHOULD_USE_SYSTEM_GMTIME(a) ( \
192  (a) <= SYSTEM_GMTIME_MAX && \
193  (a) >= SYSTEM_GMTIME_MIN \
194 )
195 #else
196 # define SHOULD_USE_SYSTEM_GMTIME(a) (0)
197 #endif
198 
199 /* Multi varadic macros are a C99 thing, alas */
200 #ifdef TIME_64_DEBUG
201 # define TIME64_TRACE(format) (fprintf(stderr, format))
202 # define TIME64_TRACE1(format, var1) (fprintf(stderr, format, var1))
203 # define TIME64_TRACE2(format, var1, var2) (fprintf(stderr, format, var1, var2))
204 # define TIME64_TRACE3(format, var1, var2, var3) (fprintf(stderr, format, var1, var2, var3))
205 #else
206 # define TIME64_TRACE(format) ((void)0)
207 # define TIME64_TRACE1(format, var1) ((void)0)
208 # define TIME64_TRACE2(format, var1, var2) ((void)0)
209 # define TIME64_TRACE3(format, var1, var2, var3) ((void)0)
210 #endif
211 
212 
213 static int is_exception_century(Year year)
214 {
215  int is_exception = ((year % 100 == 0) && !(year % 400 == 0));
216  TIME64_TRACE1("# is_exception_century: %s\n", is_exception ? "yes" : "no");
217 
218  return(is_exception);
219 }
220 
221 
222 /* Compare two dates.
223  The result is like cmp.
224  Ignores things like gmtoffset and dst
225 */
226 int cmp_date( const struct TM* left, const struct tm* right ) {
227  if( left->tm_year > right->tm_year )
228  return 1;
229  else if( left->tm_year < right->tm_year )
230  return -1;
231 
232  if( left->tm_mon > right->tm_mon )
233  return 1;
234  else if( left->tm_mon < right->tm_mon )
235  return -1;
236 
237  if( left->tm_mday > right->tm_mday )
238  return 1;
239  else if( left->tm_mday < right->tm_mday )
240  return -1;
241 
242  if( left->tm_hour > right->tm_hour )
243  return 1;
244  else if( left->tm_hour < right->tm_hour )
245  return -1;
246 
247  if( left->tm_min > right->tm_min )
248  return 1;
249  else if( left->tm_min < right->tm_min )
250  return -1;
251 
252  if( left->tm_sec > right->tm_sec )
253  return 1;
254  else if( left->tm_sec < right->tm_sec )
255  return -1;
256 
257  return 0;
258 }
259 
260 
261 /* Check if a date is safely inside a range.
262  The intention is to check if it's a few days inside.
263 */
264 int date_in_safe_range( const struct TM* date, const struct tm* min, const struct tm* max ) {
265  if( cmp_date(date, min) == -1 )
266  return 0;
267 
268  if( cmp_date(date, max) == 1 )
269  return 0;
270 
271  return 1;
272 }
273 
274 
275 /* timegm() is not in the C or POSIX spec, but it is such a useful
276  extension I would be remiss in leaving it out. Also I need it
277  for localtime64()
278 */
279 Time64_T timegm64(const struct TM *date) {
280  Time64_T days = 0;
281  Time64_T seconds = 0;
282  Year year;
283  Year orig_year = (Year)date->tm_year;
284  int cycles = 0;
285 
286  if( orig_year > 100 ) {
287  cycles = (orig_year - 100) / 400;
288  orig_year -= cycles * 400;
289  days += (Time64_T)cycles * days_in_gregorian_cycle;
290  }
291  else if( orig_year < -300 ) {
292  cycles = (orig_year - 100) / 400;
293  orig_year -= cycles * 400;
294  days += (Time64_T)cycles * days_in_gregorian_cycle;
295  }
296  TIME64_TRACE3("# timegm/ cycles: %d, days: %lld, orig_year: %lld\n", cycles, days, orig_year);
297 
298  if( orig_year > 70 ) {
299  year = 70;
300  while( year < orig_year ) {
301  days += length_of_year[IS_LEAP(year)];
302  year++;
303  }
304  }
305  else if ( orig_year < 70 ) {
306  year = 69;
307  do {
308  days -= length_of_year[IS_LEAP(year)];
309  year--;
310  } while( year >= orig_year );
311  }
312 
313  days += julian_days_by_month[IS_LEAP(orig_year)][date->tm_mon];
314  days += date->tm_mday - 1;
315 
316  seconds = days * 60 * 60 * 24;
317 
318  seconds += date->tm_hour * 60 * 60;
319  seconds += date->tm_min * 60;
320  seconds += date->tm_sec;
321 
322  return(seconds);
323 }
324 
325 
326 static int check_tm(struct TM *tm)
327 {
328  /* Don't forget leap seconds */
329  assert(tm->tm_sec >= 0);
330  assert(tm->tm_sec <= 61);
331 
332  assert(tm->tm_min >= 0);
333  assert(tm->tm_min <= 59);
334 
335  assert(tm->tm_hour >= 0);
336  assert(tm->tm_hour <= 23);
337 
338  assert(tm->tm_mday >= 1);
339  assert(tm->tm_mday <= days_in_month[IS_LEAP(tm->tm_year)][tm->tm_mon]);
340 
341  assert(tm->tm_mon >= 0);
342  assert(tm->tm_mon <= 11);
343 
344  assert(tm->tm_wday >= 0);
345  assert(tm->tm_wday <= 6);
346 
347  assert(tm->tm_yday >= 0);
348  assert(tm->tm_yday <= length_of_year[IS_LEAP(tm->tm_year)]);
349 
350 #ifdef HAS_TM_TM_GMTOFF
351  assert(tm->tm_gmtoff >= -24 * 60 * 60);
352  assert(tm->tm_gmtoff <= 24 * 60 * 60);
353 #endif
354 
355  return 1;
356 }
357 
358 
359 /* The exceptional centuries without leap years cause the cycle to
360  shift by 16
361 */
362 static Year cycle_offset(Year year)
363 {
364  const Year start_year = 2000;
365  Year year_diff = year - start_year;
366  Year exceptions;
367 
368  if( year > start_year )
369  year_diff--;
370 
371  exceptions = year_diff / 100;
372  exceptions -= year_diff / 400;
373 
374  TIME64_TRACE3("# year: %lld, exceptions: %lld, year_diff: %lld\n",
375  year, exceptions, year_diff);
376 
377  return exceptions * 16;
378 }
379 
380 /* For a given year after 2038, pick the latest possible matching
381  year in the 28 year calendar cycle.
382 
383  A matching year...
384  1) Starts on the same day of the week.
385  2) Has the same leap year status.
386 
387  This is so the calendars match up.
388 
389  Also the previous year must match. When doing Jan 1st you might
390  wind up on Dec 31st the previous year when doing a -UTC time zone.
391 
392  Finally, the next year must have the same start day of week. This
393  is for Dec 31st with a +UTC time zone.
394  It doesn't need the same leap year status since we only care about
395  January 1st.
396 
397  MODIFIED: Compile better by renaming safe_year variable to not
398  shadow function.
399 */
400 static int safe_year(const Year year)
401 {
402  int ret;
403  Year year_cycle;
404 
405  if( year >= MIN_SAFE_YEAR && year <= MAX_SAFE_YEAR ) {
406  return (int)year;
407  }
408 
409  year_cycle = year + cycle_offset(year);
410 
411  /* safe_years_low is off from safe_years_high by 8 years */
412  if( year < MIN_SAFE_YEAR )
413  year_cycle -= 8;
414 
415  /* Change non-leap xx00 years to an equivalent */
416  if( is_exception_century(year) )
417  year_cycle += 11;
418 
419  /* Also xx01 years, since the previous year will be wrong */
420  if( is_exception_century(year - 1) )
421  year_cycle += 17;
422 
423  year_cycle %= SOLAR_CYCLE_LENGTH;
424  if( year_cycle < 0 )
425  year_cycle = SOLAR_CYCLE_LENGTH + year_cycle;
426 
427  assert( year_cycle >= 0 );
428  assert( year_cycle < SOLAR_CYCLE_LENGTH );
429  if( year < MIN_SAFE_YEAR )
430  ret = safe_years_low[year_cycle];
431  else if( year > MAX_SAFE_YEAR )
432  ret = safe_years_high[year_cycle];
433  else
434  assert(0);
435 
436  TIME64_TRACE3("# year: %lld, year_cycle: %lld, safe_year: %d\n",
437  year, year_cycle, ret);
438 
439  assert(ret <= MAX_SAFE_YEAR && ret >= MIN_SAFE_YEAR);
440 
441  return ret;
442 }
443 
444 
445 void copy_tm_to_TM64(const struct tm *src, struct TM *dest) {
446  if( src == NULL ) {
447  memset(dest, 0, sizeof(*dest));
448  }
449  else {
450 # ifdef USE_TM64
451  dest->tm_sec = src->tm_sec;
452  dest->tm_min = src->tm_min;
453  dest->tm_hour = src->tm_hour;
454  dest->tm_mday = src->tm_mday;
455  dest->tm_mon = src->tm_mon;
456  dest->tm_year = (Year)src->tm_year;
457  dest->tm_wday = src->tm_wday;
458  dest->tm_yday = src->tm_yday;
459  dest->tm_isdst = src->tm_isdst;
460 
461 # ifdef HAS_TM_TM_GMTOFF
462  dest->tm_gmtoff = src->tm_gmtoff;
463 # endif
464 
465 # ifdef HAS_TM_TM_ZONE
466  dest->tm_zone = src->tm_zone;
467 # endif
468 
469 # else
470  /* They're the same type */
471  memcpy(dest, src, sizeof(*dest));
472 # endif
473  }
474 }
475 
476 
477 void copy_TM64_to_tm(const struct TM *src, struct tm *dest) {
478  if( src == NULL ) {
479  memset(dest, 0, sizeof(*dest));
480  }
481  else {
482 # ifdef USE_TM64
483  dest->tm_sec = src->tm_sec;
484  dest->tm_min = src->tm_min;
485  dest->tm_hour = src->tm_hour;
486  dest->tm_mday = src->tm_mday;
487  dest->tm_mon = src->tm_mon;
488  dest->tm_year = (int)src->tm_year;
489  dest->tm_wday = src->tm_wday;
490  dest->tm_yday = src->tm_yday;
491  dest->tm_isdst = src->tm_isdst;
492 
493 # ifdef HAS_TM_TM_GMTOFF
494  dest->tm_gmtoff = src->tm_gmtoff;
495 # endif
496 
497 # ifdef HAS_TM_TM_ZONE
498  dest->tm_zone = src->tm_zone;
499 # endif
500 
501 # else
502  /* They're the same type */
503  memcpy(dest, src, sizeof(*dest));
504 # endif
505  }
506 }
507 
508 
509 /* Simulate localtime_r() to the best of our ability */
510 struct tm * fake_localtime_r(const time_t *ytime, struct tm *result) {
511  const struct tm *static_result = localtime(ytime);
512 
513  assert(result != NULL);
514 
515  if( static_result == NULL ) {
516  memset(result, 0, sizeof(*result));
517  return NULL;
518  }
519  else {
520  memcpy(result, static_result, sizeof(*result));
521  return result;
522  }
523 }
524 
525 
526 /* Simulate gmtime_r() to the best of our ability */
527 struct tm * fake_gmtime_r(const time_t *ytime, struct tm *result) {
528  const struct tm *static_result = gmtime(ytime);
529 
530  assert(result != NULL);
531 
532  if( static_result == NULL ) {
533  memset(result, 0, sizeof(*result));
534  return NULL;
535  }
536  else {
537  memcpy(result, static_result, sizeof(*result));
538  return result;
539  }
540 }
541 
542 
543 static Time64_T seconds_between_years(Year left_year, Year right_year) {
544  int increment = (left_year > right_year) ? 1 : -1;
545  Time64_T seconds = 0;
546  int cycles;
547 
548  if( left_year > 2400 ) {
549  cycles = (left_year - 2400) / 400;
550  left_year -= cycles * 400;
551  seconds += cycles * seconds_in_gregorian_cycle;
552  }
553  else if( left_year < 1600 ) {
554  cycles = (left_year - 1600) / 400;
555  left_year += cycles * 400;
556  seconds += cycles * seconds_in_gregorian_cycle;
557  }
558 
559  while( left_year != right_year ) {
560  seconds += length_of_year[IS_LEAP(right_year - 1900)] * 60 * 60 * 24;
561  right_year += increment;
562  }
563 
564  return seconds * increment;
565 }
566 
567 
568 Time64_T mktime64(struct TM *input_date) {
569  struct tm safe_date;
570  struct TM date;
571  Time64_T ytime;
572  Year year = input_date->tm_year + 1900;
573 
574  /* MODIFIED: this check added, used to make sure that the mktime()
575  max and min structures are initialized now that they are not
576  initialized through a struct literal (see above).
577  */
578  if (!mktime_maxmin_initialized)
579  {
581  }
582  if( date_in_safe_range(input_date, &SYSTEM_MKTIME_MIN, &SYSTEM_MKTIME_MAX) )
583  {
584  copy_TM64_to_tm(input_date, &safe_date);
585  ytime = (Time64_T)mktime(&safe_date);
586 
587  /* Correct the possibly out of bound input date */
588  copy_tm_to_TM64(&safe_date, input_date);
589  return ytime;
590  }
591 
592  /* Have to make the year safe in date else it won't fit in safe_date */
593  date = *input_date;
594  date.tm_year = safe_year(year) - 1900;
595  copy_TM64_to_tm(&date, &safe_date);
596 
597  ytime = (Time64_T)mktime(&safe_date);
598 
599  /* Correct the user's possibly out of bound input date */
600  copy_tm_to_TM64(&safe_date, input_date);
601 
602  ytime += seconds_between_years(year, (Year)(safe_date.tm_year + 1900));
603 
604  return ytime;
605 }
606 
607 
608 /* Because I think mktime() is a crappy name */
609 Time64_T timelocal64(struct TM *date) {
610  return mktime64(date);
611 }
612 
613 
614 struct TM *gmtime64_r (const Time64_T *in_time, struct TM *p)
615 {
616  int v_tm_sec, v_tm_min, v_tm_hour, v_tm_mon, v_tm_wday;
617  Time64_T v_tm_tday;
618  int leap;
619  Time64_T m;
620  Time64_T ytime = *in_time;
621  Year year = 70;
622  int cycles = 0;
623 
624  assert(p != NULL);
625 
626  /* Use the system gmtime() if time_t is small enough */
627  if( SHOULD_USE_SYSTEM_GMTIME(*in_time) ) {
628  time_t safe_time = (time_t)*in_time;
629  struct tm safe_date;
630  GMTIME_R(&safe_time, &safe_date);
631 
632  copy_tm_to_TM64(&safe_date, p);
633  assert(check_tm(p));
634 
635  return p;
636  }
637 
638 #ifdef HAS_TM_TM_GMTOFF
639  p->tm_gmtoff = 0;
640 #endif
641  p->tm_isdst = 0;
642 
643 #ifdef HAS_TM_TM_ZONE
644  p->tm_zone = "UTC";
645 #endif
646 
647  v_tm_sec = (int)(ytime % 60);
648  ytime /= 60;
649  v_tm_min = (int)(ytime % 60);
650  ytime /= 60;
651  v_tm_hour = (int)(ytime % 24);
652  ytime /= 24;
653  v_tm_tday = ytime;
654 
655  WRAP (v_tm_sec, v_tm_min, 60);
656  WRAP (v_tm_min, v_tm_hour, 60);
657  WRAP (v_tm_hour, v_tm_tday, 24);
658 
659  v_tm_wday = (int)((v_tm_tday + 4) % 7);
660  if (v_tm_wday < 0)
661  v_tm_wday += 7;
662  m = v_tm_tday;
663 
664  if (m >= CHEAT_DAYS) {
665  year = CHEAT_YEARS;
666  m -= CHEAT_DAYS;
667  }
668 
669  if (m >= 0) {
670  /* Gregorian cycles, this is huge optimization for distant times */
671  cycles = (int)(m / (Time64_T) days_in_gregorian_cycle);
672  if( cycles ) {
673  m -= (cycles * (Time64_T) days_in_gregorian_cycle);
674  year += (cycles * years_in_gregorian_cycle);
675  }
676 
677  /* Years */
678  leap = IS_LEAP (year);
679  while (m >= (Time64_T) length_of_year[leap]) {
680  m -= (Time64_T) length_of_year[leap];
681  year++;
682  leap = IS_LEAP (year);
683  }
684 
685  /* Months */
686  v_tm_mon = 0;
687  while (m >= (Time64_T) days_in_month[leap][v_tm_mon]) {
688  m -= (Time64_T) days_in_month[leap][v_tm_mon];
689  v_tm_mon++;
690  }
691  } else {
692  year--;
693 
694  /* Gregorian cycles */
695  cycles = (int)((m / (Time64_T) days_in_gregorian_cycle) + 1);
696  if( cycles ) {
697  m -= (cycles * (Time64_T) days_in_gregorian_cycle);
698  year += (cycles * years_in_gregorian_cycle);
699  }
700 
701  /* Years */
702  leap = IS_LEAP (year);
703  while (m < (Time64_T) -length_of_year[leap]) {
704  m += (Time64_T) length_of_year[leap];
705  year--;
706  leap = IS_LEAP (year);
707  }
708 
709  /* Months */
710  v_tm_mon = 11;
711  while (m < (Time64_T) -days_in_month[leap][v_tm_mon]) {
712  m += (Time64_T) days_in_month[leap][v_tm_mon];
713  v_tm_mon--;
714  }
715  m += (Time64_T) days_in_month[leap][v_tm_mon];
716  }
717 
718  p->tm_year = year;
719  if( p->tm_year != year ) {
720 #ifdef EOVERFLOW
721  errno = EOVERFLOW;
722 #endif
723  return NULL;
724  }
725 
726  /* At this point m is less than a year so casting to an int is safe */
727  p->tm_mday = (int) m + 1;
728  p->tm_yday = julian_days_by_month[leap][v_tm_mon] + (int)m;
729  p->tm_sec = v_tm_sec;
730  p->tm_min = v_tm_min;
731  p->tm_hour = v_tm_hour;
732  p->tm_mon = v_tm_mon;
733  p->tm_wday = v_tm_wday;
734 
735  assert(check_tm(p));
736 
737  return p;
738 }
739 
740 
741 struct TM *localtime64_r (const Time64_T *ytime, struct TM *local_tm)
742 {
743  time_t safe_time;
744  struct tm safe_date;
745  struct TM gm_tm;
746  Year orig_year;
747  int month_diff;
748 
749  assert(local_tm != NULL);
750 
751  /* Use the system localtime() if time_t is small enough */
752  if( SHOULD_USE_SYSTEM_LOCALTIME(*ytime) ) {
753  safe_time = (time_t)*ytime;
754 
755  TIME64_TRACE1("Using system localtime for %lld\n", *ytime);
756 
757  LOCALTIME_R(&safe_time, &safe_date);
758 
759  copy_tm_to_TM64(&safe_date, local_tm);
760  assert(check_tm(local_tm));
761 
762  return local_tm;
763  }
764 
765  if( gmtime64_r(ytime, &gm_tm) == NULL ) {
766  TIME64_TRACE1("gmtime64_r returned null for %lld\n", *ytime);
767  return NULL;
768  }
769 
770  orig_year = gm_tm.tm_year;
771 
772  if (gm_tm.tm_year > (2037 - 1900) ||
773  gm_tm.tm_year < (1970 - 1900)
774  )
775  {
776  TIME64_TRACE1("Mapping tm_year %lld to safe_year\n", (Year)gm_tm.tm_year);
777  gm_tm.tm_year = safe_year((Year)(gm_tm.tm_year + 1900)) - 1900;
778  }
779 
780  safe_time = (time_t)timegm64(&gm_tm);
781  if( LOCALTIME_R(&safe_time, &safe_date) == NULL ) {
782  TIME64_TRACE1("localtime_r(%d) returned NULL\n", (int)safe_time);
783  return NULL;
784  }
785 
786  copy_tm_to_TM64(&safe_date, local_tm);
787 
788  local_tm->tm_year = orig_year;
789  if( local_tm->tm_year != orig_year ) {
790  TIME64_TRACE2("tm_year overflow: tm_year %lld, orig_year %lld\n",
791  (Year)local_tm->tm_year, (Year)orig_year);
792 
793 #ifdef EOVERFLOW
794  errno = EOVERFLOW;
795 #endif
796  return NULL;
797  }
798 
799 
800  month_diff = local_tm->tm_mon - gm_tm.tm_mon;
801 
802  /* When localtime is Dec 31st previous year and
803  gmtime is Jan 1st next year.
804  */
805  if( month_diff == 11 ) {
806  local_tm->tm_year--;
807  }
808 
809  /* When localtime is Jan 1st, next year and
810  gmtime is Dec 31st, previous year.
811  */
812  if( month_diff == -11 ) {
813  local_tm->tm_year++;
814  }
815 
816  /* GMT is Jan 1st, xx01 year, but localtime is still Dec 31st
817  in a non-leap xx00. There is one point in the cycle
818  we can't account for which the safe xx00 year is a leap
819  year. So we need to correct for Dec 31st coming out as
820  the 366th day of the year.
821  */
822  if( !IS_LEAP(local_tm->tm_year) && local_tm->tm_yday == 365 )
823  local_tm->tm_yday--;
824 
825  assert(check_tm(local_tm));
826 
827  return local_tm;
828 }
829 
830 
831 int valid_tm_wday( const struct TM* date ) {
832  if( 0 <= date->tm_wday && date->tm_wday <= 6 )
833  return 1;
834  else
835  return 0;
836 }
837 
838 int valid_tm_mon( const struct TM* date ) {
839  if( 0 <= date->tm_mon && date->tm_mon <= 11 )
840  return 1;
841  else
842  return 0;
843 }
844 
845 
846 char *asctime64_r( const struct TM* date, char *result ) {
847  /* I figure everything else can be displayed, even hour 25, but if
848  these are out of range we walk off the name arrays */
849  if( !valid_tm_wday(date) || !valid_tm_mon(date) )
850  return NULL;
851 
852  sprintf(result, TM64_ASCTIME_FORMAT,
853  wday_name[date->tm_wday],
854  mon_name[date->tm_mon],
855  date->tm_mday, date->tm_hour,
856  date->tm_min, date->tm_sec,
857  1900 + date->tm_year);
858 
859  return result;
860 }
861 
862 
863 char *ctime64_r( const Time64_T* ytime, char* result ) {
864  struct TM date;
865  memset(&date, 0, sizeof(struct TM));
866 
867  localtime64_r(ytime, &date );
868  return asctime64_r( &date, result );
869 }
870 
871 
872 /* Non-thread safe versions of the above */
873 struct TM *localtime64(const Time64_T *ytime) {
874  tzset();
875  return localtime64_r(ytime, &Static_Return_Date);
876 }
877 
878 struct TM *gmtime64(const Time64_T *ytime) {
879  return gmtime64_r(ytime, &Static_Return_Date);
880 }
881 
882 char *asctime64( const struct TM* date ) {
883  return asctime64_r( date, Static_Return_String );
884 }
885 
886 char *ctime64( const Time64_T* ytime ) {
887  tzset();
888  return asctime64(localtime64(ytime));
889 }
INT_64_T Year
Definition: time64.h:25
char * ctime64_r(const Time64_T *ytime, char *result)
Definition: time64.c:863
int valid_tm_mon(const struct TM *date)
Definition: time64.c:838
struct TM * localtime64(const Time64_T *ytime)
Definition: time64.c:873
#define SHOULD_USE_SYSTEM_LOCALTIME(a)
Definition: time64.c:187
int valid_tm_wday(const struct TM *date)
Definition: time64.c:831
#define CHEAT_DAYS
Definition: time64.c:175
Header file for the BRL-CAD common definitions.
void initialize_mktime_maxmin()
Definition: time64.c:62
#define CHEAT_YEARS
Definition: time64.c:176
int date_in_safe_range(const struct TM *date, const struct tm *min, const struct tm *max)
Definition: time64.c:264
char * asctime64(const struct TM *date)
Definition: time64.c:882
#define MIN_SAFE_YEAR
Definition: time64.c:132
#define SOLAR_CYCLE_LENGTH
Definition: time64.c:135
#define SHOULD_USE_SYSTEM_GMTIME(a)
Definition: time64.c:196
void * memset(void *s, int c, size_t n)
#define TM64_ASCTIME_FORMAT
Definition: time64.h:89
#define LOCALTIME_R(clock, result)
Definition: time64.h:76
char * asctime64_r(const struct TM *date, char *result)
Definition: time64.c:846
int cmp_date(const struct TM *left, const struct tm *right)
Definition: time64.c:226
#define TIME64_TRACE2(format, var1, var2)
Definition: time64.c:208
#define TIME64_TRACE3(format, var1, var2, var3)
Definition: time64.c:209
#define WRAP(a, b, m)
Definition: time64.c:179
Time64_T timegm64(const struct TM *date)
Definition: time64.c:279
struct TM * localtime64_r(const Time64_T *ytime, struct TM *local_tm)
Definition: time64.c:741
Time64_T timelocal64(struct TM *date)
Definition: time64.c:609
INT_64_T Time64_T
Definition: time64.h:24
struct TM * gmtime64(const Time64_T *ytime)
Definition: time64.c:878
Time64_T mktime64(struct TM *input_date)
Definition: time64.c:568
struct TM * gmtime64_r(const Time64_T *in_time, struct TM *p)
Definition: time64.c:614
void copy_TM64_to_tm(const struct TM *src, struct tm *dest)
Definition: time64.c:477
struct tm * fake_localtime_r(const time_t *ytime, struct tm *result)
Definition: time64.c:510
void copy_tm_to_TM64(const struct tm *src, struct TM *dest)
Definition: time64.c:445
#define MAX_SAFE_YEAR
Definition: time64.c:131
#define GMTIME_R(clock, result)
Definition: time64.h:81
#define days_in_gregorian_cycle
Definition: time64.c:127
#define TIME64_TRACE1(format, var1)
Definition: time64.c:207
char * ctime64(const Time64_T *ytime)
Definition: time64.c:886
#define TM
Definition: time64.h:52
#define IS_LEAP(n)
Definition: time64.c:178
struct tm * fake_gmtime_r(const time_t *ytime, struct tm *result)
Definition: time64.c:527