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)
 


 
 Posts
Posts
 
 
