#00 [foussin分室] ... [<<:home (index.html)] ... [<<:top (top.cgi)]
....... [<:prev (アマチュア・スタイル!)] ... [here: greg_calc.pl] ... [フレーム解除]

アマチュア・スタイル!

[lib 02] サブルーチン・ライブラリ(関数)
greg_calc.pl カレンダー計算ルーチン(グレゴリオ暦専用)

updated: 2010/08/04 Wed 23:56 (JST-9)
©pablo foussin

#01 能書き: greg_calc.pl とは
#02 日付の指定方法(&mold 関数)
#03 greg_calc.pl の主要関数
#04 greg_calc.pl の使用例
#05 サンプル・スクリプト
#06 ダウンロード
#07 今後の課題


[<:head #00] [here: #01] [>:next #02]

#01 能書き: greg_calc.pl とは

リニューアルされた foussin分室の topページには、次のような文章がある。

「-2号」(2000.10.28)から始め、約 *年*か月 が経過。

これは、『2000年10月28日 〜 本日』までの大まかな経過年月を greg_calc.pl を使って算出したものだ。このように、経過年月を求めたり、ある年月日の曜日を求めたりすることをカレンダー計算という。greg_calc.pl は、様々なカレンダー計算ルーチンを一つにまとめたサブルーチン・ライブラリだ。

Perl でカレンダー計算…といえば、time, gmtime, localtime などのお馴染みの関数の他に、標準モジュールの Time::local を使うのが定番だと思う。おそらく、greg_calc.pl の機能の一部は、Time::local と重複している部分もあると思うが、作者は Time::local を使ったことがないので、詳しいことは分からない(車輪の再発明をしているかもしれない)。

さて、greg_calc.pl の greg とは、Gregorian Calendar(グレゴリオ暦カレンダー) を略したものだ。グレゴリオ暦とは、現在、ほぼ世界中で採用されている現行歴(いわゆる西暦)のことだ。つまり greg_calc.pl は、『グレゴリオ暦』を基準スケールとしてカレンダー計算を行う。なので、サポート暦は『グレゴリオ暦(現行西暦)』のみで、現行歴以前のユリウス暦やローマ暦には未対応とする。当然、紀元前の計算も未対応となる。また、明治・大正・昭和・平成などの元号も未対応とする。

greg_calc.pl は、次の機能を実装している。

エポック秒関連の関数では、1970年以前や、2038年以降の日付にも対応している。上記機能の具体的な利用方法については、#03 greg_calc.pl の主要関数 で説明する。作者(foussin)は、greg_calc.pl の保守管理に長い年月をかけて取り組んできた。また、計算のアルゴリズムは単純明快なものばかりを採用している。そのため、計算ミスが発生することは、まず、ないと断言できる。なので、安心して活用してほしい。その代わり、実行効率は良いとは言えない…。


[<<:head #00] [<:prev #01] [here: #02] [>:next #03]

#02 日付の指定方法(&mold 関数)

カレンダー計算を行う関数には、引数として日付を指定する必要がある。日付の慣例表記は国によって違いがあるし、日本国内においても様々な表記が存在する。例えば――

と、さまざまだ。greg_calc.pl では、上記の文字列を全て日付文字列として認識できる。具体的には、ymd形式(ISO 8601)、UNIX形式(POSTGRES)、mdy形式(ISO 8601) の 3種類に対応している。これらの形式については、説明書モドキ(greg_calc.pl.commentful.pl)で説明しているので、ここでは詳細を省くが、日本人ユーザーが慣例的に使っている表記のほとんどをカバーしているはずだ。

しかし実際のところ、演算処理系に漢字や特殊文字を含む引数を渡すと、"." を小数点やメタ文字と誤認したり、文字化けが発生したりと、様々な問題が起こる。それを回避するなら、引数文字列をいったん『単一の内部表現形式』に変換する…という方法が考えられる。そうすれば、個々の処理ルーチンは内部表現形式だけを対象としてコーディングするだけで済む。greg_calc.pl では、そのための関数として &mold という関数を実装している。

年月日・時刻を表す引数文字列は、最初に &mold という関数に渡して『内部表現形式』に変換する必要がある。&mold(mold: 型、鋳型)は、greg_calc.pl の諸関数を利用するにあたり『一番最初に実行しなければいけないプログラム』となっている。ymd形式、UNIX形式、mdy形式の文字列を、引数として &mold に渡すことで内部表現形式が生成される。これは、ユーザーが慣例的に使っている日付の文字列を使ってカレンダー計算ができるようにするため、ある程度、指定方法に自由度の幅を持たせたものだ。一方、個々の実行関数は、内部では常に単一の内部表現形式だけを使って演算処理を行う仕様となっている。

『&mold 関数』『内部表現形式』についての詳細は『説明書モドキ』で説明しているので、ここでは詳細を省く。


[<<:head #00] [<:prev #02] [here: #03] [>:next #04]

#03 greg_calc.pl の主要関数

greg_calc.pl を使うためには、まず最初に次の記述をする必要がある。

require "greg_calc.pl";

上記の記述は必須となる。もしくは、必要に応じて次のように記述する。

require "greg_calc.pl";
&greg_calc::init_outf($type);
&greg_calc::set_tzone($tz_str, $opt);

以下に主要関数の簡単(?)な書式を示す。

書式1: $csv = &greg_calc::mold($date);
書式2:($csv, $csv2) = &greg_calc::mold($date, $date2);
概要: 西暦年月日と時刻を表す(と思われる)引数文字列を、内部表現形式に変換。 コンストラクタ的な主要関数:(元号年には未対応)
引数: $date, $date2 に指定できる文字列は、ymd形式、UNIX形式、 mdy形式に限るが、日本人ユーザーが慣例的に使っている表記は、 ほとんどサポートしている。
戻値: &mold が返す内部表現形式とは:
年月日,時刻,GMT』という 3つのフィールドを "," で区切った CSV 形式(カンマ区切り) のリストになっている。 それを一繋がりのスカラー値として返す。CSV の個々の要素は――
・年月日は、ハイフン(-)区切りのリスト(yyyy-mm-dd)
・時分秒は、コロン(:) 区切りのリスト(hh:mm:ss)
・GMT は、"GMT" または 空文字列 のスカラー値(bool値)
――と、リストの多重構造を形成する。
例:"2008-04-03,00:30:00,GMT" → GMT(国際標準時)の日付を表す
例:"2008-04-03,09:30:00," → ローカル時間 の日付を表す
文例: $line = 'Xデー: 2038年1月19日 12時14分7秒 (JST)';
$csv = &greg_calc::mold($line);
結果: "2038-01-19,12:14:07," が $csv に代入される。
備考: 日付(年月日・時刻)以外の値を引数として指定する関数に関しては、 事前に &mold を実行する必要はない。
書式1: $tz_sec = &greg_calc::set_tzone($tz_str [,$opt]);
書式2:($tz_sec, $tz_hour) = &greg_calc::set_tzone($tz_str [,$opt]);
概要: ローカル時間の設定 (環境変数 $ENV{'TZ'} の設定)
引数:$tz_str タイムゾーンを表す TZ 文字列(下記参照)を指定。環境変数 $ENV{'TZ'} に代入すべき文字列。これによってローカル時間が決まる。 引数チェックはしていないので、正確な TZ 文字列を指定する必要がある。
引数:$opt "keep" と指定すると、$ENV{'TZ'} への上書き代入は行わずに、 戻値だけを返す。これによって、gmtime, localtime を同時に実行し、 2つの国のローカル時間を同時に取得することも可能だ (Windows では無意味なオプション)。今のところ、 "keep" 以外のオプションを新たに追加する予定はない。
戻値:$tz_sec GMT との時差を秒数で返す(整数値)。
戻値:$tz_hour GMT との時差を時数で返す(実数値)。
文例: ($tz_sec, $tz_hour) = &greg_calc::set_tzone("JST-9");
結果: $tz_sec には『-32400』(-9*60*60)、 $tz_hour には『-9』が代入される。
備考: Windows のように、$ENV{'TZ'} を使わずにタイムゾーンを設定する システムの場合は、gmtime(time - $tz_sec) を実行することで、 任意のローカル時間の今の日付を得ることができる。外国に設置されている Windows マシンの CGI サーバで日本時間を求めたい時などに、 この方法を使うと良い。また、正確な TZ 文字列が分からない国のローカル時間を設定したい時は、
$tz_sec = &greg_calc::set_tzone("-10.5", "keep");
のように "keep" 指定をした上で、時差時間を表す数値だけを TZ 文字列として渡し、$local_date = gmtime(time - $tz_sec); を実行する…という使い方もある。
参考: 以下の資料は Web上のどこかにあったものを日本語で補足したもの。
    >EXAMPLES: Example TZ variables and their timezone differences are given
    >          in the table below:
    >  -------------------
    >  TZ         timezone
    >  -------------------
    >  EST5EDT     5*60*60 (米大陸東部標準時:米国,カナダ)
    >  GMT0        0*60*60 (グリニッジ標準時)
    >  JST-9      -9*60*60 (日本標準時)
    >  MET-1MEST  -1*60*60
    >  MST7MDT     7*60*60
    >  PST8PDT     8*60*60
    >  ------------------- 
($since_csv, $to_csv) = &greg_calc::mold($since_date, $to_date);
($rpass_y, $rmod_m) = &greg_calc::about_x_years($since_csv, $to_csv);
概要: 始点日(since)〜終点日(to)までの、大まかな経過年月を求める。
事前に &mold を実行する(上記のとおり)。
引数: 内部表現の日付文字列を指定。
$since_csv …内部表現の since の日付。 $to_csv …内部表現の to の日付。
戻値: $rpass_y …経過年数。 $rmod_m …剰余月数。
文例: ($rpass_y, $rmod_m) = &greg_calc::about_x_years("2008-1-1,0,", "2010-2-26,0,");
print "$rpass_y年$rmod_mか月\n";
文例 出力結果: 2年2か月
$csv = &greg_calc::mold($date);
($wd, $wname) = &greg_calc::what_day($csv [, $type]);
概要: 曜日算出ルーチン。
事前に &mold を実行する(上記のとおり)。
引数:$csv 曜日を調べたい年月日を内部表現で指定。
引数:$type "jp", "xx", "Xx", "XX", "xxx", "Xxx", "XXX", "xxxx", "Xxxx", "XXXX" のいずれかを指定する。 省略するとデフォルトとして "Xxx" がセットされる。
$typeの詳細
→ "jp" …曜日を日本語(日,月,火...) で返す。
(日本語の曜日名は greg_calc.pl のソースと同じ文字コードで返す)
→ "xx" , "Xx" , "XX" …曜日を英略 2文字で返す。
→ "xxx", "Xxx", "XXX" …曜日を英略 3文字で返す。
→ "xxxx", "Xxxx", "XXXX" …曜日をフルスペルで返す。
x → 英小文字を表す。X → 英大文字を表す。
※ 曜日名は、 2文字(xx)、3文字(xxx)、フルスペル(xxxx)のいずれかしか指定できない。 及び、全部大文字(XXX)、全部小文字(xxx)、先頭だけ大文字(Xxx)、 先頭だけ小文字(xXX)のいずれかしか指定できない。 これは内部関数 &short_or_full の仕様。
戻値:$wd 曜日番号(0-6)を返す。
戻値:$wname 引数 $type に対応した形式の "曜日名" を返す。 $type を省略すると英略 3文字(Sun, Mon, Tue, ...)で返す。
$ym_csv = &greg_calc::mold($date);
$mday = &greg_calc::happy_mon($ym_csv, $ord_day);
概要: 指定年月(yyyy-mm)の 第n d曜日の日付を算出(Happy Monday対策)。
事前に &mold を実行する(上記のとおり)。
引数:$ym_csv 指定年月を含む内部表現形式の文字列を指定。
引数:$ord_day "第n-d曜日" を表す文字列を指定する。
記述例→ '2-mo', '2nd,mon', '2/1', '第2 月曜', '2nd.Monday' などのように『序数(第n)を表す数字』と『曜日を表す文字』を、 英数字以外(a-zA-Z0-9以外)の適当な文字で区切って記述する。
戻値:$mday yyyy年 mm月 第n d曜日の日付を返す。
$from_csv = &greg_calc::mold($from_date);
$to_csv = &greg_calc::xdays_after($from_csv, $pass_days);
概要: 基点日(0日目)から経過日数目の日付(年月日・時刻)を求める。
事前に &mold を実行する(上記のとおり)。
引数:$from_csv 基点(0日目)の日付を内部表現形式で渡す。
引数:$pass_days 経過日数。正数(n)なら『n日後』、負数(-n)なら『n日前』 の年月日を求めることができる。つまり、関数名は after だが、 実際は before(ago) の機能も実装している。通常は整数値を指定するが、 実数値(もしくは実数を意味する文字列)でも構わない。
戻値:$to_csv 経過日数目の年月日・時刻を内部表現形式で返す。 経過日数を加算した結果が『紀元前』になる場合はエラーが発生し、 戻値 $to_csv は "" となる(greg_calc.pl は、紀元前には未対応)。
文例: # 2008年3月10日の『10日後』と『10日前』の日付を求める。
$from_csv = &greg_calc::mold('2008年3月10日');
$after_csv = &greg_calc::xdays_after($from_csv, 10);
$before_csv = &greg_calc::xdays_after($from_csv, -10);
文例の結果: after → "2008-03-20,0," (戻値は内部表現形式)
before → "2008-02-29,0," (2008年は閏年)
($since_csv, $to_csv) = &greg_calc::mold($since_date, $to_date);
$pass_days = &greg_calc::get_passdays($since_csv, $to_csv);
概要: 始点日(since)〜終点日(to)までの経過日数を算出。
事前に &mold を実行する(上記のとおり)。
引数:$since_csv 内部表現の since の日付。
引数:$to_csv 内部表現の to の日付。
戻値:$pass_days since を基点(0日目)とした to までの経過日数を返す。
$date_csv = &greg_calc::mold($date);
$epoch_sec= &greg_calc::get_epoch_sec($date_csv);
概要: 内部表現の日付(年月日・時刻)から『エポック秒』を求める。
事前に &mold を実行する(上記のとおり)。
※ エポック秒は『1970-1-1,00:00:00,GMT』を基点(0秒目)とする。
※ Macintosh のエポック秒(1904-1-1,00:00:00,GMT を基点とする)には未対応。
引数:$date_csv 内部表現の日付(年月日と時刻)を指定する。 1970年以前や2038年以降の年月日も指定可能。 ただし、紀元前の日付は指定できない。
戻値:$epoch_sec エポック秒を返す(整数)。負数(0未満)〜2147483648 以上のエポック秒も返す。つまり、符号付き 32ビット整数の有効範囲を超えた日時にも対応している。
備考: エポック時間は GMT を基準に算出するため、 引数がローカル時間で指定された場合、 GMT 時間に変換してから演算を行う。
($gmt_csv, $local_csv) = &greg_calc::both_time($epoch_sec);
概要: エポック秒から GMT、ローカル時間の両方(both)の日付を算出。
gmtime, localtime の機能を合体させ、さらに 1970年以前や 2038年以降の経過秒数も指定可能とした。ただし、 戻値は内部表現形式で返される。
引数:$epoch_sec gmtime, localtime と同様、エポック秒(time形式) を表す整数値を引数として指定する。$epoch_sec が "" (空文字列) or undef の場合は、0 ではなく、現在のエポック秒がセットされる (つまり time の戻値がセットされる)。
戻値:$gmt_csv GMT 時間の内部表現形式の日付。
戻値:$local_csv ローカル時間の内部表現形式の日付。
備考: リストコンテキスト専用。戻値は『yyyy-mm-dd,hh:mm:ss[,GMT]』 の内部表現形式で、必ず (GMT時間, ローカル時間) の順番で返す。 個別に取り出すなら、次のようにスライスを使うと良い。
$gmt_csv = ( &greg_calc::both_time($epoch_sec) )[0];
$local_csv= ( &greg_calc::both_time($epoch_sec) )[1];
※ greg_calc.pl は紀元前には未対応なので、GMT時間、 ローカル時間に関わらず、算出後の日付が紀元前になる場合、その日付は "" となる。
$date_csv = &greg_calc::mold($date);
$date = &greg_calc::outf($date_csv);
概要: 内部表現の日付文字列を、出力形式に変換。
事前に &mold を実行する(上記のとおり)。
引数:$date_csv 内部表現の日付文字列("yyyy-mm-dd,hh:mm:ss,[GMT]")。
戻値:$date greg_calc.pl では、デフォルトとして 3種類の出力フォームが予め定義してあり、次の形式の戻値を返す。
2008-05-04 Sun 17:46:00
これは内部表現のカンマを半角ハイフンに置換し、 英略語3文字の曜日名を挿入しただけの形式だ。 この他に次の形式も使うことができる。
Sun May 04 17:46:00 2008 → 欧米で一般的な PostGres(mdy)形式
2008/05/04 Sun 17:46:00 → 日本で一般的な 年月日(ymd)形式
これらのデフォルト設定は &init_outf サブルーチンを使う事で 切り替え可能となっている。詳細は『&init_outf』のコメントを参照。
備考: &outf 関数には、もっと複雑な書式もある (詳細は『説明書モドキ』を参照)。
&greg_calc::init_outf($type);
概要: 出力形式のデフォルト設定を選択する。
引数:$type "raw_ymd", "postgres", "ymd$sep" のいずれかを指定する。
"raw_ymd" 出力例→『2008-05-04 Sun 17:46:00[ GMT]』
これは、内部表現をちょっと加工しただけの形式で、$type が不正値の場合、デフォルトとしてこれが採用される。
"postgres" 出力例→『Sun May 04 17:46:00 2008[ GMT]』
localtime, gmtime のスカラーコンテキスト時の戻値とほぼ同じ形式 (mdy形式)。
"ymd$sep" 出力例→『2008$sep05$sep04 Sun 17:46:00[ GMT]』
見た目は "raw_ymd" とほとんど同じだが、内部での変換方法が違う。 年月日の区切り文字(セパレータ)を "-" 以外にしたい時に、 これを指定する。
例:"ymd/" →『2008/05/04 Sun 17:46:00』
例:"ymd." →『2008.05.04 Sun 17:46:00』
例:"ymd " →『2008 05 04 Sun 17:46:00』
例:"ymdjp" →『2008年05月04日 (日) 17:46:00』
例:"ymdjp "→『2008年 05月 04日 (日) 17:46:00』
※:"jp" 指定時は出力体裁が変わる(曜日も漢字になる)
※:$sep は最大3文字まで認識する("jp" は 2文字と解釈)

なお、greg_calc.pl の &mold 以外の諸関数は、引数が『実在しない日付』『年が紀元前(西暦0年)』の場合、全ての戻値は ""(空文字列)となる。


[<<:head #00] [<:prev #03] [here: #04] [>:next #05]

#04 greg_calc.pl の使用例

例) 指定年月日の曜日を求める

#!/usr/bin/perl

# "youbi_test.pl"

require "greg_calc.pl";

$date = '2008年6月24日 12:06';
$csv = &greg_calc::mold($date);

# 日本語表記の例
($wd, $wname) = &greg_calc::what_day($csv, "jp");
print $date, ' は ', $wname, '曜日', "\n";

# yyyy/mm/dd Xxx 表記の例
&greg_calc::init_outf("ymd/");
$date = &greg_calc::outf($csv);
print "$date\n";

__END__

youbi_test.pl の実行例↓

G:\lib\greg_calc\sample>youbi_test.pl
2008年6月24日 12:06 は 火曜日
2008/06/24 Tue 12:06:00

例) 2009年の成人の日(1月 第2月曜日)を求める

# "happy_mon_test.pl"

require "greg_calc.pl";

$ym = &greg_calc::mold("2009-1");
$mday = &greg_calc::happy_mon($ym, "2nd-mon");
print '2009年の成人の日は:1月', $mday, '日 (月)', "\n";

__END__

happy_mon_test.pl の実行例↓

G:\lib\greg_calc>happy_mon_test.pl
2009年の成人の日は:1月12日 (月)

例) &get_epoch_sec の動作チェック

# "get_epsec_test.pl"
# updated: 2008.06.25 Wed 00:00
# ----------------------------------------------------------------------
# 動作条件:事前に &mold を実行する(次の通り)
#        $date_csv = &greg_calc::mold($date);
# 書式1:$epoch_sec= &greg_calc::get_epoch_sec($date_csv);
# ----------------------------------------------------------------------

require "greg_calc.pl";
@date = ( "today"              , "1970-1-1 9:0:0"         ,
          "2038-1-19"          , "1969-12-31"             ,
          "2038-01-19 12:14:08", "2038-01-19 03:14:07 GMT",
          "1970-1-1 0:0:0" );

foreach (@date) {
    $csv = &greg_calc::mold($_);
    $epoch_sec = &greg_calc::get_epoch_sec($csv);
    print "元の引数: $_\n";
    print "epoch_sec: $epoch_sec";
    $local = localtime($epoch_sec);
    $local || ($local="");
    print " (localtime: $local)\n\n";
}
__END__

get_epsec_test.pl の実行結果↓

G:\usr\lib\greg_calc\sample>perl -w get_epsec_test.pl
元の引数: today
epoch_sec: 1214319652 (localtime: Wed Jun 25 00:00:52 2008)

元の引数: 1970-1-1 9:0:0
epoch_sec: 0 (localtime: Thu Jan  1 09:00:00 1970)

元の引数: 2038-1-19
epoch_sec: 2147439600 (localtime: Tue Jan 19 00:00:00 2038)

元の引数: 1969-12-31
epoch_sec: -118800 (localtime: )

元の引数: 2038-01-19 12:14:08
epoch_sec: 2147483648 (localtime: )

元の引数: 2038-01-19 03:14:07 GMT
epoch_sec: 2147483647 (localtime: Tue Jan 19 12:14:07 2038)

元の引数: 1970-1-1 0:0:0
epoch_sec: -32400 (localtime: )

例) &both_time の動作チェック

# "both_genesis.pl"
# 2008.06.25 Wed 00:25

require 'greg_calc.pl';
$local_genesis = -62_135_596_800;

@epoch = (     "-100,000,000,000",
        $local_genesis - (3600*9),
               " -62,135,596,800",
               "               0",
               ""  );

foreach (@epoch) {
    ($gmt_csv, $local_csv) = &greg_calc::both_time($_);
    print "epoch[$_]\n",
          "  gmt[$gmt_csv]\n",
          "local[$local_csv]\n\n";
}
__END__

both_genesis.pl の実行結果↓

G:\usr\lib\greg_calc\sample>perl -w both_genesis.pl
epoch[-100,000,000,000]
  gmt[]
local[]

epoch[-62135629200]
  gmt[]
local[0001-01-01,0,]

epoch[ -62,135,596,800]
  gmt[0001-01-01,0,GMT]
local[0001-01-01,09:00:00,]

epoch[               0]
  gmt[1970-01-01,0,GMT]
local[1970-01-01,09:00:00,]

epoch[]
  gmt[2008-06-24,15:46:18,GMT]
local[2008-06-25,00:46:18,]

[<<:head #00] [<:prev #04] [here: #05] [>:next #06]

#05 サンプル・スクリプト

#!/usr/local/bin/perl

# "ezcal.pl" (sample script)
=about "ezcal.pl"
    greg_calc.pl を利用したサンプル・スクリプト

   『グレゴリオ暦万年カレンダー』(ユリウス暦、ローマ暦、紀元前は未対応)
    2005.12.29 Thu 13:50 初版完成
    2006.02.12 Sun 15:07 第2版完成(greg_calc.pl のバージョンアップの都合)
    2008.05.03 Sat 08:14 第3版完成(greg_calc.pl のバージョンアップの都合)

    使用関数:mold, arg_chk("fix -ym"), what_day, short_or_full

    アクセスするグローバル変数:@misoka, @eng_m

    書式:perl ezcal.pl [西暦年月]
    引数:西暦年月は、yyyy-mm 形式で指定する。
          引数を省略すると『今月のカレンダー』を出力する。
    戻値:なし(標準出力するだけの単純なコマンド)。
    文例:perl ezcal.pl 2006-1
    文例:perl ezcal.pl 2008/2
    文例:perl ezcal.pl 2008年5月
=cut

#
# 準備(init)
#
    require "greg_calc.pl";

#
# 入力(input) --引数の解釈--
#
  # 引数が省略されると、デフォルトとして『今日の日付』がセットされる
  # 必要なのは『年月』のみ(yyyy-mm-dd の dd が実在しない日付でも構わない)
    $csv = &greg_calc::mold($ARGV[0]);
    ($ym_csv, $err, $type, $bad_arg) =
     &greg_calc::arg_chk("fix -ym", $csv);
  # $err が真値ならば実行中止する
    if ($err) {
      # $err:エラー・コードを返す
      #  値   :値の意味
      #  0(偽):戻り値($err)が 0 ならば『引数は正常』であることを示す
      #  1(真):月の指定が 1-12 以外になっている
      #  2(真):実在しない日付になっている(2005-11-31, 2006-2-29 など)
      #         -ymオプションが指定されているので、日付のチェックはしない
      #       ※このプログラムでは『コード2』を返すことは絶対にない
      #  3(真):年の指定が紀元前(西暦0年以下)になっている
      #         (これは greg_calc.pl の仕様…紀元前には未対応)
        print "$type error!: ", '引数', $bad_arg, 'の指定が変。', "\n";
        ($err==1) && (print '1: 月の指定が 1-12 以外になっている。',
        "\n");
        ($err==2) && (print '2: 実在しない日付になっている。'  ,
        "\n");
        ($err==3) && (print '3: 年の指定が紀元前になっている。',
        "\n");
        die;
    }
  # yyyy-mm 形式の文字列を yyyy-mm-1 に置換
    $ym      = ( split(/,/, $ym_csv) )[0];
    $ymd     = "$ym-1";
    $ymd_csv = "$ymd,0,"; # 内部表現のリビルド
  # $year, $mon を抽出
    ($year, $mon, $mday) = split(/-/, $ymd);

#
# 演算(exp)
#
  # 指定月の1日(ついたち)は何曜日か(曜日番号)を調べる
    $wd       = &greg_calc::what_day($ymd_csv);
  # その月の末日は何日かを調べる
    $misoka   = $greg_calc::misoka[$mon];
    if ($mon == 2) {       # 2月の時は閏年判定も行う
        $leap = &greg_calc::leap_year($year);
        $misoka += $leap;
    }
  # 月番号から英語月名を求める
    $month = $greg_calc::eng_m[$mon];
    $month = &greg_calc::short_or_full($month, "Xxxx");
  # 指定月の日数(1 .. $misoka)のリストを作成し、@mdays に格納する
  # (1 .. 9) までは " 1", " 2" のように半角スペースで補完し、2桁にする
    @mdays = ( " 1", " 2", " 3", " 4", " 5", " 6", " 7", " 8", " 9" );
    @mdays = ( @mdays, 10 .. $misoka );
  # 指定月の1日(ついたち)の曜日番号($wd)が 0以外ならば、曜日番号と同じ個数
  # の『空白文字だけの文字列要素』を用意し、それを配列 @spc に格納する
    if ($wd) { # $wd が 0 以外ならば…
        for ($j=0; $j<$wd; ++$j) {
            @spc = (@spc, "  "); # 半角スペース2個("  "=="\x20\x20")
        }
    }
    @mdays = (@spc, @mdays);

#
# 出力(output)
#
  # @mdays の要素を forループを使って 1つずつ出力する
  # その直後、1つ出力する毎にループカウンタ値を 7で割って剰余を調べる
  # その剰余が 6ならば、\n(改行)を出力する
  # 剰余が 6以外ならば、"  "(半角スペース2個)を出力する
    print "\n[ $year $month ]\n\n";
    print "Su  Mo  Tu  We  Th  Fr  Sa\n";
    $num = @mdays;
    for ($j=0; $j<$num; ++$j) {
        print $mdays[$j];
        if ($j == $num-1) { print "\n"; } # 最後の要素を出力したら改行する
        else {
            ($j % 7 == 6) && ( print "\n" );
            ($j % 7 == 6) || ( print "  " );
        }
    }

#
# 終了(end)
#
__END__

ezcal.pl の実行例↓

G:\bat>ezcal.pl 2008-7

[ 2008 July ]

Su  Mo  Tu  We  Th  Fr  Sa
         1   2   3   4   5
 6   7   8   9  10  11  12
13  14  15  16  17  18  19
20  21  22  23  24  25  26
27  28  29  30  31

以下は、&get_passdays, &outf 等のサンプル

# "ct_down.pl"

# 2009.02.23 Mon 11:00
# Windows の場合、[Ctrl]-c で実行中止( UNIX系は [Ctrl]-d )

require 'greg_calc.pl';

$to     = '2101年1月1日';  # 22世紀の始まりの日
$to_csv = &greg_calc::mold($to);
$to_date= &greg_calc::outf($to_csv);

while (1) {
    $since_csv = ( &greg_calc::both_time() )[1]; # today
    $pass_days = &greg_calc::get_passdays($since_csv, $to_csv);
    ($int_day, $float) = split(/\./, $pass_days);
    $float = "0.$float";
    $sec = int($float * 24*60*60 + 0.5);
    $hms = &greg_calc::sec2hours($sec);
    if ($cache ne $hms) {
        $cache = $hms;
        #↓Windows 専用
        system("cls");
        #↓Linux 等の場合
        # print "\e[2J"; # ← ESCシーケンスで CLS する
        $since_date = &greg_calc::outf($since_csv);
        print "\n";
        print '  ', '  22世紀:', " $to_date\n";
        print '  ', '現在時刻:', " $since_date\n";
        print '  ', '  22世紀まで あと ';
        printf("%3d", $int_day);
        print '日', " $hms ($pass_days", '日)', "\n";
    }
}
__END__

ct_down.pl の実行例(1秒ずつカウントダウンする)↓

    22世紀: 2101-01-01 Sat 00:00:00
  現在時刻: 2009-02-23 Mon 11:12:32
    22世紀まで あと 33548日 12:47:28 (33548.532962963日)

[<<:head #00] [<:prev #05] [here: #06] [>:next #07]

#06 ダウンロード

greg_calc.pl (最新版)
$VERSION= '1.63';
$UPDATE = '2010.07.31 Sat 01:26 JST-9';
$AUTHOR = 'pablo foussin(japan)';

ソース本体を見る: (39.4 KB / sjis テキスト)
説明書モドキ(?) を見る: (179 KB / sjis テキスト)

ダウンロード: [ ソース本体( greg_calc.pl )と 説明書モドキ( greg_calc.pl.commentful.pl )のアーカイブ ]
日本語 Windows 用(sjis/crlf): greg_calc_1.63_sjis.zip (66.0 KB)
日本語 UNIX 用(eucjp/lf): greg_calc_1.63_eucjp.zip (65.4 KB)
Linux 用(utf8n/lf): greg_calc_1.63_utf8n.zip (70.2 KB)

[<<:head #00] [<:prev #06] [here: #07]

#07 今後の課題

(2010/08/04 Wed 23:55 追記)

 &mold 関数の機能拡張を検討している。現状での &mold には 2種類の書式が
ある。↓


# 書式1: $ymd_hms             = &greg_calc::mold($date);
# 書式2:($ymd_hms, $ymd_hms2) = &greg_calc::mold($date, $date2);


 さらに、書式3 を追加したい…と考えている。↓


# 書式1: $date_csv            = &greg_calc::mold($date);
# 書式2:($since_csv, $to_csv) = &greg_calc::mold($since, $to);
# 書式3: @date_csv            = &greg_calc::mold($line);


 例えば、次のようなテキストがあるとする。

    $line = '1936年2月26日、二・ニ六事件。… 1945/8/15、敗戦。';

 従来の &mold では、書式1 を使って "1936-02-26,0," は抽出できるが、敗戦
の年月日を抽出することはできない。これをどうにかしたい、と考えている。書
式3 のサポートによって、CGI 支援ツールとしての利用価値が、さらに増すこと
になるはず。これはなるべく早く実装したほうが良いだろう。

 書式の特定は、引数の数と戻値のコンテキストに着目すれば何とかなる。この
機能拡張には、大幅なソースの変更、追加が予想される。これを v1.70 とする。
…しかし、よく考えたら、これはかなり面倒なことになりそうだ。やっぱ、しば
らくは保留にしておく…。

 ところで、『二・ニ六事件』を『2.26事件』のように記述すると、書式3 では
次のようなリスト値(内部表現形式)が返されるはず。

    ("1936-02-26,0,", "0002-26,0,", "1945-08-15,0,")

 y-m-d 形式ではなく、y-m 形式と判断される。つまり、西暦2年26月(26月は存
在しないのでエラーとなる)。この点は、要注意かもしれない。たぶん、当時の作
者(=筆者=foussin)は、これを嫌って、スカラー値から複数の年月日を抽出する
のを躊躇したんだと思う。自分のことなのに、なんか他人事だな…。
[<:head #00] ... [<<:prev (アマチュア・スタイル!)] ... [<<:home (index.html)] ... [<<:top (top.cgi)]

アマチュア・スタイル! に掲載の文書、プログラムは、 全て pablo foussin の著作物です。
ですが、プログラムについては改変自由のフリーソフトとします。