/************************************************************************ * caldate - perform date calculations and output results in * user-defined formats. * * Douglas Neuhauser * Seismological Laboratory * University of California, Berkeley * doug@seismo.berkeley.edu * ************************************************************************/ /* * Copyright (c) 1996-2013 The Regents of the University of California. * All Rights Reserved. * * Permission to use, copy, modify, and distribute this software and its * documentation for educational, research and non-profit purposes, * without fee, and without a written agreement is hereby granted, * provided that the above copyright notice, this paragraph and the * following three paragraphs appear in all copies. * * Permission to incorporate this software into commercial products may * be obtained from the Office of Technology Licensing, 2150 Shattuck * Avenue, Suite 510, Berkeley, CA 94704. * * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, * INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND * ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF * CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, * UPDATES, ENHANCEMENTS, OR MODIFICATIONS. */ #ifndef lint static const char sccsid[] = "$Id: caldate.c,v 1.9 2013/09/17 18:29:32 doug Exp $ "; #endif #include #include "version.h" char *syntax[] = { "%s version " VERSION, "%s [-f output_format] [-D j | m | IC | IO] [-h] date_time ", " [+-modifier_1] ... [+-modifier_n]", " or %s [-f output_format] [-h] -i | -d date_time_1 date_time_2", " or %s [-h] -s date_time_1 date_time_2", " where:", " -f output_format", " specifies the output format. It may contain the", " special format specifiers:", " %%Y - year (4 digits)", " %%y - year (2 digits)", " %%m - month (2 digits)", " %%d - day_of_month (2 digits)", " %%j - day_of_year (3 digits)", " %%H - hour (2 digits)", " %%M - minute (2 digits)", " %%S - second (2 digits)", " %%F - fractional_second (4 digits)", " %%U - microseconds (6 digits)", " %%n - nominal epoch seconds (second precison).", " %%N - nominal epoch seconds (microsecond precison).", " %%t - true epoch seconds (second precison).", " %%T - true epoch seconds (microsecond precision).", " -D j | m +| IC | IO", " specifies a default output format for date and time:", " j = julian (day of year), eg yyyy/mm/dd,hh:mm:ss.ffff", " m = month, eg year/month/day,hh:mm:ss.ffff", " IC = ISO_CALENDAR, eg yyyy-mm-ddThh:mm:ss.ffffff", " IO - ISO_ORDINAL, eg yyyy-dddThh:mm:ss.ffffff", " The -f option overrides this option.", " -i | -d | -s date_time_1 date_time_2", " compute the interval (|t1-t2|), difference (t1-t2)", " or difference in seconds and fractions (t1-t2)", " between the 2 following date_times.", " date_time date_time entered in one of the following formats:", " l, local, c, current - local time", " u, utc, g, gmt - utc time", " yyyy/mm/dd/hh:mm:ss.ffff", " yyyy/mm/dd.hh:mm:ss.ffff", " yyyy/mm/dd,hh:mm:ss.ffff", " yyyy.ddd.hh:mm:ss.ffff", " yyyy.ddd,hh:mm:ss.ffff", " yyyy,ddd,hh:mm:ss.ffff", " yyyy-mm-ddThh:mm:ss.ffffff (ISO Calendar format)", " yyyy-dddThh:mm:ss.ffffff (ISO Ordinal format)", " nominal_epoch_timeN", " true_epoch_timeT", " +-modifier date and time modifiers. They contain:", " sign: + or -", " value: integer value.", " unit: units may be:", " y - year", " m - month", " d - day", " H - hour", " M - minute", " S - second", " F - fractional second.", " -h Help - prints syntax message.", NULL }; #include #include #include #include #include #include "qlib2.h" int herrno; char *cmdname; FILE *info; EXT_TIME ext_time; char default_m_format[] = "%Y/%m/%d,%H:%M:%S.%F"; char default_j_format[] = "%Y.%j,%H:%M:%S.%F"; char default_iso_c_format[] = "%Y-%m-%dT%H:%M:%S.%U"; char default_iso_o_format[] = "%Y-%jT%H:%M:%S.%U"; int parse_cmdline_date (char *str, INT_TIME *itp, struct timeval now); EXT_TIME interval_between (INT_TIME iend, INT_TIME istart); extern int DPM[]; #define LDPM(y,m) (DPM[m] + (IS_LEAP(y) && m == 2)) /************************************************************************ * main ************************************************************************/ int main (int argc, char **argv) { INT_TIME in_time, out_time, sout_time, t1, t2; char outstr[80]; char *format = NULL; char *ip, *op; int sign; int compute_interval = 0; int compute_difference = 0; int compute_seconds = 0; int date_fmt = JULIAN_FMT; double ddiff; /* Variables needed for getopt. */ extern char *optarg; extern int optind, opterr; int c; cmdname = tail(*argv); info = stderr; /* Parse command line options. */ while ( (c = getopt(argc,argv,"hidsf:D:")) != -1) switch (c) { case '?': case 'h': print_syntax(cmdname, syntax, info); exit(0); break; case 'f': format = optarg; break; case 'i': compute_interval = 1; break; case 'd': compute_difference = 1; break; case 's': compute_seconds = 1; break; case 'D': /* Date display format. */ if (strcmp (optarg, "j") == 0) date_fmt = JULIAN_FMT; else if (strcmp (optarg, "j,") == 0) date_fmt = JULIAN_FMT_1; else if (strcmp (optarg, "m") == 0) date_fmt = MONTH_FMT; else if (strcmp (optarg, "m,") == 0) date_fmt = MONTH_FMT_1; else if (strcmp (optarg, "IC") == 0) date_fmt = ISO_CALENDAR; else if (strcmp (optarg, "IO") == 0) date_fmt = ISO_ORDINAL; else { fprintf (info, "invalid format for date display: %s\n", optarg); exit(1); } break; default: fprintf (info, "should never get here\n"); exit(1); } /* Skip over all options and their arguments. */ argv = &(argv[optind]); argc -= optind; if (format == NULL) switch (date_fmt) { case JULIAN_FMT: case JULIAN_FMT_1: format = default_j_format; break; case MONTH_FMT: case MONTH_FMT_1: format = default_m_format; break; case ISO_CALENDAR: format = default_iso_c_format; break; case ISO_ORDINAL: format = default_iso_o_format; break; default: fprintf (info, "Invalid default format\n"); exit(1); } if ((compute_interval || compute_difference || compute_seconds) && argc != 2) { fprintf (info, "improper number of arguments for interval\n"); exit(1); } else if (argc < 1) { fprintf (info, "missing date\n"); exit(1); } if (compute_interval + compute_difference + compute_seconds > 1) { fprintf (info, "invalid arguments: only specify one of -i, -d, and -s\n"); exit(1); } sign = 1; struct timeval now; if (gettimeofday(&now, (void*)NULL) != 0) { fprintf (info, "Error getting current time.\n"); exit(1); } if (compute_interval || compute_difference || compute_seconds) { if (! parse_cmdline_date (*argv,&t1,now)) { fprintf (info, "invalid date: %s\n", *argv); exit(1); } ++argv; --argc; if (! parse_cmdline_date (*argv,&t2,now)) { fprintf (info, "invalid date: %s\n", *argv); exit(1); } ++argv; --argc; ddiff = tdiff (t1, t2); if (ddiff >= 0.) { sign = 1; ext_time = interval_between (t1, t2); } else { ddiff = -ddiff; sign = -1; ext_time = interval_between (t2, t1); } if (compute_interval) sign = 1; } else { if (! parse_cmdline_date (*argv, &in_time, now)) { fprintf (info, "invalid date: %s\n", *argv); exit(1); } ++argv; --argc; out_time = in_time; /* Look for time modifiers. */ while (--argc >= 0) { if (!valid_span(*argv)) { fprintf (info, "invalid time offset: %s\n", *argv); exit(1); } out_time = end_of_span(out_time, *argv); ++argv; } ext_time = int_to_ext (out_time); } /* Now output the date in the appropriate format. */ out_time = ext_to_int(ext_time); sout_time = add_dtime(out_time, -1*ext_time.usec); op = outstr; ip = format; if (compute_seconds) { double seconds = ddiff/USECS_PER_SEC; double usecs = ddiff - ((long long)seconds)*USECS_PER_SEC; sprintf (op, "%.0f.%06d", sign*seconds, (int)usecs); printf ("%s\n", op); return (0); } if (sign < 0) { sprintf (op, "-"); op += 1; } while (*ip != '\0') { switch (*ip) { case ('%'): switch (*++ip) { case ('Y'): sprintf (op, "%04d", ext_time.year); op += 4; break; case ('y'): sprintf (op, "%02d", ext_time.year % 100); op += 2; break; case ('m'): sprintf (op, "%02d", ext_time.month); op += 2; break; case ('d'): sprintf (op, "%02d", ext_time.day); op += 2; break; case ('j'): sprintf (op, "%03d", ext_time.doy); op += 3; break; case ('H'): sprintf (op, "%02d", ext_time.hour); op += 2; break; case ('M'): sprintf (op, "%02d", ext_time.minute); op += 2; break; case ('S'): sprintf (op, "%02d", ext_time.second); op += 2; break; case ('F'): sprintf (op, "%04d", ext_time.usec / USECS_PER_TICK); op += 4; break; case ('U'): sprintf (op, "%06d", ext_time.usec); op += 6; break; case ('n'): /* Nominal epoch time - second precision. */ /* NOTE: computation performed on time with 0 usecs. */ sprintf (op, "%.0lf", int_to_nepoch(sout_time)); op += strlen(op); break; case ('t'): /* True epoch time - second precision. */ /* NOTE: computation performed on time with 0 usecs. */ sprintf (op, "%.0lf", int_to_tepoch(sout_time)); op += strlen(op); break; case ('N'): /* Nominal epoch time. */ sprintf (op, "%.6lf", int_to_nepoch(out_time)); op += strlen(op); break; case ('T'): /* True epoch time. */ sprintf (op, "%.6lf", int_to_tepoch(out_time)); op += strlen(op); break; default: fprintf (info, "invalid format\n"); exit(1); break; } ++ip; break; default: *op++ = *ip++; break; } } *op = '\0'; printf ("%s\n", outstr); return(0); } /************************************************************************ * parse_cmdline_date: * Parse the date specified on the command line. ************************************************************************/ int parse_cmdline_date (char *str, INT_TIME *itp, struct timeval now) { INT_TIME *tp; struct tm *p_tm; time_t clock; /* Get current time once, and use it for either local or GMT time. */ if ((strcmp(str,"l") == 0) || (strcmp(str,"local") == 0) || (strcmp(str,"c") == 0) || (strcmp(str,"current") == 0)) { clock = (time_t)now.tv_sec; p_tm = localtime(&clock); *itp = int_time_from_time_tm(p_tm); itp->usec = now.tv_usec; } else if ((strcmp(str,"u") == 0) || (strcmp(str,"utc") == 0) || (strcmp(str,"g") == 0) || (strcmp(str,"gmt") == 0)) { clock = (time_t)now.tv_sec; p_tm = gmtime(&clock); *itp = int_time_from_time_tm(p_tm); itp->usec = now.tv_usec; } else if ((tp = parse_date (str)) == NULL) return(0); else *itp = *tp; return(1); } /************************************************************************ * interval_between: * Compute the time interval between start and end times. * We PROMISE that iend >= istart. ************************************************************************/ EXT_TIME interval_between (INT_TIME iend, INT_TIME istart) { EXT_TIME end, start; EXT_TIME ev; int year, month, days; ev.year = ev.doy = ev.month = ev.day = 0; ev.hour = ev.minute = ev.second = ev.usec = 0; start = int_to_ext(istart); end = int_to_ext(iend); do { ++ev.year; } while (tdiff (iend, add_interval(istart,ev)) >= 0); --ev.year; do { ++ev.doy; } while (tdiff (iend, add_interval(istart,ev)) >= 0); --ev.doy; do { ++ev.hour; } while (tdiff (iend, add_interval(istart,ev)) >= 0); --ev.hour; do { ++ev.minute; } while (tdiff (iend, add_interval(istart,ev)) >= 0); --ev.minute; do { ++ev.second; } while (tdiff (iend, add_interval(istart,ev)) >= 0); --ev.second; ev.usec = tdiff (iend, add_interval(istart,ev)); if (ev.usec < 0) { fprintf (info, "error - negative usecs during interval calculation\n"); exit(1); } /* Now fill in the month and day_of_month values in case the user */ /* wants them. */ year = start.year + ev.year; month = start.month; days = ev.doy; while (days >= LDPM(year,month)) { days -= LDPM(year,month); ++ev.month; if (month == 12) { month = 1; ++year; } else ++month; } ev.day = days; return (ev); }