/* rickdate.c - small RickDate utility */ /* PUBLIC DOMAIN - NO COPYRIGHT CLAIMED - Jon Mayo - December 25, 2006 */ #include #include #include #include #include static const char base36[36] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; /* displays a usage message */ static void usage(const char *argv0) { printf( "encode : %s [-345u] [year] [month] [day]\n" "encode : %s -s [-4u] [secondsUTC]\n" "decode : %s -d [-s] [-f formatstr] [rickdate]\n" "help : %s -h\n" " -3 three character RickDate (default)\n" " -4 four character RickDate\n" " -5 five character RickDate\n" " -s use seconds (UTC only)\n" " -u use UTC instead of local time\n" " -h help\n" " -f output using format string (see also: man strftime)\n" "\n\nFurther information at http://www.yak.net/kablooey/RickDate.html\n" , argv0, argv0, argv0, argv0); } /* encodes a struct tm as a rickdate. output to string in out must be long * enough to hold max+1 characters. if out is NULL then returns a pointer to a * static string, else returns out. */ static const char *encode_rickdate(char *out, struct tm *tm, int max) { static char buf[6]; char *dest; dest=out?out:buf; if(max==0) max=5; if(max>=5) *(dest++)=base36[(tm->tm_year/36/36) % 36]; if(max>=4) *(dest++)=base36[(tm->tm_year/36) % 36]; if(max>=3) *(dest++)=base36[tm->tm_year % 36]; if(max>=2) *(dest++)=base36[(tm->tm_mon+1) % 36]; if(max>=1) *(dest++)=base36[(tm->tm_mday) % 36]; *dest=0; return out?out:buf; } /* returns the current year in the current locale */ static unsigned current_year(void) { time_t t; t=time(&t); return localtime(&t)->tm_year+1900; } /* decodes a RickDate into a struct tm. handles 5, 4 and 3 character RickDates. * If tm is NULL then a static struct tm is used and returned on success, else * tm is returned. on failured returns NULL. */ static struct tm *decode_rickdate(struct tm *tm, const char *rickdate) { static struct tm tm_static; struct tm *dest; unsigned long val; char *endptr; char len; int curr_year, tmp; dest=tm?tm:&tm_static; memset(dest, 0, sizeof *dest); len=strlen(rickdate); val=strtoul(rickdate, &endptr, 36); if(!endptr || *endptr) { fprintf(stderr, "Parse error on '%s' @ %s:%d!\n", rickdate, __FILE__, __LINE__); return 0; /* parse error */ } tm->tm_mday=val%36; val/=36; tm->tm_mon=(val%36)-1; val/=36; curr_year=current_year(); if(len==5) { tmp=curr_year-curr_year%(36*36*36); } else if(len==4) { tmp=curr_year-curr_year%(36*36); } else if(len==3) { tmp=curr_year-curr_year%36; } else { fprintf(stderr, "Parse error on '%s' @ %s:%d!\n", rickdate, __FILE__, __LINE__); return 0; /* parse error */ } tm->tm_year=(val+tmp)-1900; if(tm->tm_mday < 1 || tm->tm_mon > 31) { fprintf(stderr, "Parse error on '%s' @ %s:%d!\n", rickdate, __FILE__, __LINE__); return 0; /* parse error */ } if(tm->tm_mon < 0 || tm->tm_mon > 11) { fprintf(stderr, "Parse error on '%s' @ %s:%d!\n", rickdate, __FILE__, __LINE__); return 0; /* parse error */ } /* TODO: a sanity check for year would be nice */ return dest; } int main(int argc, char **argv) { int fl_decode = 0, fl_seconds = 0, fl_width = 3, fl_useutc = 0; int ch; const char *fl_format = "%Y-%m-%d"; while ((ch = getopt(argc, argv, "f:hsu345d")) != -1) { switch(ch) { case 'd': fl_decode = 1; break; case 's': fl_seconds = 1; break; case '3': fl_width=3; break; case '4': fl_width=4; break; case '5': fl_width=5; break; case 'u': fl_useutc=1; break; case 'f': fl_format=optarg; break; case 'h': default: usage(argv[0]); return EXIT_FAILURE; } } if(fl_decode) { int i; struct tm tm; if(optind==argc) { usage(argv[0]); return EXIT_FAILURE; } for(i=optind;i