Time::Piece - 日付・時刻を扱う新しい方法



  1. Perl




  2. モジュール



  3. here

Perlの5.10からTime::Pieceと呼ばれる時刻を便利に扱うためのモジュールが標準モジュールに加わりました。Perlの5.10以上を使用している場合はTime::Pieceモジュールを日付と時刻を扱いたい場合に利用すると便利です。

従来のPerlでは日付や時刻を直感的に扱う手段が標準モジュールにはありませんでした。日付や時刻を扱うためには少し面倒な作業が必要でした。Time::Pieceは日付と時刻の直感的な操作を提供します。また日付・時刻の書式化の機能や解析する機能も備えます。

Perlには実質的に日付を扱うモジュールのスタンダードといえるDateTimeというモジュールがあります。ただしこのモジュールは標準モジュールではなくCPANからインストールする必要があります。また巨大なモジュールですのでモジュールを読み込む時間が非常にかかります。mod_perlなどメモリ上にモジュールを展開しておくことのできる環境以外では実行速度に少し不満がでるかもしれません。

Time::Pieceはある程度の機能を備えた高速・軽量なモジュールだと考えてください。

日付・時刻に関する一般的な情報は、以下を参考にしてください。

時刻・日付の情報の取得

 Time::Pieceモジュールは次のように使用します。useをするとTime::Pieceモジュールはコアのlocaltimeとgmtimeを上書きます。

use Time::Piece;

# Time::Pieceオブジェクトの取得
my $t = localtime;

# 日付や時刻の情報の取得
my $year   = $t->year;
my $month  = $t->mon;
my $mday   = $t->mday;

my $hour   = $t->hour;
my $minute = $t->minute;
my $second = $t->sec;

 localtimeを呼び出すとTime::Pieceオブジェクトを取得することができこれから日付や時刻の情報を取得することができます。

 標準関数のlocaltimeで時刻情報を取得した場合を比較のために記しておきます。

# 標準関数のlocaltimeで時刻情報を取得した場合
my ($second, $minute, $hour, $mday, $month, $year) = localtime;

# 月は0から始まるので1を加算
$month += 1;

# 年は1900を引いた値で取得されるので1900を加算
$year  += 1900;

 Time::Pieceのlocaltimeを使用したほうが直感的に日付や時刻の情報を扱うことができます。

Time::Pieceで取得できる情報一覧

 Time::Pieceで取得できる情報の一覧です。

$t->year
$t->_year 年 - 1900 (標準関数のlocaltimeと同じ) = "aaa"
$t->yy 年の下2桁
$t->mon 月(1から始まる)
$t->_mon 月(0から始まる。標準関数のlocaltimeと同じ)
$t->monname 月名の短縮名(Febなど)
$t->month $t->monnameと同じ
$t->fullmonth 月名(Februaryなど)
$t->mday
$t->day_of_month $t->mdayと同じ
$t->hour 24 hour
$t->min
$t->minute $t->minと同じ
$t->sec
$t->second $t->secと同じ
$t->wday 週番号(1が日曜日)
$t->_wday 週番号(0が日曜日)
$t->day_of_week $t->_wday
$t->wdayname 週名の短縮名(Tueなど)
$t->day $t->wdayname
$t->fullday 週名(Tuesdayなど)
$t->yday 年の中で何日目か
$t->day_of_year $t->ydayと同じ
$t->isdst 夏時間かどうか
$t->daylight_savings $t->isdstと同じ
$t->epoch エポックからの秒数

日付の判定

 Time::Pieceを使用すればうるう年かどうかの判定と月末日かどうかの判定を行うことができます。

# うるう年かどうか
$t->is_leap_year

# 月末日の取得(28-31の数値を返す)
$t->month_last_day

日付・時刻を書式化する

 日付・時刻を書式化して出力したい場合があります。Time::Pieceでは決まった書式がいくつか用意されています。またstrftimeメソッドを使用すれば自由に書式化することができます。

[A]デフォルトで用意されているフォーマット

 デフォルトで用意されているフォーマットは以下になります。

$t->hms 12:34:56
$t->hms(".") 12.34.56
$t->time $t->hmsと同じ
$t->ymd 2000-02-29
$t->date $t->ymdと同じ
$t->mdy 02-29-2000
$t->mdy("/") 02/29/2000
$t->dmy 29-02-2000
$t->dmy(".") 29.02.2000
$t->datetime 2000-02-29T12:34:56 (ISO 8601)
$t->cdate Tue Feb 29 12:34:56 2000
"$t" $t->cdateと同じ
[B]フォーマットのカスタマイズ

 フォーマットを自由にカスタマイズしたい場合はstrftimeメソッドを使用します。$formatには時刻の表現を指定します。

$t->strftime($format)

 日付と時刻をフォーマット化したサンプルです。

$t->strftime('%Y-%m-%d %H:%M:%S'); # 2009-11-34 12:14:15

 strftimeで使用できるフォーマットの一覧です。

%a : 曜日の省略名
%A : 曜日名
%b : 月の省略名
%B : 月名
%c : デフォルトのフォーマット
%C : 年の最初の2桁
%d : 日( 01 から 31 )
%D : %m/%d/%y と同じ。月日年
%e : 日( 1 から 31 )
%F : %Y-%m-%d と同じ( 2008-11-31 など )
%G : 年( %Yとの違いはよくわからない)
%g : 年の下2桁 ( 00 〜 99 )
%h : %b と同じ。月の省略名
%H : 時( 00 から 23 )
%I : 時( 01 から 12 ) 12時間表記での時刻
%j : 年の最初から初めて何日目か。( 001 から 366 )
%k : 時( 0 から 23 ) 1桁の数字( 0〜 9 )の2桁目はスペースになる。
%l : 時( 1 から 12 ) 12時間表記での時刻 1桁の数字( 0〜 9 )の2桁目はスペースになる。
%m : 月の番号( 01 から 12 )
%M : 分( 00 から 59 ) 
%n : 改行文字 
%N : ミリ秒( %3N と書くとミリ秒を3桁で、 %6Nと書くとミリ秒を6桁で表示 ) 
%p : am か pm 
%P : AM か PM 
%r : %I:%M:%S %p と同じ。( 11:55:23 PM など) 
%R : 時分( 15:16 など ) 
%s : エポックからの秒数 
%S : 秒( 00 から 61 ) 
%t : タブ文字 
%T : %H:%M:%S と同じ。時分秒( 23:14:03 など ) 
%u : 曜日の番号( 1 から 7。 月曜日が1 ) 
%U : 年の最初から数えて何週目か。( 00 から 53 ) 年の最初の日曜日を週の最初として数える。最初の日曜日の週が01。その前の週が00。 
%V : 年の最初から数えて何週目か。( 01 から 53 ) 少なくとも4日を持つ週を年の最初の週とする。月曜日を週の最初として数える。 
%w : 曜日の番号( 0 から 6。 日曜日が0 ) 
%W : 年の最初から数えて何週目か( 00 から 53 )  年の最初の月曜日を週の最初として数える。最初の月曜日の週が01。その前の週が00。 
%x : 日付のデフォルトのフォーマット 
%X : 時刻のデフォルトのフォーマット 
%y : 年の下の2桁 
%Y : 年 
%z : UTC(協定世界時)からのタイムゾーンの時刻のずれ) 
%Z : タイムゾーン名 
%% : % 
日本語を扱うときの注意

 Time::Pieceのstrftimeメソッドは、入力がバイト文字列であっても、内部文字列であっても、常にバイト文字列を返却することに注意しましょう。

# 常にバイト文字列が返却される
my $bytes = $tp->strftime("%Y年%m月");

日付・時刻を表現する文字列を解析する

 日付や時刻の表現を解析してTime::Pieceオブジェクトを作成することができます。日付や時刻の解析するにはstrptimeメソッドを使用します。

my $t = Time::Piece->strptime(日付・時刻を表現する文字列, フォーマット);

 サンプルとしてMySQLの日付の表現を解析してTime::Pieceオブジェクトを作成してみます。フォーマットには上記のstrftimeのフォーマットを使用します。

my $datetime_mysql = '2009-12-31 23:59:59';
my $t = Time::Piece->strptime($datetime_mysql, '%Y-%m-%d %H:%M:%S');

ローカライゼーション

 デフォルトの状態では月の名前や週の名前は英語です。日本語の週名を取得したい場合は週名を取得するメソッドに曜日名の配列を渡します。

my @week_names = ('日', '月', '火', '水', '木', '金', '土');
my $t = Time::Piece::localtime;
# 日本語で週名を取得
my $wday = $t->wdayname(@week_names);

日付・時刻情報の計算

Timpe::Pieceオブジェクトの引き算

 Time::Pieceでは日付や時刻の計算を行うこともできます。Time::Pieceオブジェクトどうしの引き算を行った場合は、戻り値はTime::Secondsオブジェクトになります。このオブジェクトは文字列として評価した場合は秒数になります。

my $sec = $t2 - $t1;

 またTime::Socondsオブジェクトを日や年に変換することもできます。

$sec->seconds;
$sec->minutes;
$sec->hours;
$sec->days;
$sec->weeks;
$sec->months;

# 30日
$sec->financial_months;

 日や年への変換は次の規則によってなされます。1日は24時間と換算されます。1週間は7日と換算されます。1年は365.24225日と換算されます。1年は12ヶ月と換算されます。

Time::Pieceオブジェクトの足し算

 Time::Pieceオブジェクトどうしの足し算はすることができません。Timpe::Pieceオブジェクトには秒数あるいはかTime::Secondsオブジェクトを足すことができます。

$t + 533;
$t + Time::Seconds->new(300);

 またTime::SecondsにはTime::Pieceオブジェクトに足すことのできる定数も用意されています。

ONE_DAY
ONE_WEEK
ONE_HOUR
ONE_MINUTE
ONE_MONTH
ONE_YEAR
ONE_FINANCIAL_MONTH
LEAP_YEAR
NON_LEAP_YEAR

 5日を足すには次のようにします。これらの定数を利用するにはTime::Secondsモジュールを読み込んでおく必要があります。

use Time::Seconds;
$t + ONE_DAY * 5

日付の比較

 日付の比較に比較演算子を使用することができます。"<", ">","<=", ">=", "<=>", "==" and "!="が使用できます。

$t1 < $t2

Time::Pieceオブジェクトの生成

 任意の日付・時刻でTime::Pieceオブジェクトを生成するにはドキュメントを読む限りではstrptimeメソッドを使用するしかないようです。

my $t = Time::Piece->strptime('2009-12-31 23:59:59', '%Y-%m-%d %H:%M:%S');

 (ソースコードを読むとparseメソッドというものがあるのですが試してみたところ壊れているみたい...。ドキュメントには無いので非公開のようですし。)

今日の日付をあらわすオブジェクトを取得する

Time::Pieceモジュールを使用して、今日を表すオブジェクトを取得するには次のようにします。

use Time::Piece;

my $today = today();

sub today {
  # Today (YYYY-mm-dd HH:MM:SS)
  my $today_datetime = localtime;
   
  # Today (YYYY-mm-dd 00:00:00)
  my $today = Time::Piece->strptime($today_datetime->ymd, "%Y-%m-%d");
    
  # Localize
  $today = localtime($today);
    
  return $today;
}

なぜこれほど複雑なのかといえば、Time::Pieceモジュールはstrptime以外の方法で直接、日付や時間の項目を設定する仕組みを持っていないことと、strptimeはUTCを返すので、自力でローカルタイム化を行う必要があるからです。

今月の最初の日を表すTime::Pieceオブジェクトを取得する

Time::Pieceを使って、今月の最初の月を取得するには、以下のようなロジックを書きます。

use strict;
use warnings;
use Time::Piece;

# 今月の最初の日のTime::Pieceオブジェクトを取得
my $today_tp = localtime;
my $today_ym = $today_tp->strftime('%Y-%m');
my $this_month_first_day_tp
  = localtime Time::Piece->strptime("$today_ym-01", '%Y-%m-%d');

これは少しややこしいですね。Time::Pieceを使って日付や時間をごにょごにょするためには、少しコツがいります。

まず最初に今日を表すTime::Pieceオブジェクトを取得します。

my $today_tp = localtime;

次に今日の年と月の情報をstrftimeを使って「2012-09」のようなフォーマットで取得します。

my $today_ym = $today_tp->strftime('%Y-%m');

最後に「2012-09-01」のような日付のフォーマットを持つ文字列をstrptimeで解析して、今日を表すTime::Pieceオブジェクトを取得します。

my $this_month_first_day_tp
  = localtime Time::Piece->strptime("$today_ym-01", '%Y-%m-%d');

strptimeはロケール情報を持たないので、localtime関数を使ってローカルタイム化しています。