2018/12/04

[Python] date と datetime オブジェクトの相互変換まとめ

[datetime オブジェクト、Unix time、文字列の相互変換まとめ](https://kokufu.blogspot.com/2018/12/python-datetime-unix-time.html) を書いたので、ついでと言っては何ですが date と datetime の変換についてもまとめてみました。



2018/12/03

[Python] datetime オブジェクト、Unix time、文字列の相互変換まとめ

Python で [datetime オブジェクト]() 、 [Unix time](https://ja.wikipedia.org/wiki/UNIX%E6%99%82%E9%96%93)、文字列を相互変換する方法はいろいろあります。
中には直感に反するものもありnative になりそうなのに、ローカルのタイムゾーンを適用しちゃうとか、
過去に1度間違えたのに、再度間違えるということもしばしば。

そこで、2度と間違えないように、個人的にベストと思う方法をまとめてみました。



変換には様々な方法があるので、上記が唯一の方法というわけではありませんが、
なるべく、安全かつシンプルになるものを選んだつもりです。

また、変換可能でも、安全では無いと判断したものは矢印を引いていません安全な方法をご存じの方は教えていただけると幸いです

2018/12/02

[Python] datetime.combine() でタイムゾーンは指定しない方がよい

先日、「[datetime のコンストラクタでタイムゾーンは指定しない方がよい](https://kokufu.blogspot.com/2018/11/python-datetime.html)」と書いたのですが、
`datetime.combine()` でも同様の問題が起こりました。

以下のように、日本のタイムゾーンが `+9:19` になってしまうのです。
```python
from datetime import datetime, date, time
import pytz

d = date(2018,11,1)
datetime.combine(d, time(), tzinfo=pytz.timezone('Asia/Tokyo'))
# 2018-11-01 00:00:00+09:19
```

2018/12/01

[Python] サマータイムのある地域で時刻計算する時は、一度 UTC に変換する

「サマータイムのある地域で」とタイトルに入れたのですが、地域に関わらず、時刻計算する時は UTC に変換してから行った方が良いです。
特にサマータイムのある地域では問題が起こりやすいので、注意が必要ということです。


### 問題が起こる例
2018年の ニューヨークは 11/4 2:00 am までがサマータイムアメリカでは Daylight Saving Time というでした。

```
2018/11/4 2:00 am より前
EDT -04:00

2018/11/4 1:00 am 以降
EST -05:00
```

このような時刻をまたぐ計算を aware のまま行うと、結果がおかしくなります。

```python
>>> import datetime, pytz
>>> dt = datetime.datetime(2018, 11, 4, 7, 0, 0, 0)  # 2018/11/4 7:00 am native
>>> dt_aware = pytz.timezone('America/New_York').localize(dt)
>>> dt_aware
datetime.datetime(2018, 11, 4, 7, 0, tzinfo=)
>>> str(dt_aware)
'2018-11-04 07:00:00-05:00'
```

上記の `dt_aware` から 1日前を計算すると以下のようになります。

```python
>>> calculated = dt_aware - datetime.timedelta(days=1)
>>> calculated
datetime.datetime(2018, 11, 3, 7, 0, tzinfo=)
>>> str(calculated)
'2018-11-03 07:00:00-05:00'
```

ぱっと見正しそうですが、11月3日なのに EST になっていて、間違っています。

2018/11/30

[Python] datetime のコンストラクタでタイムゾーンは指定しない方がよい

以下を見てください。
日本のタイムゾーンが `+9:19` になってしまいました。

```python
>>> import datetime, pytz
>>> datetime.datetime(2018, 9, 1, tzinfo=pytz.timezone('Asia/Tokyo'))
datetime.datetime(2018, 9, 1, 0, 0, tzinfo=)
```

調べてみると、pytz 2017.2 以降を使う場合、上記のような動作になるようです。

原因については、以下のコメント欄が参考になります。(以下の記事で触れているのは `replace` でタイムゾーン指定した場合ですが、コンストラクタで指定した場合も同様)

> 参考
>
> [pytzの仕様が変わっている - Qiita](https://qiita.com/higitune/items/0ca244373d380cf1c060)


簡単に言うと、「複数のタイムゾーン(UTC オフセット)を持つエリアでは、日付が決まらないとオフセットが決定できないので、
勝手にデフォルトのオフセットが採用される」ということのようです。

一般的に「複数のタイムゾーンを持つエリア」といえば、サマータイムがあるようなエリアで、日本は当てはまらない気がします。
ところが、日本は 1887年以前は今とは異なるタイムゾーン (`+09:18:59`9:19:00 ではない。pytz は秒は持てないからね…) だったそうです。

> 参考
>
> [一行入魂 PostgreSQLのtime zoneについて](http://aoyagikouhei.blog8.fc2.com/blog-entry-61.html)

pytz 2017.2 以降はこれを厳密に守るようになったということみたいですねとはいえ、100年以上前のタイムゾーンがデフォルトってどうなのよ?と思いますが…。

というわけで、aware な datetime オブジェクトを作成するには、
一度 native で作成してから aware に変換するのが正しいやり方のようです。

```python
>>> import datetime, pytz
>>> dt = datetime.datetime(2018, 9, 1)
>>> pytz.timezone('Asia/Tokyo').localize(dt)
datetime.datetime(2018, 9, 1, 0, 0, tzinfo=)
```

今回、たまたま日本のタイムゾーンで問題が起こって調べましたが、他のタイムゾーンサマータイムがあるなどでは同じ問題が前からあったということですね多分。確認はしていないけど。