Temporal data appears frequently in computational work. Measurements recorded from sensors, economic results, stock prices, time-stepping numeric simulations, and so on are useless without accompanying time values. In this chapter, we’ll cover Python’s datetime module which offers capabilities similar to MATLAB’s data type of the same name.
5.1 Time
Python’s time module has functions to return the current time, either in the local timezone or UTC; measure CPU or elapsed time; and sleep for a desired amount of time.
5.1.1 Current Time
The Python time.time() function returns the same value as posixtime(datetime()) in MATLAB, a double-precision representation of current time in Unix epoch seconds, a continuous increment of seconds since midnight, January 1, 1970, ignoring leap seconds, to a resolution of a microsecond. Among other things, it is useful in Python for computing elapsed time of code segments.
MATLAB: | Python: |
---|---|
>> format long e >> posixtime(datetime()) 1.572210959845713e+09 >> t = datetime; >> datestr(t,'yyyy-mm-dd HH:MM:ss') '2019-10-27 14:25:26' | In : import time In : time.time() Out: 1572210959.845713 In : time.strftime('%Y-%m-%d %H:%M:%S') Out: '2019-10-27 14:25:26' |
5.1.2 Time String Formats
Format arguments for strftime() include all arguments accepted by the 1989 C standard library function of the same name plus a few others. In some cases, the output depends on the computer’s locale setting. This can be revealed by with the command locale in a terminal on Linux and macOS and with systeminfo in a command prompt on Windows (look for “System Locale” in the output). Examples in Table 5-1 use locale settings of en_US and de_DE, as taken from the Python project’s documentation.
strftime() and strptime() format codes
Python Directive | Meaning | Examples | MATLAB Equivalent in datestr() |
---|---|---|---|
%a | Abbreviated weekday | Sun, Mon (en_US) So, Mo (de_DE) | ddd |
%A | Abbreviated weekday | Sunday (en_US) Sonntag (de_DE) | dddd |
%w | Weekday as integer | Python: 0 (Sunday) … 6 MATLAB: 1 (Sunday) … 7 | e |
%d | Two-digit day of month | 01, …, 31 | dd |
%b | Abbreviated month | Jan, Dec (en_US) Jan, Dez (de_DE) | MMM |
%B | Month | January (en_US) Januar (de_DE) | MMMM |
%m | Two-digit month | 01, …, 12 | MM |
%y | Two-digit year | 99, 00, …, 19 | yy |
%Y | Four-digit year | 1999, 2000, …, 2019 | yyyy or u |
%H | Two-digit hour, 0–23 | 00, …, 23 | HH |
%I | Two-digit hour, 0–11 | 00, …, 11 | HH PM |
(output includes "PM") | |||
%p | AM or PM | AM, PM (en_US) am, pm (de_DE) | AM or PM |
%M | Two-digit minute | 00, …, 59 | mm |
%S | Two-digit seconds | 00, …, 59 | ss |
%f | Six-digit microseconds | 000000, …, 999999 | S .. SSSSSSSSS (tenth second to nanoseconds) |
%z | UTC offset ±HHMM[SS[.ffffff]] | +0000, –0900015 | Z |
%j | Three-digit day of year | 001, …, 366 | DDD |
%U | Two-digit week of year (week starts on Sunday) | 00, …, 53 | Not available |
%W | Two-digit week of year (week starts on Monday) | 00, …, 53 | Not available |
%c | Locale-specific date and time | Sun Oct 27 16:57:47 2019 (en US) | datetime() default |
%x | Locale-specific date | 10/27/19 (en_US) 27.10.2019 (de_DE) | Not available |
%X | Locale-specific time | 17:00:53 (en_US) 17:00:53 (de_DE) | Not available |
%% | Literal % | % | % |
5.1.3 tic, toc; %timeit
MATLAB: | Python: |
---|---|
>> N = 1000; >> tic >> a = eig(rand(N)) >> toc Elapsed time: 1.57437 seconds. | In : import time; import numpy as np In : N = 1000 In : tic = time.time() In : a = np.linalg.eig(np.random.rand(N,N)) In : time.time() - tic Out: 1.2564113140106201 |
The difference of calls to time.time() is useful in a Python program, but when working interactively in ipython, it is easier to use its “magic” command %timeit to measure how long a command takes:
%timeit will run the command several times (seven in the preceding example) and return the mean time over all runs.
5.2 Dates
datetime is the most wide-ranging and will be the focus of discussion in this section. It has many functions to simplify computations with dates and perform conversions.
date is more or less a subset of datetime and is useful when working purely with dates, without associated time within a day.
calendar has functions to determine if a year is a leap year, the day of the week for a given date, and the number of days in a month.
5.2.1 datetime Objects to and from Strings
MATLAB: | Python: |
---|---|
>> now = datetime() 28-Oct-2019 21:16:36 >> datetime(now,'Format',... 'u-MM-dd hh:mm') 2019-10-28 21:16 | In : from datetime import datetime In : datetime.now() Out: datetime(2019, 10, 28, 21, 16, 36, 225635) In : datetime.now().strftime( '%Y-%m-%d %H:%M') Out: '2019-10-28 21:16' |
Both datetime and time have a similarly named function, strptime(), that does the opposite: it takes a string containing a date and a second string describing the format, then returns a Python variable which can subsequently be used to perform date computations.
MATLAB: | Python: |
---|---|
>> S ='Aug 12 04:05:51 2006'; >> dt=datetime(S,'InputFormat',... 'MMM dd HH:mm:SS yyyy') dt = datetime 12-Aug-2006 04:05:00 | In : from datetime import datetime In : S = 'Aug 12 04:05:51 2006' In : dt = datetime.strptime(S, ...: '%b %d %H:%M:%S %Y') In : dt Out: datetime.datetime( 2006, 8, 12, 4, 5, 51) |
5.2.2 Time Deltas
MATLAB: | Python: |
---|---|
Mars_day = duration(... 24,39,35.244) Start = datetime(... 1986,6,28,12,0,0) for i = 0:6 Start + i*Mars_day end 28-Jun-1986 12:00:00 29-Jun-1986 12:39:35 30-Jun-1986 13:19:10 01-Jul-1986 13:58:45 02-Jul-1986 14:38:20 03-Jul-1986 15:17:56 04-Jul-1986 15:57:31 | from datetime import datetime, timedelta Mars_day = timedelta(hours=24, minutes=39, seconds=35.244) Start = datetime(1986,6,28,12) for i in range(7): print(Start + i*Mars_day) 1986-06-28 12:00:00 1986-06-29 12:39:35.244000 1986-06-30 13:19:10.488000 1986-07-01 13:58:45.732000 1986-07-02 14:38:20.976000 1986-07-03 15:17:56.220000 1986-07-04 15:57:31.464000 |
MATLAB: | Python: |
---|---|
Jul4 = '1976-07-04 12:00'; Aug1 = '1976-08-01 18:00'; J=datetime(Jul4,'InputFormat',... 'yyyy-MM-dd HH:mm') A=datetime(Aug1,'InputFormat',... 'yyyy-MM-dd HH:mm') duration(A-J,'Format','s') 2.4408e+06 sec | from datetime import datetime as DT Jul4 = '1976-07-04 12:00' Aug1 = '1976-08-01 18:00' J = DT.strptime(Jul4, '%Y-%m-%d %H:%M') A = DT.strptime(Aug1, '%Y-%m-%d %H:%M') (A - J).total_seconds() Out: 2440800.0 |
MATLAB: | Python: |
---|---|
>> Mars_day = duration(... 24,39,35.244); >> duration(years(1))/Mars_day 355.47 | In : Mars_day = timedelta(hours=24, ...: minutes=39, seconds=35.244) In : timedelta(days=365.24)/Mars_day Out: 355.4677472922519 |
5.3 Timezones
Datetime objects in both Python and MATLAB, by default, lack timezone information; they merely reflect the date and time of the host computer.
Python datetime objects can be made timezone-aware by populating the optional tzinfo or tz keywords in calls to datetime(), datetime.now(), or datetime.fromtimestamp() functions. Two Python modules that can create tzinfo objects are pytz and dateutil.tz. (Python 3.9 introduced the zoneinfo module which is discussed in Section 5.5. MATLAB 2020b, the version used in this book, predates Python 3.9 and therefore does not support it.) Although pytz has more features, the timezones from dateutil.tz yield more accurate results for corner cases, such as computing time deltas that span daytime/standard time transitions [1].
MATLAB: | Python: |
---|---|
>> timezones | In : import pytz In : pytz.all_timezones Out: ['Africa/Abidjan', 'Africa/Accra', 'Africa/Addis_Ababa', 'Africa/Algiers', ... 'US/Pacific', 'US/Samoa', 'UTC', 'Universal', 'W-SU', 'WET', 'Zulu'] |
Timezone names in MATLAB’s table match the names returned by pytz.all_timezones.
MATLAB: | Python: |
---|---|
>> T = datetime(2020,2,14,... 13,30,25,'TimeZone',... 'America/Los_Angeles') T = 14-Feb-2020 13:30:25 >> T.TimeZone 'America/Los_Angeles' >> datetime(T,'Format',... 'yyyy-MM-dd''T'... 'HH:mm:ss''.''SSSSSS') 2020-02-14T13:30:25.000000 >> datetime(T,'Format','Z') -0800 | In : from datetime import datetime In : from dateutil import tz In : LA_tz = tz.gettz('America/Los_Angeles') In : T = datetime(2020,2,14,13,30,25, ...: tzinfo=LA_tz) In : T Out: datetime.datetime(2020,2,14,13,30,25, tzinfo=tzfile( '/usr/share/zoneinfo/America/Los_Angeles')) In : T.isoformat() Out: '2020-02-14T13:30:25-08:00' In : T.utcoffset() Out: datetime.timedelta(days=-1,seconds=57600) |
MATLAB: | Python: |
---|---|
>> datetime(T,'TimeZone',... 'Asia/Tokyo') 15-Feb-2020 06:30:25 | In : Tokyo_tz = tz.gettz('Asia/Tokyo') In : T.astimezone(Tokyo_tz) Out: datetime.datetime(2020,2,15,6,30,25, tzinfo=tzfile( '/usr/share/zoneinfo/Asia/Tokyo')) |
5.3.1 UTC vs. Local Time
UTC, also called Zulu or Greenwich Mean Time, is the preferred timezone for recording events that span wide geographic areas. MATLAB and Python have several functions that explicitly work with UTC time as opposed to the local time on an individual computer.
Paradoxically, the datetime functions that return UTC time, datetime.utcnow() and datetime.utcfromtimestamp(), do not set tzinfo, and so they return timezone-unaware objects. In other words, the numeric values for day, hour, minute, and so on are correct, but there is no underlying metadata stating that this is the UTC timezone. If computational accuracy for subsequent date computations is important, the proper way to define UTC time is to use the functions without utc in their names and explicitly pass in tz=dateutil.tz.UTC [2]:
Similar conversions in MATLAB look like this:
5.4 Time Conversions to and from datetime Objects
Section 5.2.1 showed how a datetime object can be converted to a string and how a string can be parsed into a datetime object. In this section, we explore additional format conversions to and from datetime objects using local time and UTC. The examples employ the following imports:
5.4.1 Unix Epoch Seconds
Unix epoch seconds are independent of timezone, so the conversion from a timezone-enabled datetime object can be misleading:
For circular consistency, the Unix epoch value must be converted to the timezone where it was initially measured.
5.4.2 ISO 8601 Time String
2019-11-01T15:54:04
2019-11-01T15:54:04Z
2019-11-01T15:54:04+00:00
Although the datetime module includes a .fromisoformat() function, curiously the function does not accept the trailing “Z” (for “Zulu,” also known as UTC); one must either omit the timezone designator or specify it as a numeric offset.
Also, since the ISO format function with a timezone offset explicitly sets the timezone, the local time variable is explicitly computed from the timezone-aware datetime object using the .astimezone() method :
5.4.3 Julian Date; Modified Julian Date; GPS Time
Anaconda comes with a module, astropy, which has powerful time conversion capabilities related to astronomy. These include Julian, Modified Julian, FITS, UT1, TAI, and GPS references.
The following examples use the US/Pacific timezone:
5.5 zoneinfo in Python >= 3.9
Python 3.9 (not supported by MATLAB 2020b, Section 2.7) introduced the module zoneinfo to improve timezone support. zoneinfo’s primary benefit is its rigorous treatment of time computations across daylight savings transitions. In addition, like pytz, zoneinfo includes a mechanism to list all known timezone names, making it unnecessary to import pytz merely to access its list of timezones, pytz.all_timezones.
5.5.1 List Available Timezones
5.5.2 Date Increments Across Daylight Savings Transition
This example demonstrates correct handling of standard time to daylight savings time transition using rules for the Los Angeles entry in the timezone database:
5.6 References
- [1]
Paul Ganssle. “pytz: The Fastest Footgun in the West.” In: (Mar. 2018). URL: https://blog.ganssle.io/articles/2018/03/pytz-fastest- footgun.html
- [2]
Paul Ganssle. “Stop using utcnow and utcfromtimestamp.” In: (Nov. 2019). URL: https://blog.ganssle.io/articles/2019/11/utcnow. html