I tried to summarize the library that handles date and time in Python in a reverse lookup style.
The content of this article has been written with care for accuracy, but is not official information. Please refer to the official reference (reference # 1) for accurate information.
Most of it is about standard modules, so if you're new to Python or don't know much about the date and time library. (Because I'm still new to Python.)
Python2 has been confirmed with v2.7.10, and Python3 has been confirmed with v3.5.1. (Anaconda 4.0.0 Windows 64bit version is used.)
Those that do not deal with Python 2 and Python 3 separately can be used in common.
In that case, print
is unified with parentheses, and the execution result is Python3.
The sample code mainly shows the results in an interactive environment.
The extension module versions are as follows.
dateutil 2.5.1
pytz 2016.2
A brief description of the standard module for date and time and the types it contains. For details, refer to the official reference (reference material # 1).
In the text, from datetime import *
is applied unless otherwise specified.
module | Description |
---|---|
datetime module |
Provides standard types and operations for dates and times. |
time module |
Contains low-level (system-level) time utilities. |
calendar module |
It mainly includes utilities related to text calendars. |
module | Description |
---|---|
dateutil |
A module that makes up for the date and time processing that the standard module lacks. |
pytz |
A module that handles time zones. Details will be described later. |
Mold | Description |
---|---|
datetime.date |
Date object. |
datetime.datetime |
Date and time object. |
datetime.time |
Time object.time Not to be confused with modules. |
datetime.timedelta |
An object that represents the difference between two dates and times. |
datetime.tzinfo |
An abstract base class for timezone information. |
datetime.timezone |
Implementation with fixed offset of time zone information. |
time.struct_time |
UNIXstruct tm A named tuple that corresponds to (structure). |
The type hierarchy of the datetime
module is as follows.
timezone
type below Python 3.2.object
timedelta
tzinfo
timezone
time
date
datetime
Hereafter, the module name of the type is omitted in the text.
Some of the same datetime
objects have a timezone and some do not.
In Python, datetime
with a time zone is called ** aware ** (date and time), and datetime
without a time zone is called ** naive ** (date and time).
The date
object can always be naive and the time
object can be either.
The timezone
object added in Python 3.2 is available as an instance of the timezone (tzinfo
).
Prior to that, the tzinfo
implementation did not exist in standard modules.
In such cases, use the pytz
module.
The implementation of tzinfo
also has a submodule tz
of the dateutil
module.
Also, if you want to indicate the time zone with an arbitrary offset value (+09: 00 for JST), timezone
is sufficient, but
The pytz
module is recommended if you need to handle timezone handling rigorously.
The pytz
module references the IANA timezone database (Olson database).
For details, refer to the official reference (reference material # 1).
For the IANA timezone database, please refer to the following articles.
tz database - Wikipedia https://ja.wikipedia.org/wiki/Tz_database
The conversion method between naive and aware is explained in this volume.
It can be generated by the constructor. The time
object is omitted.
>>> from datetime import datetime, date, timezone, timedelta
>>>
>>> datetime(2016, 7)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Required argument 'day' (pos 3) not found
>>> datetime(2016, 7, 5)
datetime.datetime(2016, 7, 5, 0, 0)
>>> datetime(2016, 7, 5, 13)
datetime.datetime(2016, 7, 5, 13, 0)
>>> datetime(2016, 7, 5, 13, 45)
datetime.datetime(2016, 7, 5, 13, 45)
>>> datetime(2016, 7, 5, 13, 45, 27)
datetime.datetime(2016, 7, 5, 13, 45, 27)
>>> datetime(2016, 7, 5, 13, 45, 27, 123456)
datetime.datetime(2016, 7, 5, 13, 45, 27, 123456)
>>> datetime(2016, 7, 5, 13, 45, 27, 123456, timezone(timedelta(hours=+9)))
datetime.datetime(2016, 7, 5, 13, 45, 27, 123456, tzinfo=datetime.timezone(datetime.timedelta(0, 32400)))
>>>
>>> date(2016, 7)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Required argument 'day' (pos 3) not found
>>> date(2016, 7, 5)
datetime.date(2016, 7, 5)
>>> date(2016, 7, 5, 0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: function takes at most 3 arguments (4 given)
The seventh element of datetime
is microseconds (1 / 1,000,000 seconds).
It can also be generated from a time stamp. See the next section for time stamps of the current date and time.
>>> datetime.fromtimestamp(12345678901.234567)
datetime.datetime(2361, 3, 22, 4, 15, 1, 234568)
>>> date.fromtimestamp(12345678901.234567)
datetime.date(2361, 3, 22)
The time stamp (float) here is a value called ** UNIX time ** or epoch seconds.
UNIX time-Wikipedia https://ja.wikipedia.org/wiki/UNIX%E6%99%82%E9%96%93
UNIX time is an integer value, but on Python it is represented by float
.
Values after the decimal point are treated as microseconds in Python, but as you can see in the example, they are represented by the decimal part of float
, so the precision is reduced.
With the datetime.date ()
method, you can retrieve only the date part as date
.
>>> from datetime import datetime, date
>>>
>>> dt = datetime.now()
>>> dt
datetime.datetime(2016, 7, 14, 14, 24, 46, 157000)
>>> dt.date()
datetime.date(2016, 7, 14)
datetime.now ()
returns a datetime
object that indicates the current date and time.
date.today ()
returns a date
object that indicates the current day.
>>> from datetime import datetime, date
>>>
>>> datetime.now()
datetime.datetime(2016, 7, 14, 14, 14, 2, 900000)
>>> date.today()
datetime.date(2016, 7, 14)
You can get the timestamp of the current date and time with the time ()
function of the time
module.
For the time stamp, refer to the previous section.
>>> import time
>>>
>>> time.time()
1468473337.565 # 2016-07-14 14:15:37.565000
There are some other (old-fashioned) functions that return a struct_time
in the time
module, but I won't cover them this time.
Please read the explanation about the time.gmtime ()
function.
The datetime
module uses thedatetime.strptime ()
function.
However, as you can see from the results below, this function is inflexible.
>>> from datetime import datetime, timezone, timedelta
>>> datetime.strptime("2016/07/05 13:45:06", "%Y/%m/%d %H:%M:%S")
datetime.datetime(2016, 7, 5, 13, 45, 6)
>>> datetime.strptime("2016/07/05 13:45", "%Y/%m/%d %H:%M:%S")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "...\lib\_strptime.py", line 500, in _strptime_datetime
tt, fraction = _strptime(data_string, format)
File "...\lib\_strptime.py", line 337, in _strptime
(data_string, format))
ValueError: time data '2016/07/05 13:45' does not match format '%Y/%m/%d %H:%M:%S'
(* The trace contents are partially processed.)
The parse ()
function of the parser
submodule of the dateutil
module is powerful, so let's use that.
>>> from dateutil.parser import parse
>>>
>>> parse("Tue Aug 23 17:00:35 JST 2016")
datetime.datetime(2016, 8, 23, 17, 0, 35)
>>> parse("2016-07-03T11:22:33Z")
datetime.datetime(2016, 7, 3, 11, 22, 33, tzinfo=tzutc())
>>> parse("2016/07/03 04:05:06")
datetime.datetime(2016, 7, 3, 4, 5, 6)
>>> parse("2016/07/03")
datetime.datetime(2016, 7, 3, 0, 0)
In the case of Python2, the following error occurred.
.../dateutil/parser.py:598: UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal
elif res.tzname and res.tzname in time.tzname:
It seems to be related to the issue described in this ↓ article, but it occurs even though the version of dateutil
is 2.5.1.
Is it a similar but different (unfixed) bug?
twitter - python dateutil unicode warning - Stack Overflow http://stackoverflow.com/questions/21296475/python-dateutil-unicode-warning
To convert to ISO8601 format, use the date.isoformat ()
method.
This is used when simply converting a datetime
or date
object to a string.
If you want to convert it to a string in any format, use the strftime ()
method.
>>> from datetime import datetime, timezone, timedelta
>>>
>>> dt1 = datetime(2016, 7, 5, 13, 45) # naive
>>> dt2 = datetime(2016, 7, 5, 13, 45, tzinfo=timezone(timedelta(hours=+9))) # aware
>>> d = dt1.date()
>>> dt1, dt2, d
(datetime.datetime(2016, 7, 5, 13, 45), datetime.datetime(2016, 7, 5, 13, 45, tzinfo=datetime.timezone(datetime.timedelta(0, 32400))), datetime.date(2016, 7, 5))
>>>
>>> dt1.isoformat()
'2016-07-05T13:45:00'
>>> str(dt1)
'2016-07-05 13:45:00'
>>>
>>> dt2.isoformat()
'2016-07-05T13:45:00+09:00'
>>> str(dt2)
'2016-07-05 13:45:00+09:00'
>>>
>>> d.isoformat()
'2016-07-05'
>>> str(d)
'2016-07-05'
>>>
>>> dt1.strftime("%Y/%m/%d %H:%M:%S")
'2016/07/05 13:45:00'
>>> dt1.strftime("%Y/%m/%d %H:%M:%S %Z %z")
'2016/07/05 13:45:00 '
>>> dt2.strftime("%Y/%m/%d %H:%M:%S %Z %z")
'2016/07/05 13:45:00 UTC+09:00 +0900'
Please refer to the reference below for the format of the format.
8.1.8. Behavior of strftime () and strptime () --8.1. datetime — Basic date and time types — Python 3.5.1 documentation http://docs.python.jp/3.5/library/datetime.html#strftime-strptime-behavior
Comparisons can be made using the comparison operators ==
, ! =
, <
, <=
, >
, > =
.
You cannot compare datetime
and date
.
(TypeError: can't compare datetime.datetime to datetime.date
)
Also, datetime
cannot be compared between aware and naive.
(TypeError: can't compare offset-naive and offset-aware datetimes
)
You need to match them to either type before comparing.
>>> from datetime import datetime, date
>>>
>>> dt1 = datetime(2016, 7, 1, 22, 30)
>>> dt2 = datetime(2016, 7, 5, 13, 45)
>>>
>>> dt1 == datetime(2016, 7, 1, 22, 30, 0)
True
>>> dt1 == dt1, dt1 == dt2, dt1 != dt1, dt1 != dt2, dt1 < dt2, dt1 > dt2, dt1 <= dt1, dt2 <= dt1, dt2 >= dt2, dt1 >= dt2
(True, False, False, True, True, False, True, False, True, False)
>>>
>>> d1 = dt1.date()
>>> d2 = dt2.date()
>>>
>>> d1 == date(2016, 7, 1)
True
>>> d1 == d1, d1 == d2, d1 != d1, d1 != d2, d1 < d2, d1 > d2, d1 <= d1, d2 <= d1, d2 >= d2, d1 >= d2
(True, False, False, True, True, False, True, False, True, False)
In Python, the date and time intervals and differences are represented by timedelta
objects.
(Delta
means difference (difference ⇒ Δ).)
timedelta
can be obtained by subtracting datetime
s or date
s (-
operation).
It cannot be calculated with datetime
and date
.
(TypeError: unsupported operand type(s) for -
)
Also, datetime
cannot be calculated between aware and naive.
(TypeError: can't subtract offset-naive and offset-aware datetimes
)
You need to match it to either type before calculating.
>>> from datetime import datetime, date
>>>
>>> d1, d2 = date(2016, 7, 1), date(2016, 7, 3)
>>>
>>> type(d2 - d1)
<class 'datetime.timedelta'>
>>> d2 - d1
datetime.timedelta(2)
>>> str(d2 - d1)
'2 days, 0:00:00'
>>> str(d1 - d2)
'-2 days, 0:00:00'
>>>
>>> dt1, dt2 = datetime(2016, 7, 1, 10, 30), datetime(2016, 7, 3, 9, 10)
>>> dt2 - dt1
datetime.timedelta(1, 81600)
>>> str(dt2 - dt1)
'1 day, 22:40:00'
As mentioned in the previous section, the difference between the two dates is the number of days returned. What if I want to know "what month is the interval between two dates"?
In that case, try using the relativedelta
submodule of the dateutil
module.
Basically this should solve it.
>>> from datetime import date
>>> from dateutil.relativedelta import relativedelta
>>>
>>> d1, d2 = date(2014, 11, 22), date(2016, 3, 1)
>>> str(d2 - d1)
'465 days, 0:00:00'
>>> delta = relativedelta(d2, d1)
>>> delta
relativedelta(years=+1, months=+3, days=+8)
>>> month_count = delta.years * 12 + delta.months
>>> month_count
15
It will be years
for 12 months or more, but I was able to calculate the number of months with years * 12 + months
.
What is a little worrisome here is the calculation involving the end of the month, especially around February of a leap year. So, I shifted the two dates by one day and checked what the delta would be in that case.
--A script that shifts two dates by one day and examines the result of relativedelta
from datetime import date, timedelta
from dateutil.relativedelta import relativedelta
d1 = date(2016, 2, 26)
d2 = date(2016, 3, 26)
delta1day = timedelta(days=1)
for x in range(9):
print(d1, d2, relativedelta(d2, d1))
d1 += delta1day
d2 += delta1day
2016-02-26 2016-03-26 relativedelta(months=+1)
2016-02-27 2016-03-27 relativedelta(months=+1)
2016-02-28 2016-03-28 relativedelta(months=+1)
2016-02-29 2016-03-29 relativedelta(months=+1)
2016-03-01 2016-03-30 relativedelta(days=+29)
2016-03-02 2016-03-31 relativedelta(days=+29)
2016-03-03 2016-04-01 relativedelta(days=+29)
2016-03-04 2016-04-02 relativedelta(days=+29)
2016-03-05 2016-04-03 relativedelta(days=+29)
I looked at the dates further ahead, but roughly speaking, it seems that they are treated specially only when they cross the end of February. Other than that, the number of days has been the same (+29).
When I increased the interval between the two dates by one day, the number of days changed depending on whether it straddled the end of the 30th month or not. I'm sure it will be corrected on the last day of the month.
It seems that it is okay if you understand the nature of this area.
I've already written that datetime --datetime
and date --date
become timedelta
,
For increase / decrease of date / date / time, you can get the increased / decreased datetime
and date
by adding / subtracting timedelta
.
>>> from datetime import datetime, date, timedelta
>>>
>>> dt = datetime(2016, 7, 5, 13, 45)
>>> d = dt.date()
>>>
>>> dt + timedelta(days=5, minutes=168)
datetime.datetime(2016, 7, 10, 16, 33)
>>> d - timedelta(days=13)
datetime.date(2016, 6, 22)
For monthly adjustment, use the submodule relativedelta
of the dateutil
module, as in the case of monthly calculation.
>>> from datetime import date
>>> from dateutil.relativedelta import relativedelta
>>>
>>> date(2016, 7, 5) - relativedelta(months=32)
datetime.date(2013, 11, 5)
The following example confirms the mutual conversion between naive and aware in Python3 only with the standard module.
Python2 (less than Python3.2 to be exact) doesn't have a timezone
type, so use the pytz
module instead.
To turn a naive datetime
into an aware datetime
, use the datetime.replace ()
method.
>>> from datetime import datetime, timezone, timedelta
>>>
>>> jst = timezone(timedelta(hours=+9)) #When using pytz pytz.timezone('Asia/Tokyo')
>>> jst
datetime.timezone(datetime.timedelta(0, 32400))
>>> dt = datetime(2016, 7, 5, 13, 45) # naive
>>> dt
datetime.datetime(2016, 7, 5, 13, 45)
>>> dt2 = dt.replace(tzinfo=jst) # aware
>>> dt2
datetime.datetime(2016, 7, 5, 13, 45, tzinfo=datetime.timezone(datetime.timedelta(0, 32400)))
>>> str(dt2)
'2016-07-05 13:45:00+09:00'
According to reference # 4-2, it was reported that when the time zone was added by the datetime.replace ()
method, it behaved a little strangely.
An alternative is shown to use jst.localize (datetime (...))
.
I didn't reproduce it in my environment, so it may be a bug in an older version of pytz
.
Conversely, to turn an aware datetime
into a naive datetime
, use the same datetime.replace ()
method.
For the time zone, specify None
which means" none ".
#(Continued from above)
>>> dt3 = dt2.replace(tzinfo=None)
>>> dt3
datetime.datetime(2016, 7, 5, 13, 45)
>>> str(dt3)
'2016-07-05 13:45:00'
struct_time
to datetime
or date
Sometimes the timestamp type is represented by struct_time
, but if you want to convert this to the datetime
type.
Using the time.mktime ()
and datetime.fromtimestamp ()
functions,
struct_time
→timestamp(float)
→datetime
Convert in the order of.
>>> from datetime import datetime, date
>>> from time import localtime, mktime
>>>
>>> tm = localtime() # struct_time
>>> t = mktime(tm) #Time stamp(float)Conversion to
>>> datetime.fromtimestamp(t)
datetime.datetime(2016, 7, 12, 22, 31, 15)
>>> date.fromtimestamp(t)
datetime.date(2016, 7, 12)
Taking advantage of the fact that struct_time
is a tuple, you can also write:
#(Continued from above)
>>> datetime(*tm[:6])
datetime.datetime(2016, 7, 12, 22, 31, 15)
tm [: 6]
gives a tuple of year, month, day, hour, minute, and second, so we pass it to the constructor of datetime
as six arguments.
This can be written concisely, but it reduces readability and may be avoided.
datetime
or date
to struct_time
Contrary to the previous section, if you want to convert a datetime
object or adate
object to a struct_time
object,
You can get struct_time
by using thedate.timetuple ()
method.
>>> from datetime import datetime
>>>
>>> dt = datetime(2016, 7, 3, 12, 25)
>>> d = dt.date()
>>>
>>> dt.timetuple()
time.struct_time(tm_year=2016, tm_mon=7, tm_mday=3, tm_hour=12, tm_min=25, tm_sec=0, tm_wday=6, tm_yday=185, tm_isdst=-1)
>>> d.timetuple()
time.struct_time(tm_year=2016, tm_mon=7, tm_mday=3, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=6, tm_yday=185, tm_isdst=-1)
Here, only the date example is written. In the case of date and time, either method should be applicable by changing the type and unit.
To generate a finite date list, I quickly came up with a way to combine timedelta
and range
to create a list with comprehensions.
>>> from datetime import date, timedelta
>>>
>>> start_date = date(2016, 7, 5)
>>> a = [start_date + timedelta(days=x) for x in range(5)]
>>> a
[datetime.date(2016, 7, 5), datetime.date(2016, 7, 6), datetime.date(2016, 7, 7), datetime.date(2016, 7, 8), datetime.date(2016, 7, 9)]
... but it's a little disappointing that I can't write in one line.
(You can write date (2016, 7, 5)
directly instead of start_date
, but of course it doesn't.)
You can write in one line using the rrule
submodule of the dateutil
module.
>>> from datetime import date
>>> from dateutil.rrule import rrule, DAILY
>>>
>>> a = [x.date() for x in rrule(DAILY, count=5, dtstart=date(2016, 7, 5))]
>>> a
[datetime.date(2016, 7, 5), datetime.date(2016, 7, 6), datetime.date(2016, 7, 7), datetime.date(2016, 7, 8), datetime.date(2016, 7, 9)]
Oh yeah, I wanted to write like this.
In some cases it may be more convenient to use the Pandas module (pandas.date_range
).
See reference # 5 for details.
Use the ʻisleap function of the
calendar` module.
>>> import calendar
>>>
>>> calendar.isleap(2016)
True
>>> calendar.isleap(2015)
False
>>>
>>> from datetime import date
>>>
>>> d = date(2016, 7, 5)
>>> calendar.isleap(d.year)
True
datetime
moduleAs of Python 3.5, all datetime related objects in the datetime
module are evaluated as True
, except for the equivalent of timedelta (0)
.
(Timezone
is not included in" all "because it was not mentioned in the document.)
>>> from datetime import *
>>>
>>> bool(datetime.fromtimestamp(0))
True
>>> bool(date.fromtimestamp(0))
True
>>> bool(time(0, 0, 0))
True
>>> bool(timedelta(1))
True
>>> bool(timedelta(0))
False
>>> bool(timezone.utc)
True
By the way, in Python2, the time
object at midnight is evaluated as False
.
>>> from datetime import *
>>> bool(time(0, 0, 0))
False
This is a bug (Issue13936) that existed in less than Python3.5 rather than Python2, and is the behavior fixed in Python3.5.
When I checked it with Python 3.4.3, it was still False
.
This method is also used in other languages, but it is calculated by (Timestamp after processing-Timestamp before processing).
import time
t = time.time()
time.sleep(3.1)
print(time.time() - t)
# => 3.0999999046325684
Although not covered this time, you can also use the following modules to measure the execution time of code fragments.
27.5. timeit — Running time for small code snippets — Python 3.5.1 documentation http://docs.python.jp/3.5/library/timeit.html
This is an image when each object is serialized by pickle
.
--Execution result in Python2
>>> from datetime import datetime
>>> import pickle
>>>
>>> dt = datetime(2016, 7, 5, 13, 45)
>>>
>>> pickle.dumps(dt) # datetime
"cdatetime\ndatetime\np0\n(S'\\x07\\xe0\\x07\\x05\\r-\\x00\\x00\\x00\\x00'\np1\ntp2\nRp3\n."
>>> pickle.dumps(dt.date()) # date
"cdatetime\ndate\np0\n(S'\\x07\\xe0\\x07\\x05'\np1\ntp2\nRp3\n."
>>> pickle.dumps(dt.timetuple()) # struct_time
'ctime\nstruct_time\np0\n((I2016\nI7\nI5\nI13\nI45\nI0\nI1\nI187\nI-1\ntp1\n(dp2\ntp3\nRp4\n.'
>>> pickle.dumps(dt - dt) # timedelta
'cdatetime\ntimedelta\np0\n(I0\nI0\nI0\ntp1\nRp2\n.'
--Execution result in Python3
>>> from datetime import datetime
>>> import pickle
>>>
>>> dt = datetime(2016, 7, 5, 13, 45)
>>> pickle.dumps(dt) # datetime
b'\x80\x03cdatetime\ndatetime\nq\x00C\n\x07\xe0\x07\x05\r-\x00\x00\x00\x00q\x01\x85q\x02Rq\x03.'
>>> pickle.dumps(dt.date()) # date
b'\x80\x03cdatetime\ndate\nq\x00C\x04\x07\xe0\x07\x05q\x01\x85q\x02Rq\x03.'
>>> pickle.dumps(dt.timetuple()) # struct_time
b'\x80\x03ctime\nstruct_time\nq\x00(M\xe0\x07K\x07K\x05K\rK-K\x00K\x01K\xbbJ\xff\xff\xff\xfftq\x01}q\x02(X\x07\x00\x00\x00tm_zoneq\x03NX\t\x00\x00\x00tm_gmtoffq\x04Nu\x86q\x05Rq\x06.'
>>> pickle.dumps(dt - dt) # timedelta
b'\x80\x03cdatetime\ntimedelta\nq\x00K\x00K\x00K\x00\x87q\x01Rq\x02.'
#1. (Official document / standard library reference)
8.1. datetime — Basic date and time types — Python 3.5.1 documentation http://docs.python.jp/3.5/library/datetime.html#module-datetime 8.1. datetime — basic date and time types — Python 2.7.x documentation http://docs.python.jp/2/library/datetime.html#module-datetime
(Links for modules other than datetime
are redundant and will be omitted. There is a link somewhere on the above link page, so please skip from there.)
#2. dateutil - powerful extensions to datetime — dateutil 2.5.3 documentation https://dateutil.readthedocs.io/en/stable/
#3. pytz 2016.4 : Python Package Index https://pypi.python.org/pypi/pytz/
#4-1. Python Date Processing and TimeZone | Nekoya Press http://nekoya.github.io/blog/2013/06/21/python-datetime/
#4-2. Don't use replace to give tzinfo to datetime in Python | Nekoya Press http://nekoya.github.io/blog/2013/07/05/python-datetime-with-jst/
#5. Easily manipulate date and time related data with Python pandas-StatsFragments http://sinhrks.hatenablog.com/entry/2014/11/09/183603
Recommended Posts