2
// Copyright (C) 2009 Alan W. Irwin
4
// This file is part of PLplot.
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.
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.
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
22
#include "qsastimeP.h"
45
// MJD for Jan 01, 1970 00:00:00 Gregorian, the Unix epoch.
46
#define MJD_1970 40587
48
// Recommended (by Linux timegm man page) POSIX equivalent of Linux timegm C library function
49
time_t my_timegm( struct tm *tm )
55
setenv( "TZ", "", 1 );
59
setenv( "TZ", tz, 1 );
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 )
69
int year1, month1, day1, hour1, min1;
74
struct tm *ptm1 = &tm1;
75
time_t secs_past_epoch, secs_past_epoch1, delta_secs;
77
MJDtime MJD1, *pMJD1 = &MJD1;
79
int ifleapyear, ifleapday, iffeb29, ifsamedate, ifsametime;
80
ptm->tm_year = year - 1900;
85
ptm->tm_sec = (int) sec;
90
printf( "Start of Julian proleptic inner test\n" );
91
printf( "input and output (strfMJD) date/time\n" );
95
printf( "Start of Gregorian proleptic inner test\n" );
96
printf( "input and output (strftime), and output (strfMJD) date/time\n" );
98
printf( "%.4d-%02d-%02dT%02d:%02d:%018.15fZ\n", year, month + 1, day, hour, min, sec );
101
setFromUT( year, month, day, hour, min, sec, pMJD1, forceJulian );
103
// Inner TEST01: compare setFromUT with my_timegm.
104
if ( !forceJulian && ( inner_test_choice & TEST01 ) )
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 )
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" );
119
// Inner TEST02: check minimal fields of strfMJD (Julian) or
120
// strftime and strfMJD (Gregorian)
121
if ( inner_test_choice & TEST02 )
125
strftime( &( buf[0] ), 360, "%Y-%m-%dT%H:%M:%SZ\n", ptm );
129
strfMJD( &( buf[0] ), 360, "%Y-%m-%dT%H:%M:%S%.Z\n", pMJD1, forceJulian, 0 );
136
jd = 2400000.5 + pMJD1->base_day + pMJD1->time_sec / 86400.;
137
printf( "setFromUT JD = %25.16f days\n", jd );
141
ifleapyear = ( year % 4 == 0 );
143
ifleapyear = ( ( year % 4 == 0 && year % 100 != 0 ) || year % 400 == 0 );
144
iffeb29 = month == 1 && day == 29;
145
ifleapday = ( ifleapyear && iffeb29 );
147
// Inner TEST03: compare setFromUT with its inverse, breakDownMJD
148
if ( inner_test_choice & TEST03 )
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 );
154
if ( !( ifsamedate && ifsametime ) )
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" );
162
// Inner TEST04: compare setFromUT with its inverse, the C library gmtime.
163
if ( !forceJulian && ( inner_test_choice & TEST04 ) )
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 );
169
if ( !( ifsamedate && ifsametime ) )
171
printf( "test failed with inconsistency between my_timegm and its C library inverse gmtime" );
178
int testlib_MJD( const MJDtime *MJD, int forceJulian, int inner_test_choice, int verbose )
180
int year, month, day, hour, min;
183
int year1, month1, day1, hour1, min1;
186
struct tm *ptm = &tm;
188
struct tm *ptm1 = &tm1;
189
time_t secs_past_epoch, secs_past_epoch1;
191
MJDtime MJD1_value, *MJD1 = &MJD1_value;
192
MJDtime MJD2_value, *MJD2 = &MJD2_value;
194
int ifleapyear, ifleapday, iffeb29, ifsamedate, ifsametime;
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 );
201
ptm->tm_year = year - 1900;
206
ptm->tm_sec = (int) sec;
211
printf( "Start of Julian proleptic inner test\n" );
212
printf( "input and output (strfMJD) date/time\n" );
216
printf( "Start of Gregorian proleptic inner test\n" );
217
printf( "input and output (strftime), and output (strfMJD) date/time\n" );
219
printf( "%.4d-%02d-%02dT%02d:%02d:%018.15fZ\n", year, month + 1, day, hour, min, sec );
222
// Inner TEST01: compare breakDownMJD with gmtime.
223
if ( !forceJulian && ( inner_test_choice & TEST01 ) )
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 ) )
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" );
235
// Inner TEST02: check minimal fields of strfMJD (Julian) or
236
// strftime and strfMJD (Gregorian)
237
if ( inner_test_choice & TEST02 )
241
strftime( &( buf[0] ), 360, "%Y-%m-%dT%H:%M:%SZ\n", ptm );
245
strfMJD( &( buf[0] ), 360, "%Y-%m-%dT%H:%M:%S%.Z\n", MJD1, forceJulian, 0 );
252
jd = 2400000.5 + MJD1->base_day + MJD1->time_sec / 86400.;
253
printf( "JD = %25.16f days\n", jd );
257
ifleapyear = ( year % 4 == 0 );
259
ifleapyear = ( ( year % 4 == 0 && year % 100 != 0 ) || year % 400 == 0 );
260
iffeb29 = month == 1 && day == 29;
261
ifleapday = ( ifleapyear && iffeb29 );
263
// Inner TEST03: compare breakDownMJD with its inverse, setFromUT
264
if ( inner_test_choice & TEST03 )
266
setFromUT( year, month, day, hour, min, sec, MJD2, forceJulian );
267
if ( !( MJD2->time_sec == MJD1->time_sec && MJD2->base_day == MJD1->base_day ) )
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" );
276
// Inner TEST04: compare breakDownMJD with its inverse, my_timegm
277
if ( !forceJulian && ( inner_test_choice & TEST04 ) )
279
secs_past_epoch1 = my_timegm( ptm );
280
if ( !( secs_past_epoch == secs_past_epoch1 ) )
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" );
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.
300
int year, month, day, hour, min;
302
int year1, month1, day1, hour1, min1;
305
struct tm *ptm = &tm;
307
struct tm *ptm1 = &tm1;
310
MJDtime MJD1_value, *MJD1 = &MJD1_value;
312
int test_choice, date_choice, ret;
314
// choose test(s) to be run using bit-pattern in test_choice that is
316
scanf( "%i", &test_choice );
318
printf( "sizeof(time_t) = %d\n", (int) sizeof ( time_t ) );
319
if ( sizeof ( time_t ) < 8 )
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" );
325
printf( "sizeof(int) = %d\n", (int) sizeof ( int ) );
326
if ( sizeof ( int ) != 4 )
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" );
331
// strftime affected by locale so force 0 timezone for this complete test.
332
setenv( "TZ", "", 1 );
335
if ( test_choice & TEST01 )
337
printf( "Test 01 of calendar dates in the vicinity of the JD epoch \n" );
339
for ( date_choice = 0; date_choice < 5; date_choice++ )
341
if ( date_choice == 0 )
346
else if ( date_choice == 1 )
351
else if ( date_choice == 2 )
356
else if ( date_choice == 3 )
361
else if ( date_choice == 4 )
370
for ( year = -4717; year <= -4707; year++ )
373
ret = testlib_broken_down_time( year, month, day, hour, min, sec, 1, 0xffff, 1 );
376
ret = testlib_broken_down_time( year, month, day, hour, min, sec, 0, 0xffff, 1 );
383
if ( test_choice & TEST02 )
385
printf( "Test 02 of calendar dates in the vicinity of the year epoch. \n" );
387
for ( date_choice = 0; date_choice < 5; date_choice++ )
389
if ( date_choice == 0 )
394
else if ( date_choice == 1 )
399
else if ( date_choice == 2 )
404
else if ( date_choice == 3 )
409
else if ( date_choice == 4 )
418
for ( year = -5; year <= 5; year++ )
421
ret = testlib_broken_down_time( year, month, day, hour, min, sec, 1, 0xffff, 1 );
424
ret = testlib_broken_down_time( year, month, day, hour, min, sec, 0, 0xffff, 1 );
431
if ( test_choice & TEST03 )
433
printf( "Test 03 of calendar dates in the vicinity of the MJD epoch. \n" );
435
for ( date_choice = 0; date_choice < 6; date_choice++ )
437
if ( date_choice == 0 )
442
else if ( date_choice == 1 )
447
else if ( date_choice == 2 )
452
else if ( date_choice == 3 )
457
else if ( date_choice == 4 )
462
else if ( date_choice == 5 )
471
for ( year = 1853; year <= 1863; year++ )
474
ret = testlib_broken_down_time( year, month, day, hour, min, sec, 1, 0xffff, 1 );
477
ret = testlib_broken_down_time( year, month, day, hour, min, sec, 0, 0xffff, 1 );
484
if ( test_choice & TEST04 )
486
printf( "Test 04 of small second range near Year 0 (Julian)\n" );
488
ret = setFromUT( 0, 0, 1, 0, 0, 0., MJD1, 1 );
491
printf( "Test 04 cannot even start for Year 0 (Julian)" );
495
for ( seconds = -5; seconds < 5; seconds++ )
498
ret = testlib_MJD( MJD1, 1, 0xffff, 1 );
504
printf( "Test 04 of small second range near Year 0 (Gregorian)\n" );
507
ret = setFromUT( 0, 0, 1, 0, 0, 0., MJD1, 0 );
510
printf( "Test 04 cannot even start for Year 0 (Gregorian)" );
514
for ( seconds = -5; seconds < 5; seconds++ )
517
ret = testlib_MJD( MJD1, 0, 0xffff, 1 );
523
printf( "Test 04 of small second range near 2009-01-01 (Gregorian) when a leap second was inserted\n" );
526
ret = setFromUT( 2009, 0, 1, 0, 0, 0.1234567890123456 - 5., MJD1, 0 );
529
printf( "Test 04 cannot even start for Year 0 (Gregorian)" );
533
for ( seconds = -5; seconds < 5; seconds++ )
536
ret = testlib_MJD( MJD1, 0, 0xffff, 1 );
543
if ( test_choice & TEST05 )
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.;
549
for ( iepsilon = -1; iepsilon < 2; iepsilon++ )
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 );
564
if ( test_choice & TEST06 )
566
printf( "Test 06 (non-verbose) of calendar dates for every year from -5000000 to 5000000\n" );
568
for ( date_choice = 0; date_choice < 5; date_choice++ )
570
if ( date_choice == 0 )
575
else if ( date_choice == 1 )
580
else if ( date_choice == 2 )
585
else if ( date_choice == 3 )
590
else if ( date_choice == 4 )
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 )
603
ret = testlib_broken_down_time( year, month, day, hour, min, sec, 1, 0xffff, 0 );
606
ret = testlib_broken_down_time( year, month, day, hour, min, sec, 0, 0xffff, 0 );
613
if ( test_choice & TEST07 )
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 );
619
printf( "Test 06 cannot even start" );
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++ )
626
MJD1->time_sec = (double) seconds;
627
ret = testlib_MJD( MJD1, 1, 0xffff, 0 );
631
ret = testlib_MJD( MJD1, 0, 0xffff, 0 );