PythonでUTC時刻をISO形式の文字列で受け取ってJSTのdatetimeに変換する

# ISO形式の時刻

年月日と時間の間にTが入っている日時の表現規格。
末尾にはTimezoneの情報も保持する規格です。

[ISO 8601 - Wikipedia](https://ja.wikipedia.org/wiki/ISO_8601)

基本形式、拡張形式があったりTimezoneの表現が複数あり、
ISO形式といっても同じ時間の表現に幅があります。

- ISO形式の日時例
 
 - 2019-03-08T22:34:56.123456+00:00

 - 2019-03-08T22:34:56.123456+0000

 - 2019-03-08T22:34:56.123456+00

 - 2019-03-08T22:34:56.123456Z

 - 20190308T223456Z

文字列の表現になるため、コードレベルで日時や日付として正確に扱うために、
このISO形式の **文字列** を **datetime** に変換します。

特に、コード上でJSTの日付や時間で扱いたい場合には、
UTCのISO形式の時刻を受け取ったら、datetimeに変換しJSTに変換します。
**dateutil.parser** がISOの表現の幅に対応していて優秀です。

# UTC時刻をISO形式で受け取った時のJSTへの変換方法

## 準備

**dateutil.parser** を使うために必要なライブラリをpipで導入します。

```
pip install python-dateutil
```

## 変換方法

以下、手順で変換します。

1. **dateutil.parser.parse** で **datetime** に変換
2. **datetime.astimezone** でJSTに変換

```python:
import datetime
import dateutil.parser

str_timestamp = '2019-03-09T22:34:56.123456Z'

JST = datetime.timezone(datetime.timedelta(hours=+9), 'JST')
jst_timestamp = dateutil.parser.parse(str_date).astimezone(JST)
```

## 実行例

ISOの表現の幅のなかで、いろんな入力値で結果を確認します。

```python:iso_utc_to_jst.py
import datetime
import dateutil.parser

str_datetimes = ['2019-03-09T22:34:56.123456+00:00',
                 '2019-03-09T22:34:56.123456+0000',
                 '2019-03-09T22:34:56.123456+00',
                 '2019-03-09T22:34:56.123456Z',
                 '20190309T223456+00:00',
                 '20190309T223456Z']

JST = datetime.timezone(datetime.timedelta(hours=+9), 'JST')

for i, str_datetime in enumerate(str_datetimes):
    try:
        print('# ' + str(i+1))
        print('  入力値 ：', str_datetime, type(str_datetime))
        jst_datetime = dateutil.parser.parse(str_datetime).astimezone(JST)
        print('  JST時刻：', jst_datetime, type(jst_datetime))
        print('  JST日付：', jst_datetime.date(), type(jst_datetime.date()))
    except Exception as e:
        print(e.args)
```

すべて変換できます。

```s:実行結果
>python iso_utc_to_jst.py
# 1
  入力値 ： 2019-03-09T22:34:56.123456+00:00 <class 'str'>
  JST時刻： 2019-03-10 07:34:56.123456+09:00 <class 'datetime.datetime'>
  JST日付： 2019-03-10 <class 'datetime.date'>
# 2
  入力値 ： 2019-03-09T22:34:56.123456+0000 <class 'str'>
  JST時刻： 2019-03-10 07:34:56.123456+09:00 <class 'datetime.datetime'>
  JST日付： 2019-03-10 <class 'datetime.date'>
# 3
  入力値 ： 2019-03-09T22:34:56.123456+00 <class 'str'>
  JST時刻： 2019-03-10 07:34:56.123456+09:00 <class 'datetime.datetime'>
  JST日付： 2019-03-10 <class 'datetime.date'>
# 4
  入力値 ： 2019-03-09T22:34:56.123456Z <class 'str'>
  JST時刻： 2019-03-10 07:34:56.123456+09:00 <class 'datetime.datetime'>
  JST日付： 2019-03-10 <class 'datetime.date'>
# 5
  入力値 ： 20190309T223456+00:00 <class 'str'>
  JST時刻： 2019-03-10 07:34:56+09:00 <class 'datetime.datetime'>
  JST日付： 2019-03-10 <class 'datetime.date'>
# 6
  入力値 ： 20190309T223456Z <class 'str'>
  JST時刻： 2019-03-10 07:34:56+09:00 <class 'datetime.datetime'>
  JST日付： 2019-03-10 <class 'datetime.date'>
```


# Python 3.7のfromisoformatの利用は注意

他の方法として、ISO形式の文字列をdatetimeに変換するfromisoformatという関数がバージョン3.7から追加されています。

[Python ドキュメント - datetime] (https://docs.python.org/ja/3/library/datetime.html)

ただし、拡張形式のISOで末尾のtimezoneの表現は+hh:mmしか対応できません。

## 変換方法

```python
import datetime

str_timestamp = '2019-03-09T22:34:56.123456Z'

JST = datetime.timezone(datetime.timedelta(hours=+9), 'JST')
jst_datetime = datetime.fromisoformat(date_string).astime(JST)
```

## 実行例

ISOの表現の幅のなかで、いろいろな入力値で結果を確認します。

```python:iso_utc_to_jst2.py
import datetime

str_datetimes = ['2019-03-08T22:34:56.123456+00:00',
                 '2019-03-08T22:34:56.123456+0000',
                 '2019-03-08T22:34:56.123456+00',
                 '2019-03-08T22:34:56.123456Z',
                 '20190308T223456+00:00',
                 '20190308T223456Z']

JST = datetime.timezone(datetime.timedelta(hours=+9), 'JST')

for i, str_datetime in enumerate(str_datetimes):
    try:
        print('# ' + str(i+1))
        print('  入力値 ：', str_datetime, type(str_datetime))
        jst_datetime = datetime.datetime.fromisoformat(str_datetime).astimezone(JST)
        print('  JST時刻：', jst_datetime, type(jst_datetime))
        print('  JST日付：', jst_datetime.date(), type(jst_datetime.date()))
    except Exception as e:
        print(e.args)
```

拡張形式のISOで末尾のtimezoneの表現は+hh:mmのみ変換できています。

```s:実行結果
>python iso_utc_to_jst2.py
# 1
  入力値 ： 2019-03-08T22:34:56.123456+00:00 <class 'str'>
  JST時刻： 2019-03-09 07:34:56.123456+09:00 <class 'datetime.datetime'>
  JST日付： 2019-03-09 <class 'datetime.date'>
# 2
  入力値 ： 2019-03-08T22:34:56.123456+0000 <class 'str'>
("Invalid isoformat string: '2019-03-08T22:34:56.123456+0000'",)
# 3
  入力値 ： 2019-03-08T22:34:56.123456+00 <class 'str'>
("Invalid isoformat string: '2019-03-08T22:34:56.123456+00'",)
# 4
  入力値 ： 2019-03-08T22:34:56.123456Z <class 'str'>
("Invalid isoformat string: '2019-03-08T22:34:56.123456Z'",)
# 5
  入力値 ： 20190308T223456+00:00 <class 'str'>
("Invalid isoformat string: '20190308T223456+00:00'",)
# 6
  入力値 ： 20190308T223456Z <class 'str'>
("Invalid isoformat string: '20190308T223456Z'",)
```