Perl入門ゼミ

テキスト処理、Linuxサーバー管理、Web開発ならPerl
  1. Perl
  2. モジュール
  3. here

DateTime - 日付・時刻を汎用的に扱う

DateTimeを使用すると、日付と時刻を汎用的に扱うできます。

これまでは、なんとか標準モジュールで日付を扱おうと記事を書いてきましたが、本当はあまりよくない方法です。日付と時刻というのは例外が多く自分でスクリプトを書くとバグが混入しやすいです。

このモジュールは便利ですが、汎用的に日付を扱うために依存するモジュールの数が非常に多いです。また日付の計算を早く行うために、一部はC言語で記述されています。このため、Windows環境でのインストールには少し準備が必要です。

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

CPANからDateTimeをインストールする

コマンドプロンプトで

cpan DateTime

と打ってください。cpanを使用するのがはじめての場合は、cpanの設定の初期化の作業が必要です(たぶん)。何かたずねられたらEnterを押して進めてください(非常に長いです)。

cpanの初期設定が終わっている場合は、ダウンロードできます。依存するモジュールをインストールしますかと何回も聞かれるので、ENTERを押して進めます。

最後まで進めば終了です。

Perl 5.10以降はPerlの標準モジュールとしてTime::Pieceを使えるので、Time::Pieceを使うのもよい方法でしょう。Time::Pieceによる時刻の扱いについては「「Time::Piece」で日付と時刻を扱う」をご覧ください。

DateTime 初期化と日付・時刻情報の取得

汎用的に日付を扱うには、DateTimeモジュールを利用します。

use DateTime;
my $dt = DateTime->new( 
  year => 1964,
  month => 10,
  day => 16,
  hour => 16,
  minute => 12,
  second => 47,
);

DateTimeモジュールはオブジェクト指向のモジュールです。DateTime->new メソッドでDateTimeオブジェクトを作成します。年と月はそのままの数を指定します。

標準関数のlocaltime関数や標準モジュールのTime::Localのように、年を1900年からの年数で指定したり、月を0から始まる数値で指定したりする必要がありません。

日付・時刻情報の取得

# 時刻情報を取得するGetメソッド
my $year = $dt->year;
my $mon = $dt->month;
my $mday = $dt->day;
my $wday = $dt->day_of_week;
my $hour = $dt->hour;
my $minute = $dt->minute;
my $second = $dt->second;
my $yday = $dt->day_of_year;
my $qtr = $dt->quarter;

上記のメソッドで、年月日、時分秒、曜日、1年で何日目か、4半期で数えていくつ目かなどを取得できます。曜日は月曜日が1,日曜日が7になります。

現在の日付と時刻を取得する

現在時刻を表現するDateTimeオブジェクトを作成するにはnowメソッドを使用します。引数で、time_zone にlocalを指定すれば、OSに設定されているはずである自分の国のタイムゾーンになります。

my $dt = DateTime->now(time_zone => 'local');

タイムゾーンを明示的に指定することもできます。

my $dt = DateTime->now(time_zone => 'Asia/Tokyo'); 

タイムゾーンを明示的に指定するほうが、localを指定するより速度がやや遅いらしいです。ただし、明示的に指定すると移植性が損なわれます。

引数を指定しなかった場合はUTC(協定世界時)になります。

my $dt = DateTime->now;

エポック秒から日付情報を作成する

エポック秒からDateTimeオブジェクトを作成するには from_epochメソッドを使用します。

my $dt = DateTime->from_epoch(epoch => $epoch);

引数に time_zone を指定しない場合は、UTC(協定世界時)になります。以下のように、 time_zone => 'local' を加えると、ローカル時刻を取得することができます。

my $dt = DateTime->from_epoch( epoch => $epoch, time_zone => 'local' );

タイムゾーンオブジェクトを作成する

DateTimeはコンストラクタで、タイムゾーンを指定することができます。DateTime::TimeZoneオブジェクトを先に作成しておくと再利用することができます。

my $time_zone = DateTime::TimeZone->new(name => 'Asia/Tokyo');

タイムゾーンオブジェクトの作成

タイムゾーンオブジェクトを作成するには、newメソッドを使用します。引数で、name に タイムゾーン名を指定します。(タイムゾーン名一覧)

my $time_zone = DateTime::TimeZone->new(name => 'Asia/Tokyo');

タイムゾーンオブジェクトの自動生成

以下のように、DateTimeのコンストラクタ(new, nowなど)で、タイムゾーンを指定した場合は、内部で自動的に、DateTime::TimeZoneオブジェクトが作成されます。

my  $dt = DateTime->now(time_zone => 'Asia/Tokyo');

DateTimeオブジェクト作成するたびに、DateTime::TimeZoneオブジェクトが作成されることになります。

これはコストになりますので、2回以上DateTimeオブジェクトを作成する場合は、最初にDateTime::TimeZoneオブジェクトを作っておくほうがよいです。

タイムゾーンオブジェクトの利用

DateTimeのコンストラクタの引数の、time_zone に、作成したDateTime::TimeZoneオブジェクトを指定するだけです。

my $dt1 = DateTime->now(time_zone => $time_zone);

タイムゾーンの一覧は「タイムゾーンの一覧」をご覧ください。

月名と曜日名を日本語で出力する ロケールの指定

日付の表現は国や地域によって表現が異なります。たとえば1月はアメリカではJanuaryと表現します。月曜日はMondayと表現します。国や地域ごとの表現のセットをロケールと呼びます。DateTimeモジュールは、さまざまな国や地域のロケールを指定することができます。

my $dt = DateTime->now(locale => 'ja');

日本のロケールは、jaになります。

ロケールの指定

nowメソッドで、現在の時間情報を持つDateTimeオブジェクトが作成されます。引数に、locale を指定することができます。

またtime_zone にlocalを指定することで、ローカル時刻で時間情報が作成されます。

my $dt = DateTime->now(locale => 'ja' , time_zone => 'local');

ロケールに依存する情報 月名と曜日名

下記の4つのメソッドがロケールに依存します。たとえば、day_name は、ロケールが日本の場合は月曜日、ロケールがアメリカ地域のどこかだった場合は、Monday のようになります。

abbr は abbreviation( 省略 ) の略です。

# 月名
my $month_name = $dt->month_name;

# 月の省略名( 省略名がない場合は月名と同じ)
my $month_abbr = $dt->month_abbr;

# 曜日名
my $day_name = $dt->day_name;

# 曜日の省略名
my $day_abbr = $dt->day_abbr;

出力するためにエンコードする

月名や曜日名を取得したとき、そこで使われている文字はUTF-8フラグが立っています。UTF-8フラグが立っているということは、Perlが文字列を内部表現で扱かっているということです。

画面やファイルに出力する時は、UTF-8フラグを落とす必要があります。つまり、エンコードして文字列を内部表現から外部表現に変換する必要があります。

Windowsの人は、Shift-JIS に、それ以外のOSの人は、UTF-8で出力するようにしてあります。

# UTF-8フラグが立っているので、エンコードする。
my $encode_to;
if ($^O eq 'MSWin32') { $encode_to = 'shift-jis' }
else { $encode_to = 'utf8' }

print(
  encode($encode_to, $month_name) . "\n" .
  encode($encode_to, $month_abbr) . "\n" .
  encode($encode_to, $day_name) . "\n" .
  encode($encode_to, $day_abbr) . "\n"
);

ロケール指定のためのクラス

DateTimeのコンストラクタの引数でロケール名を指定することができます。

my $locale = DateTime::Locale->load('ja');

DateTimeはコンストラクタの中で、DateTime::Localeオブジェクトを生成しています。そして生成したロケールオブジェクトに基づいて地域や国ごとの日付情報の出力を作ります。

ロケールオブジェクトの明示的な作成

my $locale = DateTime::Locale->load('ja');

明示的にDateTime::Localeオブジェクトを作成するには、loadメソッドを使用します。loadメソッドは、初回はDateTime::Localeオブジェクトを作成しますが、2回目以降はメモリに保存したDateTime::Localeオブジェクトを再利用します。

プログラム中で'ja'を表現するDateTime::Localeオブジェクトは常に存在しないかたったひとつであるかのどちらかです。このようなオブジェクトをsingleton(シングルトン)と呼びます。DateTime::TimeZoneオブジェクトのように毎回作成されるわけではありません。

ロケールオブジェクトをコンストラクタに渡す

ロケールオブジェクトをDateTimeのコンストラクタに渡すには、以下のようにします。

my $dt = DateTime->now(locale => $locale , time_zone => 'local');

明示的にロケールオブジェクトを渡すこともできますが、

my $dt = DateTime->now(locale => 'ja' , time_zone => 'local');

のように、文字列で指定するのが楽です。こう書くと、DateTimeのコンストラクタの中で自動的にDateTime::Localeオブジェクトが作成されます。

利用できるロケールについては「利用できるロケール一覧」をご覧ください。

年月日、時刻情報をまとめて取得する

DateTimeを使って、日付や時刻の情報を得ることができます。さまざまなフォーマットで取得できます。引数で区切り文字を指定することができます。引数を省略すると、年月日の情報の場合は-で連結され、時刻情報の場合は:で連結されます。

# 年月日
# yyyy-mm-dd
my $ymd = $dt->ymd;

# yyyy/mm/dd
my $ymd_slash = $dt->ymd('/');

# mm-dd-yyyy
my $mdy = $dt->mdy;

# mm/dd/yyyy
my $mdy_slash = $dt->mdy('/');

# dd-mm-yyyy
my $dmy = $dt->dmy;

# dd/mm/yyyy
my $dmy_slash = $dt->dmy('/');

# 時刻の情報
# hh:mm:ss
my $hms = $dt->hms;           

# hh-mm-ss
my $hms_hyphen = $dt->hms('-');

# 年月日と時刻の情報
my $ymdhms = $dt->datetime;

出力結果

(1)DateTimeの日付・時刻の便利な出力方法
年月日(ハイフン)   2008-11-14
年月日(スラッシュ) 2008/11/14
月日年(ハイフン)   11-14-2008
月日年(スラッシュ) 11/14/2008
日月年(ハイフン)   14-11-2008
日月年(スラッシュ) 14/11/2008

時分秒(コロン)     23:48:14
時分秒(ハイフン)   23-48-14

年月日時分秒       2008-11-14T23:48:14

うるう年の判定 月末日の取得

DateTimeはうるう年を判定するメソッド、月末日を取得するメソッドを持っています。

# うるう年かどうか判定
my $is_leap_year = $dt1->is_leap_year;

# 月末日
my $dt2 = DateTime->last_day_of_month(year => 2008, month => 11);

うるう年かどうかを判定する。

うるう年かどうかを判定するにはis_leap_yearメソッドを使用します。

my $is_leap_year = $dt1->is_leap_year;

月末日を取得する

月末日を取得するには、last_day_of_monthメソッドを使用します。引数に year と month を指定すると、月末日を表現するDateTimeオブジェクトが返却されます(月末日そのものが返却されるわけではありません)。返却されたDateTimeオブジェクトからからdayメソッドで日付を取得します。

my $dt2 = DateTime->last_day_of_month(year => 2008, month => 11);

日付の書式指定「strftime」メソッド

日付を書式化するためのはstrftimeメソッドを使用します。引数には、書式指定子を渡します。

$dt->strftime("%a");

以下のように文字列を組み合わせて複数指定することもできます。

$dt->strftime("%Y-%m-%d");

strftime書式指定子一覧

strftimeの書式指定子一覧です。

%a 曜日の省略名
%A 曜日名
%b 月の省略名
%B 月名
%c デフォルトのフォーマット
%C 年の最初の2桁
%d 日( 01 から 31 )
%D %m/%d/%y と同じ。月日年
%e 日( 1 から 31 ) :1桁の数字( 1 ~ 9 )の2桁目はスペースになる。
%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 タイムゾーン名
%% %
%{method} DateTime.pm のメソッドを利用することができる。例 %{ymd}

日付の足し算

日付や時刻を加算するにはaddメソッドを使用します。

# 日の加算
$dt->add(days => 1);

# 月の加算
$dt->add(months => 1);

# 年の加算
$dt->add(years => 1);

# 時の加算
$dt->add(hours => 1);

# 分の加算
$dt->add(minutes => 1);

# 秒の加算
$dt->add(seconds => 1);

#まとめて加算
$dt->add(years => 1, months => 1, days => 1, hours => 1, minutes => 1, seconds => 1);

日付の引き算

日付からある期間を減算するにはsubtractメソッドを使用します。

# 日の減算
$dt->subtract(days => 1);

# 月の減算
$dt->subtract(months => 1);

# 年の減算
$dt->subtract(years => 1);

# 時の減算
$dt->subtract(hours => 1);

# 分の減算
$dt->subtract(minutes => 1);

# 秒の減算
$dt->subtract(seconds => 1);

日付どうしの引き算を行うこともできます。-記号を使って普通に計算するようにできます。(演算子のオーバーロードという機能を使って実現されています。)戻り値は、DateTimeオブジェクトではなくて、期間を表すDateTime::Durationオブジェクトになります。years, months, days メソッドで、期間の年月日を取得できます。

my $duration = $dt2 - $dt1;

日付の引き算には2種類あります。日付にある期間を引き算(たとえば1ヶ月前)するものと、日付どうしの引き算(たとえば2008年10月10日と2009年3月14日の差)するものがあります。

期間を表すオブジェクト

日付と日付の間の期間はDateTime::Durationオブジェクトによって表現されます。DateTimeが日付の情報を表現するのに対して、DateTime::Durationは期間の情報を表現します。日付にある期間を加えたりある期間を引いたりした場合は、addメソッドやsubstructメソッドを使用しましたが、内部的には、DateTime::Durationオブジェクトが作成された上で演算がなされています。

DateTimeオブジェクトどうしの引き算を行うと、戻り値としてDateTime::Durationオブジェクトが返却されます。演算子のオーバーロードという機能が利用されていて、- 記号を使ってDateTimeオブジェクトどうしの引き算を行うことができます。

my $duration = $dt2 - $dt1;

以下のメソッドで期間の情報を取得できます。

# 年
$duration->years;

# 月
$duration->months;

# 日 
$duration->days;

# 時間
$duration->hours;

# 分
$duration->minutes;

# 秒
$duration->seconds;

注意しないといけないことは、それぞれのメソッドは単独では意味を持たないということです。たとえば、$duration->days は、期間の日数そのものを表しているのではないということです。期間が1ヶ月と30日だとすると、$duration->monthsは1に、$duration->days は30になります。

上記のメソッドは常に正の値を返却するので、どちらの期間が先かという判定を行うことができません。期間の順序を判定するには、is_positive, is_zero, is_negativeメソッドを使用します。

# 日付2 - 日付1が正
$duration->is_positive

# 日付2 - 日付1が同じ
$duration->is_zero

# 日付2 - 日付1が負
$duration->is_negative

期間をある単位で取得するにはin_unitsメソッドを使用します。

# 期間が1年と15ヶ月の場合は27になる
my $months = $duration->in_units('months');

# 期間が1年と15ヵ月の場合は2になる 
my $years = $duration->in_inits('years');

# 期間が1年と15ヶ月の場合は( 2, 3 )になります。
my ($years, $months) = $duration->in_units('years', 'months');

引数を2つ渡す場合は、リストコンテキストで受け取る必要があります。

単位で変換可能であるのは以下の4つだけです。

年と月 (1年は12ヶ月)
週と日 (1週間は7日)
時間と分 (1時間は60分 )
秒とミリ秒 (1秒は1000ミリ秒)

日と月は相互に変換できません。日数が月によって異なるためです。

分と秒は相互に変換できそうですが、変換できません。うるう秒の存在によって、1分が61秒になることがあるためです。

日付計算の注意点

日付の計算は不規則な部分があるのでできるだけ単純な方法で済ますほうがよいです。日付計算を複雑にしている原因を含めて、日付計算の注意点について書いておきます。

フローティングタイムゾーン( floating TimeZone )

DateTimeオブジェクトは、new メソッドで作成されて、タイムゾーンが指定されない場合は、デフォルトのタイムゾーンは、フローティングタイムゾーンと呼ばれるものになります。

フローティングタイムゾーンとは、存在するどのタイムゾーンとも関係がない、うるう秒を考慮しないタイムゾーンのことです。

タイムゾーンとうるう秒を考慮する必要がない場合は、フローティングタイムゾーンを用いて計算を行えばもっとも安心だということになります。

たとえばログから年月日を取得して、3ヶ月後の年月日を取得したいといった場合、フローティングタイムゾーンで計算して問題はありません。

うるう秒とは

時刻は絶対的なものではなくて、地球の自転が1週したときが一日と数えますから、地球の自転が遅れると一日の長さが変わってしまいます。うるう秒とは地球の自転が年々遅れていくために、何年かに一度挿入される秒のことです。

うるう秒が考慮されているタイムゾーンを用いて、うるう秒が間に挟まるように日付を計算すると、うるう秒が間に挟まらない場合と1秒のずれが生じます。

サマータイムとは

サマータイムとは1年のある期間だけ時刻を1時間早めるという政策のことです。ある瞬間に時刻を1時間早めるわけですから、1時間時刻が重複してしまいます。普通の時刻が2時だったとしたら、サマータイム変更した直後は1時になるけです。

この日は、1時から2時が重なってしまいます。

だからサマータイムを導入している国では、時刻の計算を行おうとすると非常に複雑なことになってしまいます。

日本の場合はサマータイムがありませんので、サマータイムの影響は考える必要がありません。

特定のタイムゾーンをUTCに変換する

日付の計算を単純にするために、特定のタイムゾーンをUTCに変換してから計算します。そして計算が終わった後で、元のタイムゾーンに戻します。

my $dt = DateTime->new(%user_input, time_zone => $user_tz);

# UTCに変換
$dt->set_time_zone('UTC');

# 日付の計算を行う。
# 元のタイムゾーンに戻す。
$dt->set_time_zone($user_tz);
print $dt->datetime;

日本の場合はサマータイムがありませんので、ローカル時刻で計算をしても奇妙な結果になることはありません。世界的にはサマータイムを導入している国が結構あるようです。

特定のタイムゾーンでの計算

特定のタイムゾーンで計算を行ってもたいていの場合は正しい結果が返ります。ただし、サマータイムが導入されている場合で、サマータイムへの変更の時間帯付近を含む場合は、奇妙な結果が返ります。

上記で解説したように、いったんUTCに変換して計算してから元に戻すのが安全です。

nowメソッドのデフォルトのタイムゾーン

nowメソッドで現在時刻を取得することができますが、デフォルトのタイムゾーンはUTCになります。

newでDateTimeオブジェクト作った場合は、デフォルトのタイムゾーンは、フローティングタイムゾーンになります。

前の月と次の月を取得する方法

DateTimeを使って、前の月と次の月を取得する方法です。(でも少し自信がない)

# 現在の年月
my $current_dt = DateTime->now(time_zone => 'local');
  
# 前の月
my $prev_dt = $current_dt->clone->add(months => -1, end_of_month => 'limit');

# 次の月
my $next_dt = $current_dt->clone->add(months => 1, end_of_month => 'limit');

「end_of_month => 'limit'」オプションをつけると、日付が1/31日とかでも、正しく日付が取得できる気がします。たとえば1/31に1月を足した結果は、2/28とかになる。

先の月や次の月にページ遷移したいアプリケーションとかでは便利ではないかと思います。

サンプルコード

最後にサンプルコードを掲載しておきます。

DateTimeを使ったサンプルです。

use strict;
use warnings;
use DateTime;

# DateTimeオブジェクトの初期化
# (1900年からの経過年ではなく、そのままの年を使用する。)
# (月は0から数えるのではなくて1から数える。 10 は 10月。)
my $dt = DateTime->new( 
  year => 1964,
  month => 10,
  day => 16,
  hour => 16,
  minute => 12,
  second => 47,
);

# 時刻情報を取得するGetメソッド
my $year = $dt->year;
my $mon = $dt->month;
my $mday = $dt->day;

# 曜日の番号(曜日は1から始まり1は月,7は日になる。)
my $wday = $dt->day_of_week; 
my $hour = $dt->hour;
my $minute = $dt->minute;
my $second = $dt->second;
my $yday = $dt->day_of_year;
my $qtr = $dt->quarter;

print "(1)DateTimeの日付・時刻情報\n";
print(
  "${year}年${mon}月${mday}日${wday}番目の曜日\n" .
  "${hour}時${minute}分${second}秒\n" .
  "${yday}日目(1年で数えて)\n" .
  "${qtr}期(4半期のうち)\n"
);

実行結果

(1)DateTimeの日付・時刻情報
1964年10月16日 5番目の曜日
16時12分47秒
290日目(1年で数えて)
4期(4半期のうち)

現在の日付と時刻を取得するサンプルです。

use strict;
use warnings;
use DateTime;

# (1)-1 現在の時刻を取得する( ローカル時間 )
my $dt = DateTime->now(time_zone => 'local');

# (1)-2 タイムゾーンを明示的に指定する場合。
# my $dt = DateTime->now(time_zone => 'Asia/Tokyo');

# (1)-3 UTC 協定世界時
# my $dt = DateTime->now;

# 時刻情報を取得するGetメソッド
my $year = $dt->year;
my $mon = $dt->month;
my $mday = $dt->day;
my $wday = $dt->day_of_week;
my $hour = $dt->hour;
my $minute = $dt->minute;
my $second = $dt->second;
my $yday = $dt->day_of_year;
my $qtr = $dt->quarter;

print "(1)現在の時刻を取得する。\n";
print(
  "${year}年${mon}月${mday}日${wday}番目の曜日\n" .
  "${hour}時${minute}分${second}秒\n" .
  "${yday}日目(1年で数えて)\n" .
  "${qtr}期(4半期のうち)\n"
);

実行結果

(1)現在の時刻を取得する。
2008年11月2日 7番目の曜日
8時11分5秒
307日目(1年で数えて)
4期(4半期のうち)

エポック秒から日付情報を作成するサンプルです。

use strict;
use warnings;
use DateTime;

# エポック秒から日付情報を作成する。
my $epoch = 1225589752;
my $dt = DateTime->from_epoch( epoch => $epoch );

# 時刻情報を取得するGetメソッド
my $year = $dt->year;
my $mon = $dt->month;
my $mday = $dt->day;
my $wday = $dt->day_of_week; #曜日の番号(曜日は1から始まり1は月,7は日になる。)
my $hour = $dt->hour;
my $minute = $dt->minute;
my $second = $dt->second;
my $yday = $dt->day_of_year;
my $qtr = $dt->quarter;

print "(1)エポック秒から日付情報を作成する。\n";
print(
  $year, "年",
  $mon, "月",
  $mday, "日 ",
  $wday, "番目の曜日\n",
  $hour, "時",
  $minute, "分",
  $second, "秒\n",
  $yday, "日目(1年で数えて)\n",
  $qtr,"期(4半期のうち)\n"
);

実行結果

(1)エポック秒から日付情報を作成する。
2008年11月2日 7番目の曜日
1時35分52秒
307日目(1年で数えて)
4期(4半期のうち)

タイムゾーンを指定するサンプルです。

use strict;
use warnings;
use DateTime;
use DateTime::TimeZone;

# (1)タイムゾーンオブジェクトの作成
my $time_zone = DateTime::TimeZone->new(name => 'Asia/Tokyo');

# OSの設定から自動的にタイムゾーンを取得する場合
# my $time_zone = DateTime::TimeZone->new(name => 'local'); 
                                                     
# (2)タイムゾーンオブジェクトの利用
my $dt1 = DateTime->now(time_zone => $time_zone);
my $dt2 = DateTime->now(time_zone => $time_zone); 

ロケールを設定するサンプルです。

use strict;
use warnings;
use DateTime;
use Encode;

print "(1)月名と曜日名を日本語で出力する。\n";
my $dt = DateTime->now(locale => 'ja', time_zone => 'local');

# 月名
my $month_name = $dt->month_name;

# 月の省略名( 省略名がない場合は月名と同じ)
my $month_abbr = $dt->month_abbr;

# 曜日名
my $day_name = $dt->day_name;

# 曜日の省略名
my $day_abbr = $dt->day_abbr;

# UTF-8フラグが立っているので、エンコードする。
my $encode_to;
if ($^O eq 'MSWin32') { $encode_to = 'shift-jis' }
else { $encode_to = 'utf8' }

print(
  encode($encode_to, $month_name) . "\n" .
  encode($encode_to, $month_abbr) . "\n" .
  encode($encode_to, $day_name) . "\n" .
  encode($encode_to, $day_abbr) . "\n"
);

出力結果

(1)月の名前と曜日を日本語で出力する。
11月
11月
金曜日
金

DateTimeでロケールを指定するサンプルです。

use strict;
use warnings;
use DateTime;
use DateTime::Locale;

# エンコード用
use Encode;
my $encode_to;
if ($^O eq 'MSWin32') { $encode_to = 'shift-jis' }
else { $encode_to = 'utf8' }

# ロケールの生成
print "(1) DateTime::Localeオブジェクト\n";
my $locale = DateTime::Locale->load('ja'); 
my $dt = DateTime->now(locale => $locale , time_zone => 'local');

# 曜日名
my $day_name = $dt->day_name;
print encode($encode_to, $day_name) . "\n";

日付や時刻を取得するサンプルです。

use strict;
use warnings;
use DateTime;
use DateTime::TimeZone;
use DateTime::Locale;

my $dt = DateTime->now(time_zone => 'local');

print "(1)DateTimeの日付・時刻の便利な出力方法\n";
my $ymd = $dt->ymd;
my $ymd_slash = $dt->ymd('/');
my $mdy = $dt->mdy;
my $mdy_slash = $dt->mdy('/');
my $dmy = $dt->dmy;
my $dmy_slash = $dt->dmy('/');
my $hms = $dt->hms;
my $hms_hyphen = $dt->hms('-');
my $ymdhms = $dt->datetime;

print(
  "年月日(ハイフン)   $ymd\n" .
  "年月日(スラッシュ) $ymd_slash\n" .
  "月日年(ハイフン)   $mdy\n" .
  "月日年(スラッシュ) $mdy_slash\n" .
  "日月年(ハイフン)   $dmy\n" .
  "日月年(スラッシュ) $dmy_slash\n\n" .
  "時分秒(コロン)     $hms\n" .
  "時分秒(ハイフン)   $hms_hyphen\n\n" .
  "年月日時分秒       $ymdhms\n"
);

うるう年の判定、月末日を取得するサンプルです。

use strict;
use warnings;
use DateTime;

print "(1)うるう年かどうかを判定する。\n";
my $dt1 = DateTime->now(time_zone => 'local');
my $is_leap_year = $dt1->is_leap_year;
if ($is_leap_year) { print "今年はうるう年です。\n\n" }
else { print "今年はうるう年ではありません。\n\n" }

print "(2)月末日を取得する。\n";
my $dt2 = DateTime->last_day_of_month(year => 2008, month => 11);
print "2008年11月の月末日は" . $dt2->day . "日です。\n";

strftimeメソッドのサンプルです。全書式をサンプルで試しています。

use strict;
use warnings;
use DateTime;

print "(1)strftime 書式指定一覧\n";
my $dt = DateTime->now(time_zone => 'local');

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

実行結果

(1)strftime 書式指定一覧
%a : 曜日の省略名 : Mon
%A : 曜日名 : Monday
%b : 月の省略名 : Nov
%B : 月名 : November
%c : デフォルトのフォーマット : Nov 17, 2008 12:09:37 AM
%C : 年の最初の2桁 : 20
%d : 日( 01 から 31 ) : 17
%D : %m/%d/%y と同じ。月日年 : 11/17/08
%e : 日( 1 から 31 ) 1桁の数字( 1 ~ 9 )の2桁目はスペースになる。 : 17
%F : %Y-%m-%d と同じ( 2008-11-31 など ) : 2008-11-17
%G : 年( %Yとの違いはよくわからない) : 2008
%g : 年の下2桁 ( 00 ~ 99 ) : 08
%h : %b と同じ。月の省略名 : Nov
%H : 時( 00 から 23 ) : 00
%I : 時( 01 から 12 ) 12時間表記での時刻 : 12
%j : 年の最初から初めて何日目か。( 001 から 366 ) : 322
%k : 時( 0 から 23 ) 1桁の数字( 0~ 9 )の2桁目はスペースになる。 :  0
%l : 時( 1 から 12 ) 12時間表記での時刻 1桁の数字( 0~ 9 )の2桁目はスペースになる。 : 12
%m : 月の番号( 01 から 12 ) : 11
%M : 分( 00 から 59 ) : 09
%n : 改行文字 :

%N : ミリ秒( %3N と書くとミリ秒を3桁で、 %6Nと書くとミリ秒を6桁で表示 ) : 000
%p : AM か PM : AM
%P : am か pm : am
%r : %I:%M:%S %p と同じ。( 11:55:23 PM など) : 12:09:37 AM
%R : 時分( 15:16 など ) : 00:09
%s : エポックからの秒数 : 1226848177
%S : 秒( 00 から 61 ) : 37
%t : タブ文字 :
%T : %H:%M:%S と同じ。時分秒( 23:14:03 など ) : 00:09:37
%u : 曜日の番号( 1 から 7。 月曜日が1 ) : 1
%U : 年の最初から数えて何週目か。( 00 から 53 ) : 年の最初の日曜日を週の最初として数える。最初の日曜日の週が01。その前の
週が00。 : 46
%V : 年の最初から数えて何週目か。( 01 から 53 ) : 少なくとも4日を持つ週を年の最初の週とする。月曜日を週の最初として数え
る。 : 47
%w : 曜日の番号( 0 から 6。 日曜日が0 ) : 1
%W : 年の最初から数えて何週目か( 00 から 53 )  年の最初の月曜日を週の最初として数える。最初の月曜日の週が01。その前の週
が00。 : 46
%x : 日付のデフォルトのフォーマット : Nov 17, 2008
%X : 時刻のデフォルトのフォーマット : 12:09:37 AM
%y : 年の下の2桁 : 08
%Y : 年 : 2008
%z : UTC(協定世界時)からのタイムゾーンの時刻のずれ) : +0900
%Z : タイムゾーン名 : JST
%% : % : %
%{method} : DateTime.pm のメソッドを利用することができる。%{ymd} : 2008-11-17

DateTimeを使って、日付や時刻の計算を行うサンプルです。

use strict;
use warnings;
use DateTime;

print "(1)-1 日付の足し算\n";
my $dt = DateTime->now(time_zone => 'local');

# 日の加算
my $dt_tommorow = $dt->clone;
$dt_tommorow->add(days => 1);

# 月の加算
my $dt_next_month = $dt->clone;
$dt_next_month->add(months => 1);

# 年の加算
my $dt_next_year = $dt->clone;
$dt_next_year->add(years => 1);

print '現在は' . $dt->date . "です。\n\n";
print '明日は' . $dt_tommorow->date . "です。\n";
print '1年後は' . $dt_next_month->date . "です。\n";
print '1年後は' . $dt_next_year->date . "です。\n\n";

# 時の加算
print "(1)-2 時刻の加算\n";
my $dt_next_hour = $dt->clone; 
$dt_next_hour->add(hours => 1);

# 分の加算
my $dt_next_minutes = $dt->clone;
$dt_next_minutes->add(minutes => 1);

# 秒の加算
my $dt_next_second = $dt->clone;
$dt_next_second->add(seconds => 1);

print '現在は' . $dt->time . "です。\n\n";
print '1時間後は' . $dt_next_hour->time . "です。\n";
print '1分後は' . $dt_next_minutes->time . "です。\n";
print '1秒後は' . $dt_next_second->time . "です。\n";

実行結果

(1)-1 日付の加算
現在は2008-11-18です。

明日は2008-11-19です。
1年後は2008-12-18です。
1年後は2009-11-18です。

(1)-2 時刻の加算
現在は23:04:06です。

1時間後は00:04:06です。
1分後は23:05:06です。
1秒後は23:04:07です。

DateTimeを使って日付の引き算を行うサンプです。

use strict;
use warnings;
use DateTime;

print "(1) 日付の引き算\n";
my $dt = DateTime->now(time_zone => 'local');

# 日の減算
my $dt_yesterday = $dt->clone;
$dt_yesterday->subtract(days => 1);

print '現在の日付は' . $dt->date . "です。\n";
print '昨日の日付は' . $dt_yesterday->date . "です。\n\n";

# 加算と減算を組み合わせる。
my $dt_mix = $dt->clone;
$dt_mix = $dt_mix->add(months => 1)->subtract(days => 1);

print "(2)DateTimeオブジェクト同士の引き算\n";
my $dt1 = DateTime->new(year => 2000 , month => 2, day => 1);
my $dt2 = DateTime->new(year => 2003 , month => 3, day => 19);

my $duration = $dt2 - $dt1;

print '期間は' . $duration->years . '年と'
  . $duration->months . 'ヶ月と'
  . $duration->days . "日\n";

実行結果

(1) 日付の引き算
現在の日付は2008-11-18です。
昨日の日付は2008-11-17です。

(2)DateTimeオブジェクト同士の引き算
期間は3年と1ヶ月と4日

期間の情報を取得するサンプルです。

use strict;
use warnings;
use DateTime;

# DateTimeオブジェクト同士の引き算
my $dt1 = DateTime->new(
  year => 2000,
  month => 2,
  day => 1,
  hour => 12,
  minute => 44,
  second => 30
);

my $dt2 = DateTime->new(
  year => 2003,
  month => 3,
  day => 19,
  hour => 23,
  minute => 54,
  second => 40
);

my $duration = $dt2 - $dt1;

print "(1) 期間を取得する。\n";
print "期間は" .$duration->years ."年と"
  . $duration->months ."ヶ月と"
  . $duration->days . "日と"
  . $duration->hours . "時間と"
  . $duration->minutes . "分と"
  . $duration->seconds . "秒\n\n";

print "(2)期間の順序を判定する。\n";
if ($duration->is_positive) {
  print "\$dt2は\$dt1より後の日付です。\n";
}
elsif ($duration->is_zero) {
  print "\$dt2は\$dt1と等しいです。\n";
}
elsif ($duration->is_negative) {
  print "\$dt2は\$dt1より前の日付です。\n";
}
print "\n";

print "(3)期間を月換算で取得する。\n";
print '期間は月換算で' . $duration->in_units('months') . "ヶ月です。\n";

実行結果

(1) 期間を取得する。
期間は3年と1ヶ月と4日と11時間と10分と10秒

(2)期間の符号を判定する。
$dt2は$dt1より後の日付です。

(3)期間を月換算で取得する。
期間は月換算で37ヶ月です。