生年月日から年齢を簡易計算する数式

生年月日からシンプルに年齢を算出する数式とその証明

※記事内の数式の図が表示されるまで1分ぐらい掛かることがあります

##計算式

誕生日から年齢を簡易的に求めたいとき、
`yyyymmdd`形式で表記した今日の日付から、同じく`yyyymmdd`形式で表記した誕生日をそのまま引き算し、10,000で割った解の整数部分を現時点での年齢とすることができる。

例えば、本日が2017/08/18で、誕生日が1990/08/20 なら、

```math
\begin{align}
\mbox{年齢}
&=floor\left(\frac{20170818-19900820}{10000}\right)\\
&=floor\left(\frac{269998}{10000}\right)\\
&=floor\left(26.9998\right)\\
&=26
\end{align}
```

※$floor$は小数部分を切り捨てる処理

これを一般化すると以下の数式になる。

```math
\begin{align}
\mbox{年齢}
&=floor\left(\frac{\left(\mbox{現在年}\times10000+\mbox{現在月}\times100+\mbox{現在日}\right)-\left(\mbox{誕生年}\times10000+\mbox{誕生月}\times100+\mbox{誕生日}\right)\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ }{10000}\right)
\end{align}
```

###計算例

本日が2017/08/18で、誕生日が1990/08/20 なら、

```math
\begin{align}
\text{年齢}
&= floor\left(\frac{\left(2017\times1000+08\times100+18\right)-\left(1990\times1000+08\times100+20\right)}{10000}\right)\\
&= floor\left(\frac{20170818-19900820}{10000}\right)\\
&= floor\left(\frac{269998}{10000}\right)\\
&= floor\left(26.9998\right)\\
&= 26
\end{align}
```

本日が2017/08/20で、誕生日が1990/08/20なら、

```math
\begin{align}
\text{年齢}
&= floor\left(\frac{\left(2017\times1000+08\times100+20\right)-\left(1990\times1000+08\times100+20\right)}{10000}\right)\\
&=floor\left(\frac{20170820-19900820}{10000}\right)\\
&=floor\left(\frac{270000}{10000}\right)\\
&=floor\left(27\right)\\
&=27
\end{align}
```

つい脊髄反射で「UNIXタイムからの経過ミリ秒に直して…」の様にやりたくなるが、冷静に考えるとこのロジックこそ とても自然な発想だと思う。

（イマイチ納得できない方向けに[**こちらでもう少し分かりやすく解説**](#%E6%95%B0%E5%BC%8F%E3%81%AE%E3%82%84%E3%82%84%E8%A9%B3%E3%81%97%E3%81%84%E8%A7%A3%E8%AA%AC)）

但し、年齢計算の式は（特に法律関係で）利用対象によって定義が変わることがあるため、この計算式をそのまま使えるとは限らない。

##コード

###JavaScriptのとき

```js:calcAge.js
/**
 * 誕生日から年齢を返す関数
 * @param {!string} birthdayStr - 誕生日をyyyymmdd形式で入力
 * @return {number} 計算された年齢
 **/
var calcAge = function(birthdayStr){
  if(isNaN(birthdayStr)||birthdayStr.length !== 8){
    return -1;
  }
  var d = new Date();
  var dStr = ''+d.getFullYear()+('0'+(d.getMonth()+1)).slice(-2)+('0'+d.getDate()).slice(-2);
  return Math.floor((parseInt(dStr)-parseInt(birthdayStr))/10000);
};

console.log(calcAge('19900820'));
```

***********************

###Python 3.Xのとき

```py3:calcAge.py
# -*- coding: utf-8 -*-
from datetime import datetime
import math

def calcAge(birthdayStr):
  if not (birthdayStr.isdigit() and len(birthdayStr)==8):
    return -1
  dStr = datetime.now().strftime("%Y%m%d")
  return math.floor((int(dStr)-int(birthdayStr))/10000)

print(calcAge("19900817"))
```


`return`の式は

```py3:
return (int(dStr)-int(birthdayStr))//10000
```

でもOK。

***********************

###Rubyのとき

```rb:calcAge.rb
require "date"
def calcAge(birthdayStr)
  if birthdayStr !~ /^[0-9]{8}$/
    return -1
  end
  return (Date.today.strftime("%Y%m%d").to_i - birthdayStr.to_i) / 10000
end

print calcAge("19900820")
```

Rubyでは整数型同士の除算で小数点以下が勝手に切り捨てられるためfloor処理が不要。

***********************

###SQLのとき
→ [SQL日付関数（年齢計算） | dbSheetClient　IT技術ブログ](http://www.dbsheetclient.jp/blog/?p=133)

###PHP、Java、Perlのとき
→ [佐野裕のサーバ管理者日記 - 生年月日から年齢を計算する簡単な計算式：ITpro](http://itpro.nikkeibp.co.jp/article/Watcher/20070822/280097/)

***********************

###Excelのとき
[DATEDIF()関数](https://support.office.com/ja-jp/article/DATEDIF-%E9%96%A2%E6%95%B0-25dba1a4-2812-480b-84dd-8b32a451b35c)があるのでこの数式は不要だが、CSVを取り込んで処理するときに元データが日付型になっていないときには有効な手段。

```
=FLOOR.MATH((TEXT(TODAY(),"yyyymmdd")-[yyyymmdd形式の生年月日])/10000)
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/103957/3bf4efc3-835f-629b-cf13-72cb0e91edfd.png)

[FLOOR.MATH()関数](https://support.office.com/ja-jp/article/FLOOR-MATH-%E9%96%A2%E6%95%B0-c302b599-fbdb-4177-ba19-2c2b1249a2f5)は古いExcelでは使えない。そのときは[INT関数](https://support.office.com/ja-jp/article/INT-%E9%96%A2%E6%95%B0-a6c4af9e-356d-4369-ab6a-cb1fd9d343ef)を使えばOK。

```
=INT((TEXT(TODAY(),"yyyymmdd")-[yyyymmdd形式の生年月日])/10000)
```

***********************

##小話 - 年をとる瞬間

この数式では**現時点での年齢**を算出しているが、**今日何歳になるか**を算出している訳ではない。
[年齢計算ニ関スル法律](https://ja.wikipedia.org/wiki/%E5%B9%B4%E9%BD%A2%E8%A8%88%E7%AE%97%E3%83%8B%E9%96%A2%E3%82%B9%E3%83%AB%E6%B3%95%E5%BE%8B)によると、**加齢するタイミング**は誕生日当日ではなく、**誕生日の前日を満了する瞬間**（＝前日24時0分0秒）とのこと。
即ち、**2000年4月1日生まれ**の人は2006年3月31日が始まった時点では5歳であるが、**3月31日中に満6歳**になり、**既に満6歳になった状態で2006年4月1日を迎える**。

この定義により、うるう年2月29日生まれの人でも毎年1歳ずつ加齢する。

### 誕生日と学年について
学校教育法によると、**4月1日の前日までに満6歳となっている人**は、小学校へ入学することとなっている。
「2000年4月1日生まれの人」は**2006年4月1日の前日である2006年3月31日には満6歳になっている**ので、**2006年4月1日より小学1年生**になる。同様の理由で「2000年3月15日生まれの人」、「1999年4月20日生まれの人」も同じ学年である。

「2000年4月2日生まれの人」は2006年4月1日の前日には満6歳に達していないため、翌年の2007年4月1日より小学1年生になる。

***********************


##数式のやや詳しい解説
もう少し掘り下げて考えたい方向け。
誕生年と現在の年だけの情報でも、「今年で何歳になるか」までは分かる。

今年の年齢 = 現在年 - 誕生年

だが、今年の誕生日を迎えたか分からないので「今の年齢」が分からない。
もし、**今年の誕生日を迎えていない**のであれば、**「今の年齢」は「今年の年齢」から$1$を引いて計算**する必要がある。

これを**場合分けの式**で表現すると以下のようになる。

```math
\begin{eqnarray}
\mbox{年齢}=\left\{
\begin{array}{ll}
\mbox{現在年}-\mbox{誕生年}&\left(\mbox{誕生月日}\leq\mbox{現在月日}\right)\cdots\mbox{(1)今年の誕生日以降の式}\\
\mbox{現在年}-\mbox{誕生年}\color{red}{-1}&\left(\mbox{誕生月日}>\mbox{現在月日}\right)\cdots\mbox{(2)今年の誕生日前日までの式}\\
\end{array}
\right.
\end{eqnarray}
```

年齢の簡易計算式を変形すると、上記の**場合分けの式**と似た数式を作ることができる。
この変形後の数式の$\color{red}{赤文字部分}$に「今年の誕生日を迎えていないなら$-1$する」という処理が内包されている。

```math
\begin{align}
\mbox{年齢}
&=floor\left(\frac{\left(\mbox{現在年}\times10000+\mbox{現在月}\times100+\mbox{現在日}\right)-\left(\mbox{誕生年}\times10000+\mbox{誕生月}\times100+\mbox{誕生日}\right)\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ }{10000}\right)\\
&=floor\left(\frac{\left(\mbox{現在年}-\mbox{誕生年}\right)\times10000+\left(\mbox{現在月}-\mbox{誕生月}\right)\times100+\mbox{現在日}-\mbox{誕生日}\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ }{10000} \right)\\
&=\color{red}{floor}\left(\mbox{現在年}-\mbox{誕生年}\color{red}{+\frac{\mbox{現在月}-\mbox{誕生月}}{100}+\frac{\mbox{現在日}-\mbox{誕生日}}{10000}}\right)\\
\end{align}
```

この式の**月と日の計算結果の合計部分**を$X$と置き換えると、先ほど登場した**場合分けの式**と良く似たシンプルな数式になる。

```math
\begin{align}
\mbox{年齢}&=\color{red}{floor}\left(\mbox{現在年}-\mbox{誕生年}\color{red}{+X}\right)\\\\
\color{red}{X}&=\frac{\mbox{現在月}-\mbox{誕生月}}{100}+\frac{\mbox{現在日}-\mbox{誕生日}}{10000}\\\\\\
\end{align}
```

$X$の値が取りうる範囲については以下のとおり

- 本日が誕生日当日であるとき

→ 現在月=誕生月、現在日=誕生日なので$0$

- 本日が12月31日で、誕生日が1月1日のとき

→ 最大値$X_{max}$

```math
\begin{align}
X_{max}&=\frac{\mbox{12}-\mbox{1}}{100}+\frac{\mbox{31}-\mbox{1}}{10000}=0.1130\\\\
\end{align}
```

- 本日が1月1日で誕生日が12月31日のとき

→ 最小値$X_{min}$

```math
\begin{align}
X_{min}&=\frac{\mbox{1}-\mbox{12}}{100}+\frac{\mbox{1}-\mbox{31}}{10000}=-0.1130
\end{align}
```

よって

```math
-0.1130\leq X \leq 0.1130
```

|現在と年内の誕生日の関係|$X$の取りうる値|
|:-------------------|:-------------|
|$\mbox{誕生月日}\leq\mbox{現在月日}$のとき（誕生日以降）|$0\leq X_{after}\leq0.1130$|
|$\mbox{誕生月日}>\mbox{現在月日}$のとき（誕生日前日以前）|$-0.1130\leq X_{before}<0$|



####今年の誕生日当日以降の計算
「 $\mbox{現在年}-\mbox{誕生年}$ 」の結果は必ず$0$以上の整数になり、$0\leq X_{after}\leq0.1130$であるため、
$X$は計算結果に影響を与えず、無視される。

```math
\begin{align}
\mbox{年齢}
&=floor\left(\mbox{現在年}-\mbox{誕生年}\color{red}{+X_{after}}\right)\\
&=\mbox{現在年}-\mbox{誕生年}
\end{align}
```
これは**場合分けの式「(1)今年の誕生日以降の式」**と一致する。

####まだその年の誕生日を迎えていないときの計算
「 $\mbox{現在年}-\mbox{誕生年}$ 」の結果は必ず$0$以上の整数になり、$-0.1130\leq X_{before}<0$であるため、
$X$は年の計算結果から1を引く影響を与える。

```math
\begin{align}
\mbox{年齢}
&=floor\left(\mbox{現在年}-\mbox{誕生年}\color{red}{+X_{before}}\right)\\
&=\mbox{現在年}-\mbox{誕生年}\color{red}{-1}
\end{align}
```

これは**場合分けの式「(2)今年の誕生日前日までの式」**と一致する。

よって、数式

```math
\begin{align}
\mbox{年齢}
&=floor\left(\frac{\left(\mbox{現在年}\times10000+\mbox{現在月}\times100+\mbox{現在日}\right)-\left(\mbox{誕生年}\times10000+\mbox{誕生月}\times100+\mbox{誕生日}\right)\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ }{10000}\right)
\end{align}
```
の解は、以下の場合分けの式と同じ解となり、誕生日から現在の年齢を求めることができる。

```math
\begin{eqnarray}
\mbox{年齢}=\left\{
\begin{array}{ll}
\mbox{現在年}-\mbox{誕生年}&\left(\mbox{誕生月日}\leq\mbox{現在月日}\right)\cdots\mbox{(1)今年の誕生日以降の式}\\
\mbox{現在年}-\mbox{誕生年}\color{red}{-1}&\left(\mbox{誕生月日}>\mbox{現在月日}\right)\cdots\mbox{(2)今年の誕生日前日までの式}\\
\end{array}
\right.
\end{eqnarray}
```

##あとがき
プログラミングの面白さの１つとして、脳内で行っている処理を**如何に具体化してシンプルなロジックに落とし込めるか**というものがあります。
このコードにはその面白さがシンプルに詰まっているので、眺めているだけで楽しかったです。

Excelの日付処理に興味が湧いた方はこちらもどうぞ。
→ [【Excel】 日時情報の正体 - Qiita](http://qiita.com/AQRiL_1132/items/4e1d8eb980b952275c09)

`yyyymmdd`形式の検査をもっとしっかりやりたい方へ
→ [yyyymmdd形式日付文字列の妥当性をチェックする正規表現を読み解く - Qiita](http://qiita.com/shojit/items/c633a329dbbcc56923d6)

##参考になるリンク

- [「生年月日から年齢を計算する簡単な計算式」は使えるとは限らない - ある地方公務員電算担当のナヤミ](http://d.hatena.ne.jp/alittlething/20070827/p1)
- [結局人はいつ年をとるのか：ナナメ読みには最適の日々](http://ishimori-t.hatenablog.com/entry/2015/03/25/112043)
- [法制執務コラム - ４月１日生まれの子どもは早生まれ？：参議院法制局](http://houseikyoku.sangiin.go.jp/column/column011.htm)
- [4月1日生まれの児童生徒の学年について：文部科学省](http://www.mext.go.jp/a_menu/shotou/shugaku/detail/1309966.htm)
- [年齢：Wikipedia](https://ja.wikipedia.org/wiki/%E5%B9%B4%E9%BD%A2)