Perl与MySQL比较日期

时间:2010-09-09 00:39:47

标签: mysql perl datetime

我正在努力弄清楚如何使用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

提前致谢。我希望我已经解释得很好,很难解释一切如何相关。

6 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

试试这个

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

答案 2 :(得分:1)

我在这里使用Ether,因为DateTime文档建议立即将所有内容转换为UTC以避免时区问题。

如果这不是时区问题,我想知道将>运算符与DateTimes一起使用是否存在一些含糊之处。您是否尝试按照建议in the documentation检查DateTime->compare($now, $end)的结果?

答案 3 :(得分: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值的时区随夏令时变化(或变化),事情会变得棘手:例如,每个秋天有一个1小时的时间段,相同的时间戳出现两次,没有办法通过查看你所说的保存值来判断。这就是为什么通常情况下,如果可以的话,将时间戳存储在GMT / UTC中通常会更好。

现在,关于id的UNIQUE-ness。如果WHERE子句保证只匹配1行,那么上面的代码和你的代码将做同样的事情。但是如果它可以匹配多于1,并且如果这些匹配的行可以具有不同的table.date值,那么您的代码可能有一个错误。也就是说,它将迭代所有n个匹配的行,并且对于每个行,它将设置所有n行以具有table.expired为1或0,具体取决于它所在的行。结果是所有n行最终都会以1或0结尾,具体取决于返回的最后一行(未定义),因此您的结果基本上是随机的。

你从上面的UPDATE中得不到的一件事是匹配或更改的id的列表,但看起来你不需要那些信息。如果你确实需要它,那就有一个聪明的方法来获得它;回应,我会分享:)

答案 4 :(得分: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了解循环。

答案 5 :(得分:-3)

可能是因为那些正在返回字符串值。您要做的是在每个日期调用epoch()方法,并进行比较。