Perl Сравните даты с MySQL - PullRequest
       2

Perl Сравните даты с MySQL

3 голосов
/ 09 сентября 2010

Я пытаюсь понять, как сравнивать даты MySQL с текущим системным временем, используя perl.

У меня есть скрипт, который выполняется на задании cron, который будет отправлять уведомление, если текущая системная дата / время превышает дату / время возвращенной записи:

Приложение отображает табличное представление:

EventId    Device        Location

CC: 123    something     BFE
TT: 456    anotherthing  BFE

Как работает скрипт, он находит значения в поле EventID, анализирует тип (CC :, TT: и т. Д.) По идентификатору (числовая часть). Идентификатор - это уникальный идентификатор в другой базе данных / таблице, которая содержит поле времени окончания. Сам EventID не является уникальным и может содержать повторяющиеся элементы в таблице. Подпрограммы для каждого «типа» существуют, поскольку каждый тип имеет свою базу данных и / или таблицу.

Цель состоит в том, чтобы скрипт запускался каждую минуту и ​​переключал значение Expired. Возможно, что-то истекает, вносятся изменения и впоследствии не действуют.

Сценарий работает отлично, за исключением 1 проблемы, которая, по-видимому, связана с часовыми поясами, основанными на обратной связи, которую я получил здесь до сих пор. Если я не устанавливаю часовой пояс «Америка / 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 ]

1 голос
/ 09 сентября 2010

Вы идете об этом неправильно. Реляционная база данных не является хранилищем значений ключей, которое вы должны читать и записывать по одному значению за раз. Вы можете сделать это атомарно, намного быстрее, с меньшим количеством кода и меньшим количеством зависимостей (и, если столбец 'id' не является УНИКАЛЬНЫМ, без ошибки). Попробуйте это:

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, будем надеяться, будет хранить значения времени table.date, которые находятся в том же часовом поясе, в котором запущен сам mysqld. Похоже, что ваши значения table.date находятся в часовом поясе Америки / Нью-Йорка, так что с небольшой удачей ваш mysqld работает с его часовой пояс установлен таким же образом. В этом удачном случае NOW () будет временной меткой, которую можно напрямую сравнить с table.date, как указано выше.

Если вы передавали значения из другого часового пояса, вы, вероятно, захотите настроить сравнение, например:

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

Ситуация усложняется, если часовой пояс, в котором вы храните значения table.date, меняется (или менялся) в зависимости от летнего времени: например, каждое падение составляет 1 час, когда одна и та же временная метка встречается дважды, и нет никакого способа сказать, глядя на сохраненное значение, которое вы имеете в виду. Это одна из причин, по которой, как правило, лучше хранить временные метки в GMT / UTC.

Теперь, что касается уникальности id. Если предложение WHERE гарантированно соответствует только 1 строке, то приведенный выше код и ваш будут делать то же самое. Но если он может соответствовать более 1, и если эти совпадающие строки могут иметь разные значения table.date, то ваш код, вероятно, содержит ошибку. А именно, он будет перебирать все n совпадающих строк и для каждой из них будет устанавливать для всех n строк значение table.expired, равное 1 или 0, в зависимости от того, в какой строке он находится. В результате все n строк будут иметь 1 или 0 в зависимости от последней возвращенной строки, которая не определена, поэтому ваш результат будет по существу случайным.

Единственное, что вы не получаете из вышеупомянутого ОБНОВЛЕНИЯ - это список идентификаторов, которые были сопоставлены или изменены, но, похоже, эта информация вам не нужна. Если вам это нужно, есть умный способ получить его; ответьте и я поделюсь этим:)

1 голос
/ 09 сентября 2010

Я здесь с Ether, потому что документация DateTime предлагает немедленно преобразовать все в UTC, чтобы избежать проблем с часовым поясом.

Если это не проблема с часовым поясом, я хотел бы знать, есть ли некоторая двусмысленность в использовании оператора > с DateTimes. Вы пробовали проверить результат DateTime->compare($now, $end), как предложено в документации ?

1 голос
/ 09 сентября 2010

Попробуйте это

if (DateTime->compare( $now, $end ) == 1) {  
  # do something
} else {  
  # do something else
}  
1 голос
/ 09 сентября 2010

Вам нужно будет сбросить объекты для подтверждения, но, похоже, у вас проблема с часовым поясом. DateTime::Format::*->parse_datetime (или любой другой конструктор) будет использовать плавающий часовой пояс (приблизительно приблизительный к UTC), если вы не укажете его, поэтому сравнение будет отключено на 5 часов.

0 голосов
/ 31 октября 2013

Используйте lt и gt операторы Perl:

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

Также см. пример для цикла.

0 голосов
/ 09 сентября 2010

Вероятно, потому что они возвращают строковые значения.Вам нужно будет вызвать метод epoch() для каждой из этих дат и сравнить его.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...