PERL 日期和時間 @ 不知道 :: 隨意窩 Xuite日誌
  • 200510071733PERL 日期和時間

    日期和資料的處理,一向是程式設計師頭痛的問題。有很多不同的格式和特殊的細節必須處理
    (例如今年是否為閏年?)日期和時間的表示也成問題。該如何儲存和顯示呢?
    使用者可以簡單的指定輸出格式嗎?使用者可能輸入12/ 25,25 dec、dec 25、december 25th 和
    25t h of december 這些東西,卻希望得到相同的結果!

    在演算中牽涉到日期也很痛苦,就像假期一樣。今年的父親節是哪一天?六月的第三個星期日
    是幾號?

    我去巴里島渡假還要等多久?
    幸虧,Perl 模組提供了不少解決方案。Perl 提供了方便且高效率的介面,用來處理各方面的解析
    、計算和顯示。

    取得日期和時間

    進入處理日期和時間的模組之前,讓我們快速回顧Perl 內建的日期和時間處理工具。

    取得當地目前的日期和時間的主要函式是local time。當以純量內容(scalar context)的方式來處理
    時,localt ime 會以字串的型式傳回目前的時間和日期:print scalar localtime, "n";

    這會以下列的格式印出字串:day month date time year,並產生下面的輸出:Mon Feb 2 13:23:00 1998

    localt ime 函式可以選擇性的接受UNIX 的時間參數。時間的值是一個從1970 年1月1 日至今以秒計
    算的整數值。

    你如果熟悉C 的time 函式,你就會知道localtime也高明不到哪裡去,一樣非常煩人。
    如果你不提供一個時間值(time value)給localt ime 函式,它會呼叫Per l 的time 函式。
    你也可以以串列內容(list context)的方式使用localtime 函式,它會產生下面的值:
    ($sec, $min, $hour, $mday, $mon, $year,, $wday, $yday, $isdst) = localtime;
     

    localtime 的值
    時間元素 說明
    $sec
    $min
    $hour 小時
    $mon
    $year目前的年減去1900 ,不是僅將19xx 年的19去掉,因此不會有Y2K的困擾
    $wday每週的日期(如Sunday 是0 )
    $yday每年的日期(如Jan1 是0 )
    $isdst 如果日光節約時間使用則是正值,其它為0 。

    我想說明的是,$year 在2000 年後仍然有效。到了公元2000 年,$year 將會是100而不是0。


    使用Net::Time

    我們第一個使用的日期和時間模組是Net::Time,它用來從遠端系統取得日期和時間。這個模組比較
    特殊的地方是,Net::Time是時間協定RFC868和日期協定RFC867 的介面。inet_time函式應用這個時間
    協定並且傳回一個Unix的時間值,而inet_daytime函式應用日期協定並傳回人們瞭解的時間值。

    這兩個函式有同樣的介面:參數分別是遠端機器的名稱,所使用的網路協定(TCP或UDP),和逾
    時值(timeout value)。

    若沒有任何參數,我們會試著連接區域的主機,使用UDP協定並且以120秒當時間限制。雖然看起
    來十分簡單,但這些函式只有在這些機器使用這個協定下才會成功執行。完全看你的工作站或是
    機器的設定。

    範例time.pl:使用Time協定
    use Net::Time qw(inet_time);
    use strict;
    my $host = shift || 'localhost';
    my $host_time = inet_time($host, 'tcp', 10);
    if($host_time) { print "Time on $host is $host_timen"; }
    else { print "inet_time failed: $!"; }

    在主機上(名稱為somehos t)執行此範例會產生下面的輸出:
    Time on somehost is 886452598

    範例daytime.pl:使用Daytime 協定
    use Net::Time qw(inet_daytime);
    use strict;
    my $host = shift || 'localhost';
    my $host_time = inet_daytime($host, 'tcp', 10);
    if($host_time) { print "Time on $host is $host_timen"; }
    else { print "inet_daytime failed: $!"; }

    在同樣的主機上執行,會產生下面的輸出:
    Time on somehost is Mon Feb 2 13:52:19 1998

    日期和時間的表示法

    暫停一下,回想你曾經見過的日期表示法。舉例來說,1998年7月21日可以被表示成
    7/21/98,21/7/98,21-July-1998,21.7.1998 和21-JUL-1998 等等。幸虧有一
    個國際的日期表示標準稱為ISO8601。它的表示法是:YYYY-MM-DD YYYY 是表示年份,
    MM 表示月份,DD 是表示日期。

    國際標準的時間表示法是:hh:mm:ss
    hh 是表示幾點(0-23),mm 是表示分鐘,ss 是表示秒鐘。如果時間未加修飾,則表示當地時間;
    在後面加上Z 則表示zero mer idian,這個時間是U niversal 時間(UTC )。

    我們已經看過Unix的時間,而Perl時間是表示成從1970-1-1 至今經過的秒數。
    所以現在的UTC 時間是寫成886457054,或是Mon Feb 2 22:04:14 1998。

    Date::Format

    Date::Format 可以將日期轉換成字串。它提供的方法相對於C函式庫的strftime和ctime。

    Date::Format 提供的函式是time2str〔其參數為格式樣版(format template)和(Unix時間)
    和strftime(其參數為格式樣版和一連串的時間值(例如由localtime所傳回的陣列)〕。
    這兩個函式都使用同樣的格式指定方式

    日期格式的指定方式
    格式指定元 說明
    %% 插入百分號
    %a 縮寫的星期幾, 如Mon
    %A 完整的星期幾, 如Monday
    %b 縮寫的月份,如Jan
    %B 完整的月份,如January
    %c ctime 的日期和時間的表示方式, 如02/03/98 02:43:11
    %d 每個月第幾天,使用兩位數表示(01-31),如03
    %e 每個月第幾天,使用整數表示(1 - 31),如3
    %D mm/dd/yy 格式, 如02/03/98
    %h 縮寫的月份,如Jan
    %H 使用24小時的時間格式(00 - 23)
    %I 使用12小時的時間格式(01 - 12)
    %j 每年的日期,使用三位數表示(001 - 366 )
    %k 用24 小時的時間格式(0 -23 )
    %l 使用12小時的時間格式(01 - 12)
    %m 月,使用整數表示(1 - 12)
    %M 分鐘,使用兩位數表示( 00-59 )
    %n換行字元
    %o每月的第幾天, 如1st,2nd
    %pam或pm
    %r時間格式:02:51:50am
    %R 時間格式:02:51
    %s 從1970-1-1起算經過多少秒(UTC),如8866499802
    %S 使用兩位數表示的秒(00 - 59)
    %t tab字元
    %T 時間格式: 02:56:42
    %U 每年第幾個禮拜(01-51),星期天是每個禮拜第一天
    %w 星期幾用十進位表示(0-6 )
    %W 每年第幾個禮拜(01-51),星期一是每個禮拜第一天
    %x 日期表示02/03/98
    %X 時間表示02:56:42
    %y 不表示世紀的年(00-99 )
    %Y 表示世紀的年,如1998
    Z %ASCII時區的縮寫,如PST、EST
    %z 時區,使用-/+0000 的格式

    Date::Format 也包含函式ctime 和asctime,功能與使用格式指定字串“%a %b %e %T %Yn”
    來呼叫time2str及strftime函式相同。Date::Format也可以格式化成數個不同的語言,請看模組文件內附
    的完整說明。

    Time::Zone

    我們已經介紹過時間和日期的表示法,為了完整性,我們將討論時區。時區一般是以三個字元表示
    ,如GMT、PST 、或EST。這種表示法並非是標準,全世界各地可能有不同的表示法。

    有一個國際時區的標準表示法:單一字元碼(one-character code)(請看世界時區圖
    http://aa.usno.navy.mil/AA/faq/docs/world_tzones.html)。至於正式的Internet 時
    區標準,請參看ftp://elsie.nci.nib.gov/pub/。我回頭介紹每個人真正使用的時區表示方式。

    Time::Zone 模組所提供的服務是關於時區的處理。它可以解析TZ 環境變數,決定本地或某一特定時
    區與GMT的秒偏差(off set in seconds)。給定與GMT 的秒偏差,它也可以產生時區的表示方式。

    tz2zone函式解析TZ環境變數(或是一個選擇性的時區引數),並產生適合顯示的輸出。
    下面範例中,程式以不同的格式當作參數呼叫tz2zone,所以你可以看它對各種格式所做出的反應。

    範例tz2zone: 時區顯示方式的示範
    use Time::Zone;
    use strict;
    print tz2zone('MST'), "n";                                # just use MST
    print tz2zone('MST7MDT'), "n";                     # give it a full
                                                                              # zone-offset-daylight zone string
    print tz2zone('MST7MDT', undef, 1), "n";      # lie and make it think
                                                                              # it's daylight savings time

    這會產生下面的輸出(在標準時間的機器上執行):
    MST
    MST
    MDT

    要找出GMT和MST( Mountain Standard Time)的秒偏差, 我們可以使用tz_offset函式
    (若要取得與目前時區的秒偏差,可以使用tz_local_of fset)。

    範例tzoffset.pl:示範如何顯示時區之間的秒偏差
    use Time::Zone;
    use strict;
    my $time_zone = 'MST';
    my $offset = tz_offset($time_zone);
    print "$time_zone is ", $offset/3600, " hours from GMTn";

    輸出如下:
    MST is –7 hour from GMT
    tz_name函式根據偏差來決定時區的名稱。所以給定一個和GMT 的偏差值(以秒作單位),
    tz_time會做出時區的表示。

    範例:tzlist.pl:時區顯示程式
    use Time::Zone;
    use strict;
    my ($tz);
    for (-12 .. 12) {
    $tz = tz_name($_ * 3600);            # convert hours to seconds
            print "$tz: $_ n";                   # but print hours, since that's
    } # easier to understand

    Date::Parse

    Date::Parse模組的功能和Date::Format 相反,也就是給一個日期敘述的字串,它會傳回一個用秒表示
    的日期值,或是一個日期元素的陣列。

    範例dateparse.pl:Date::Parse的使用範例
    use Date::Parse;
    use strict;
    my $date_expr = "Feb 3 1998 03:06:32";
    print str2time($date_expr), "n"; # prints seconds since the epoch
    print join(" ", strptime($date_expr), "n"); # prints list of date elements

    它會產生下面的輸出:
    886500392
    32 06 03 03 02 98
    如同Date::Format,Date::Parse可以處理不同語言的日期。

    Date::DateCalc

    Date::DateC alc 可用來解析及計算與ISO/R 2015-1971 和DIN 1355 標準相容的日
    期。例如,你可以增加日期使用calc_new_dat e 的函式或是利用check_date 函式檢
    查一個日期是否合法。範例4-7 使用calc_new_date 函式將日期增加30 天。

    範例datecalc.pl: Date::DateCalc 範例
    use Date::DateCalc qw(calc_new_date date_to_string);
    use strict;
    my ($year, $month, $day) = (1998, 2, 14); # start with Valentine's
    Day($year, $month, $day) = calc_new_date($year, $month, $day, 30);
    print date_to_string($year, $month, $day), "n";

    這產生下面的輸出:
    Monday, 16 March 1998
    你也可以使用dates_dif ference 去找出耶誕節和萬聖節之間隔了多少天。

    範例datediff.pl:使用dates_difference 函式
    use Date::DateCalc qw(dates_difference);
    use strict;
    my ($y1, $m1, $d1) = (1998, 10, 31); # specify dates in their
    my ($y2, $m2, $d2) = (1998, 12, 25); # normal chronological order
    print "There are ",
    dates_difference($y1, $m1, $d1, $y2, $m2, $d2)," days between Christmas and Halloweenn";

    Date::Manip

    最後一個模組是Date::Manip,它是另一個方便的日期解析器和計算器。
    Date::Manip比Date::DateCalca更進一步,它讓你處理日期表示法(date expression),
    像是“today”和“+3 business days”甚至“3rd Sunday in June 1998”
    【譯註】。Date::Manip 甚至可以讓你將假期和一些特殊的日期儲存在一個檔案中,
    在表示法中直接使用這些設定的日期。

    使用Date::Manip之前必須做些準備工作:確定它可以從TZ環境變數推論出時區。如果你不能或
    不願意設定這個變數,你可以自行告訴Date::Manip時區是使用$Date::Manip::TZ 變數。然而當你
    如此做時,若沒有這個變數,Date::Manip 可能無法順利執行。

    ParseDate 函式接受很多不同形式的日期串列,並且以下面的格式
    傳回日期串列:YYYYMMDDHH:MM:SS

    下面的例子顯示一些ParseDate可以處理的日期格式:
    use Date::Manip;
    use strict;
    my $dt;
    $dt = ParseDate('today');
    $dt = ParseDate('tomorrow');
    $dt = ParseDate('third Sunday in June 1998');
    $dt = ParseDate('June 21 1998');
    $dt = ParseDate('last Thursday');
    $dt = ParseDate('in 2 months');

    如果傳給par sedate 的參數是它無法處理的格式,它會傳回undef:
    $dt = ParseDate("When Hell freezes over"); # dt will be undef

    除了處理日期之外,Date::Manip提供一個UnixDate函式將日期格式化。和 Date::Format函式類似
    ,UnixDate 讀取一個日期字串(如同ParseDate 傳回的)和特殊格式的指定字,並且傳回一個
    相對於每一個格式的陣列。

    範例today.pl:使用ParseDate 函式
    use Date::Manip;
    use strict;
    print UnixDate(ParseDate('today'), "%c"), "n";
    這會產生下面的結果:
    Tue Feb 3 04:21:16 1998

    UnixDate 額外的日期指定格式

    格式指定字 敘述
    %E 每月的日期(例如1st, 2nd , 3rd)
    %f 月,用整數表示(例如1 - 12)
    %F %A %B %e , %Y (例如Tuesday, February 3 ,1 9 98 )
    %g%a , %d %b %Y %H :%M:%S :%z( 例如Tue, 03 Feb 1998 0 4:27:33 MST )
    %i 小時, 使用1-12 的格式, 例如4
    %l 如果在六個月內則日期使用%b %e %H:%M 的格式,否則使用%b %e %y。
    這是以Unix ls 指令的輸出為基礎(類似於dir)。
    %P %Y%m%d%H %:%M:%S (例如1998 0 20 30 4:27:33)
    %q %Y%m%d%H%M%S (例如1998 02 0 30 4 27 33 )
    %Q %Y%m%d (例如1998 02 03)
    %u 和%C 相同(例如Tue Feb 30 4:27:33 MST 1998)
    %v 縮寫的每一周的日期(例如 T 表示Tuesday)
    %V %m%d %H%M%y ( 例如0203042798)

    除了解析和格式化日期外,Date::Manip 提供許多了函式,可以用更彈性的方式計算日期。
    如下範例中就增加五個工作天到目前的日期。

    你也可以使用Date_IsWorkDay,Date_NextWorkDay,Date_PrevWorkDay 函式處理工作天。

    範例add_date.pl:使用DateCalc
    use Date::Manip;
    use strict;
    my $dt = ParseDate("today");                                  # parse today into a date format
    $dt = DateCalc($dt, "+ 5 business days");               # add 5 business days
    print UnixDate($dt, "%F"), "n";

    Date::Manip 讓你定義你自己的假期或是特殊的日子,並且將它們儲存在你指定的目錄或目前目
    錄的檔案中。

    如果你想深入關於Date::Manip 如何保存設定檔的細節,
    請參考「Perl Module Reference」的Date::Manip 部份。              

    沒有上一則|日誌首頁|沒有下一則
    回應