2010-09-09 136 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