2010-09-09 132 views
3

我在努力弄清楚如何使用perl比较MySQL日期和当前系统时间。Perl与MySQL比较日期

我有一个cron作业,如果当前系统日期/时间为过去的日期返回的记录/时间会发送通知运行的脚本:

应用程序显示一个表视图:

EventId Device  Location 

CC: 123 something  BFE 
TT: 456 anotherthing BFE 

脚本如何工作是它在字段EventID中查找值,从ID(数字部分)中解析类型(CC:,TT:等)。该ID是另一个数据库/表中包含结束时间字段的唯一ID。 EventID本身不是唯一的,并且可能在表中有重复的项目。因为每个类型都有不同的数据库和/或表,所以存在每个“类型”的子例程。

目标是脚本每分钟运行一次并切换Expired的值。有可能会有某些东西过期,需要做出改变并随后取消。

该脚本工作正常,除了1个问题,这似乎与时区有关,基于我迄今收到的反馈。如果我不隐式设置当前系统时间为'America/New_York'($now)的时区,则会关闭几个小时($notz)。因此我需要找到一种方法来使从MySQL返回的日期与当前系统时间进行准确比较。

$now->set_time_zone('America/New_York')似乎也不工作。

我不确定如何做到这一点,甚至如果我有迄今的代码是最好的办法(仍然相当新的Perl):

#!/usr/bin/perl 
use DBI; 
use DateTime; 
use DateTime::Format::MySQL; 
use Switch; 
my $now   = DateTime->now(time_zone => 'America/New_York'); 
my $notz  = DateTime->now(); 
my $maindb  = DBI=>connect(database); 
my $seteventsql = qq { select * from view where EventId like 'IE:' or EventId like 'TT:' or EventId like 'CC:';}; 
my $commit  = $livedb->prepare($seteventsql); 
$commit->execute() || die "could not set event: $DBI::errstr"; 

while(@events = $commit->fetchrow_array()) { 
       (my $type, my $id) = split (/ /,$events[0]); 
       $id =~ s|\D||g; 
       switch ($type) { 
         case ('CC:') {check_expired_case($id);} 
         case ('TT:') {check_expired_task($id);} 
         case ('IE:') {check_expired_event($id);} 
       } 
} 

sub check_expired_case { 
     my $id = shift; #id = 123 
     my $sql = qq { select id, status_id, item_enddate from item where id = ?; }; 
     my $exec = $itemdb->prepare($sql); 
     $exec->execute($id); 
     while(my @row = $exec->fetchrow_array()) { 
       my $status = $row[1]; 
       my $end = DateTime::Format::MySQL->parse_datetime($row[2]); 
       if ($now > $end || $status ne 3 || $status ne 6) { 
         $sql = qq { update item set Expired = 1 where EventId = '$eventid';}; 
         $maindb->do($sql) 
       }else{ 
         $sql = qq { update item set Expired = 0 where EventId = '$eventid';}; 
         $maindb->do($sql) 
       } 
     } 
     $exec->finish(); 
} 


NoTZ: 2010-09-10T01:27:19 
Now: 2010-09-09T21:27:19 
End: 2010-09-10T17:00:00 

在此先感谢。我希望我已经很好地解释了这一点,它很难解释所有事情的关系。

+0

所以要澄清,你的代码将在“做什么”的路径,而不是“做点别的? – Ether 2010-09-09 00:59:03

+0

实际生产中的脚本我有设置一个值的1或0数据库的当前时间是否是之前或结束时间后 – Mose 2010-09-09 01:06:54

+0

@mose:我想确认你的代码错误地说,2010-09-08T20:03:38> 2010-09-10T17:00:00 – Ether 2010-09-09 01:08:20

回答

1

您必须转储对象才能确认,但听起来您有时区问题。如果你没有指定一个,那么DateTime::Format::*->parse_datetime(或任何其他构造函数)将使用浮动时区(大致接近UTC),因此您的比较将会关闭5个小时。

+0

您能否详细说明“转储对象确认”?我对Perl比较陌生。 – Mose 2010-09-09 01:03:08

+0

my $ end = DateTime :: Format :: MySQL-> parse_datetime($ row [2],time_zone =>“America/New_York”); – Mose 2010-09-09 01:03:35

+0

^似乎并没有解决这个问题。 – Mose 2010-09-09 01:03:58

-3

这很可能是因为那些返回的字符串值。你想要做的是在这些日期的每一个上调用epoch()方法,并比较它。

+0

datetime对象重载比较运算符。 – Ether 2010-09-09 00:53:39

+0

@Ether其实,从源,>和<未被重载。但是Spaceship和'cmp' ...... – 2010-09-09 01:01:39

+0

这只是语义。只要太空船操作员超载,'''操作将使用它;不需要单独的重载(同样,'gt','ge','lt','le','ne'和'eq'都将使用'cmp')。 – Ether 2010-09-09 01:13:55

1

试试这个

if (DateTime->compare($now, $end) == 1) { 
    # do something 
} else { 
    # do something else 
} 
+0

打败我吧。 :) – pjmorse 2010-09-09 01:05:40

+0

该实现与'$ dt1> $ dt2'完全相同。 – Ether 2010-09-09 01:08:59

+0

AFAICS它与$ dt1> $ dt2不相同:DateTime将代表与_compare()进行比较,约为。 5.10.0(64b)中的50行代码...你能展示一下如何减少到'>'操作的身份吗? – Rondo 2010-09-09 03:28:02

1

我用醚是来这里,因为DateTime文档建议立即UTC转换所能避免时区的问题。

如果这不是一个时区问题,我不知道是否在DateTimes中使用>运算符只是含糊不清。您是否试过按照建议的in the documentation检查DateTime->compare($now, $end)的结果?

+0

现在我正在执行DateTime-> compare($ now,$ end),到目前为止没有变化,但是我也想尝试Dumper建议,看看那里会发现什么。 – Mose 2010-09-09 01:34:12

+0

所以我一直在按照之前的建议去做Dumper打印。如果我没有将时区隐式设置为America/New_York,则系统时间的关闭时间为5小时 - 但即使设置时间,从检索的日期/时间起,“浮动”时区似乎也存在问题MySQL的。尝试将时区设置为America/New_York,或者添加 – Mose 2010-09-09 08:10:41

+0

$ ENV {TZ} ='America/New_York'; #tzset; 到目前为止还没有帮助。 – Mose 2010-09-09 08:11:01

1

你正在以这种错误的方式去做。关系数据库不是您必须一次读取和写入一个值的键值存储。你可以做到这一点,原子,更快,代码少,依赖性少(如果'id'列是非UNIQUE,没有错误)。试试这个:

sub set_expired { 
    my $id = shift; 
    my $dbh = DBI->connect(database); 
    my $sql = qq{UPDATE table SET expired=IF(NOW() > date, 1, 0) WHERE id = ?}; 
    my $sth = $dbh->prepare($sql); 
    $sth->execute($id); 
    my $rows_affected = $sth->rows(); 
    # if no matches, $rows_affected will be 0; on error, -1 
    $sth->finish(); 
} 

你的mysqld将有​​望被存储是在mysqld的本身是在运行相同的时区的table.date时间值它看起来像你的table.date值在美国/纽约。时区,所以有一点点运气,你的mysqld以its timezone的方式运行。在那种快乐的情况下,NOW()将是一个时间戳,可以直接与table.date比较,如上所述。

如果你已经喂养它从不同的时区值,你可能要调整的比较,例如:

... SET expired=IF(NOW() > DATE_ADD(date, INTERVAL 4 HOUR), 1, 0) WHERE ... 

事情变得棘手,如果你要存储table.date时区夏时制值的变化(或变化):例如,每年秋天都会有一小时的时间,同一时间戳会出现两次,而且无法通过查看您所指的保存值来判断。这就是为什么通常以GMT/UTC格式存储时间戳的原因之一。现在

,为ID的独特的烦躁。如果WHERE子句保证只匹配1行,那么上面的代码和你的代码将做同样的事情。但是,如果它可以匹配超过1,并且如果这些匹配的行可以有不同的table.date值,那么你的代码可能有一个错误。也就是说,它将遍历所有n个匹配的行,并且对于每一行,它将设置所有n行,以获得一个table.expired为1或0,具体取决于它所在的行。结果将是所有的n行将以1或0结尾,这取决于返回的最后一行,这没有定义,所以你的结果本质上是随机的。

的一件事,你不要从上面的UPDATE得到的是相匹配或更改ID的列表,但它并不像你所需要的信息呢。如果你需要它,有一个聪明的方法来获得它;回应,我会分享:)

+0

我会用完整的代码块来更新这篇文章,我试图尽量减少代码的简洁性,并关注我发现有问题的部分代码。 – Mose 2010-09-10 00:13:52

0

使用ltgt perl的运营商:

my $isLessThan = '0001-01-01' lt '2050-01-01'; # Returns 1. 
my $isBiggerThan = '0001-01-01' gt '2050-01-01'; # Returns ''. 

还看到一个循环的example