某些程序/脚本需要在与系统时区不同的时区中的特定时间运行。
Perl中的la crontab,但是在与配置系统的区域不同的区域中遵守时区和DST规则。
以下是用例:我将创建一个excel表,其中PT中的时间为B列,相应的程序/ Perl脚本则在C列中运行。
Excel信息表中没有任何关于此信息的具体内容 - 也可以是纯文本文件/“crontab”条目。
Perl脚本将读取excel表中的数据并在正确的时间运行/生成这些脚本。
要记住的是,无论运行它的系统是什么时区,Perl脚本都应该正确运行。
无论脚本是在纽约还是IL或CA的Box上运行,它都应该在文件条目中提到的时间按照太平洋标准时间和DST生成脚本。
正如我之前所说,非常重要的是,它知道“自动”(没有我做任何明确的编程)最新的PT区域DST规则。
你会建议什么?
也许我可以访问某个显示该区域当前时间并从中扫描时间值的网站,并在正确的时间运行脚本?
任何这样的Perl屏幕刮刀友好网站?
或许我可以使用一些智能Perl模块,例如Schedule::Cron
为了记录,在http://www.perlmonks.org/index.pl?node_id=772934,然而提出了大量好的建议,它们以典型的/ cron方式按照系统配置的时区工作。
答案 0 :(得分:2)
在计划中,存储每次运行时的纪元数,而不是日期/时间字符串。
扩大一点:
#!/usr/bin/perl
use strict; use warnings;
use DateTime;
my $dt = DateTime->new(
year => 2010,
month => 3,
day => 14,
hour => 2,
minute => 0,
second => 0,
time_zone => 'America/Chicago',
);
print $dt->epoch, "\n";
给了我
Invalid local time for date in time zone: America/Chicago
因为2010年3月14日凌晨2点是切换发生的时间。另一方面,使用hour => 3
,我得到:1268553600
。现在,在纽约,我使用:
C:\Temp> perl -e "print scalar localtime 1268553600" Sun Mar 14 04:00:00 2010
因此,解决方案似乎是避免在 本地 时区中的不存在时间内安排这些事件。这不需要复杂的逻辑:只需将DateTime
构造函数调用包装在eval
中并处理异常时间。
答案 1 :(得分:2)
一般情况下,如果您关心时区,请在内部以某种通用格式表示时间,并将转换时间仅用于显示目的。
将此问题应用于您的问题,请编写一个crontab,其时间以GMT表示。在每台工作计算机上,转换为本地时间并安装crontab。
前面的事情:
#! /usr/bin/perl
use warnings;
use strict;
use feature qw/ switch /;
use Time::Local qw/ timegm /;
对于此程序支持的转换,请使用今天的日期并替换当前cronjob中的时间。返回调整后的小时和星期几的偏移量:
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);
}
从当前的cronjob中取出五场时间规格,并将其从GMT转换为当地时间。请注意,完全通用的实现将支持32(即,2 ** 5)个案。
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;
}
}
}
最后,主循环从输入读取每一行并生成适当的输出。请注意,我们不会破坏未处理的时间:它们会在输出中显示为注释。
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";
}
}
对于这种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)
在美国中部时区运行时输出如下:
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)
答案 2 :(得分:1)
虽然我当然认为可能存在“更清洁”的解决方案,但以下工作会如何?
将cron设置为在您希望脚本运行的可能范围之前几小时运行脚本
处理脚本中的时区检测并让它在适当的时间内休眠
同样,我知道这有点像kludgey但我想我会把它放在那里。
答案 3 :(得分:0)
使用DateTime模块计算时间。
因此,如果您的设置说每天凌晨2:30运行脚本,则需要逻辑:
尝试在时区America \ Los_Angeles上午2:30创建一个DateTime
对象。
如果没有任何对象添加5分钟,请重试。放弃2小时后放弃。
获得DateTime
个对象后,您可以与DateTime->now
进行比较,或从对象中提取一个纪元时间,并将其与time
的结果进行比较。
请注意,我选择了凌晨2:30,因为那个时间每年至少不会存在1天。这就是为什么你需要一个增加偏移量的循环。