1. ホーム
  2. スクリプト・コラム
  3. パール

Perlの時間処理関数 使い方入門

2022-01-08 09:44:52

I. Perlの時間表現関数

1. 日付を表すには様々な方法があります。
"1973年1月18日 "です。
"18/01/1973";
"01/18/1973";
"Jan181973 "です。
"18-01-73";
"18-01-1973";
"01/73".
これらのフォーマットの中には不明瞭なものもある(例えば "01-06-1973 "は6月1日を意味するのか、1月6日を意味するのか?)
日付の形式を指定しないと対処が難しい。

18Jan1973" と "6Sep1950" の違いを理解するには、数字に変換する必要があります。
/{br Unixでは、内部的に時間を表現するために時分秒を使用しています。
日付と時刻を足したものです。
1970年1月1日グリニッジ標準時の午前0時(エポック)から現在までの秒数です。
例:「1973年1月18日のエポック秒数。(午前0時と仮定)は96163200です。

2. このシステムにおいて、午前0時は1日の始まりの瞬間を示す。

Perlで提供されているgmtime関数で日付を生成してみよう。
時間の始まりからの秒数を表す整数が与えられると、gmtime関数はそれに対応する日時を計算することができます。 {
例1.
gmtime()関数を呼び出すと、時間、分、秒、日付、月、年などの値のリストが得られます。

#! /usr/bin/perl
use Time::localtime;

$t_num = 96163200;
$tm = scalar(gmtime($t_num));
print $tm,"\n";

出力されます。
1月18日(木) 00:00:00 1973

例2: 時刻を", "で区切って出力する
print join(",", gmtime(96163200));

0,0,0,18,0,73,4,17,0
セマンティクス
最初の3つの数字。0,0,0はそれぞれ秒、分、時を表します。時間は0〜23までなので、午後は12時以降になります。
4番目の数字:18は、その月の日数(この場合は18日)です。
5番目の数字:0 、0から始まる月の数(1月の場合)。
0から始まる理由は、月が月配列の添え字に対応するためです。

@months = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
$month = @months[(gmtime($t_num))[4]];
print "MONTH: ",$month,"\n";

6番目の数字:73、年、(この場合は73)は、少し違った表現になります。西暦の下2桁ではありません。
1900年から始まる年号を表します。
なぜこのような表現になるのですか?
これは、C言語がそのように処理しているからです。
Perlは、ライブラリやシステムコールをOSが扱う方法にできるだけ近づけようとしています。 そのため、
つまり、4桁の西暦を出力したい場合、以下のような表現になります。
$year=(gmtime(96163200))[5]+1900;

この扱いを理解しないと、Y2K疑惑が生まれるので、こんな風に書くかもしれません。
$year="19 "です。(gmtime(96163200))[5]; #Error! 2000年は19100年になります
7番目の数字:4 , 曜日を示す(日曜日は0)。
8の数字:17、1年の日数(0が1日)。
番号9:0 、サマータイムが使えるかどうか(0はノー、プラスはイエス、マイナスは不明)。

3. Perl の time() 関数は、現在の日付と時刻を時系列で秒単位で返します。

文字列に変換するつもりなら、gmtime()関数やlocaltime()関数を使えばよいでしょう。

$now=localtime(time());
($sec,$min,$hour,$day,$mon,$year,$wday,$yday,$isdst)=localtime(time());

localtime()またはgmtime()が引数なしで呼ばれた場合、time()自身を呼び出します。

$now=localtime();
($sec,$min,$hour,$day,$mon,$year,$wday,$yday,$isdst)=localtime();

II. 日付と時刻の操作)におけるPerlの時刻処理関数

の2つの瞬間の間の時間を計算する。
対応するエポック秒に変換して、2つの数字を引き算するだけです。
difference_in_seconds=$later_datetime - $earlier_datetime;

秒を分、時間、日に変換するには、それぞれ60、3600、86400で割ればよい。

$difference_in_minutes=$difference_in_seconds/60;
$difference_in_hours=$difference_in_seconds/3600;
$difference_in_day=$difference_in_seconds/86400;

2. 4日後の日付は何ですか」を計算する :


$then=time()+86400*4;
print scalar(localtime($then));

秒単位の正確な答えが出ます。

4日後のエポック秒の値が932836935の場合、以下のように日付文字列を出力することができます。
1999年7月24日(土)11:23:17

3.ある日付の午前0時を出力する
/{br 例:"Sat Jul 24 00:00:00 1999",
を以下のモジュールで使用します。
$then=$then-$then%86400;#Remove that date tail

同様に、四捨五入を利用して、午前0時に最も近い日付を出力することも可能です。

$then += 43200; #add on half a day
$then = $then - $then%86400; #truncate to the day

これは、あなたのタイムゾーンがGMTから偶数時間離れている場合に機能します。
すべてのタイムゾーンに簡単に対応できるわけではありません。
本当に必要なのは、GMTではなく、自分のタイムゾーンでエポック秒を計算することです。

PerlのTime::Localというモジュールです。
は2つの関数 timelocal() とtimegm()を提供することができます。戻り値は localtime() と gmtime() と同じである。

use Time::Local;
$then = time() + 4*86400;
$then = timegm(localtime($then)); #local epoch seconds
$then -= $then%86400; #truncate to the day
$then = timelocal(gmtime($then)); #back to gmt epoch seconds
print scalar(localtime$then,"\n".

III. Perlの時刻処理関数で日常的に使われる日付と時刻を表現する

時、分、年などの意味は先に紹介したが、時系列的な秒の意味も理解することができた。
そして、日常生活における日付と時刻は、文字列で表されます。
日常生活で使われている日付と時刻の文字列形式を時分秒に変換するにはどうしたらいいですか?

{1. 1. 一つの方法として、構文解析アプレットを書くと、柔軟で高速になります。

プレ #! /usr/bin/perl use Time::Local; @months{qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)}=(0..11); $_ = "19 Dec 1997 15:30:02"; /(\d\d)\s+(\w+)\s+(\d+)\s+(\d+)\s+(\d+):(\d+):(\d+)/ or die "Notadate"; $mday=$1; $mon=exists($months{$2})? $months{$2}:die"Badmonth"; $year=$3-1900; ($h,$m,$s)=($4,$5,$6); $epoch_seconds = timelocal($s,$m,$h,$mday,$mon,$year); print "day: ",$mday,"\n"; print "mon: ",$mon,"\n"; print "year: ",$year,"\n"; print "seconds: ",$epoch_seconds,"\n";

2. より一般的な方法としては、CPANからDate::Manipモジュールをインストールすることです。

useDate::Manip;
$epoch_seconds=UnixDate("19 Dec 1997 15:30:02","s");

なお、Date::Manipは大きなモジュールなので、このモジュールを適用すると、アプリケーションの開始時刻が追加されます。
その理由の一つは、Date::Manipは様々なフォーマットを認識するからです。
のようなものです。
"今日"
"今"
「2000年4月の最初の日曜日"
"3時15分,今日"
「2000年4月最初の日曜日の午後3時15分"
「2000/01/18 09:15」 日付操作
2036,2037,2038,...,1901?!

IV. ほとんどのC言語プログラムは秒を符号付き整数として保存し、正の日付と負の日付の両方を表現することができます。
しかし、コンピュータのメモリは、表現できる整数の大きさが有限であり、秒を表現するビット数も有限である。
つまり、時系列で秒を計算するときに表現できる日数には限りがあるのです。 {これは、エポック秒を計算するときに、日付が制限されることを意味します。 /正確な制限は、お使いのマシンに依存します。 正確な限界は、あなたのマシンが表現できる整数のビット数に依存する。

Perlは32ビットまでの長さの整数を保存します。
大雑把に言うと、1ビットがプラスとマイナスの符号を表すのに使われ、残りの31ビットが数を表すのに使われます。
8ビットを使用する場合、格納できる最大数値は2の8乗から1を引いた255となります。
つまり、Perlに格納される32ビット符号付き数値の最大数は

print 2**31-1,"\n";
2147483647

この数字はどの日付に対応するのでしょうか?

print scalar(gmtime(2**31-1)),"\n";
Tue Jan 19 03:14:07 2038

その瞬間から1秒後に何が起こるのか?

print scalar(gmtime(2**31)),"\n";
Fri Dec 13 20:45:52 1901

2**31 は 32 ビット符号付き整数としては大きすぎます。
ロールオーバーして符号ビットに負符号が設定され、表現できる最大の負数になります。 {その結果、表現できる最大の負数になる。
1970年初頭までの秒数の最大値に相当する。
この結果は何を物語っているのでしょうか?gmtime(2**31)より前、またはgmtime(2**31-1)より後のエポック秒数で表現された日付は格納できません。
大きなクエリではないので、深く考える必要はありません。
秒以外の時間に32ビット符号付き整数を使用したい場合は、表現を次のように変更するだけです。
/CPANからかなりの数を見つけることができます。 CPAN にはたくさんの日付モジュールがありますが、その中でも Date::Calc と Date::Manip はおそらく最も強力なものでしょう。 {その中でも、Date::Calc と Date::Manip は最も強力なモジュールでしょう。
この2つのモジュールは、Y1901-Y2038の資格を防ぐために、独自の日付表現を適用しています。
Date::Manip は西暦0000年から西暦9999年までのローマ暦を適用します。
Date::Calc もローマ暦を使用し、1 から 32767 までの年数を表現できます。

概要

Perlの時刻処理機能は、1902年から2037年の範囲の日付と期間を時系列の秒に変換します。
これらの数値にアクセスするには、整数演算、gmtime() と localtime() 関数、そして標準の Time::Local モジュールを使用すればよいのです。 また
この範囲外の日付に対して計算を行いたい場合や、特定の日付形式を分析したい場合は
CPAN にある Date::Manip と Date::Calc モジュールを使用することができます。