numpy.datetime64と別形式の相互変換

## はじめに

numpyでの日付処理は速度を最優先にするためpandasやdatetimeと異なった形式で格納される。  
<https://numpy.org/doc/stable/reference/arrays.datetime.html>  
公式サイトを見るとわかる通り、Unix時間を単位[s or msなど]に応じて整数で格納される。  
msの場合はUnix時間の秒数を1000倍にして整数にして格納する。  
もちろんタイムゾーンもつけない。  
numpy.datetime64に変換する際はタイムゾーンを外して行う必要がある。今のところエラーにはならないが、非推奨。    

## タイムゾーン付文字列(ISO 8601形式)からnumpy.datetime64に変換する

```python
import datetime

import numpy as np

dateList = ['2021-01-01T12:00:00Z', '2021-01-02T12:00:00Z', '2021-07-02T12:00:00+09:00']
datetimeList = [datetime.datetime.fromisoformat(date).astimezone(tz=datetime.UTC).replace(tzinfo=None) for date in dateList]
datetimeList = np.array(datetimeList, dtype='datetime64[s]')
print(datetimeList)  # ['2021-01-01T12:00:00' '2021-01-02T12:00:00' '2021-07-02T03:00:00']
```

このように`astimezone`メソッドを使用して使用するタイムゾーンに変換して、`replace`メソッドを使用してタイムゾーンを外して変換する。

## datatime.datetimeからnumpy.datetime64に変換する

上記の例に含まれるので割愛

## numpy.datetime64からタイムゾーン付文字列(ISO 8601形式)に変換

```python
import datetime
import time

import numpy as np

dateList = ['2021-01-01T12:00:00Z', '2021-01-02T12:00:00Z', '2021-07-02T12:00:00Z']
datetimeList = [datetime.datetime.fromisoformat(date).astimezone(tz=datetime.UTC).replace(tzinfo=None) for date in dateList]
datetimeList = np.array(datetimeList, dtype='datetime64[s]')
print(datetimeList)  # ['2021-01-01T12:00:00' '2021-01-02T12:00:00' '2021-07-02T12:00:00']

# 方法1: numpy.datetime64[s]をstr関数で文字列化してZをつける
timeA = time.time()
for i in range(100000):
    datetimeStrList = [str(datetime)+'Z' for datetime in datetimeList]
timeB = time.time()
print(timeB-timeA)  # 0.2197432518005371

# 方法2: numpy.datetime64[s]を.astype('str')で文字列化してZをつける
timeA = time.time()
for i in range(100000):
    datetimeStrList = [string+'Z' for string in datetimeList.astype('str')]
timeB = time.time()
print(timeB-timeA)  # 0.4033396244049072

# 方法3: numpy.datetime64[s]を.astype(np.float64)でunix時間に変換し、datetimeにして文字列に直す。
timeA = time.time()
for i in range(100000):
    strList = [datetime.datetime.fromtimestamp(timestamp, tz=datetime.UTC).strftime('%Y-%m-%dT%H:%M:%SZ')
               for timestamp in datetimeList.astype(np.float64)]
timeB = time.time()
print(timeB-timeA)  # 1.1203858852386475
```

方法2,3は配列の操作が2回発生し、時間がかかるので方法1で行うほうが良い。  
方法3の場合はnumpy.datetime64[s]で格納されているものを使わないといけない。単位がmsだと値が1000倍違うためである。numpyのデフォルトの格納単位はmsなので特に注意が必要である。  

## numpy.datetime64からdatatime.datetimeに変換

```python
import datetime

import numpy as np

dateList = ['2021-01-01T12:00:00Z', '2021-01-02T12:00:00Z', '2021-07-02T12:00:00Z']
datetimeList = [datetime.datetime.fromisoformat(date).astimezone(tz=datetime.UTC).replace(tzinfo=None) for date in dateList]
datetimeList = np.array(datetimeList, dtype='datetime64[s]')
print(datetimeList)  # ['2021-01-01T12:00:00' '2021-01-02T12:00:00' '2021-07-02T12:00:00']

datetimeList = [datetime.datetime.fromtimestamp(timestamp, tz=datetime.UTC) for timestamp in datetimeList.astype(np.float64)]
print(datetimeList)
```

`datetimeList.astype(np.float64)`により、整数のみ(ここではUNIX時間)を抜き出す。それから`datetime.datetime.fromtimestamp(timestamp, tz=datetime.UTC)`により`datetime.datetime`に変換する。タイムゾーンをつけたい場合はtzに必要なタイムゾーンを入れ、つけない場合はNoneにする。  
ここでもnumpy.datetime64[s]で格納されているか注意する。そうでなければいったん変換する。  

## 参考

公式サイト

https://numpy.org/doc/stable/reference/arrays.datetime.html