時刻差分のアルゴリズム:日をまたぐ時間差も考慮した正確な計算方法
ざっくり先に確認しておきたいこと
-
アルゴリズムって何?(※別の記事にとびます)
「アルゴリズムって聞いたことあるけど、なんだっけ…?」という人向けに、やさしくまとめてます。 -
この記事の進め方について(※別の記事にとびます)
どういう流れで説明してるか、ざっくり紹介してます。
読む順番が気になる人はちらっとどうぞ。
はじめに
「ログ見ると、開始と終了の時刻はあるけど、差分は自分で計算しないと…」
「23:55から00:10って、15分だよな…? うっかり間違えそう」
こんなふうに、時刻の差を計算する処理って、地味だけどあちこちで出てきます。
今回は、時刻差分のアルゴリズムをコードと一緒に紹介していきます。
このアルゴリズムで出来ること
動作順序
①2つの指定時刻をそれぞれ[時間,分,秒]に分ける
②2つの時刻の[時間の差,分の差,秒の差]を求める
③時間差を3600倍,分差を60倍する
④すべての差を合計すると[秒]の時刻差分が求まる
⑤時刻表示とするため、まずは差分を3600で割り、その商が[時間]部分になる
⑥⑤の余りをあらたに時刻差分とする
⑦この時刻差分を60で割った商が[分]になり、余りが[秒]部分になる
計算対象選別
判定 | 計算対象 | 関係 | 数値 |
---|---|---|---|
データの比較回数 | = | 0 | |
データの交換回数 | = | 0 | |
○ | 関数の呼び出し回数 | = | 1 |
変数定義
変数にする対象 | 変数 | 時間 | 分 | 秒 |
---|---|---|---|---|
1時間 | ||||
1分間 | ||||
1秒間 | ||||
1つ目の時刻 | ||||
2つ目の時刻 | ||||
秒の時刻差分 |
一般化
1つ目の時刻の秒部分は
それをふまえて1つ目の時刻全体を秒であらわすと
これは
別途で計算量を表現するならば上記のように
- 秒の時刻差分
を求めるS_t - 求めた時刻差分を時刻表示にして出力する
この2つの処理が可能な関数を呼び出すことになる
※ここで関数の引数は2つの時刻
よって計算量としては関数を1回呼び出すことに等しいので
漸近計算量は
実装
Python
def parse_time(time_str):
h, m, s = map(int, time_str.split(":"))
return h, m, s
def to_seconds(h, m, s):
return h * 3600 + m * 60 + s
def from_seconds(total_seconds):
if total_seconds < 0:
total_seconds += 24 * 3600
total_seconds = int(total_seconds)
h = total_seconds // 3600
total_seconds %= 3600
m = total_seconds // 60
s = total_seconds % 60
return f"{h:02}:{m:02}:{s:02}"
def main(start, end):
h1, m1, s1 = parse_time(start)
h2, m2, s2 = parse_time(end)
sec1 = to_seconds(h1, m1, s1)
sec2 = to_seconds(h2, m2, s2)
diff = sec2 - sec1
return from_seconds(diff)
入力
10:00:00
13:00:00
実行結果
実行時間 : 0.000058 秒
メモリ(現時点): 49 bytes
メモリ(ピーク): 339 bytes
実行結果 : 03:00:00
理屈を押さえておくと、こういう応用がしやすくなる
ただ引き算するだけじゃなくて…
- 時刻の順番が逆だとどうなる?
- 時間帯(タイムゾーン)が違ったら?
- 日付をまたいでたら?
- 秒単位で見る?分単位?それとも日数?
こんな「ちょっとした落とし穴」も、仕組みを知っていれば柔軟に対応できる。
コードを書く人だけじゃなくて、データを扱う仕事なら、誰でも知っておいて損はないアルゴリズム。
応用場面
「ふたつの時刻の差を計算する」──たったそれだけの処理でも、ちょっとしたアイデアと組み合わせると、いろんなところで使えるようになる。
- イベントや作業の遅れを検出できる
例えば、何かの処理が「5分以内に終わるはず」と決まっていたら、その始まりと終わりの時刻を比べるだけで、遅延の有無をチェックできる。 - ユーザーの反応時間を測れる
アンケートの開始・終了、ログインしてから操作するまでの時間など、行動の速さや間を可視化できる。
マーケティングやユーザー体験の分析にも活きる。 - 一定時間内の 空白(無反応) を検出できる
センサーやログデータなどで、しばらく何も起きてない時間を見つけたいときに便利。たとえば「最後の記録から10分以上何も起きてない」ことに気づける。 - ピークの時間帯を見つけやすくなる
たくさんの時刻データがあるなら、それぞれの差分から集中している時間帯や混んでいる時間帯が見えてくる。
アクセス解析やシフト調整にも応用できる。 - 平均時間や最大・最小間隔を分析できる
何かが何回も起きてるなら、その「間隔の平均」や「最も開いた時間」を出すことで、ばらつきやリズムが見えてくる。
比較検証
いろんなライブラリで時刻差分をとったときの実測値がこちら。
ライブラリ | 実行時間 | メモリ(現時点) | メモリ(ピーク) |
---|---|---|---|
自作 | 0.0001秒 | 49B | 339B |
datetime | 0.0038秒 | 615,243B | 617,794B |
dateutil | 0.0155秒 | 1,395,562B | 1,395,962B |
pandas | 0.6152秒 | 30,851,005B | 30,885,673B |
Python
#標準
def calc_datetime(start_str, end_str):
from datetime import datetime
fmt = "%H:%M:%S"
start = datetime.strptime(start_str, fmt)
end = datetime.strptime(end_str, fmt)
diff = end - start
return from_seconds(diff.total_seconds())
#拡張
def calc_dateutil(start_str, end_str):
from dateutil import parser
start = parser.parse(start_str)
end = parser.parse(end_str)
diff = end - start
return from_seconds(diff.total_seconds())
def calc_pandas(start_str, end_str):
import pandas as pd
start = pd.Timestamp(start_str)
end = pd.Timestamp(end_str)
diff = end - start
return from_seconds(diff.total_seconds())
- 詳しい検証はこちらのページで行いました
動作検証
参考サイト
- 一週間で身につくアルゴリズムとデータ構造
Discussion