今さらPython3 (70) - 日付時刻

第10章、日付とか時刻の扱い方の部分。

入門 Python 3

入門 Python 3

calendar

日付のフォーマットやタイムゾーンは悩ましい問題なのは確かで、ここではなぜか、isleap()で閏年判定が紹介されている。

>>> import calendar
>>> calendar.isleap(1900)
False
>>> calendar.isleap(1996)
True
>>> calendar.isleap(1999)
False
>>> calendar.isleap(2000)
True
>>> calendar.isleap(2002)
False
>>> calendar.isleap(2016)
True
>>> calendar.isleap(2100)
False
>>> 

何だっけ?西暦下二桁が4で割り切れる年は閏年。だけど、下二桁00の年は閏年でない。でも例外で上二桁が4で割りきれる場合は閏年になるみたいなルールだったよね。ま、上の通りです。

date

datetimeとdate両方があってなんでやねん!と思った事は確かに昔あったけど、ここから解説してくれるんだよねと期待して読み進む。

>>> from datetime import date
>>> halloween = date(2015, 10, 31)
>>> halloween
datetime.date(2015, 10, 31)
>>> halloween.day
31
>>> halloween.month
10
>>> halloween.year
2015
>>> halloween.isoformat()
'2015-10-31'

まず、datetimeの中に、date, time, datetime, timedeltaという4つのメインオブジェクトがあるというところを押さえてからのdateを使ってますと。isoformat()以外は見たことも使ったこともあるかな。どこかの練習問題でstrftime()をゴニョゴニョして日付を表示させるたけど、それより簡単だね。

>>> from datetime import date
>>> now = date.today()
>>> now
datetime.date(2015, 12, 27)

本日日付を出す場合は、date.today()が使える。それをnowに入れるから話がややこしくなるような。

>>> from datetime import timedelta
>>> one_day = timedelta(days=1)
>>> tomorrow = now + one_day
>>> tomorrow
datetime.date(2015, 12, 28)
>>> now + 17 * one_day
datetime.date(2016, 1, 13)
>>> yesterday = now - one_day
>>> yesterday
datetime.date(2015, 12, 26)

じゃ、月とか年でもできるんだよね?

>|python|
>>> one_month = timedelta(months=1)
Traceback (most recent call last):
File "", line 1, in
TypeError: 'months' is an invalid keyword argument for this function
|

ダメなのかということで、ドキュメントを確認。

6.10.2 timedelta オブジェクト

weeksはあるみたいなので、週でやってみよか。

>>> one_week = timedelta(weeks=1)
>>> now + one_week
datetime.date(2016, 1, 3)
>>> now + one_week * 2
datetime.date(2016, 1, 10)

dateの許容範囲は9999/12/31までね。

timeとdatetime

>>> from datetime import time
>>> noon = time(12,0,0)
>>> noon
datetime.time(12, 0)
>>> noon.hour
12
>>> noon.minute
0
>>> noon.second
0
>>> noon.microsecond
0

timeはこんな感じ。dateとだいたい同じだけど、マイクロ秒まであるあたりは知っておいても良いかもね。

>>> from datetime import datetime
>>> some_day = datetime(2016,1,2,3,4,5,6)
>>> some_day
datetime.datetime(2016, 1, 2, 3, 4, 5, 6)
>>> some_day.isoformat()
'2016-01-02T03:04:05.000006'

datetime()に引数を渡して日付+時刻を生成。isoformat()は日付だけじゃなくてdatetimeでも使える。

>>> from datetime import datetime
>>> now = datetime.now()
>>> now
datetime.datetime(2015, 12, 27, 10, 55, 12, 680875)
>>> now.year
2015
>>> now.month
12
>>> now.day
27
>>> 

さっき、now使うとややこしくなるといったのはこれのこと。now()関数で現在のタイムスタンプをゲット。2月中旬のふりして、これ書いているのが年末だったりするけど。

>>> from datetime import datetime, time, date
>>> noon = time(12)
>>> this_day = date.today()
>>> noon_today = datetime.combine(this_day, noon)
>>> noon_today
datetime.datetime(2015, 12, 27, 12, 0)

combine()でdate型とtime型を合体させてdatetime型に。

>>> noon_today.date()
datetime.date(2015, 12, 27)
>>> noon_today.time()
datetime.time(12, 0)

これはその逆で、datetimeからdate, timeでそれぞれ取り出したところ。

datetimeの中にあるのとは別のtime

ややこしい。

>>> import time
>>> now = time.time()
>>> now
1451181661.426077
>>> time.ctime(now)
'Sun Dec 27 11:01:01 2015'

こっちのtimeは1970年1月1日0:00UTCを起点とする秒数表示ってことね。ctime()を使うと、ユーザフレンドリーなフォーマットで出てくる。

>>> time.localtime(now)
time.struct_time(tm_year=2015, tm_mon=12, tm_mday=27, tm_hour=11, tm_min=1, tm_sec=1, tm_wday=6, tm_yday=361, tm_isdst=0)
>>> time.gmtime(now)
time.struct_time(tm_year=2015, tm_mon=12, tm_mday=27, tm_hour=2, tm_min=1, tm_sec=1, tm_wday=6, tm_yday=361, tm_isdst=0)
>>>
>>> tm = time.localtime(now)
>>> time.mktime(tm)
1451181661.0
>>> tm.tm_year
2015
>>> tm.tm_min
1 

localtime()とgmtime()でやると、ローカル時刻とUTCがそれぞれ取得できる。そこから逆にmktime()でUNIX時間に戻したり、各要素を取り出したり出来るんだね。

日時の読み書き

time.time()の復習から。

>>> import time
>>> now = time.time()
>>> time.ctime(now)
'Sun Dec 27 11:13:03 2015'
>>>

こんどはstrftime()の出番だけど、指定子についてはこのリンクに書いてある。

8.1. datetime — 基本的な日付型および時間型 — Python 3.4.3 ドキュメント

>>> import time
>>> fmt = "It's %A, %B, %d, %Y, local time %I:%M:%S%p"
>>> t = time.localtime()
>>> t
time.struct_time(tm_year=2015, tm_mon=12, tm_mday=27, tm_hour=11, tm_min=16, tm_sec=52, tm_wday=6, tm_yday=361, tm_isdst=0)
>>> time.strftime(fmt, t)
"It's Sunday, December, 27, 2015, local time 11:16:52AM"
>>> 

これは、いつぞやの復習課題でやったパターンだね。

>>> from datetime import date
>>> some_day = date(2015, 7, 4)
>>> some_day.strftime(fmt)
"It's Saturday, July, 04, 2015, local time 12:00:00AM"
>>>

日付だけさっきのフォーマット(fmt)に放り込むと、時刻の部分は午前0時になる。ま、当たり前だね。

>>> from datetime import time
>>> some_time = time(10,53)
>>> some_time.strftime(fmt)
"It's Monday, January, 01, 1900, local time 10:53:00AM"
>>> 

逆に時刻だけ入れたパターン。良かったのは1900年1月1日が月曜日だと分かったことぐらいかな?

>>> import time
>>> fmt = "%Y-%m-%d"
>>> time.strptime("2012 01 29", fmt)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/_strptime.py", line 494, in _strptime_time
    tt = _strptime(data_string, format)[0]
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/_strptime.py", line 337, in _strptime
    (data_string, format))
ValueError: time data '2012 01 29' does not match format '%Y-%m-%d'
>>> time.strptime("2012-01-29", fmt)
time.struct_time(tm_year=2012, tm_mon=1, tm_mday=29, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=6, tm_yday=29, tm_isdst=-1)
>>> 

逆に文字列の日付時刻を(システム的に)意味のある情報に変換するパターンはstrptime()を使うんだね。

>>> time.strptime("2012-13-29", fmt)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/_strptime.py", line 494, in _strptime_time
    tt = _strptime(data_string, format)[0]
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/_strptime.py", line 337, in _strptime
    (data_string, format))
ValueError: time data '2012-13-29' does not match format '%Y-%m-%d'

実存しない月とかを放り込むと怒られる。てか、そのぐらいのチェックはやるだろ普通。

>>> import locale
>>> from datetime import date
>>> halloween = date(2015, 10, 31)
>>> for lang_country in ['en_us', 'fr_fr', 'de_de', 'es_es', 'is_is', 'ja_JP', 'zh_TW']:
...     locale.setlocale(locale.LC_TIME, lang_country)
...     halloween.strftime('%A, %B, %d')
... 
'en_us'
'Saturday, October, 31'
'fr_fr'
'Samedi, octobre, 31'
'de_de'
'Samstag, Oktober, 31'
'es_es'
'sábado, octubre, 31'
'is_is'
'laugardagur, október, 31'
'ja_JP'
'土曜日, 10月, 31'
'zh_TW'
'周六, 10月, 31'
>>> 

setlocale()すると、指定したロケールで日付を返してくれると。最後に出ている中国語をどう指定するのかよく分からなかったけど、何やら方法があるらしい。

>>> import locale
>>> names = locale.locale_alias.keys()
>>> good_names = [name for name in names if \
... len(name) == 5 and name[2] == '_']
>>> len(good_names)
251

locale.pyのソースコードを見たときに、zh_CHSというのがあったので、それを使おうと思ったらダメだった理由が分かった。5文字で真ん中に_があるやつだけが使えるんだね。

>>> ja_names = [name for name in names if name.startswith('ja')]
>>> ja_names
['ja_jp.pck', 'ja_jp.mscode', 'ja', 'ja_jp', 'japan', 'japanese', 'ja_jp.euc', 'japanese-euc', 'japanese.euc']
>>> zh_names = [name for name in names if name.startswith('zh')]
>>> zh_names
['zh_sg', 'zh_cn.euc', 'zh_tw.euc', 'zh_tw.euctw', 'zh_hk', 'zh_cn', 'zh_cn.big5', 'zh_tw', 'zh_hk.big5hk', 'zh', 'zh_sg.gbk']

これを見ると、日本語の場合はja_jpを使うしかなくて、中国語の場合はzh_sg, zh_hk, zh_cn, zh_twの中から選ぶ必要があるという事だね。

>>> for lang_country in ['zh_hk', 'zh_cn', 'zh_tw']:
...     locale.setlocale(locale.LC_TIME, lang_country)
...     halloween.strftime('%A, %B, %d')
... 
'zh_hk'
'周六, 10月, 31'
'zh_cn'
'星期六, 十月, 31'
'zh_tw'
'周六, 10月, 31'
>>> 

ちなみにzh_sgは指定してもエラーになったので削ってます。中国に以前住んでいたので、星期六の方がしっくりくるかな。(周とか礼拜も使うのは聞いたことあるけどね。)

そのほか

日付時刻系の他モジュールを列挙しておく。

crsmithdev.com
python-dateutil - Labix
pypi.python.org
github.com

(つづく)