home *** CD-ROM | disk | FTP | other *** search
/ C!T ROM 2 / ctrom_ii_b.zip / ctrom_ii_b / PROGRAM / C / JULCAL10 / TJUL.C < prev   
C/C++ Source or Header  |  1992-12-21  |  11KB  |  335 lines

  1. #define MKTIME
  2. /*
  3.     NAME
  4.         tjul - test julcal family of Julian day number programs
  5.  
  6.     SYNOPSIS
  7.         julcal [-v] [datafile]
  8.  
  9.     NOTES
  10.         Default datafile name is "data".  Nothing is printed unless an
  11.         error is found.  
  12.  
  13.     OPTIONS
  14.         -v    verbose: print all comment lines, input data, calculated dates,
  15.             first 10 lines of each set of random tests
  16.  
  17.     INPUT
  18.         A ';' introduces a comment.  
  19.  
  20.         If the first character is 'T', it is followed by the Julian
  21.         date for transition to the Gregorian calendar.  (Initially, the
  22.         transition date is JD2361222 = September 14, 1752, as in England.)
  23.  
  24.         Otherwise, each line has six items:    
  25.             julian_day   year  month  mday  wday  yday
  26.               2299159    1582    10     3    Wed   275
  27.         where 
  28.             julian_day is the number of days since Jan 1, 4713 B.C.
  29.             year is negative for B.C.
  30.             month is in the range [1...12]
  31.             mday is in the range [1...31]
  32.             yday is in the range [0...365]
  33.  
  34.     OUTPUT
  35.         In case of error...
  36.  
  37.         The input data.
  38.         The Julian day calculated from the date by juldn.
  39.         The year, month, day, weekday, and day of year (0...365)
  40.         calculated from the Julian day by julcd.
  41.  
  42.         Outputs that don't match the corresponding inputs are marked with '*'.
  43.  
  44.            JD              date            juldn(date)           julcd(JD)
  45.         -------     ------------------     -----------     --------------------
  46.         2430336     1941 12  7 Sun 340       2430336       1941 12  7  Sun  340  
  47.  
  48.         There are also three sets of random tests.  Again, '*'
  49.         indicates a disagreement.
  50.  
  51.         The first set is for self consistency: two dates, ten days
  52.         apart, should result in Julian dates that differ by ten.
  53.  
  54.         The second set compares the normalization of dates as found by
  55.         mktime and juldn.  Due to buggy implementations of mktime
  56.         (Borland, Sun), in case of a disagreement the test program
  57.         tries to identify which function is incorrect.  First, it
  58.         adjusts the month and compensates with the year.  If the two
  59.         functions then agree, it prints their results.  Otherwise, it
  60.         looks for a boundary between agreement and disagreement.  It
  61.         then prints results for dates on each side of the boundary.  In
  62.         either case, comparing the last two lines should show which
  63.         function is incorrect.
  64.  
  65.         The third set compares tm_wday and tm_yday as found by julcd(),
  66.         juldn(), and gmtime().
  67.  
  68. */
  69. #include <stdio.h>
  70. #include <string.h>
  71. #include <time.h>
  72. #include "julcal.h"
  73.  
  74. #define BUFSIZE 200
  75.  
  76. char buf[BUFSIZE+1];
  77. FILE *ifile;
  78. char *filename = "data";
  79. #define streq(a,b) (strcmp(a,b)==0)
  80.  
  81. main(argc, argv) int argc; char **argv;
  82. {    char *s, tdayname[4];
  83.     static char *weekdayname[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
  84.     static char item1_heading[]=
  85. "    JD              date            juldn(date)           julcd(JD)\n\
  86.  -------     ------------------     -----------     --------------------\n";
  87.     static char item2_heading[]=
  88. "\n\
  89.      before                 after\n\
  90. --------------    --------------------------\n\
  91. year month day    year month day  Julian day\n";
  92. #ifdef MKTIME
  93.     static char item3_heading[]=
  94. "\n\
  95.      before           mktime             juldn\n\
  96. --------------    --------------    --------------\n";
  97.     int item3=0;
  98. #endif
  99.     static char item4_heading[]=
  100. "\n\
  101.                        gmtime               julcd           juldn\n\
  102.                ----------------------     ----------     ----------\n\
  103.    time_t         date     wday  yday     wday  yday     wday  yday\n";
  104. /*   xxxxxxxxxx -> 4444/22/22 -> 1    333      1    333 */
  105.     int item4=0;
  106.  
  107.     int fields, same_jd, same_date, i;
  108.     int verbose=0, item1=0, item2=0;
  109.     long tjd; struct tm tbdt;    /* test data, from file */
  110.     long rjd; struct tm rbdt;    /* results, from functions */
  111.     long j0, j10;
  112.     int tyear, ryear, dy, dd, error;
  113.     time_t when;
  114.     struct tm *bdt1, *bdt2;
  115.     struct tm before, before0, before10, after0, after10, theirs, ours;
  116.  
  117.     for (i = 1; i < argc; i++)
  118.         {if(streq(argv[i],"-v")) verbose=1;
  119.         else filename = argv[i];
  120.         }
  121.  
  122.     ifile = fopen(filename, "r");
  123.     if(ifile == 0) {fprintf(stderr, "can't open file %s\n", filename); exit(1);}
  124.  
  125.     while(fgets(buf, BUFSIZE, ifile))
  126.         {
  127.         if(s = strchr(buf, '\n')) *s = 0;    /* zap LF */
  128.         if(buf[0] == ';') 
  129.             {
  130.             if(verbose) printf("%s\n", buf);  /* echo cmt line */
  131.             continue;
  132.             }
  133.         if(buf[0] == 'T')
  134.             {    /* set new default transition date */
  135.             sscanf(buf, "T%ld", &jul_transition);
  136.             if(verbose) printf("%s\n", buf);
  137.             continue;
  138.             }
  139.         if(s = strchr(buf, ';')) *s = 0;    /* zap trailing comment */
  140.         tbdt.tm_year = 0;
  141.         tbdt.tm_mon = 0;
  142.         tbdt.tm_mday = 1;
  143.         tbdt.tm_wday = 1;
  144.         fields = sscanf(buf, "%ld %d %d %d %3s %d", &tjd, &tbdt.tm_year,
  145.             &tbdt.tm_mon, &tbdt.tm_mday, &tdayname, &tbdt.tm_yday);
  146.         if(fields < 1) continue;
  147.         for (i = 0; i < 7; i++)
  148.             if(streq(tdayname, weekdayname[i]))
  149.                 break;
  150.         tbdt.tm_wday = i; 
  151.  
  152.                     /* adjust for struct_tm offsets */
  153.         if(tbdt.tm_year < 0) tbdt.tm_year -= 1899;
  154.         else tbdt.tm_year -= 1900;
  155.         tbdt.tm_mon--;
  156.  
  157.         rjd = juldn(&tbdt);
  158.         rbdt = *julcd(tjd);
  159.  
  160.         same_date = (rbdt.tm_year == tbdt.tm_year)
  161.                 && (rbdt.tm_mon == tbdt.tm_mon)
  162.                 && (rbdt.tm_mday == tbdt.tm_mday)
  163.                 && (rbdt.tm_wday == tbdt.tm_wday)
  164.                 && (rbdt.tm_yday == tbdt.tm_yday);
  165.         same_jd = (rjd == tjd);
  166.         if(verbose || !same_jd || !same_date)
  167.             {
  168.             if(!item1) printf(item1_heading);
  169.             item1 = 1;
  170.             tyear = tbdt.tm_year + 1900; if(tyear < 1) tyear--;
  171.             ryear = rbdt.tm_year + 1900; if(ryear < 1) ryear--;
  172.             printf("%8ld    %5d %2d %2d %3s %3d       %7ld %s    %5d %2d %2d  %s  %3d %s\n",
  173.                 tjd, tyear, tbdt.tm_mon+1, tbdt.tm_mday, tdayname, tbdt.tm_yday,
  174.                 rjd, same_jd?" ":"*", 
  175.                 ryear, rbdt.tm_mon+1, rbdt.tm_mday,
  176.                 weekdayname[rbdt.tm_wday], rbdt.tm_yday, 
  177.                 same_date?" ":"*");
  178.             }
  179.         }
  180.  
  181. /*
  182.     Check normalization (correction for day or month out of the normal
  183.     range) of juldnj.
  184.  
  185. */
  186.     for (i = 0; i < 5000; i++)
  187.         {    /* test period is 1780 - 5 - 1 ... 1780 + 700 + 5 + 1
  188.                         or 1774 ... 2486 
  189.                 (after transition to Gregorian calendar) */
  190.         before0.tm_year = 1780 - 1900 + rand()%700;
  191.         before0.tm_mon = -60 + rand()%120;
  192.         before0.tm_mday = -360 + rand()%720;
  193.         before0.tm_sec = before0.tm_min = before0.tm_hour = 0;
  194.         before10 = before0;
  195.         dy = rand()%14;
  196.         before10.tm_year -= dy;
  197.         before10.tm_mon += dy*12;
  198.         before10.tm_mday += 10;
  199.                 /* 'before10' now has a date 10 days later than 'before0' */
  200.  
  201.         after0 = before0;
  202.         after10 = before10;
  203.         j0 = juldn(&after0);
  204.         j10 = juldn(&after10);
  205.  
  206.                 /* check that final dates are normalized & 10 days apart */
  207.         error =    j10 != j0 + 10 
  208.             || after0.tm_mon < 0
  209.             || after0.tm_mon > 11
  210.             || after0.tm_mday < 1
  211.             || after0.tm_mday > 31
  212.             || after10.tm_mon < 0
  213.             || after10.tm_mon > 11
  214.             || after10.tm_mday < 1
  215.             || after10.tm_mday > 31
  216.             || ((after0.tm_mon == after10.tm_mon)?
  217.                 (after10.tm_mday != after0.tm_mday + 10)
  218.                 : ((after0.tm_mon + 1)%12 != after10.tm_mon
  219.                     || after0.tm_mday - after10.tm_mday + 10 > 31
  220.                     || after0.tm_mday - after10.tm_mday + 10 < 28));
  221.         if((verbose && i < 10) || error)
  222.             {
  223.             if(!item2) printf(item2_heading); 
  224.             printf("%4d %4d %4d -> %4d %4d %4d = JD%ld\n", 
  225.                 before0.tm_year+1900, before0.tm_mon+1, before0.tm_mday,
  226.                 after0.tm_year+1900, after0.tm_mon+1, after0.tm_mday, j0);
  227.             printf("%4d %4d %4d -> %4d %4d %4d = JD%ld\n\n", 
  228.                 before10.tm_year+1900, before10.tm_mon+1, before10.tm_mday,
  229.                 after10.tm_year+1900, after10.tm_mon+1, after10.tm_mday, j10);
  230.             if(++item2 > 15) break;
  231.             }
  232.         }
  233.  
  234. #ifdef MKTIME
  235. /*
  236.     Compare normalization (correction for day or month out of the
  237.     normal range) of juldnj with mktime().  
  238.  
  239. */
  240.     for (i = 0; i < 5000; i++)
  241.         {
  242.         before.tm_year = 1980 - 1900 + rand()%30;
  243.         before.tm_mon = -60 + rand()%120;
  244.         before.tm_mday = -200 + rand()%400;
  245.         before.tm_sec = before.tm_min = 0;
  246.         before.tm_hour = 0;
  247.         error = make_comparison(&before, &theirs, &ours);
  248.         if((verbose && i < 10) || error)
  249.             {
  250.             if(!item3) printf(item3_heading);
  251.             print_comparison(&before, &theirs, &ours, error);
  252.             if(error)
  253.                 {
  254.                 dy = before.tm_mon/12;
  255.                 if(before.tm_mon < 0) dy--;
  256.                 before.tm_mon -= dy*12;
  257.                 before.tm_year += dy;
  258.                 error = make_comparison(&before, &theirs, &ours);
  259.                 if(!error) print_comparison(&before, &theirs, &ours, error);
  260.                 else
  261.                     {
  262.                     dd = before.tm_mday>0 ? 1 : -1;
  263.                     for (i = 0; i < 250; i++)
  264.                         {
  265.                         before.tm_mday -= dd;
  266.                         error = make_comparison(&before, &theirs, &ours);
  267.                         if(!error) 
  268.                             {
  269.                             before.tm_mday += dd;
  270.                             error = make_comparison(&before, &theirs, &ours);
  271.                             print_comparison(&before, &theirs, &ours, error);
  272.                             before.tm_mday -= dd;
  273.                             break;
  274.                             }
  275.                         }
  276.                     error = make_comparison(&before, &theirs, &ours);
  277.                     print_comparison(&before, &theirs, &ours, error);
  278.                     }
  279.                 printf("\n");
  280.                 }
  281.             if(++item3 > 15) break;
  282.             }
  283.         }
  284. #endif /* MKTIME */
  285.  
  286. /*
  287.     Compare calculation of tm_wday and tm_yday with gmtime().  (All these
  288.     are after 1 Jan 1970, so necessarily A.D. and for Gregorian calendar.)
  289. */
  290.     for (i = 0; i < 5000; i++)    /* this takes ~5 sec on a 25 MHz 386 */
  291.         {
  292.         when = (((long)rand())<<16) + rand();
  293.         bdt1 = gmtime(&when);
  294.         ours = *bdt1;
  295.         bdt2 = julcd(juldn(&ours)); /* both ours and *bdt2 get wday and yday */
  296.         error =    bdt1->tm_wday != bdt2->tm_wday 
  297.             || bdt1->tm_yday != bdt2->tm_yday
  298.             || bdt1->tm_wday != ours.tm_wday 
  299.             || bdt1->tm_yday != ours.tm_yday;
  300.         if((verbose && i < 10) || error)
  301.             {
  302.             if(!item4) printf(item4_heading);
  303.             printf("%11ld -> %4d/%02d/%02d    %d    %3d       %d    %3d",
  304.                 when, 
  305.                 bdt1->tm_year+1900, bdt1->tm_mon+1, bdt1->tm_mday,
  306.                 bdt1->tm_wday, bdt1->tm_yday, 
  307.                 bdt2->tm_wday, bdt2->tm_yday);
  308.             printf("       %d    %3d %s\n",
  309.                 ours.tm_wday, ours.tm_yday,
  310.                 error?"*":" ");
  311.             if(++item4 > 15) break;
  312.             }
  313.         }
  314. }
  315.  
  316. make_comparison(before, theirs, ours)
  317.     struct tm *before, *theirs, *ours;
  318. {        *ours = *theirs = *before;
  319.         juldn(ours);
  320.         mktime(theirs);
  321.         return ours->tm_year != theirs->tm_year
  322.             || ours->tm_mon != theirs->tm_mon
  323.             || ours->tm_mday != theirs->tm_mday;
  324. }
  325.  
  326. print_comparison(before, theirs, ours, error)    
  327.     struct tm *before, *theirs, *ours; int error;
  328. {
  329.             printf("%4d %4d %4d -> %4d %4d %4d    %4d %4d %4d %s\n", 
  330.                 before->tm_year+1900, before->tm_mon+1, before->tm_mday,
  331.                 theirs->tm_year+1900, theirs->tm_mon+1, theirs->tm_mday,
  332.                 ours->tm_year+1900, ours->tm_mon+1, ours->tm_mday,
  333.                 error?"*":" ");
  334. }
  335.