Below is a sketch at converting simple crontabs whose times are expressed in GMT to the host's local time. This blog post wishes it were a literate Haskell program.
In general, if you care about timezones, represent times internally in some universal format and convert times for display purposes only.
Front matter:
#! /usr/bin/perl
use warnings;
use strict;
use feature qw/ switch /;
use Time::Local qw/ timegm /;
Given a five-field job time in GMT, gmtoday returns the hour in the local timezone and the day offset. The function's name comes from its implementation, nearly always a terrible practice. It uses the time the program started ($^T), decomposes it with gmtime, substitutes the hour from cron, and goes the other direction with timegm.
Now that I think about it, this probably doesn't handle the day-of-week wraparound: Sunday is 0 and Saturday is 6, but the days are adjacent.
sub gmtoday {
my($gmmin,$gmhr,$gmmday,$gmmon,$gmwday) = @_;
my @gmtime = gmtime $^T;
my(undef,undef,$hour,$mday,$mon,$year,$wday) = @gmtime;
my @args = (
0, # sec
$gmmin eq "*" ? "0" : $gmmin,
$gmhr,
$mday,
$mon,
$year,
);
my($lhour,$lwday) = (localtime timegm @args)[2,6];
($lhour, $lwday - $wday);
}
Given the five-field time specification from the current cronjob, localcron converts it from GMT to local time. Note that a fully general implementation would support 32 (i.e., 2 ** 5) cases.
This is a nice use of given-when, new in perl-5.10, and resembles a familiar shell idiom.
sub localcron {
my($gmmin,$gmhr,$gmmday,$gmmon,$gmwday) = @_;
given ("$gmmin,$gmhr,$gmmday,$gmmon,$gmwday") {
# trivial case: no adjustment necessary
when (/^\d+,\*,\*,\*,\*$/) {
return ($gmmin,$gmhr,$gmmday,$gmmon,$gmwday);
}
# hour and maybe minute
when (/^(\d+|\*),\d+,\*,\*,\*$/) {
my($lhour) = gmtoday @_;
return ($gmmin,$lhour,$gmmday,$gmmon,$gmwday);
}
# day of week, hour, and maybe minute
when (/^(\d+|\*),\d+,\*,\*,\d+$/) {
my($lhour,$wdoff) = gmtoday @_;
return ($gmmin,$lhour,$gmmday,$gmmon,$gmwday+$wdoff);
}
default {
warn "$0: unhandled case: $gmmin $gmhr $gmmday $gmmon $gmwday";
return;
}
}
}
Finally, the main loop reads each line from the input and generates the appropriate output. Note that we do not throw away unhandled times: they instead appear in the output as comments.
while (<>) {
if (/^\s*(?:#.*)?$/) {
print;
next;
}
chomp;
my @gmcron = split " ", $_, 6;
my $cmd = pop @gmcron;
my @localcron = localcron @gmcron;
if (@localcron) {
print join(" " => @localcron), "\t", $cmd, "\n"
}
else {
print "# ", $_, "\n";
}
}
For this sorta-crontab
33 * * * * minute only 0 0 * * * minute and hour 0 10 * * 1 minute, hour, and wday (same day) 0 2 * * 1 minute, hour, and wday (cross day)the output is the following when run in the US Central timezone:
33 * * * * minute only 0 18 * * * minute and hour 0 4 * * 1 minute, hour, and wday (same day) 0 20 * * 0 minute, hour, and wday (cross day)
1 comment:
Please filter out perl posts from planet haskell. We don't want to see this garbage. Have a nice day.
Post a Comment