ruby の Date.parse の私の知らなかった機能

ruby の `Date.parse` に思いがけない機能があったのでメモ。

いずれも [class Date のリファレンスマニュアル]( https://docs.ruby-lang.org/ja/latest/class/Date.html#S_PARSE )には載っていなかった。

# `Date.parse("20")`

二桁の数値を入れると、今月のn日目( n=="01" の場合、1日 )になる。

```ruby:ruby2.6
Date.parse("01").strftime("%Y.%m.%d")
#=> "2019.04.01"

Date.parse("30").strftime("%Y.%m.%d")
#=> "2019.04.30"

Date.parse("00").strftime("%Y.%m.%d")
#=> ArgumentError: invalid date

Date.parse("31").strftime("%Y.%m.%d")
#=> ArgumentError: invalid date
```

# `Date.parse("110")`

三桁の数値を入れると、今の年の n日目( n=="001" の場合、元旦 )になる。

```ruby:ruby2.6
Date.parse("001").strftime("%Y.%m.%d")
#=> "2019.01.01"

Date.parse("000").strftime("%Y.%m.%d")
#=> ArgumentError: invalid date

Date.parse("365").strftime("%Y.%m.%d")
#=> "2019.12.31"

Date.parse("366").strftime("%Y.%m.%d")
#=> ArgumentError: invalid date
```

# `Date.parse("19110")`

つづいて、五桁の場合。

最初の2桁が年。`00`〜`68` は 2000年以降。`69`〜`99` は 1999年かそれ以前。
あとの3桁が、元旦を1とした日付。`001` なら 1月1日で、`365`でうるう年でなければ 12月31日。

```ruby:ruby2.6
Date.parse("68001").strftime( "%Y.%m.%d" )
#=> "2068.01.01"

Date.parse("69001").strftime( "%Y.%m.%d" )
#=> "1969.01.01"

Date.parse("12345").strftime( "%Y.%m.%d" )
#=> "2012.12.10"

Date.parse("19000").strftime( "%Y.%m.%d" )
#=> ArgumentError: invalid date

Date.parse("19001").strftime( "%Y.%m.%d" )
#=> "2019.01.01"

Date.parse("19365").strftime( "%Y.%m.%d" )
#=> "2019.12.31"

Date.parse("19366").strftime( "%Y.%m.%d" )
#=> ArgumentError: invalid date
```

# `Date.parse("2019110")`

そして七桁の場合。

最初の四桁が年。これが厄介。
`0000`〜`0068` なら、2000年から2068年。
`0069`〜`0099` なら、1969年から1999年。
それ以外はそのまま年になる。

あとの3桁が、元旦を1とした日付。`001` なら 1月1日で、`365`でうるう年でなければ 12月31日。

```ruby:ruby2.6

Date.parse("0000001").strftime( "%Y.%m.%d" )
#=> "2000.01.01"

Date.parse("0068365").strftime( "%Y.%m.%d" )
#=> "2068.12.30"

Date.parse("0069001").strftime( "%Y.%m.%d" )
#=> "1969.01.01"

Date.parse("0099365").strftime( "%Y.%m.%d" )
#=> "1999.12.31"

Date.parse("0100001").strftime( "%Y.%m.%d" )
#=> "0100.01.01"

Date.parse("9999365").strftime( "%Y.%m.%d" )
#=> "9999.12.31"
```

# それ以外の桁数

1桁から100桁まで調べてみたけど、上記以外は`ArgumentError: invalid date`になる。
受け入れる桁数は 2, 3, 5, 7桁。一桁の素数の集合と一致。

#元号対応

M, T, S, H は元号とみなされてよろしくやってくれる。
手元ではまだ 2.6.3 になっていないので、令和には対応していない。
※ 2.6.3 が `"R1.5.1"` をどうするのかは、未確認。

明治999年とか、明治0年とかでもエラーにせずに適当にやってくれる。

怖いのは、M, T, S, H 以外のアルファベットは無視されて西暦になるところ。

```ruby:ruby2.6.2
Date.parse("M45.1.1").strftime( "%Y.%m.%d" )
#=> "1912.01.01"

Date.parse("M45.8.1").strftime( "%Y.%m.%d" )
#=> "1912.08.01"

Date.parse("M46.1.1").strftime( "%Y.%m.%d" )
#=> "1913.01.01"

Date.parse("M999.1.1").strftime( "%Y.%m.%d" )
#=> "2866.01.01"

Date.parse("M0.1.1").strftime( "%Y.%m.%d" )
#=> "1867.01.01"

Date.parse("T12.3.4").strftime( "%Y.%m.%d" )
#=> "1923.03.04"

Date.parse("S56.7.8").strftime( "%Y.%m.%d" )
#=> "1981.07.08"

Date.parse("H31.4.20").strftime( "%Y.%m.%d" )
#=> "2019.04.20"

Date.parse("R1.5.1").strftime( "%Y.%m.%d" )
#=> "2001.05.01" ( 2.6.2 なので R は無視されて西暦になる )
```

ちなみに、`"㍻31.4.20"`, `"平31.4.20"`, `"平成31.4.20"` は、元号名が無視されて 2031年になる。

# まとめ

|例|意味|
|:--|:--|
|`Date.parse("20")`|今月の20日|
|`Date.parse("110")`|今年の110日目|
|`Date.parse("19110")`|19年の110日目|
|`Date.parse("2019110")`|2019年の110日目|
|`Date.parse("H31.4.20")`|平成31年4月20日|

いずれも、現象を確認しているだけでソースコードは見ていない。