#!/usr/bin/env perl # countdown --- display a countdown timer to a specified time # Author: Noah Friedman # Created: 2006-05-20 # Public domain # $Id: countdown,v 1.4 2009/02/12 21:44:37 friedman Exp $ # Commentary: # Code: $^W = 1; # enable warnings use strict; use POSIX qw(strftime); use Time::Local; use Getopt::Long; (my $progname = $0) =~ s=.*/==; my $verbose = 0; sub compute_deadline { local $_ = join (" ", @_); if (/(\d+(?:[---.\/]\d+){2}(?:\s+|:)\d+:\d+(?::\d+|))/) # 2006-05-26 21:55:00 { my @ts = split (/[---.\/: ]+/, $1); $ts[0] -= 1900 if ($ts[0] >= 1900); # denormalize year $ts[1] -= 1; # denormalize month $ts[5] = 0 unless defined $ts[5]; # specify seconds # timelocal ($sec, $min, $hours, $mday, $mon, $year); # We use timelocal, not timegm, since the stamp is in local time. return timelocal (reverse @ts); } elsif (/^(\d+:\d+(?::\d+|))/) # 21:55 or 21:55:00 { my @tm = split (/[.:]+/, $1); my @ts = localtime (time); $ts[0] = $tm[2] || 0; $ts[1] = $tm[1]; $ts[2] = $tm[0]; return timelocal (@ts); } else { # We use an eval and test so that the script doesn't die when the # module can't be loaded at compile-time. # Die at run-time if the module is actually needed but isn't found. eval "use Date::Parse"; die if $@; return str2time ($_); } return; } sub duration { my $sec = shift; #my @time_mult = (qw(31536000 2592000 86400 3600 60 1)); #my @time_name = (qw(year month day hour minute second)); my @time_mult = (qw(86400 3600 60 1)); my @time_name = (qw(day)); my @result; my @clock; for (my $i = 0; $i < @time_mult; $i++) { my $val = int ($sec / $time_mult[$i]); $sec -= $val * $time_mult[$i]; if (defined $time_name[$i]) { push @result, sprintf ("%d %s%s", $val, $time_name[$i], ($val == 1 ? "" : "s")) if ($val != 0 || (@result == 0 && $i+1 == @time_mult)); } else { push @clock, $val; } } my $clock = join (":", map { sprintf ("%02d", $_) } @clock); push @result, $clock if $clock; join (", ", @result); } sub display { my ($deadline, $verbose) = @_; my $daysec = 60 * 60 * 24; my $ws = ' ' x 8 . chr(0x8) x 8; # whitespace + backspace my $tsfmt = "%Y-%m-%d %H:%M:%S"; my $s_deadline = strftime ($tsfmt, localtime ($deadline)); my $width = length ($s_deadline) + 2; my $lfmt = sprintf ("%%-%ss %%-%ss %%s", $width, $width); print sprintf ($lfmt, "Current", "Deadline", "Remaining"), "\n" if $verbose; my $displayed = 0; local $| = 1; while (1) { my $now = time; my $left = $deadline - $now; last if $left < 0 && $displayed; my $s_now = strftime ($tsfmt, localtime ($now)); my $s_left = ($left < 0 ? "T+" . duration (-$left) : duration ($left)); my $s = ($verbose ? sprintf ($lfmt, $s_now, $s_deadline, $s_left) : $s_left); print $ws, "\r", $s; $displayed = 1; select (undef, undef, undef, 0.50); # pause 1/2 sec } print "\nTime!\n"; } sub main { local @ARGV = @_; Getopt::Long::config ('bundling', 'autoabbrev'); GetOptions ("v|verbose", \$verbose, ); unless (@ARGV) { print STDERR "Usage: $progname [timestamp]\n"; exit (1); } $0 = join (" ", $progname, @ARGV); display (compute_deadline (@ARGV), $verbose); } main (@ARGV); # eof