// // alarm.exe - Countdown timer version of sleep. // // - Reaches alarm time with millisecond precision. // // 24-Feb-2007 Tom Van Baak (tvb) www.LeapSecond.com/tools // #include #include #include #include #include char Usage[] = "Usage: alarm [seconds] [align/offset]\n" "\n" " The ALARM command is just a countdown timer version of the SLEEP\n" " command. It displays remaining time each second and exits at zero.\n" "\n" " Try: ALARM 60 or ALARM 3661 or ALARM 86400\n" "\n" " Like SLEEP, times are given in seconds. Unlike SLEEP, times may\n" " also be given as s seconds or as m minutes or as h hours.\n" "\n" " Try: ALARM 5m or ALARM 1h to sleep for five minutes or one hour.\n" "\n" " The second parameter is optional and if given it rounds the alarm\n" " time to an aligned value. So ALARM 5m 0 would set the alarm for\n" " the nearest future time that is a 5 minute multiple. Note this\n" " will end up being a sleep duration that could be as short as a\n" " second to as long as a full 5 minutes.\n" "\n" " Try: ALARM 1h 0 which sleeps until the top of the hour.\n" "\n" " Finally, if the second parameter is present and non-zero, it is\n" " added as an offset to the aligned time.\n" "\n" " Try: ALARM 1h 10m sleeps until 10 minutes past the next hour.\n" " Try: ALARM 2h -15m means 15 minutes before the next even hour.\n" "\n" " ALARM is more desirable than SLEEP in some periodic scripts.\n" ; // Define millisecond-resolution time functions. typedef double mstime_t; char *dhms_str (mstime_t mstime); mstime_t get_mstime (void); char *mstime_str (mstime_t mstime); double parse_time_units (char *s); #define SMALL_TIME 3 int main (int argc, char *argv[]) { mstime_t now, when, delay, resid; long ms; now = get_mstime(); fprintf(stderr, "Time now: %s\n", mstime_str(now)); // First argument (required): delay value. if (argc > 1) { delay = parse_time_units(argv[1]); if (delay <= 0) { exit(0); } when = now + delay; } else { fprintf(stderr, Usage); exit(1); } // Second argument (optional): adjust alarm to period boundary (plus offset). if (argc > 2) { when -= fmod(when, delay); when += parse_time_units(argv[2]); if (when < now) { when += delay; } } // Sleep one second at a time (use ever smaller fractions near the end). when += 5e-4; fprintf(stderr, "Alarm at: %s\n", mstime_str(when)); ms = 1000; while ((resid = when - now) > 0.0) { fprintf(stderr, "%s\r", dhms_str(resid)); if (resid <= SMALL_TIME) { ms = (long) (resid * 1000.0); ms = ms / SMALL_TIME; ms = max(ms, 10); } _sleep(ms); now = get_mstime(); } fprintf(stderr, "\rTime now: %s\n", mstime_str(now)); return 0; } // Convert number as time with optional second, minute, or hour units. mstime_t parse_time_units (char *s) { mstime_t value; char units; units = 's'; sscanf(s, "%lf%c", &value, &units); switch (units) { case 'd' : // days value *= 24; case 'h' : // hours value *= 60; case 'm' : // minutes value *= 60; case 's' : // seconds break; default : fprintf(stderr, "Invalid number: %s\n", s); exit(1); } return value; } // Convert seconds to pretty day-hour-minute-second-millisecond string. char *dhms_str (mstime_t mstime) { static char buf[20]; // e.g., 7d 23h 59m 59s time_t seconds; int d, h, m, s, ms; static int prev_len = 0; int i, n; // Convert to days, hours, minutes, seconds, milliseconds. seconds = (time_t) mstime; ms = (int) ((mstime - seconds) * 1000); d = seconds / 86400; seconds %= 86400; h = seconds / 3600; seconds %= 3600; m = seconds / 60; seconds %= 60; s = seconds; // Convert to string, suppressing leading zeros. if (d > 0) { sprintf(buf, "%dd %dh %dm %ds", d, h, m, s); } else if (h > 0) { sprintf(buf, "%dh %dm %ds", h, m, s); } else if (m > 0) { sprintf(buf, "%dm %ds", m, s); } else if (s > SMALL_TIME) { sprintf(buf, "%ds", s); } else { sprintf(buf, "%.3lfs", s + ms / 1000.0); } // If necessary pad string to erase previous display width. n = strlen(buf); for (i = n; i < prev_len; i += 1) { strcat(buf, " "); } prev_len = n; return buf; // STATIC } // Display date/time with millisecond resolution. char *mstime_str (mstime_t mstime) { static char buf[100]; long mslong = (long) mstime; int ms = (int) ((mstime - mslong) * 1000.0); struct tm *tm; if (NULL == (tm = localtime(&mslong))) { static struct tm tm0 = { 0, 0, 0, 0, -1, -1900 }; tm = &tm0; } sprintf(buf, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d.%.3d", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, ms); return buf; // STATIC } // Return Modified Julian Day (MJD) given calendar year, month (1-12), and day (1-31). // Valid for Gregorian dates from 17-Nov-1858. Adapted from sci.astro FAQ. long date_to_mjd (long year, long month, long day) { return 367 * year - 7 * (year + (month + 9) / 12) / 4 - 3 * ((year + (month - 9) / 7) / 100 + 1) / 4 + 275 * month / 9 + day + 1721028 - 2400000; } #define WIN32_LEAN_AND_MEAN #include // Get UNIX-like time (epoch is 1970-01-01, type is double, resolution is 0.001 seconds). mstime_t get_mstime (void) { long days, stod; SYSTEMTIME st; GetSystemTime(&st); days = date_to_mjd(st.wYear, st.wMonth, st.wDay) - date_to_mjd(1970, 1, 1); stod = (((st.wHour * 60) + st.wMinute) * 60) + st.wSecond; return (days * 86400.0) + stod + st.wMilliseconds / 1000.0; }