在不同时区安排脚本

发布于 2024-08-12 09:58:17 字数 901 浏览 7 评论 0原文

有些程序/脚本需要在与系统时区不同的时区的特定时间运行。

Perl 中的 crontab,但它遵循与系统配置所在区域不同的时区和 DST 规则。

这是用例:我将创建一个 Excel 工作表,其中 B 列中包含 PT 时间,并在 C 列中运行相应的程序/Perl 脚本。Excel

工作表中的此信息没有任何具体内容 - 可以是纯文本文件/也是“crontab”条目。

Perl 脚本将从 Excel 工作表中读取数据并在正确的时间运行/生成这些脚本。

需要记住的是,无论 Perl 脚本运行的系统位于哪个时区,它都应该正确运行。

无论脚本是否在纽约、伊利诺伊州或加利福尼亚州的 Box 上运行,它都应该按照太平洋标准时间(考虑 DST)在文件条目中提到的时间生成脚本。

正如我之前所说,“自动”(无需我进行任何显式编程)了解 PT 地区的最新 DST 规则非常重要。

你有什么建议?

也许我可以访问一些显示该地区当前时间的网站并从中扫描时间值,并在正确的时间运行脚本?

有这样的 Perl 屏幕抓取工具友好网站吗?

或者也许我可以使用一些智能 Perl 模块,例如 Schedule::Cron

作为记录, http://www.perlmonks.org/index 提供了大量好的建议.pl?node_id=772934但是,它们以典型的 at/cron 方式按照系统配置的时区工作。

There are some programs/scripts that need to be run at specific times in a timezone different from the system timezone.

A la crontab in Perl, but one that honors a timezone and DST rules in a region different from that in which the system is configured.

Here is the use case : I will create an excel sheet with the time in PT in column B and the corresponding program/Perl script to run in column C.

Nothing specific about this information being in a Excel sheet - could be plain text file/"crontab" entry too.

A Perl script will read in the data from the excel sheet and run/spawn those scripts at the correct time.

The thing to keep at mind is that the Perl script should run correctly regardless of what timezone the system that it is running on is.

Regardless of whether the script is running on a Box in NY or IL or CA, it should spawn the scripts at the time mentioned in the file entries as per the Pacific Standard Time with DST at mind.

It is very important, as I said before, of it being aware, "automagically" ( without me doing any explicit programmming ) of the latest DST rules for the PT region.

What would you suggest?

Maybe I can visit some website that shows current time in that region and scan the time value from it, and run the scripts when it's the correct time?

Any such Perl screen scraper friendly site?

Or maybe I can use some smart Perl module, like Schedule::Cron

For the record, a large number of good suggestions came by at http://www.perlmonks.org/index.pl?node_id=772934, however, they, in typical at/cron fashion, work as per the system configured timezone.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

羁〃客ぐ 2024-08-19 09:58:17

一般来说,如果您关心时区,请以某种通用格式在内部表示时间,并仅出于显示目的转换时间。

将其应用于您的问题,编写一个 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 个(ie,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)

In general, if you care about timezones, represent times internally in some universal format and convert times for display purposes only.

Applying this to your problem, write a crontab whose times are expressed in GMT. On each worker machine, convert to local time and install the crontab.

Front matter:

#! /usr/bin/perl

use warnings;
use strict;

use feature qw/ switch /;

use Time::Local qw/ timegm /;

For the conversions this program supports, use today's date and substitute the time from the current cronjob. Return the adjusted hour and day-of-week offset:

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);
}

Take the five-field time specification from the current cronjob and convert it from GMT to local time. Note that a fully general implementation would support 32 (i.e., 2 ** 5) cases.

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 destroy 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)
流殇 2024-08-19 09:58:17

在计划中,存储每次运行应发生的时间点的秒数,而不是日期/时间字符串。

稍微扩展一下:

#!/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:00 是切换发生的时间。另一方面,使用 hour =>; 3,我得到:1268553600。现在,在纽约,我使用:

C:\Temp> perl -e "print scalar localtime 1268553600"
Sun Mar 14 04:00:00 2010

因此,解决方案似乎是避免在您的本地时区中不存在的时间安排这些活动。这不需要复杂的逻辑:只需将 DateTime 构造函数调用包装在 eval 中并处理异常时间即可。

In the schedule, store the number of seconds from the epoch when each run should occur rather than a date/time string.

Expanding a little:

#!/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";

gives me

Invalid local time for date in time zone: America/Chicago

because 2:00 am on March 14, 2010 is when the switch occurs. On the other hand, using hour => 3, I get: 1268553600. Now, in New York, I use:

C:\Temp> perl -e "print scalar localtime 1268553600"
Sun Mar 14 04:00:00 2010

So, the solution seems to be to avoid scheduling these events during non-existent times in your local time zone. This does not require elaborate logic: Just wrap the DateTime constructor call in an eval and deal with the exceptional time.

我偏爱纯白色 2024-08-19 09:58:17

虽然我当然认为可能有“更干净”的解决方案,但以下方法可行吗?

  • 将 cron 设置为比您实际希望脚本运行的可能时间范围提前几个小时运行脚本

  • 处理脚本中的时区检测并让它休眠适当的时间

再次,我知道这是有点笨拙,但我想我会把它放在那里。

While I certainly think that there are likely "cleaner" solutions, would the following work?

  • set the cron to run the scripts several hours ahead of the possible range of times you actually want the script to run

  • handle the timezone detection in the script and have it sleep for the appropriate amount of time

Again, I know this is kinda kludgey but I thought I would put it out there.

゛时过境迁 2024-08-19 09:58:17

使用 DateTime 模块来计算时间。

因此,如果您的设置要求每天凌晨 2:30 运行脚本,您将需要逻辑来:

  • 尝试在 America\Los_Angeles 时区的凌晨 2:30 创建一个 DateTime 对象。< /p>

  • 如果没有物体,请增加 5 分钟的时间,然后重试。偏移 2 小时后放弃。

  • 一旦有了 DateTime 对象,您就可以与 DateTime->now 进行比较,或者从对象中提取纪元时间并将其与 DateTime->now 的结果进行比较code>time。

请注意,我选择了凌晨 2:30,因为一年中至少不会有 1 天存在这个时间。这就是为什么您需要一个添加偏移量的循环。

Use the DateTime module to calculate times.

So if your setup says to run a script at 2:30 am every day, you will need logic to:

  • Try to create a DateTime object for 2:30am in timezone America\Los_Angeles.

  • If no object add 5 minutes to the time and try again. Give up after 2 hours offset.

  • Once you have a DateTime object, you can do comparisons with DateTime->now or extract an epoch time from your object and compare that with the results of time.

Note that I chose 2:30 am, since that time won't exist at least 1 day a year. That's why you need to have a loop that adds an offset.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文