読者です 読者をやめる 読者になる 読者になる

Perl製自作便利ツール紹介: 時間電卓

Perl製・掌編自作ツール紹介の第3回です。

以前の回はこちら。(古い順に)

今回は前回まで以上に「他の人はどうしてんだろ・・」的な案件なのですが、時間どうしを足す電卓、「時間電卓」のご紹介です。

この電卓では差分は計算せず、二つ以上の時間をひたすら足す時に使います。
(ちなみに、二つの時間の引き算は前回紹介した日時電卓の datetime_substract.pl で出来ます)

動機: CDブックと「Excel様」

そもそも、なぜそんなものが必要なのかと言うと、僕の場合はCDブックの編集・制作という仕事の都合上、たとえば15曲入りのCDを作ろう! などとなったときに、それがCDに収録できる最大許容時間(72分とか74分とかいろいろ言われていますが、それについては割愛)に収まるかどうかをつねに留意しておく必要がありまして、「はあ、この15曲、超いいラインナップだな〜」なんて思っても、「いやちょっと待て、これって合計何分だろ?」とふと我に返り、サクッと時間の足し算をしなければならないケースがよくあります。

まあ、そういう立場にある人はそう多くはないかもしれないんですが、とはいえ、他にまったく居ないということも無いはずで、そういう人は普段、どうやって計算してるのかなあ? とも思います。

と言いつつ、これはそれこそ、Excelだと結構簡単にできます。というか実際、僕にしても自分のこのスクリプトだけだとちょっと不安なので、超大事な状況下であったり、すでにExcelが起動していたりする時にはExcelも併用するのですが、Excelって案外「Excel様」的に、こちらの言うことをあまり聞いてくれない時があるというか、想定したとおりに動かない場合があり、とくにそれが「時間」を扱うときに顕著な印象があるため(僕の知識やスキルが足りないせい、とも言えますが)、こうやって裏側までちゃんと把握できる、原理がシンプルなツールを作る&使うメリットはあるかなとも思います。

でも単純に、それ以前の話として、一般的な電卓にこういう「時間を加減する機能」が入っていないのはどういうことだろう? とよく思います。ニーズないのか??

詳細・使い方

肝心のコードはこちらです。

#!/usr/bin/env perl
use strict;
use warnings;
use feature 'say';
use integer;
use Time::Piece;
use Time::Seconds;

my ($time_total, $time_calc, $ans_minute, $ans_hour, $ans_second);

my @list = <DATA>;
for my $calc_batch(@list) {
    if ($calc_batch =~ /^(\d\d*):(\d\d):(\d\d)$/) {
        $time_total += $1*60*60 + $2*60 + $3;
    } elsif ($calc_batch =~ /^(\d\d?):(\d\d)$/) {
        $time_total += $1*60 + $2;
    } else {
        $calc_batch = "Error!\n";
    }
}
$ans_hour = ($time_total / (60 * 60));
$ans_minute = (($time_total / 60) - ($ans_hour * 60) );
$ans_second = ($time_total) - ($ans_hour * 60 * 60) - ($ans_minute * 60);

say "total: $time_total sec";
say "ans: $ans_hour hr $ans_minute min $ans_second sec.";
say "---";
for my $view(@list) {
    print $view;
}

__DATA__

例のごとく、「__DATA__」の下に素材を入れていきます。以下の内容を入れて実行すると、

__DATA__
6:58:14
17:09:49
1:02
02:45

こんな風に出てきます。

total: 87110 sec
ans: 24 hr 11 min 50 sec.
---
6:58:14
17:09:49
1:02
02:45

このとき、6ケタなら「時:分:秒」と認識し、4ケタなら「分:秒」と認識します。

やっていることは単純明快で、一旦全部「秒」に置き換えて、すべてを足し終わってから「時・分・秒」に戻しているだけです。

でも最初はそんな方法にも気づかず、とりあえず「時間電卓作るで〜!」みたいになって、「ええと、まず秒と秒を足して、60を過ぎたら分に1個繰り上げて・・それを分同士の足した数値に足して、60を超えたら・・」とかやって結構大変な目に遭いました。

というか、さらに正直に言うとそこで一旦挫折して、何ヶ月も放置してからあらためてトライした際に「もしかして一旦秒に揃えればいいのでは・・」と気づいてから何とか現状のようになったのですが。

まあ、前回の日付電卓もそうですが、まだ自分の想定する使い方しかしていないので、バグというのか、普通に使えない部分もいろいろあるとは思いますし、それとは別にもっと洗練できるところもあるとは思うのですが、ひとまず現状、「Excelを開かずに複数の時間を一気に足す」ことはできるので、いいんじゃないかなと思っています。

付録: 数値電卓

当初の予定では上記で終わるつもりだったのですが、オマケ的な何かとして、「数値電卓」もご紹介します。

いや、そもそも電卓って数値を計算するものでしょ、という感じですが、これは数十個の数字を一気に足し算することを想定して作りました。それだけの数になると、電卓に打っていくのも面倒ですし、ミスタイプする可能性もありますから。
というかまあ、それなら今度は(それこそ)Excelでやれば一瞬なのですけど、ようは上のを作ったついでに試したかったというか・・

たとえば、こんな数字群があったとして、

95, 83, 47, 44, 43

それを一気に足したいな、という時に、以下のコードの「__DATA__」以下にそれを入れて実行すると(1行に数字1個)、

#!/usr/bin/env perl
use strict;
use warnings;
use feature 'say';

my $time_total;
my @list = <DATA>;
for my $calc_batch(@list) {
    if ($calc_batch =~ /^(\d+)$/) {
        $time_total += $1;
    } else {
        $time_total += 0;
    }
}

say "total: $time_total";
say "---";
for my $view(@list) {
    print $view;
}

__DATA__
95
83
47
44
43

こんな感じに出てきます。

total: 312
---
95
83
47
44
43

Congrats!!

付録: 曜日電卓

まだありました。こちらは以下のコードの「__DATA__」以下に日付を入れておくと、それに応じた曜日を出す、というものです。

具体的には、このコードを実行すると、

#!/usr/bin/env perl
use strict;
use warnings;
use feature 'say';
use Time::Piece;
use Time::Seconds;

my $switch = 'short';
#my $switch = 'long';

my ($stamp_local, $stamp_day);
my @week = <DATA>;
for (@week) {
    chomp $_;
    my $output = $_;
    $_ =~ s/\//-/g;
    $stamp_local = localtime->strptime($_, '%Y-%m-%d');
    if ($switch eq 'Sun') {
        $stamp_day = $stamp_local->wdayname;
    } elsif ($switch eq 'Sunday') {
        $stamp_day = $stamp_local->fullday;
    }
    say $output."\t".$stamp_day;
}

__DATA__
2015/2/4
2015/2/21
2015/2/26
2015/2/27

このような結果が出ます。

2015/2/4	Wed
2015/2/21	Sat
2015/2/26	Thu
2015/2/27	Fri

日付の後に、タブを挟んで曜日が出てくるという。

現状、曜日名が「Wed」や「Sat」になっていますが、以下の行をコメントアウトして、

my $switch = 'short';

上記ではコメントアウトしてある以下を活かすと、曜日名がフルで出てきます。

my $switch = 'long';
動機: 「曜日電卓」編

ちなみに、なぜこのようなものを作ったのかと言うと、ぼくは日頃、「曜日」が記憶を呼び起こすトリガーとして有効であると思っているので、自分の記録を付けるときにはなるべく曜日を併記するようにしているのですが、とはいえ、いちいち毎回、日付を記述するごとに曜日を思い出したり、それを打ち込んだりするのも徒労感があるので、この辺をザバッとやってくれるスクリプトがほしいなあ、と思って作った次第でした。

で、こちらも Time::Piece と Time::Seconds モジュールを使っているので、曜日名を日本語にローカライズすることも可能ですね。
ちょっと時間がかかりそうですけど、練習としてはその機能を持たせるのもいい気がします。

・・と思い始めたら気になって眠れなそうだったので、サクッとやってみたら出来ました。これで良さそうです。(コード部分のみ)

#my $switch = 'Sun';
#my $switch = 'Sunday';
#my $switch = '日';
my $switch = '日曜日';

my ($stamp_local, $stamp_day);
my @week = <DATA>;
for (@week) {
    chomp $_;
    my $output = $_;
    $_ =~ s/\//-/g;
    $stamp_local = localtime->strptime($_, '%Y-%m-%d');
    if ($switch eq 'Sun') {
        $stamp_day = $stamp_local->wdayname;
    } elsif ($switch eq 'Sunday') {
        $stamp_day = $stamp_local->fullday;
    } elsif ($switch eq '日') {
        $stamp_day = $stamp_local->wdayname(qw{日 月 火 水 木 金 土});
    } elsif ($switch eq '日曜日') {
        $stamp_day = $stamp_local->wdayname(qw{日曜日 月曜日 火曜日 水曜日 木曜日 金曜日 土曜日});
    }
    say $output."\t".$stamp_day;
}

__DATA__

上記同様、冒頭の変数 $switch の宣言時に活かしたい書式のコメントアウトを外してもらえばそれで出力されます。

2015/2/4	水曜日
2015/2/21	土曜日
2015/2/26	木曜日
2015/2/27	金曜日

前回紹介した「CPANモジュールガイド」のp97を見たら1分かからず書けました。トミールさん、ありがとうございました!

on GitHub

最後に、ここ数回でご紹介したコードの公開リポジトリへのリンクを張っておきます。日付電卓と時間電卓は以下。
note103/calculators · GitHub

曜日電卓と、ちょっと前に紹介した overlap.pl は以下に置いてあります。
note103/toolbox · GitHub

以上です!