[Python] 日時に関するtimeとdatetimeのまとめ

# 初めに

Pythonにおける日時に関するモジュールをまとめた。
UNIX時間を取得するならtimeモジュール、日時を取得するならdatetimeモジュールを使う。
pandasのTimestamp型はdatetime.datetimeとほぼ同一なので、取り扱う場合はdatetime型を意識する。
本記事ではtimeとdatetimeモジュールを取り扱う。

 * time : 実行時間計測
 * datetime：日付利用
 * pandas.Timestamp：データ解析
 * numpy.datetime64：データ解析

# time
時間に関するpythonの標準モジュール。

>このモジュールでは、時刻に関するさまざまな関数を提供します。
[[公式] 時刻データへのアクセスと変換](https://docs.python.org/ja/3/library/time.html)


時刻は[UNIX時間[秒]](#unix時間)で表現されたもの。
time.time() はUNIX時間を示しており、この時間を用いてプログラムの実行時間計測などに使用する。
time.strftime() は日時などを文字列で表現するが、個人的には使用しない。
time.ctime() は日時を含めた表現。こちらもdatetimeを用いるので、個人的には使用しない。
理由としては後述するdatetimeに類似する型がpandasやnumpyなどの演算系ライブラリで使用されているため。

|コード|type|value|
|---|---|---|
|time.time()|float|1612560000.0000000|
|time.strftime('%Y/%m/%d %H:%M:%S')|string|2021/01/02 03:04:05|
|time.ctime()|string|Sat Feb  2 03:04:05 2021|

```python
import time
def data_print(value):
    print('    type: ', type(value))
    print('    value: ', value)
print('time')
data_print(time.time())
data_print(time.strftime('%Y/%m/%d %H:%M:%S'))
data_print(time.ctime())
# time
#     type:  <class 'float'>
#     value:  1612560000.0000000
#     type:  <class 'str'>
#     value:  2021/01/02 03:04:05
#     type:  <class 'str'>
#     value:  Sat Feb  2 03:04:05 2021
```

なお、文字に関するフォーマットは以下の通り。
[[公式] 時刻データへのアクセスと変換  time.strftime(format[, t])](https://docs.python.org/ja/3/library/time.html)

| ディレクティブ | 意味 |
|---|---|
| %a | ロケールの短縮された曜日名 |
| %A | ロケールの曜日名 |
| %b | ロケールの短縮された月名 |
| %B | ロケールの月名 |
| %c | ロケールの日時の適切な形式 |
| %d | 月中の日にちの 10 進表記[01,31] |
| %H | 時 (24 時間表記) の 10 進表記[00,23] |
| %I | 時 (12 時間表記) の 10 進表記 [01,12] |
| %j | 年中の日にちの 10 進表記[001,366] |
| %m | 月の 10 進表記 [01,12] |
| %M | 分の 10 進表記[00,59] |
| %p | ロケールの AM もしくは PM と等価な文字列 |
| %S | 秒の 10 進表記 [00,61] |
| %U | 年の初めから何週目か (日曜を週の始まりとします) を表す 10 進数 [00,53]|
| %w | 曜日の 10 進表記[0 (日曜日),6] |
| %W | 年の初めから何週目か (月曜を週の始まりとします) を表す 10 進数 [00,53]|
| %x | ロケールの日付を適切な形式 |
| %X | ロケールの時間を適切な形式 |
| %y | 西暦の下 2 桁の 10 進表記 [00,99] |
| %Y | 西暦 ( 4桁) の 10 進表記 |
| %z | タイムゾーンと UTC/GMT との時差を表す正または負の時間を +HHMM、-HHMM で表します。[-23:59, +23:59] |
| %Z | タイムゾーンの名前 |
| %% | 文字 '%' |

## UNIX時間
UNIX時間とは、時刻の起点（エポック：epoch）からの経過秒数。
時刻の起点は協定世界時 (UTC) の1970年1月1日午前0時0分0秒。
なお、[UTC時間](#utc時間)とは異なり、閏秒の存在を無視している。
例えば、[UTC時間](#utc時間)で閏秒+1秒が挿入された+2秒において、[UTC時間](#utc時間)は+1秒が挿入される。
これは、日時を表現するときに数秒の差が発生することを意味するが、その影響は意識することはあまりない。

## UTC時間
UTC時間（UTC：Coordinated Universal Time）とは、国際原子時 (TAI) に由来する原子時系の時刻。協定世界時ともいう。
エポックからの経過秒数を示しており、閏秒も考慮している。
各国における時間は（主に）UTCからの時差を算出することでその国における時間を規定している。
日本の標準時刻を日本標準時 (JST)といい、UTCに対して9時間進めた時間を採用している。
タイムゾーン（地域における同一の標準時を使用する規定地域）を指定することで、その時刻差を規定し、補正した値を使用する。

>協定世界時は国際度量衡局 (BIPM) が国際地球回転・基準系事業 (IERS) の支援を受けて維持する時刻系。
世界各地の標準時は協定世界時を基準としている。日本標準時 (JST) は協定世界時より9時間進めた時間である。
[@wiki](https://ja.wikipedia.org/wiki/%E5%8D%94%E5%AE%9A%E4%B8%96%E7%95%8C%E6%99%82)

## 実行時間を計測する
あるプログラムのの実行時間を計測する場合、演算を行う関数などを挟んで、そのUNIX時間を取得し、その時間差を実行時間とする。
実行速度を検証する際や、演算速度比較を行う際に使用する。

```python:main.py
import time
def func1():
    values = []
    for val in range(10**6):
        values.append(val)

def func2():
    values = [0] * 10**6
    for num, val in enumerate(range(10**6)):
        values[num] = val

def func3():
    values = [val for val in range(10**6)]
    
time1 = time.time()
func1()
time2 = time.time()
func2()
time3 = time.time()
func3()
time4 = time.time()
print('func1: {:.3f} sec'.format(time2 - time1))
print('func2: {:.3f} sec'.format(time3 - time2))
print('func3: {:.3f} sec'.format(time4 - time3))
# func1: 0.120 sec
# func2: 0.095 sec
# func3: 0.073 sec
```

# datetime

datetimeモジュールは、日時を操作する際に使用されるモジュール。
日付や時刻などを取り扱う場合はtimeモジュールでなく、こちらをよく使う。
オブジェクトはstringではないので、表示の際は注意する。
また、よく使用されるdatetime.datetimeとdatetime.dateオブジェクトは別なので注意する。
>datetime モジュールは、日付や時刻を操作するためのクラスを提供しています。
日付や時刻に対する算術がサポートされている一方、実装では出力のフォーマットや操作のための効率的な属性の抽出に重点を置いています。
[[公式] 基本的な日付型および時間型: datetime](https://docs.python.org/ja/3/library/datetime.html)

|オブジェクト|概要|
|---|---|
|datetime.datetime | 年月日と時刻|
|datetime.date | 年月日|
|datetime.time | 時刻|
|datetime.timedelta | 時間差・経過時間|
|datetime.timezone | タイムゾーン|

## datetime.date：年月日
datetime.dateオブジェクトは、日付のみの情報を使用する。
時間などの細かい情報が必要ない場合に使用する。

>date オブジェクトは、両方向に無期限に拡張された現在のグレゴリオ暦という理想化された暦の日付 (年月日) を表します。
1 年 1 月 1 日は日番号 1、1 年 1 月 2 日は日番号 2 と呼ばれ、他も同様です。
[[公式] date オブジェクト](https://docs.python.org/ja/3/library/datetime.html#date-objects)

|コード|type|value (example)|
|---|---|---|
|datetime.date.today()|datetime.date|2021-01-02|
|datetime.date(year=2021, month=1, day=2)|datetime.date|2021-01-02|

```bash
>>> import datetime
>>> today = datetime.date.today()
>>> today
datetime.date(2021, 1, 2)
>>> type(today)
<class 'datetime.date'>
>>> test = datetime.date(year = 2021, month = 1, day = 2)
>>> test
datetime.date(2021, 1, 2)
>>> type(test)
<class 'datetime.date'>
```

## datetime.datetime：年月日と時刻
datetimeオブジェクトは、日時を表す。
dateおよびtimeが単一に含まれている。

>datetime オブジェクトは date オブジェクトおよび time オブジェクトの全ての情報が入っている単一のオブジェクトです。
[[公式] datetime オブジェクト](https://docs.python.org/ja/3/library/datetime.html#datetime-objects)

|コード|type|value (example)|
|---|---|---|
|datetime.datetime.now()|datetime.datetime|datetime.datetime(2021, 1, 2, 3, 4, 5, 666666)|
|datetime.datetime(year = 2021, month = 1, day = 2, hour = 3, minute = 4, second = 5, microsecond = 6)|datetime.datetime|datetime.datetime(2021, 1, 2, 3, 4, 5, 6)|

```bash
>>> import datetime       
>>> now = datetime.datetime.now()
>>> now
datetime.datetime(2021, 2, 6, 18, 28, 50, 729471)
>>> type(now)
<class 'datetime.datetime'>
>>> test = datetime.datetime(year = 2021, month = 1, day = 2, hour = 3, minute = 4, second = 5, microsecond = 6)
>>> test
datetime.datetime(2021, 1, 2, 3, 4, 5, 6)
>>> type(test)
<class 'datetime.datetime'>
```

## datetime.timedelta：時刻差
datetime.timedelta オブジェクトは、2つのdatetimeの時刻差を表すオブジェクト。
二つの日付や時刻間の差を使用する場合に用いる。

>全ての引数がオプションで、デフォルト値は 0 です。 引数は整数、浮動小数点数でもよく、正でも負でも可。
datetime.timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0)
[[公式] timedelta オブジェクト](https://docs.python.org/ja/3/library/datetime.html#timedelta-objects)

使用する場合は、対象となるdatetimeオブジェクトに対して、演算子を用いて演算を行う。
基本的には時刻差や日時の差を操作する際に使用する。

```bash
>>> import datetime
>>> test = datetime.datetime(year = 2021, month = 1, day = 2, hour = 3, minute = 4, second = 5, microsecond = 6)
>>> datetime.timedelta(days=1)
datetime.timedelta(days=1)
>>> test + datetime.timedelta(days=1)
datetime.datetime(2021, 1, 3, 3, 4, 5, 6)
```

# datetime型 ↔ string型
datetimeと文字列の変換は、日時を取り扱っているため、使用することが多い。
文字列化する際は、datetime.strftime()を使用し、
文字列からdatetimeオブジェクト化する際は、datetime.strptime()を使用する。
>date, datetime, time オブジェクトは全て strftime(format) メソッドをサポートし、時刻を表現する文字列を明示的な書式文字列で統制して作成しています。
逆に datetime.strptime() クラスメソッドは日付や時刻に対応する書式文字列から datetime オブジェクトを生成します。
[[公式] strftime() と strptime() の振る舞い](https://docs.python.org/ja/3/library/datetime.html#strftime-and-strptime-behavior)

||datetime.strftime()| datetime.strptime()
|---|---|---|
|使用法|与えられた書式に従って文字列に変換|指定された書式で文字列を構文解析して datetime オブジェクトに変換|
|メソッドの種類|インスタンスメソッド|クラスメソッド|
|メソッドを持つクラス|date, datetime, time|datetime|
|シグネチャ|strftime(format)| strptime(date_string, format)|

| 指定子 | format文中の意味 |
|---|---|
| %a | ロケールの曜日名を短縮形。 |
| %A | ロケールの曜日名 |
| %w | 曜日を10進表記した文字列を表示します。0 が日曜日で、6 が土曜日を表します。 |
| %d | 0埋めした10進数で表記した月中の日にち |
| %b | ロケールの月名を短縮形 |
| %B | ロケールの月名を表示 |
| %m | 0埋めした10進数で表記した月 |
| %y | 0埋めした10進数で表記した世紀無しの年 |
| %Y | 西暦 (4桁) の 10 進表記 |
| %H | 0埋めした10進数で表記した時 (24時間表記) |
| %I | 0埋めした10進数で表記した時 (12時間表記) |
| %p | ロケールの AM もしくは PM と等価な文字列 |
| %M | 0埋めした10進数で表記した分 |
| %S | 0埋めした10進数で表記した秒 |
| %f | 10進数で表記したマイクロ秒 (左側から0埋めされます) |
| %z | UTCオフセットを ±HHMM[SS[.ffffff]] の形式で表示します (オブジェクトがnaiveであれば空文字列) |
| %Z | タイムゾーンの名前を表示します (オブジェクトがnaiveであれば空文字列) |
| %j | 0埋めした10進数で表記した年中の日にち|
| %U | 0埋めした10進数で表記した年中の週番号 (週の始まりは日曜日とする) |
| %W | 0埋めした10進数で表記した年中の週番号 (週の始まりは月曜日とする) |
| %c | ロケールの日時を適切な形式 |
| %x | ロケールの日付を適切な形式 |
| %X | ロケールの時間を適切な形式|
| %% | 文字 '%' を表します。 |

## datetime型 → 文字列型
文字列化に関して、実施例を示す。
指定子に従って、文字列化できる。

```bash
>>> import datetime
>>> now = datetime.datetime.now()
>>> now.strftime('%Y-%m-%d %H:%M:%S')
'2021-02-06 19:01:48'
```

## 文字列型 → datetime型
datetimeオブジェクト化に関して、実施例を示す。
指定子に従った解釈でdatetimeオブジェクトに変換できる。

```bash
>>> import datetime
>>> datetime.datetime.strptime('2021-02-06 19:01:48', '%Y-%m-%d %H:%M:%S')
datetime.datetime(2021, 2, 6, 19, 1, 48)
```
# datetime型 → date型
datetime型とdate型は直接比較演算が実施できない。
したがって、比較演算などを行う場合は変換が必要となる。
また、取り扱う場合は混ざらないようにする必要がある。

datetime型でdate()を実行すると、date型で返してくれる。

```bash
>>> test = datetime.datetime(year = 2021, month = 1, day = 2, hour = 3, minute = 4, second = 5, microsecond = 6)
>>> test.date()
datetime.date(2021, 1, 2)
```

# 最後に
関数の実行時間確認や時系列データの処理などに利用することはよくある。
利用の際に本記事を思い出しの参考にする予定。
また、pandasとnumpyに関しても変換が必要になるので、別途まとめたい。

# 参考・引用
【time】
[[公式] 時刻データへのアクセスと変換](https://docs.python.org/ja/3/library/time.html)
[[公式] 時刻データへのアクセスと変換  time.strftime(format[, t])](https://docs.python.org/ja/3/library/time.html)

【datetime】
[[公式] 基本的な日付型および時間型: datetime](https://docs.python.org/ja/3/library/datetime.html)
[[公式] datetime オブジェクト](https://docs.python.org/ja/3/library/datetime.html#datetime-objects)
[[公式] strftime() と strptime() の振る舞い](https://docs.python.org/ja/3/library/datetime.html#strftime-and-strptime-behavior)
[[公式] timedelta オブジェクト](https://docs.python.org/ja/3/library/datetime.html#timedelta-objects)