PLplot  5.10.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
qsastime_testlib.c
Go to the documentation of this file.
1 //
2 // Copyright (C) 2009 Alan W. Irwin
3 //
4 // This file is part of PLplot.
5 //
6 // PLplot is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU Library General Public License as published
8 // by the Free Software Foundation; either version 2 of the License, or
9 // (at your option) any later version.
10 //
11 // PLplot is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU Library General Public License for more details.
15 //
16 // You should have received a copy of the GNU Library General Public License
17 // along with PLplot; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 //
20 //
21 #include "qsastime.h"
22 #include "qsastimeP.h"
23 #include <time.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <math.h>
27 #include <errno.h>
28 
29 #define TEST01 0x1
30 #define TEST02 0x2
31 #define TEST03 0x4
32 #define TEST04 0x8
33 #define TEST05 0x10
34 #define TEST06 0x20
35 #define TEST07 0x40
36 #define TEST08 0x80
37 #define TEST09 0x100
38 #define TEST10 0x200
39 #define TEST11 0x400
40 #define TEST12 0x800
41 #define TEST13 0x1000
42 #define TEST14 0x2000
43 #define TEST15 0x4000
44 #define TEST16 0x8000
45 // MJD for Jan 01, 1970 00:00:00 Gregorian, the Unix epoch.
46 #define MJD_1970 40587
47 
48 // Recommended (by Linux timegm man page) POSIX equivalent of Linux timegm C library function
49 time_t my_timegm( struct tm *tm )
50 {
51  time_t ret;
52  char *tz;
53 
54  tz = getenv( "TZ" );
55  setenv( "TZ", "", 1 );
56  tzset();
57  ret = mktime( tm );
58  if ( tz )
59  setenv( "TZ", tz, 1 );
60  else
61  unsetenv( "TZ" );
62  tzset();
63  return ret;
64 }
65 
66 int testlib_broken_down_time( int year, int month, int day, int hour, int min, double sec, int forceJulian, int inner_test_choice, int verbose )
67 {
68  char buf[360];
69  int year1, month1, day1, hour1, min1;
70  double sec1;
71  struct tm tm;
72  struct tm *ptm = &tm;
73  struct tm tm1;
74  struct tm *ptm1 = &tm1;
75  time_t secs_past_epoch, secs_past_epoch1, delta_secs;
76 
77  MJDtime MJD1, *pMJD1 = &MJD1;
78  double jd;
79  int ifleapyear, ifleapday, iffeb29, ifsamedate, ifsametime;
80  ptm->tm_year = year - 1900;
81  ptm->tm_mon = month;
82  ptm->tm_mday = day;
83  ptm->tm_hour = hour;
84  ptm->tm_min = min;
85  ptm->tm_sec = (int) sec;
86  if ( verbose )
87  {
88  if ( forceJulian )
89  {
90  printf( "Start of Julian proleptic inner test\n" );
91  printf( "input and output (strfMJD) date/time\n" );
92  }
93  else
94  {
95  printf( "Start of Gregorian proleptic inner test\n" );
96  printf( "input and output (strftime), and output (strfMJD) date/time\n" );
97  }
98  printf( "%.4d-%02d-%02dT%02d:%02d:%018.15fZ\n", year, month + 1, day, hour, min, sec );
99  }
100 
101  setFromUT( year, month, day, hour, min, sec, pMJD1, forceJulian );
102 
103  // Inner TEST01: compare setFromUT with my_timegm.
104  if ( !forceJulian && ( inner_test_choice & TEST01 ) )
105  {
106  secs_past_epoch1 = (time_t) ( 86400. * ( (double) pMJD1->base_day - (double) MJD_1970 ) + (int) pMJD1->time_sec );
107  secs_past_epoch = my_timegm( ptm );
108  delta_secs = abs( secs_past_epoch1 - secs_past_epoch );
109  if ( delta_secs != 0 )
110  {
111  printf( "setFromUT secs_past_epoch = %lld seconds\n", (long long) secs_past_epoch1 );
112  printf( "my_timegm secs_past_epoch = %lld seconds\n", (long long) secs_past_epoch );
113  printf( "delta secs_past_epoch = %lld seconds\n", (long long) ( secs_past_epoch1 - secs_past_epoch ) );
114  printf( "test failed with inconsistency between setFromUT and my_timegm\n" );
115  return 1;
116  }
117  }
118 
119  // Inner TEST02: check minimal fields of strfMJD (Julian) or
120  // strftime and strfMJD (Gregorian)
121  if ( inner_test_choice & TEST02 )
122  {
123  if ( !forceJulian )
124  {
125  strftime( &( buf[0] ), 360, "%Y-%m-%dT%H:%M:%SZ\n", ptm );
126  if ( verbose )
127  printf( "%s", buf );
128  }
129  strfMJD( &( buf[0] ), 360, "%Y-%m-%dT%H:%M:%S%.Z\n", pMJD1, forceJulian, 0 );
130  if ( verbose )
131  printf( "%s", buf );
132  }
133 
134  if ( verbose )
135  {
136  jd = 2400000.5 + pMJD1->base_day + pMJD1->time_sec / 86400.;
137  printf( "setFromUT JD = %25.16f days\n", jd );
138  }
139 
140  if ( forceJulian )
141  ifleapyear = ( year % 4 == 0 );
142  else
143  ifleapyear = ( ( year % 4 == 0 && year % 100 != 0 ) || year % 400 == 0 );
144  iffeb29 = month == 1 && day == 29;
145  ifleapday = ( ifleapyear && iffeb29 );
146 
147  // Inner TEST03: compare setFromUT with its inverse, breakDownMJD
148  if ( inner_test_choice & TEST03 )
149  {
150  breakDownMJD( &year1, &month1, &day1, &hour1, &min1, &sec1, pMJD1, forceJulian );
151  ifsamedate = ( year1 - year == 0 && ( ( ( !iffeb29 || ifleapday ) && ( month1 - month == 0 && day1 - day == 0 ) ) || ( ( iffeb29 && !ifleapday ) && ( month1 == 2 && day1 == 1 ) ) ) );
152  ifsametime = ( hour1 - hour == 0 && min1 - min == 0 && fabs( sec1 - sec ) < 1.e-10 );
153 
154  if ( !( ifsamedate && ifsametime ) )
155  {
156  printf( "output date calculated with breakDownMJD = %d-%02d-%02dT%02d:%02d:%018.15fZ\n", year1, month1 + 1, day1, hour1, min1, sec1 );
157  printf( "test failed with inconsistency between setFromUT and breakDownMJD\n" );
158  return 1;
159  }
160  }
161 
162  // Inner TEST04: compare setFromUT with its inverse, the C library gmtime.
163  if ( !forceJulian && ( inner_test_choice & TEST04 ) )
164  {
165  ptm1 = gmtime( &secs_past_epoch );
166  ifsamedate = ( ptm1->tm_year == ptm->tm_year && ( ( ( !iffeb29 || ifleapday ) && ( ptm1->tm_mon == ptm->tm_mon && ptm1->tm_mday == ptm->tm_mday ) ) || ( ( iffeb29 && !ifleapday ) && ( ptm1->tm_mon == 2 && ptm1->tm_mday == 1 ) ) ) );
167  ifsametime = ( ptm1->tm_hour == ptm->tm_hour && ptm1->tm_min == ptm->tm_min && ptm1->tm_sec == ptm->tm_sec );
168 
169  if ( !( ifsamedate && ifsametime ) )
170  {
171  printf( "test failed with inconsistency between my_timegm and its C library inverse gmtime" );
172  return 1;
173  }
174  }
175  return 0;
176 }
177 
178 int testlib_MJD( const MJDtime *MJD, int forceJulian, int inner_test_choice, int verbose )
179 {
180  int year, month, day, hour, min;
181  double sec;
182  char buf[360];
183  int year1, month1, day1, hour1, min1;
184  double sec1;
185  struct tm tm;
186  struct tm *ptm = &tm;
187  struct tm tm1;
188  struct tm *ptm1 = &tm1;
189  time_t secs_past_epoch, secs_past_epoch1;
190 
191  MJDtime MJD1_value, *MJD1 = &MJD1_value;
192  MJDtime MJD2_value, *MJD2 = &MJD2_value;
193  double jd;
194  int ifleapyear, ifleapday, iffeb29, ifsamedate, ifsametime;
195 
196  *MJD1 = *MJD;
197  normalize_MJD( MJD1 );
198  secs_past_epoch = (time_t) ( 86400. * ( (double) MJD1->base_day - (double) MJD_1970 ) + MJD1->time_sec );
199  breakDownMJD( &year, &month, &day, &hour, &min, &sec, MJD1, forceJulian );
200 
201  ptm->tm_year = year - 1900;
202  ptm->tm_mon = month;
203  ptm->tm_mday = day;
204  ptm->tm_hour = hour;
205  ptm->tm_min = min;
206  ptm->tm_sec = (int) sec;
207  if ( verbose )
208  {
209  if ( forceJulian )
210  {
211  printf( "Start of Julian proleptic inner test\n" );
212  printf( "input and output (strfMJD) date/time\n" );
213  }
214  else
215  {
216  printf( "Start of Gregorian proleptic inner test\n" );
217  printf( "input and output (strftime), and output (strfMJD) date/time\n" );
218  }
219  printf( "%.4d-%02d-%02dT%02d:%02d:%018.15fZ\n", year, month + 1, day, hour, min, sec );
220  }
221 
222  // Inner TEST01: compare breakDownMJD with gmtime.
223  if ( !forceJulian && ( inner_test_choice & TEST01 ) )
224  {
225  ptm1 = gmtime( &secs_past_epoch );
226  if ( !( ( ptm1->tm_year + 1900 ) == year && ptm1->tm_mon == month && ptm1->tm_mday == day && ptm1->tm_hour == hour && ptm1->tm_min == min && ptm1->tm_sec == (int) sec ) )
227  {
228  printf( "date calculated with breakDownMJD = %d-%02d-%02dT%02d:%02d:%018.15fZ\n", year, month + 1, day, hour, min, sec );
229  printf( "date calculated with gmtime = %d-%02d-%02dT%02d:%02d:%02dZ\n", ptm1->tm_year + 1900, ptm1->tm_mon + 1, ptm1->tm_mday, ptm1->tm_hour, ptm1->tm_min, ptm1->tm_sec );
230  printf( "test failed with inconsistency between breakDownMJD and gmtime\n" );
231  return 1;
232  }
233  }
234 
235  // Inner TEST02: check minimal fields of strfMJD (Julian) or
236  // strftime and strfMJD (Gregorian)
237  if ( inner_test_choice & TEST02 )
238  {
239  if ( !forceJulian )
240  {
241  strftime( &( buf[0] ), 360, "%Y-%m-%dT%H:%M:%SZ\n", ptm );
242  if ( verbose )
243  printf( "%s", buf );
244  }
245  strfMJD( &( buf[0] ), 360, "%Y-%m-%dT%H:%M:%S%.Z\n", MJD1, forceJulian, 0 );
246  if ( verbose )
247  printf( "%s", buf );
248  }
249 
250  if ( verbose )
251  {
252  jd = 2400000.5 + MJD1->base_day + MJD1->time_sec / 86400.;
253  printf( "JD = %25.16f days\n", jd );
254  }
255 
256  if ( forceJulian )
257  ifleapyear = ( year % 4 == 0 );
258  else
259  ifleapyear = ( ( year % 4 == 0 && year % 100 != 0 ) || year % 400 == 0 );
260  iffeb29 = month == 1 && day == 29;
261  ifleapday = ( ifleapyear && iffeb29 );
262 
263  // Inner TEST03: compare breakDownMJD with its inverse, setFromUT
264  if ( inner_test_choice & TEST03 )
265  {
266  setFromUT( year, month, day, hour, min, sec, MJD2, forceJulian );
267  if ( !( MJD2->time_sec == MJD1->time_sec && MJD2->base_day == MJD1->base_day ) )
268  {
269  printf( "(normalized) input MJD components are = %d, %f\n", MJD1->base_day, MJD1->time_sec );
270  printf( "(output MJD2 components generated by setFromUT are = %d, %f\n", MJD2->base_day, MJD2->time_sec );
271  printf( "test failed with inconsistency between breakDownMJD and setFromUT\n" );
272  return 1;
273  }
274  }
275 
276  // Inner TEST04: compare breakDownMJD with its inverse, my_timegm
277  if ( !forceJulian && ( inner_test_choice & TEST04 ) )
278  {
279  secs_past_epoch1 = my_timegm( ptm );
280  if ( !( secs_past_epoch == secs_past_epoch1 ) )
281  {
282  printf( "secs_past_epoch calculated from input = %lld\n", (long long) secs_past_epoch );
283  printf( "secs_past_epoch calculated from my_timegm = %lld\n", (long long) secs_past_epoch1 );
284  printf( "delta secs_past_epoch = %lld seconds\n", (long long) ( secs_past_epoch1 - secs_past_epoch ) );
285  printf( "test failed with inconsistency between breakDownMJD and its C library based inverse, my_timegm\n" );
286  return 1;
287  }
288  }
289  return 0;
290 }
291 
292 // Test program to do extensive comparisons between setFromUT, breakDownMJD,
293 // and strfMJD and the closest corresponding _Linux_ C library routines,
294 // timegm, gmtime, and strftime.
295 
296 int main()
297 {
298  char buf[360];
299  char buf1[360];
300  int year, month, day, hour, min;
301  double sec;
302  int year1, month1, day1, hour1, min1;
303  double sec1;
304  struct tm tm;
305  struct tm *ptm = &tm;
306  struct tm tm1;
307  struct tm *ptm1 = &tm1;
308  int seconds;
309 
310  MJDtime MJD1_value, *MJD1 = &MJD1_value;
311  double jd;
312  int test_choice, date_choice, ret;
313 
314  // choose test(s) to be run using bit-pattern in test_choice that is
315  // input from stdin.
316  scanf( "%i", &test_choice );
317 
318  printf( "sizeof(time_t) = %d\n", (int) sizeof ( time_t ) );
319  if ( sizeof ( time_t ) < 8 )
320  {
321  printf( "tests abandoned because time_t is too small on this platform to represent the extremely large date range used for many of these tests. Note, the limitation is in the C library routines (gmtime and mktime) used for these test comparisons and not libqsastime itself.\n" );
322  return 1;
323  }
324 
325  printf( "sizeof(int) = %d\n", (int) sizeof ( int ) );
326  if ( sizeof ( int ) != 4 )
327  {
328  printf( "tests abandoned because int must be 32-bits to test this library properly for how well it will potentially perform on 32-bit platforms\n" );
329  return 2;
330  }
331  // strftime affected by locale so force 0 timezone for this complete test.
332  setenv( "TZ", "", 1 );
333  tzset();
334 
335  if ( test_choice & TEST01 )
336  {
337  printf( "Test 01 of calendar dates in the vicinity of the JD epoch \n" );
338 
339  for ( date_choice = 0; date_choice < 5; date_choice++ )
340  {
341  if ( date_choice == 0 )
342  {
343  month = 0;
344  day = 1;
345  }
346  else if ( date_choice == 1 )
347  {
348  month = 1;
349  day = 28;
350  }
351  else if ( date_choice == 2 )
352  {
353  month = 1;
354  day = 29;
355  }
356  else if ( date_choice == 3 )
357  {
358  month = 2;
359  day = 1;
360  }
361  else if ( date_choice == 4 )
362  {
363  month = 11;
364  day = 31;
365  }
366  hour = 12;
367  min = 0;
368  sec = 0.;
369 
370  for ( year = -4717; year <= -4707; year++ )
371  {
372  printf( "\n" );
373  ret = testlib_broken_down_time( year, month, day, hour, min, sec, 1, 0xffff, 1 );
374  if ( ret )
375  return ret;
376  ret = testlib_broken_down_time( year, month, day, hour, min, sec, 0, 0xffff, 1 );
377  if ( ret )
378  return ret;
379  }
380  }
381  }
382 
383  if ( test_choice & TEST02 )
384  {
385  printf( "Test 02 of calendar dates in the vicinity of the year epoch. \n" );
386 
387  for ( date_choice = 0; date_choice < 5; date_choice++ )
388  {
389  if ( date_choice == 0 )
390  {
391  month = 0;
392  day = 1;
393  }
394  else if ( date_choice == 1 )
395  {
396  month = 1;
397  day = 28;
398  }
399  else if ( date_choice == 2 )
400  {
401  month = 1;
402  day = 29;
403  }
404  else if ( date_choice == 3 )
405  {
406  month = 2;
407  day = 1;
408  }
409  else if ( date_choice == 4 )
410  {
411  month = 11;
412  day = 31;
413  }
414  hour = 0;
415  min = 0;
416  sec = 0.;
417 
418  for ( year = -5; year <= 5; year++ )
419  {
420  printf( "\n" );
421  ret = testlib_broken_down_time( year, month, day, hour, min, sec, 1, 0xffff, 1 );
422  if ( ret )
423  return ret;
424  ret = testlib_broken_down_time( year, month, day, hour, min, sec, 0, 0xffff, 1 );
425  if ( ret )
426  return ret;
427  }
428  }
429  }
430 
431  if ( test_choice & TEST03 )
432  {
433  printf( "Test 03 of calendar dates in the vicinity of the MJD epoch. \n" );
434 
435  for ( date_choice = 0; date_choice < 6; date_choice++ )
436  {
437  if ( date_choice == 0 )
438  {
439  month = 0;
440  day = 1;
441  }
442  else if ( date_choice == 1 )
443  {
444  month = 1;
445  day = 28;
446  }
447  else if ( date_choice == 2 )
448  {
449  month = 1;
450  day = 29;
451  }
452  else if ( date_choice == 3 )
453  {
454  month = 2;
455  day = 1;
456  }
457  else if ( date_choice == 4 )
458  {
459  month = 10;
460  day = 17;
461  }
462  else if ( date_choice == 5 )
463  {
464  month = 11;
465  day = 31;
466  }
467  hour = 0;
468  min = 0;
469  sec = 0.;
470 
471  for ( year = 1853; year <= 1863; year++ )
472  {
473  printf( "\n" );
474  ret = testlib_broken_down_time( year, month, day, hour, min, sec, 1, 0xffff, 1 );
475  if ( ret )
476  return ret;
477  ret = testlib_broken_down_time( year, month, day, hour, min, sec, 0, 0xffff, 1 );
478  if ( ret )
479  return ret;
480  }
481  }
482  }
483 
484  if ( test_choice & TEST04 )
485  {
486  printf( "Test 04 of small second range near Year 0 (Julian)\n" );
487 
488  ret = setFromUT( 0, 0, 1, 0, 0, 0., MJD1, 1 );
489  if ( ret )
490  {
491  printf( "Test 04 cannot even start for Year 0 (Julian)" );
492  return ret;
493  }
494 
495  for ( seconds = -5; seconds < 5; seconds++ )
496  {
497  printf( "\n" );
498  ret = testlib_MJD( MJD1, 1, 0xffff, 1 );
499  if ( ret )
500  return ret;
501  MJD1->time_sec++;
502  }
503 
504  printf( "Test 04 of small second range near Year 0 (Gregorian)\n" );
505 
506 
507  ret = setFromUT( 0, 0, 1, 0, 0, 0., MJD1, 0 );
508  if ( ret )
509  {
510  printf( "Test 04 cannot even start for Year 0 (Gregorian)" );
511  return ret;
512  }
513 
514  for ( seconds = -5; seconds < 5; seconds++ )
515  {
516  printf( "\n" );
517  ret = testlib_MJD( MJD1, 0, 0xffff, 1 );
518  if ( ret )
519  return ret;
520  MJD1->time_sec++;
521  }
522 
523  printf( "Test 04 of small second range near 2009-01-01 (Gregorian) when a leap second was inserted\n" );
524 
525 
526  ret = setFromUT( 2009, 0, 1, 0, 0, 0.1234567890123456 - 5., MJD1, 0 );
527  if ( ret )
528  {
529  printf( "Test 04 cannot even start for Year 0 (Gregorian)" );
530  return ret;
531  }
532 
533  for ( seconds = -5; seconds < 5; seconds++ )
534  {
535  printf( "\n" );
536  ret = testlib_MJD( MJD1, 0, 0xffff, 1 );
537  if ( ret )
538  return ret;
539  MJD1->time_sec++;
540  }
541  }
542 
543  if ( test_choice & TEST05 )
544  {
545  printf( "Test 05 of normalization of breakDownMJD result and strfMJD results near the hour.\n" );
546  MJD1->base_day = 51910;
547  MJD1->time_sec = 3600.;
548  int iepsilon;
549  for ( iepsilon = -1; iepsilon < 2; iepsilon++ )
550  {
551  MJD1->time_sec = 3600. + 1.e-8 * (double) iepsilon;
552  breakDownMJD( &year, &month, &day, &hour, &min, &sec, MJD1, 0 );
553  printf( "MJD = {%d,%20.15f}\n", MJD1->base_day, MJD1->time_sec );
554  printf( "breakDownMJD result is year, month, day, hour, min, sec = %d, %d, %d, %d, %d, %20.15f\n", year, month, day, hour, min, sec );
555  strfMJD( &( buf[0] ), 360, "%Y-%m-%dT%H:%M:%S%9Z\n", MJD1, 0, 0 );
556  printf( "strfMJD %%S%%9 result is %s", buf );
557  strfMJD( &( buf[0] ), 360, "%Y-%m-%dT%H:%M:%S%.Z\n", MJD1, 0, 0 );
558  printf( "strfMJD %%S%%. result is %s", buf );
559  strfMJD( &( buf[0] ), 360, "%H:%M:%S, %H:%M:%S%0, %H:%M:%S%1, %H:%M:%S%2, %H:%M:%S%3, %H:%M:%S%4\n %H:%M:%S %0,%H:%M:%S %1,%H:%M:%S %2,%H:%M:%S %3,%H:%M:%S %4\n", MJD1, 0, 0 );
560  printf( "strfMJD more heavily rounded results (the latter ones with a blank before the\ndecimal point to prove separated formatting works) for H:M:S are the following:\n%s", buf );
561  }
562  }
563 
564  if ( test_choice & TEST06 )
565  {
566  printf( "Test 06 (non-verbose) of calendar dates for every year from -5000000 to 5000000\n" );
567 
568  for ( date_choice = 0; date_choice < 5; date_choice++ )
569  {
570  if ( date_choice == 0 )
571  {
572  month = 0;
573  day = 1;
574  }
575  else if ( date_choice == 1 )
576  {
577  month = 1;
578  day = 28;
579  }
580  else if ( date_choice == 2 )
581  {
582  month = 1;
583  day = 29;
584  }
585  else if ( date_choice == 3 )
586  {
587  month = 2;
588  day = 1;
589  }
590  else if ( date_choice == 4 )
591  {
592  month = 11;
593  day = 31;
594  }
595  hour = 0;
596  min = 0;
597  sec = 0.123456;
598 
599  // test reduced range of years that just barely misses overflowing
600  // the MJD integer. e.g., 6000000 overflows it.
601  for ( year = -5000000; year <= 5000000; year += 1 )
602  {
603  ret = testlib_broken_down_time( year, month, day, hour, min, sec, 1, 0xffff, 0 );
604  if ( ret )
605  return ret;
606  ret = testlib_broken_down_time( year, month, day, hour, min, sec, 0, 0xffff, 0 );
607  if ( ret )
608  return ret;
609  }
610  }
611  }
612 
613  if ( test_choice & TEST07 )
614  {
615  printf( "Test 07 (non-verbose) of all seconds from late 2007 to early 2009\n" );
616  ret = setFromUT( 2007, 11, 30, 0, 0, 0., MJD1, 0 );
617  if ( ret )
618  {
619  printf( "Test 06 cannot even start" );
620  return ret;
621  }
622 
623  // 430 days or ~ 37 million seconds should cover the complete next year for both Julian and Gregorian .
624  for ( seconds = 0; seconds < 430 * 86400; seconds++ )
625  {
626  MJD1->time_sec = (double) seconds;
627  ret = testlib_MJD( MJD1, 1, 0xffff, 0 );
628  if ( ret )
629  return ret;
630 
631  ret = testlib_MJD( MJD1, 0, 0xffff, 0 );
632  if ( ret )
633  return ret;
634  }
635  }
636 
637 
638  return 0;
639 }