/* * jdate jdate [tz] (outputs yyyy mm dd hh mm ss) * jdate yyyy mm dd hh mm ss [tz] (outputs jdate, tz is [+-][0-9]{4}) */ #define JOFFSET 1721060 /* jdate of 0000-01-01 12:00:00 */ #include #include #include #include #include extern const char *__progname; #define SECPERDAY (24*60*60) typedef struct date DATE; struct date { int y; /* CE year */ int m; /* month, 1-12 */ int d; /* day within month, 1-31 */ int j; /* jdate (integer part, JOFFSET removed) */ unsigned int v_ymd; /* y/m/d are valid */ unsigned int v_j; /* j is valid */ } ; /* Return number of days in a year, given year number */ inline static unsigned int ydays(unsigned int) __attribute__ ((const)); inline static unsigned int ydays(unsigned int y) { return( (y % 4) ? 365 : (y % 100) ? 366 : (y % 400) ? 365 : 366 ); } /* Return number of days in a month of a year, assuming month in range */ inline static unsigned int mdays(unsigned int, unsigned int) __attribute__ ((const)); inline static unsigned int mdays(unsigned int y, unsigned int m) { return( (m == 2) ? ( (y % 4) ? 28 : (y % 100) ? 29 : (y % 400) ? 28 : 29 ) /*JanFebMarAprMayJunJulAugSepOctNovDec*/ : "\0\37\00\37\36\37\36\37\37\36\37\36\37"[m] ); } /* Compute d->y, d->m, d->d. d->j must be valid. */ static void convert_j_ymd(DATE *d) { int n; int j; if (! d->v_j) abort(); /* First cut, compute the correct 400-year cycle */ j = d->j; d->y = 400 * (j / 146097); /* 146097d = 400y */ j %= 146097; /* Get to within "a few" years of correct, making sure we don't overshoot. This is a bit icky. We assume 365-day years, but that may overshoot by as much as about 100 days (one day per leap year, but we know <400y at this point); we subtract off 500 days (a nice round number more than the greatest possible overshoot) and then add them back at the end. We do this only when n will be at least 1 within the if, because only then is it correct to add back 499 days after taking off 500. This apparent peculiarity occurs because the corrections for leap years and century non-leap years does not take into account the fact that the first year of the 400-year cycle is a leap year in spite of being a century year. The difference between 500 and 499 corrects for this day. This is why n must be at least 1 for this computation to be correct: it is correct only when j is high enough that the leap-year day in that first year of the cycle is actually included. */ if (j > 1100) { j -= 500; n = j / 365; j %= 365; d->y += n; j += 499 + ((n + 99) / 100) - ((n + 3) / 4); } /* Now, j is small enough we can just loop through the remaining years, since there will be only "a few" of them (three or four, max). */ while (1) { n = ydays(d->y); if (j < n) break; d->y ++; j -= n; } /* Now step past whole months. Abort if we fall off the end of the year, because that "can't happen" (it would mean mdays() thinks some year is shorter than ydays() does). */ d->m = 1; while (1) { n = mdays(d->y,d->m); if (j < n) break; d->m ++; j -= n; if (d->m > 12) abort(); } /* Now, just set the day number within the month. */ d->d = j + 1; /* y, m, d are now valid! */ d->v_ymd = 1; } /* Compute d->j. d->y, d->m, d->d must be valid. */ static void convert_ymd_j(DATE *d) { int j; int y; int m; if (! d->v_ymd) abort(); /* Compute figure for years. First, 4-century cycles. */ y = d->y; j = 146097 * (y / 400); y %= 400; /* Now, if there's at least one more year, correct for the first year of the cycle being a leap year even though it's a century year. */ if (y > 0) j ++; /* And count off centuries. */ j += 36524 * (y / 100); y %= 100; /* Now, if there's at least one more year, correct for the first year of the century not being a leap year even though it's divisible by 4. */ if (y > 0) j --; /* And count off 4-year leap-year cycles. */ j += 1461 * (y / 4); y %= 4; /* Now, if there's at least one more year, add a day because the first year of the 4-year cycle has 366 days instead of 365. */ if (y > 0) j ++; /* And count off remaining years. */ j += 365 * y; /* Now, count off whole months, if any. */ y = d->y; for (m=d->m-1;m>0;m--) j += mdays(y,m); /* And add in the number of days into the current month. */ d->j = j + d->d - 1; /* j is now valid! */ d->v_j = 1; } static int parse_tz_offset(const char *arg) { char sign; int hh; int mm; int s; sscanf(arg,"%c%2d%2d",&sign,&hh,&mm); s = (hh * 60) + mm; if (sign == '-') s = - s; return(s); } static const char *tzstr(int tz) { static char buf[6]; char *bp; bp = &buf[0]; if (tz < 0) { *bp++ = '-'; tz = - tz; } else { *bp++ = '+'; } sprintf(bp,"%02d",tz/60); sprintf(bp+2,"%02d",tz%60); return(&buf[0]); } static void j_to_ymdhms(char **av) { int subday; char *dot; DATE d; int tz; dot = index(av[1],'.'); if (dot == 0) { d.j = atoi(av[1]); subday = 0; } else { *dot = '\0'; d.j = atoi(av[1]); *dot = '.'; subday = floor((SECPERDAY*atof(dot))+.5); } subday += 12 * 60 * 60; tz = 0; if (av[2]) { tz = parse_tz_offset(av[2]); subday += tz * 60; } while (subday < 0) { subday += SECPERDAY; d.j --; } while (subday >= SECPERDAY) { subday -= SECPERDAY; d.j ++; } if (d.j < JOFFSET) { fprintf(stderr,"%s: jdate out of range\n",__progname); exit(1); } d.j -= JOFFSET; d.v_j = 1; d.v_ymd = 0; convert_j_ymd(&d); printf("%4d %02d %02d %02d %02d %02d %s\n", d.y,d.m,d.d,subday/3600,(subday/60)%60,subday%60,tzstr(tz)); } static void ymdhms_to_j(char **av) { int subday; DATE d; char fpart[32]; int tz; d.y = atoi(av[1]); d.m = atoi(av[2]); d.d = atoi(av[3]); d.v_ymd = 1; d.v_j = 0; convert_ymd_j(&d); subday = (atoi(av[4]) * 3600) + (atoi(av[5]) * 60) + atoi(av[6]); subday -= 12 * 60 * 60; tz = 0; if (av[7]) { tz = parse_tz_offset(av[7]); subday -= tz * 60; } while (subday < 0) { subday += SECPERDAY; d.j --; } while (subday >= SECPERDAY) { subday -= SECPERDAY; d.j ++; } sprintf(&fpart[0],"%.8f",subday/(double)SECPERDAY); printf("%d%s\n",d.j+JOFFSET,&fpart[1]); } int main(int, char **); int main(int ac, char **av) { if ((ac == 2) || (ac == 3)) { j_to_ymdhms(av); } else if ((ac == 7) || (ac == 8)) { ymdhms_to_j(av); } exit(0); }