NGW開発BLOG
← 記事一覧へ
·#013

休暇の「1日」は勤務時間と同じではない時間→日数換算の罠

時間単位の休暇を日数に換算するとき、割る数を「1日の勤務時間」にすると間違う。制度上の「1日」が持つ文脈依存の定義を掘り下げる。

「1日」を割り算で出そうとして詰まる

休暇簿を作っていると、どこかで必ず時間単位の休暇を日数に直す場面が来る。3時間の休暇は何日ぶんか、という計算だ。素朴に考えれば「休暇時間 ÷ 1日の勤務時間」で出る。そして多くの実装は、手近にある勤務時間の定数を割る数に使ってしまう。

ここに罠がある。制度上の「1日」は、実際に働く時間の長さと一致するとは限らない。むしろ一致しないほうが普通だ。この記事は、その「割る数を何にするか」という一見どうでもよさそうな一点に、制度実装のポイントが詰まっている、という話である。

勤務時間と「休暇換算上の1日」は別物

ある団体では、1日の実勤務時間が7時間45分だとする。一方で、休暇を1日と数えるときの基準時間は8時間00分と定められている、ということが起こる。昼休みを含めるか含めないかの扱いが違えば、この差はさらに開く。

なぜ揃わないのか。実勤務時間は「その日に何時間働くか」という運用の数字であり、休暇換算の基準時間は「休暇制度上、何時間で1日ぶんとみなすか」という規程上の数字だからだ。出自が違う以上、たまたま同じ値になることはあっても、同じ概念ではない。

さらにやっかいなことに、この基準は職種で分かれることがある。一般職と会計年度任用職員で、1日と数える時間が違う団体は珍しくない。つまり割る数は、団体ごとにも職種ごとにも変わりうる。勤務時間の定数を流用した瞬間に、これらの差がすべて誤差として保存値に混入する。

換算の入口を1つの関数に閉じ込める

対策の骨格は3つだ。

  1. 換算の入口を必ずヘルパー関数に集約する。呼び出し側は「この職員の休暇1日は何時間か」を自分で判断しない。職種を渡せば正しい基準時間が返る、という唯一の窓口を通す。基準時間そのものは、団体・職種別の定数として外に出しておく。勤務時間の定数とは明確に別の系統にする。
  2. この換算を使う差引計算は、実は一箇所ではない。申請を承認するとき、管理者が残数を手修正するとき、夜間バッチが回るとき、申請が更新されたとき——差引の計算は複数の経路に散らばっている。ここで全経路が同じ割る数を使うことが要になる。1つでも勤務時間側に戻してしまうと、その経路を通った分だけ保存値がズレ、残数の整合が静かに壊れる。厄介なのは、日常業務では表に出ず、年度末の突き合わせで初めて発覚することだ。
  3. 境界を決めておく。すでに保存された残数は、表示のたびに再計算しない。保存値をそのまま見せる、と割り切る。基準時間は規程改正で変わりうるから、過去の保存値を今の基準で計算し直すと、当時正しかった値まで書き換わってしまう。換算するのは新たに差し引く分だけ、既存の残数は動かさない。この線引きが、時間軸をまたいだ不整合を防ぐ。

1点目の「入口をヘルパー関数に集約する」を、悪い例と良い例で並べておく。

// 悪い例: 勤務時間の定数を割る数に流用してしまう
$days = $leaveMinutes / $WORK_MINUTES_PER_DAY;

// 良い例: 休暇換算専用の基準を職種から引く
$base = leaveMinutesPerDay($staffType); // 一般職/会計年度で分岐
$days = $leaveMinutes / $base;

教訓 — 当たり前の単位ほど文脈依存を疑う

「1日」のように、誰もが同じ意味だと思い込む単位こそ、制度の世界では文脈依存の定義を持っている。実勤務時間と休暇換算時間は、名前も概念も近いがゆえに混同されやすく、混同したまま動いても普段は破綻しない。だからこそ質が悪い。

守るべきは2つだけだ。単位換算の基準は定数として団体・職種ごとに外出しすること。そして換算ロジックは唯一の関数に閉じ込め、割る数の選択をその中に隠すこと。地味な原則だが、似て非なる概念を取り違えないという一点が、後年の帳尻合わせの手間を確実に減らす。