2010-10-12 38 views
10

我有一個格式爲YYYY-MM-DD的n字符串(例如「2010-10-31」)。使用Perl,如何比較YYYY-MM-DD形式的日期?

如何將日期與此數組中的字符串進行比較?

例如,30多天前刪除字符串?

+1

爲什麼被否決?公平的問題,是嗎? – Xailor 2010-10-12 02:41:40

+2

我很喜歡這個問題如何引發純粹主義者和實用主義者之間的爭論。 – paddy 2012-10-29 13:50:08

回答

5
use strict; use warnings; 
use DateTime(); 
use DateTime::Duration(); 
use DateTime::Format::Natural(); 

my $parser = DateTime::Format::Natural->new; 
my $now = DateTime->now; 
my $delta = DateTime::Duration->new(days => 30); 
my $cutoff = $now->subtract_duration($delta); 

my @new_dates = map { $_->[1] } 
       grep { -1 == $_->[0] } 
       map { 
        chomp; 
        [ 
         DateTime->compare(
          $parser->parse_datetime($_), 
          $cutoff 
         ), 
         $_ 
        ] 
       } <DATA>; 

print "@new_dates"; 

__DATA__ 
2010-07-31 
2010-08-31 
2010-09-30 
2010-10-31 
+1

現在只需使用'my $ now = DateTime->;'不需要解析'localtime'的輸出。 – daotoad 2010-10-12 04:57:09

+0

感謝daotoad。 – 2010-10-12 05:01:46

+0

OP尋找'YYYY-MM-DD'格式,而不是'YYYY/MM/DD' – 2010-10-12 05:57:04

1

一個好的開始是閱讀The Many Dates of PerlDateTime網站。

YYYY-MM-DD格式是ISO 8601日期表示形式。有些變體被認爲是可以接受的,例如YYYY-MM-DDYYYYMMDD,甚至是舊數據中的YYMM。在選擇比較這些日期的方法之前,您應該查看definitive list

如果ISO 8601日期字符串是:1)有效日期; 2)使用或不使用-分隔符的相同格式; 3)缺少主要和尾隨空白,一個有吸引力的屬性是,你可以排序或比較字符串與簡單的詞典字符串比較。

一般則:

  1. IFF你不打算檢查日期是否有效和IFF它們是相同的格式,IFF有沒有開頭或結尾空白,您可以用相同的格式與代表目標日期的另一個字符串進行比較。

---否則---

  1. 決定一個CPAN模塊上的解析您的日期字符串(或自己與之相匹配),

  2. 轉換,如果,如果你的日期大紀元(或使用CPAN模塊進行大尺寸日期/時間操作,如Date :: Manip或Date :: Calc)

  3. 對時間類型(曆元時間,絕對天數, whateve R)

  4. 轉換的時間回到你想要的格式...

這裏是代碼,不會說:

use warnings; use strict; 
use Date::Calc qw/:all/; 

my (@date_strings, @abs_days); 

my $target=Date_to_Days(2010, 1, 15); 

# set @date_string to "YYYY-MM-DAY" between some dates 
for my $AbsDay(Date_to_Days(2009,1,1)..Date_to_Days(2011,12,31)) { 
    my ($year, $mon, $day)=Add_Delta_Days(1,1,1,$AbsDay-1); 
    my $s="$year-$mon-$day"; 
    push @date_strings, $s; 
} 

foreach my $s (@date_strings) { 
    my ($year, $mon, $day); 

    if(($year, $mon, $day)=$s=~/^(\d+)-(\d+)-(\d+)/) { 
     my $days=Date_to_Days($year, $mon, $day); 
     push @abs_days, $days 
      if ($target-$days <= 30 && $target-$days >= -30); 
    } 
} 

print "absolute day way:\n"; 
foreach my $days (@abs_days) { 
    my ($year, $mon, $day)=Add_Delta_Days(1,1,1,$days-1); 
    print "$year-$mon-$day\n"; 
} 
+0

工作太多! :) – 2010-10-12 14:38:07

+0

@brian d foy:恭敬地,我不同意。我的代碼並不是很美,但使用經過良好測試的CPAN庫(如Date :: Calc(用C語言編寫)或Date :: Time)是值得的。這裏的大部分工作只是生成要測試的字符串! : - }對於你所倡導的詞典解決方案,你仍然需要檢查(或者希望)你輸入的是相同的分隔符,是一個有效的日期,並且沒有前導或尾隨空格。一旦你這樣做,它真的不那麼工作? – dawg 2010-10-12 20:41:32

+1

是的,我一直處於工作量較少的情況,以及在合理時間內完成和從未完成的區別。 – 2010-10-12 23:51:15

1

您可以使用Time::ParseDate模塊,

use strict; 
use warning; 
use Time::ParseDate; 

my @dates = ('2010-10-12', '2010-09-14', '2010-08-12', '2010-09-13'); 
my @dates = 
    grep {parsedate($_, NO_RELATIVE => 1, UK => 1) > parsedate('-30 days') }@dates; 
#output: 2010-10-12 2010-09-14 
8

猜猜不止一種方法去做一件事,但我喜歡Date::Simple對於這樣的東西..

從文檔的一個例子:

use Date::Simple ('date', 'today'); 

# Difference in days between two dates: 
$diff = date('2001-08-27') - date('1977-10-05'); 

# Offset $n days from now: 
$date = today() + $n; 
print "$date\n"; # uses ISO 8601 format (YYYY-MM-DD) 

這是偉大的物體上做++運算。

只有日期但,沒有小時,分鐘或秒

14

偉大的事情有關YYYY-MM-DD - 格式化日期是,你可以使用簡單的字符串比較比較。在Perl中,這是ltgt運營商。

在這種情況下,聽起來你只是想檢查數組中的日期是早於還是晚於給定的目標日期(恰好是「30天前」)。對於這種情況,我會採取的方法是首先確定30天前的日期,然後將其作爲字符串與數組中的每個日期進行比較。我不會介紹將所有YYYY-MM-DD字符串轉換爲「正確的」日期對象,時代等,並僅僅爲了測試代表早期日期而引入的開銷。

#!/usr/bin/env perl 

use strict; 
use warnings; 

my $thirty_days = 30 * 24 * 60 * 60; 
my ($old_day, $old_month, $old_year) = (localtime(time - $thirty_days))[3..5]; 
my $cutoff = sprintf('%04d-%02d-%02d', 
        $old_year + 1900, $old_month + 1, $old_day); 

my @dates = ('2010-10-12', '2010-09-12', '2010-08-12', '2010-09-13'); 
for my $date (@dates) { 
    print "$date\n" if $date gt $cutoff; 
} 
+2

我很驚訝有這麼多人跳到這種複雜的膝蓋混亂的解決方案。我們在_Effective Perl Programming_中有一個這樣的解決方案。 :) – 2010-10-12 14:41:44

+0

這假定源日期沒有錯誤,例如'2010-02-29',它會按照字典順序進行比較,但仍然是錯誤的日期。日期包會指出源日期錯誤,這似乎是腳本工作的一部分,不是嗎? – dawg 2010-10-12 15:53:30

+0

測試'$ date gt $ cutoff'將失敗:1)'sprintf'中使用的'-'以外的分隔符; 2)在應該匹配的字符串上帶有空白字符; 3)用不應該匹配的字符串拖尾空格。幾乎所有的CPAN日期解析庫都可以處理這三種情況,還有很多更多。一旦你檢查了分隔符並刪除前後的空格,你真的可以節省那麼多的「開銷」或代碼嗎? Date :: Calc是用C編寫的,速度非常快。 Date :: Time功能非常豐富。兩者都檢查有效的日期輸入,而這種方法沒有。 – dawg 2010-10-12 20:17:09

1

我這樣做,有點冗長,但很容易理解並完成工作。 @ out2是一個2d數組,我正在使用for循環讀取值。每個循環都會將輸入與@ out2進行比較,以查看它是早期還是晚期時間/日期。如果是,那麼我將值寫入數組,然後比較下一個輸入。

if ($year < $out2[$j][7]) { 
    $lt = 1; 
    goto JUMP; 
} 
if ($year > $out2[$j][7]) { 
    $gt = 1; 
    goto JUMP; 
} 
if ($month < $out2[$j][5]) { 
    $lt = 1; 
    goto JUMP; 
} 
if ($month > $out2[$j][5]) { 
    $gt = 1; 
    goto JUMP; 
} 
if ($day < $out2[$j][6]) { 
    $lt = 1; 
    goto JUMP; 
} 
if ($day > $out2[$j][6]) { 
    $gt = 1; 
    goto JUMP; 
} 
if ($time < $out2[$j][4]) { 
    $lt = 1; 
    goto JUMP; 
} 
if ($time > $out2[$j][4]) { 
    $gt = 1; 
    goto JUMP; 
} 

JUMP:

if ($lt == 1) { 
    $out2[$j][2] = "$dtime $month\/$day\/$year"; 
    $out2[$j][4] = $time; 
    $out2[$j][5] = $month; 
    $out2[$j][6] = $day; 
    $out2[$j][7] = $year; 
    $lt = 0; 
    } 

if ($gt == 1) { 
    $out2[$j][3] = "$dtime $month\/$day\/$year"; 
    $out2[$j][4] = $time; 
    $out2[$j][5] = $month; 
    $out2[$j][6] = $day; 
    $out2[$j][7] = $year; 
    $gt = 0; 
} 
0

爲什麼不自5.10 Time::PieceTime::Seconds核心,而不是CPAN搜索的前幾個結果嗎?

use strict; 
use warnings; 
use Time::Piece(); # we don't need to include overloaded locatime 
use Time::Seconds; 
use Data::Dumper; 

my @dates = qw/2010-10-31 2012-10-16 2011-09-08/; 

my $now = Time::Piece::localtime(); 

my @date_objects = map { 
    Time::Piece->strptime($_, '%F') # %F is the same as %Y-%m-%d 
} @dates; 

my @filtered_dates = grep { 
    $now - $_ < (ONE_DAY * 30) 
} @date_objects; 

print Dumper(map($_->strftime('%F'), @filtered_dates)); 
0

要找到一個循環的最小日期:

var minDate = ...; 
var maxDate = ...; 

foreach my $date (@$dates) { 
    if ($minDate gt $date){ # Less than. 
     $minDate = $date; # Minimal date. 
    } 
    if ($minDate lt $date){ # Greater than. 
     $minDate = $date; # Maxamal date. 
    } 
} 
+0

這與這個問題有什麼關係? – Toto 2013-10-31 08:05:07