aus V1.4: --------------------------------------- PART VI: RELATED COMPUTER PARAPHERNALIA --------------------------------------- VI.1. "STARDATE" PROGRAM -- CALCULATE AND DISPLAY THE CURRENT STARDATE /** * stardate.c: program to calculate the current stardate * by Andrew Main * 1995-01-21, stardate [-31]5109.44 * * This has been written to compile under traditional C. **/ /** * This program will display the current stardate to two decimal places, or * whatever is specified on the command line. The output will change every * three minutes (actually 172.8s) with the default two decimal places. **/ #include #include #include void stardate(); int main(argc, argv) int argc; char *argv[]; { char *ptr; long places; time_t tm; long issue, integer, fraction; char fractionbuffer[7]; /* Parse command-line arguments. The first argument specifies the number */ /* of decimal places to output (0-6). Default is 2. */ places = 2; if(argc > 2) { fprintf(stderr, "Too many arguments\n"); return 1; }; if(argc == 2) { if(*argv[1] == '\000') { fprintf(stderr, "Non-numeric argument\n"); return 1; }; places = strtol(argv[1], &ptr, 10); if(*ptr != '\000') { fprintf(stderr, "Non-numeric argument\n"); return 1; }; if(places<0 || places>6) { fprintf(stderr, "Argument out of range\n"); }; }; /* Get current time in UNIX format. */ if((tm=time((time_t *)0)) == (time_t)-1) { fprintf(stderr, "Error in time()\n"); return 1; }; /* Get stardate from stardate() function. */ stardate((unsigned long)tm, &issue, &integer, &fraction); /* Output stardate, with requested number of decimal places. */ if(places == 0) { printf("[%ld]%04ld\n", issue, integer); } else { sprintf(fractionbuffer, "%06ld", fraction); fractionbuffer[places] = '\000'; printf("[%ld]%04ld.%s\n", issue, integer, fractionbuffer); }; } /** * stardate(): calculates stardate * Parameters: unsigned long tm; UNIX-type time * long *issue; returns issue number * long *integer; returns integral part of stardate * long *fraction; returns fractional part of stardate * * The long type is assumed to be at least 32 bits. This makes unsigned long * suitable to hold a UNIX-type time. It is also assumed that the date is * before 2270-01-26, but this is after the end of time on UNIX systems, so * it is not a problem. * * The fraction returned is scaled so that it is in the range 000000 to * 999999 inclusive. Its units digit represents 0.000001 stardate units. * This digit theoretically changes every 0.01728 (exactly) seconds, but of * course not every value can actually be returned. This is because the time * passed is only specified to a precision of one second. The intent in * making the function provide six fractional digits is to make the returned * stardate different every second. * * The returned values are not rounded at all, but are instead deliberately * truncated. This behaviour is desired, so that the date that is returned * is known to be before or equal to the date passed; this is how times and * dates are normally treated. * * The function uses the knowledge that 00:00 UTC on 1970-01-01, the zero * point for UNIX time, is SD [-36]9350. It also `knows' that each stardate * unit covers 17280 seconds. **/ void stardate(tm, issue, integer, fraction) unsigned long tm; long *issue, *integer, *fraction; { /* It would be convenient to calculate the fractional part with */ /* *fraction = ( (tm%17280) *1000000) / 17280; */ /* but the long int type may not be long enough for this (it requires 36 */ /* bits). Cancelling the 1000000 with the 17280 gives an expression that */ /* takes only 27 bits. */ *fraction = ( (tm%17280) *3125) / 54; /* Get integer part. */ *integer = tm/17280 + 9350; /* At this stage, *integer contains the issue number in the obvious place, */ /* biased to always be non-negative. The issue number can be extracted by */ /* simply dividing *integer by 10000 and offsetting it appropriately: */ *issue = (*integer/10000) - 36; /* Remove the issue number from *integer. */ *integer %= 10000; /* Successful return. */ return; } /** * EOF: stardate.c **/ VI.2. UNIX MAN PAGE FOR "STARDATE" PROGRAM .TH STARDATE 1 "23 January 1995, SD [-31]5119.08" .SH NAME stardate - calculate and display the current stardate .SH SYNOPSIS .B stardate [ .I places ] .SH DESCRIPTION \fIstardate\fR reads the system clock, and calculates the current stardate from it. The stardate is sent to standard output. This is an alternative to date(1), performing the same type of function in the same way, but outputting in a much more compact format. .PP \fIplaces\fR specifies the number of decimal places to provide in the output, defaulting to 2. The output changes every 172.8 seconds, when outputting 2 decimal places. (Actually, because of the resolution of UNIX time, four-fifths of the changes are 173 seconds after the previous change, and the other fifth are 172 seconds after.) .PP The algorithm used is based on information in version 1 of the mini-FAQ on stardates that is regularly posted to the USENET newsgroup rec.arts.startrek.tech. The program itself appears in the FAQ from version 1.2. .PP This program will produce correct output until the end of time (2038). .SH COPYRIGHT The mini-FAQ in which this program appears is copyright by Andrew Main. The program itself, and this man page, are in the public domain. The algorithm is not subject to any patent, because the author (unlike the U.S. patent office) feels that mathematical processes should not be patentable. .SH AUTHOR Andrew Main .SH "SEE ALSO" date(1), stardates(1) VI.3. "STARDATES" PROGRAM -- CONVERT BETWEEN STARDATES AND OTHER CALENDARS /** * stardates.c: convert between calendars * by Andrew Main * 1995-02-05, stardate [-31]5184.29 * * This has been written to compile under traditional C. **/ /* Definitions to help with leap years. */ static long nrmdays[13]={31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; static long lyrdays[13]={31, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; #define jleapyear(y) ( !((y)%4L) ) #define gleapyear(y) ( !((y)%4L) && ( ((y)%100L) || !((y)%400L) ) ) /** * This program will accept input, on the command line, of a date in Julian, * Gregorian or Quadcent calendar, or a stardate; and output that date in all * four formats. * * It calls a group of eight translation routines to do this. The eight * routines are in four pairs, chosen for (relative) ease of translation: * s1toj: Stardate (issue 20-) to Julian * jtos1: Julian to Stardate (issue 20-) * jtog: Julian to Gregorian * gtoj: Gregorian to Julian * gtos2: Gregorian to Stardate (issue 21+) * s2tog: Stardate (issue 21+) to Gregorian * s2toq: Stardate (issue 21+) to Quadcent * qtos2: Quadcent to Stardate (issue 21+) * * Note that the `s2' routines will actually accept dates outside the range * of when these stardates were used. This is helpful when dealing with the * Quadcent calendar. **/ #include void s1toj(), jtos1(), jtog(), gtoj(), gtos2(), s2tog(), s2toq(), qtos2(); int main(argc, argv) int argc; char *argv[]; { enum cal_types {JULIAN='=', GREGORIAN='-', QUADCENT='*', STARDATE='@'}; char caltype, *ptr; long multiplier; long year, month, day; long hour, minute, second; long issue2, integer2, fraction2; long issue, integer, fraction; long jyear, jmonth, jday; long gyear, gmonth, gday; long qyear, qmonth, qday; long jhour, jminute, jsecond; long qhour, qminute, qsecond; /* Interpret command line arguments. */ if(argc>3 || argc<2 || (argc==3 && *argv[1]=='[')) { fprintf(stderr, "Wrong number of arguments\n"); return 1; } else if(*argv[1]=='[') { /* Try to read a stardate. The issue number is compulsory. */ argv[1]++; issue = strtol(argv[1], &ptr, 10); if(ptr==argv[1] || *ptr!=']') { fprintf(stderr, "Argument not recognised\n"); return 1; }; argv[1] = ptr+1; integer = strtol(argv[1], &ptr, 10); if(ptr==argv[1] || (*ptr!='.' && *ptr!='\000')) { fprintf(stderr, "Argument not recognised\n"); return 1; }; if(integer<0L || integer>99999L || (issue<20L && integer>9999L) || (issue==20L && integer>=5006L)) { fprintf(stderr, "Stardate out of range\n"); return 1; }; fraction = 0L; if(*ptr != '\000') { argv[1] = ptr+1; if(*argv[1]=='\000' || strlen(argv[1])>6) { fprintf(stderr, "Argument not recognised\n"); return 1; }; multiplier = 100000L; for(ptr=argv[1]; *ptr!='\000'; ptr++) { if(*ptr < '0' || *ptr > '9') { fprintf(stderr, "Argument not recognised\n"); return 1; }; fraction += multiplier * (long)(*ptr-'0'); multiplier /= 10L; }; }; caltype=STARDATE; } else { /* Try to read a more conventional date. May be Julian, Gregorian or */ /* Quadcent calendar. */ year = strtol(argv[1], &ptr, 10); caltype = *ptr; if(ptr==argv[1] || (caltype!=JULIAN && caltype!=GREGORIAN && caltype!=QUADCENT)) { fprintf(stderr, "Argument not recognised\n"); return 1; }; if(year < 1L) { fprintf(stderr, "Year out of range\n"); return 1; }; argv[1] = ptr+1; month = strtol(argv[1], &ptr, 10); if(ptr==argv[1] || *ptr!=caltype) { fprintf(stderr, "Argument not recognised\n"); return 1; }; if(month<1L || month>12L) { fprintf(stderr, "Month out of range\n"); return 1; }; argv[1] = ptr+1; day = strtol(argv[1], &ptr, 10); if(ptr==argv[1] || *ptr!='\000') { fprintf(stderr, "Argument not recognised\n"); return 1; }; if(day<1L || day>lyrdays[month] || (day==29 && month==2 && ((caltype==JULIAN && !jleapyear(year)) || (caltype==GREGORIAN && !gleapyear(year)) || caltype==QUADCENT))) { fprintf(stderr, "Day out of range\n"); return 1; }; }; /* Check for a time of day. */ hour = minute = second = 0; if(argc == 3) { hour = strtol(argv[2], &ptr, 10); if(ptr==argv[2] || *ptr!=':') { fprintf(stderr, "Argument not recognised\n"); return 1; }; if(hour<0L || hour>23L) { fprintf(stderr, "Hour out of range\n"); return 1; }; argv[2] = ptr+1; minute = strtol(argv[2], &ptr, 10); if(ptr==argv[2] || (*ptr!=':' && *ptr!='\000')) { fprintf(stderr, "Argument not recognised\n"); return 1; }; if(minute<0L || minute>59L) { fprintf(stderr, "Minute out of range\n"); return 1; }; if(*ptr != '\000') { argv[2] = ptr+1; second = strtol(argv[2], &ptr, 10); if(ptr==argv[2] || *ptr!='\000') { fprintf(stderr, "Argument not recognised\n"); return 1; }; if(second<0L || second>59L) { fprintf(stderr, "Second out of range\n"); return 1; }; }; }; /* Translate the date provided on the command line into the other */ /* calendars. */ switch(caltype) { case JULIAN: jyear=year; jmonth=month; jday=day; jhour=hour; jminute=minute; jsecond=second; jtog(jyear, jmonth, jday, &gyear, &gmonth, &gday); gtos2(gyear, gmonth, gday, jhour, jminute, jsecond, &issue2, &integer2, &fraction2); s2toq(issue2, integer2, fraction2, &qyear, &qmonth, &qday, &qhour, &qminute, &qsecond); if(gyear < 2323L) jtos1(jyear, jmonth, jday, jhour, jminute, jsecond, &issue, &integer, &fraction); else { issue = issue2; integer = integer2; fraction = fraction2; }; break; case GREGORIAN: gyear=year; gmonth=month; gday=day; jhour=hour; jminute=minute; jsecond=second; gtoj(gyear, gmonth, gday, &jyear, &jmonth, &jday); gtos2(gyear, gmonth, gday, jhour, jminute, jsecond, &issue2, &integer2, &fraction2); s2toq(issue2, integer2, fraction2, &qyear, &qmonth, &qday, &qhour, &qminute, &qsecond); if(gyear < 2323L) jtos1(jyear, jmonth, jday, jhour, jminute, jsecond, &issue, &integer, &fraction); else { issue = issue2; integer = integer2; fraction = fraction2; }; break; case QUADCENT: qyear=year; qmonth=month; qday=day; qhour=hour; qminute=minute; qsecond=second; qtos2(qyear, qmonth, qday, qhour, qminute, qsecond, &issue2, &integer2, &fraction2); s2tog(issue2, integer2, fraction2, &gyear, &gmonth, &gday, &jhour, &jminute, &jsecond); gtoj(gyear, gmonth, gday, &jyear, &jmonth, &jday); if(qyear < 2323L) jtos1(jyear, jmonth, jday, jhour, jminute, jsecond, &issue, &integer, &fraction); else { issue = issue2; integer = integer2; fraction = fraction2; }; break; case STARDATE: if(issue < 21L) { s1toj(issue, integer, fraction, &jyear, &jmonth, &jday, &jhour, &jminute, &jsecond); jtog(jyear, jmonth, jday, &gyear, &gmonth, &gday); gtos2(gyear, gmonth, gday, jhour, jminute, jsecond, &issue2, &integer2, &fraction2); s2toq(issue2, integer2, fraction2, &qyear, &qmonth, &qday, &qhour, &qminute, &qsecond); } else { s2toq(issue, integer, fraction, &qyear, &qmonth, &qday, &qhour, &qminute, &qsecond); s2tog(issue, integer, fraction, &gyear, &gmonth, &gday, &jhour, &jminute, &jsecond); gtoj(gyear, gmonth, gday, &jyear, &jmonth, &jday); }; break; }; /* Display the given date/time in all four calendars. */ printf("%ld=%02ld=%02ld %02ld:%02ld:%02ld\n", jyear, jmonth, jday, jhour, jminute, jsecond); printf("%ld-%02ld-%02ld %02ld:%02ld:%02ld\n", gyear, gmonth, gday, jhour, jminute, jsecond); printf("%ld*%02ld*%02ld %02ld:%02ld:%02ld\n", qyear, qmonth, qday, qhour, qminute, qsecond); if(issue < 21) { printf("[%ld]%04ld.%06ld\n", issue, integer, fraction); } else { printf("[%ld]%05ld.%06ld\n", issue, integer, fraction); }; return 0; } /** * s1toj(): Stardate (issue 20-) to Julian **/ void s1toj(issue, integer, fraction, jyear, jmonth, jday, jhour, jminute, jsecond) long issue, integer, fraction; long *jyear, *jmonth, *jday, *jhour, *jminute, *jsecond; { long ndays; /* Check if stardate needs to be adjusted, for changes in stardate rate. */ /* SD [19]7340 is the first change, and the second change is at SD */ /* [19]7840 (and needs to become [22]2340. */ if(issue>19L || (issue==19L && integer>=7840L)) { ndays = (issue-19L)*10000L + integer - 7840L; integer = ndays * 10L + fraction / 100000L + 2340L; issue = 22L + integer/10000L; integer %= 10000L; fraction = (fraction*10L) % 1000000L; } else if(issue>19L || (issue==19L && integer>=7340L)) { ndays = (issue-19L)*10000L + integer - 7340L; integer = ndays * 50L + fraction / 20000L + 7340L; issue = 19L + integer/10000L; integer %= 10000L; fraction = (fraction*50L) % 1000000L; }; /* Calculate number of complete days since 01=01=01. */ ndays = (issue+395L)*2000L + integer/5L - 706L; /* Calculate year (first guess), and adjust ndays accordingly. */ *jyear = ndays / 366L + ndays / 178608L; ndays -= *jyear*365L + *jyear/4L; (*jyear)++; /* Sort out date. */ *jday = ndays + 1L; *jmonth = 1; while(*jday > (jleapyear(*jyear)?lyrdays:nrmdays)[*jmonth]) { (*jday) -= (jleapyear(*jyear)?lyrdays:nrmdays)[*jmonth]; (*jmonth)++; if(*jmonth == 13L) {*jmonth=1L; (*jyear)++;}; }; /* Sort out time. */ *jsecond = (integer%5L)*17280L + fraction*54L/3125L; *jminute = (*jsecond/60L) % 60L; *jhour = *jsecond / 3600L; (*jsecond) %= 60L; /* Successful return. */ return; } /** * jtos1(): Julian to Stardate (issue 20-) **/ void jtos1(jyear, jmonth, jday, jhour, jminute, jsecond, issue, integer, fraction) long jyear, jmonth, jday, jhour, jminute, jsecond; long *issue, *integer, *fraction; { long ndays, month; /* Calculate number of complete days since 00=01=01. */ ndays = (jyear-1L)*365L + ((jyear-1L)/4L); for(month=1L; month22L || (*issue==22L && *integer>=2340L)) { *fraction = ((*integer)%10L)*100000L + *fraction/10L; ndays = (*issue-22L)*10000L + *integer - 2340L; *integer = ndays / 10L + 7840L; *issue = 19L + *integer/10000L; (*integer) %= 10000L; } else if(*issue>19L || (*issue==19L && *integer>=7340L)) { *fraction = ((*integer+10L)%50L)*20000L + *fraction/50L; ndays = (*issue-19L)*10000L + *integer - 7340L; *integer = ndays / 50L + 7340L; *issue = 19L; }; /* Successful return. */ return; } /** * jtog(): Julian to Gregorian **/ void jtog(jyear, jmonth, jday, gyear, gmonth, gday) long jyear, jmonth, jday; long *gyear, *gmonth, *gday; { /* Basic transformation, yielding a non-normalised date. */ *gyear = jyear; *gmonth = jmonth; *gday = jday + jyear/100L - jyear/400L - (!(jyear%100L) && jyear%400L && jmonth<3L) - 2; /* Normalise for day<1. */ while(*gday < 1L) { (*gmonth)--; (*gday) += (gleapyear(*gyear)?lyrdays:nrmdays)[*gmonth]; if(*gmonth == 0L) {*gmonth=12L; (*gyear)--;}; }; /* Normalise for day too large. */ while(*gday > (gleapyear(*gyear)?lyrdays:nrmdays)[*gmonth]) { (*gday) -= (gleapyear(*gyear)?lyrdays:nrmdays)[*gmonth]; (*gmonth)++; if(*gmonth == 13L) {*gmonth=1L; (*gyear)++;}; }; /* Successful return. */ return; } /** * gtoj(): Gregorian to Julian **/ void gtoj(gyear, gmonth, gday, jyear, jmonth, jday) long gyear, gmonth, gday; long *jyear, *jmonth, *jday; { /* Basic transformation, yielding a non-normalised date. */ *jyear = gyear; *jmonth = gmonth; *jday = gday - gyear/100L + gyear/400L + (!(gyear%100L) && gyear%400L && gmonth<3L) + 2; /* Normalise for day<1. */ while(*jday < 1L) { (*jmonth)--; (*jday) += (jleapyear(*jyear)?lyrdays:nrmdays)[*jmonth]; if(*jmonth == 0L) {*jmonth=12L; (*jyear)--;}; }; /* Normalise for day too large. */ while(*jday > (jleapyear(*jyear)?lyrdays:nrmdays)[*jmonth]) { (*jday) -= (jleapyear(*jyear)?lyrdays:nrmdays)[*jmonth]; (*jmonth)++; if(*jmonth == 13L) {*jmonth=1L; (*jyear)++;}; }; /* Successful return. */ return; } /** * gtos2(): Gregorian to Stardate (issue 21+) **/ void gtos2(gyear, gmonth, gday, ghour, gminute, gsecond, issue, integer, fraction) long gyear, gmonth, gday, ghour, gminute, gsecond; long *issue, *integer, *fraction; { long month; double ndays, nyears; /* Get number of complete days since last xx23-01-01 00:00:00. */ ndays = (gyear%400L)*365L + ((gyear%400L)+3L)/4L - ((gyear%400L)-1L)/100L; ndays = ((long)(ndays+77L*365L+19L)) % 146097L; for(month=1L; month (gleapyear(*gyear)?lyrdays:nrmdays)[*gmonth]) { (*gday) -= (gleapyear(*gyear)?lyrdays:nrmdays)[*gmonth]; (*gmonth)++; if(*gmonth == 13L) {*gmonth=1L; (*gyear)++;}; }; /* Sort out time. */ *ghour = ((long)(ndays*24.0)) % 24L; *gminute = ((long)(ndays*1440.0)) % 60L; *gsecond = ((long)(ndays*86400.0)) % 60L; /* Successful return. */ return; } /** * s2toq(): Stardate (issue 21+) to Quadcent **/ void s2toq(issue, integer, fraction, qyear, qmonth, qday, qhour, qminute, qsecond) long issue, integer, fraction; long *qyear, *qmonth, *qday, *qhour, *qminute, *qsecond; { double ndays; /* Sort out date. */ *qyear = issue*100L + 223L + integer/1000L; ndays = 0.365 * ((integer%1000) + (fraction/1000000.0)); *qday = ((long)ndays) + 1L; for(*qmonth=1; *qday>nrmdays[*qmonth]; (*qmonth)++) (*qday) -= nrmdays[*qmonth]; /* Sort out time. */ *qhour = ((long)(ndays*24.0)) % 24L; *qminute = ((long)(ndays*1440.0)) % 60L; *qsecond = ((long)(ndays*86400.0)) % 60L; /* Successful return. */ return; } /** * qtos2(): Quadcent to Stardate (issue 21+) **/ void qtos2(qyear, qmonth, qday, qhour, qminute, qsecond, issue, integer, fraction) long qyear, qmonth, qday, qhour, qminute, qsecond; long *issue, *integer, *fraction; { long month; double ndays; /* Get number of days into year. */ ndays = 0.0; for(month=1L; month .SH "SEE ALSO" stardate(1) .SH BUGS This program will not handle any date BCE. VI.5. ADDDING A STARDATE HEADER TO EMAIL MESSAGES If you use the Elm electronic mail reader to send email, it is possible to have an extra header line added to all your email messages, giving the stardate at which the message was sent. To do this, you must make sure the "stardate" program (from section VI.1) is in your execution path. Then, add the following line to your ~/.elm/elmheaders file, or, if the file doesn't exist, create it containing only this line: X-Stardate: `stardate` (Note the backquotes.) An extra line will then be added to the header of all email messages sent using Elm.