Python datetime – the 9 tips you shall know
Dealing with date and time are quite common whenever you are writing Python scripts, for instance, the simplest use cases would be logging some events with a timestamp, or saving a file with date and timing info in the file name. It can be challenging when you have more complicated scenarios such as handling time zone, daylight saving and recurrences etc. The built-in Python datetime module is capable of handling most of the date and time operations, and there are third party libraries can help you to easily manage the time zone and daylight saving challenges. In this article, we will be discussion some tips for using the Python datetime module as well as the third party package dateutil.
If you do not have dateutil installed yet, you shall install the latest version to your working environment. Below is the pip command to install the package:
pip install python-dateutil
Let’s get started!
Various ways to get current date and time
The top one use cases that you need a Python datetime object is to get the current date or time. There are plenty of ways to get current date and time from Python datetime module, for instance:
>>>from datetime import datetime >>>import time #Local timezone >>>datetime.now() datetime.datetime(2020, 10, 24, 21, 31, 11, 761666) >>>datetime.today() datetime.datetime(2020, 10, 24, 21, 31, 12, 139719) >>>datetime.fromtimestamp(time.time()) datetime.datetime(2020, 10, 24, 21, 31, 12, 559183) #Not suggested >>>datetime.fromtimestamp(time.mktime(time.localtime())) datetime.datetime(2020, 10, 24, 21, 33, 5) #UTC timezone >>>datetime.now(timezone.utc) datetime.datetime(2020, 10, 24, 13, 31, 13, 443442, tzinfo=datetime.timezone.utc) >>>datetime.utcnow() datetime.datetime(2020, 10, 24, 13, 31, 14, 240517)
Most of the above methods will return a date object in local machine time, and the last two methods will get the date and time in UTC time zone.
If you only need the date info, you can discard the time portion by using the date() method as per below:
>>>datetime.now().date() datetime.date(2020, 10, 24)
Get year, month, day and time from Python datetime
From the datetime object, you can easily get each individual components such as year, month, day, hour etc. Below examples show you how to extract the date and time components from the datetime object, as well as the weekday or week number information:
>>>TODAY = datetime.today() >>>TODAY.year, TODAY.month, TODAY.day, TODAY.hour, TODAY.minute, TODAY.second, TODAY.microsecond (2020, 10, 24, 21, 36, 35, 842689) #Monday is 0 and Sunday is 6 >>>TODAY.weekday() 5 #Monday is 1 and Sunday is 7 >>>TODAY.isoweekday() 6 #Return year, weekno, and weekday >>>TODAY.isocalendar() (2020, 43, 6)
Take note on the start day when you get the weekday in numbers, weekday() returns 0 for Monday, while isoweekday() returns 1 for Monday. There are some programming languages use 0 for Sunday, in this case you can use the %w format code to get the weekday number where it starts from 0 as Sunday.
Date plus or minus X days
Very often you will need to do some arithmetic calculation on the dates such as calculating number of days backward or forward from the current date. To do that, you will need to use the timedelta class. Below is the syntax to create a timedelta object, you can specify number of weeks, days, hours, minutes etc. for initialization:
>>>timedelta(days=1, seconds=50, microseconds=1000, milliseconds=1000, minutes=10, hours=6, weeks=1) datetime.timedelta(days=8, seconds=22251, microseconds=1000)
All the arguments passed to timedelta will be eventually converted into days, seconds and microseconds.
So to calculate today plus 1 day, you can specify the timedelta with 1 day and add it up to the current date:
>>>tomorrow = datetime.today().date() + timedelta(days=1) datetime.date(2020, 10, 25)
Similarly, calculating the date backwards can be achieved by specifying the arguments as negative numbers:
>>>yesterday = datetime.today().date() + timedelta(days=-1) datetime.date(2020, 10, 23)
When calculating the difference between two dates, it will also return a timedelta object:
>>>tomorrow - yesterday datetime.timedelta(days=2)
Get the first day of the month
With the replace() method, you can replace the year, month or day of the current date and return a new date. The most commonly used scenario would be getting the first day of the month based on current date, e.g.:
>>>datetime.today().date().replace(day=1) datetime.date(2020, 10, 1)
Format date with strftime and strptime
There are many scenarios that you need to convert a date from string or format a date object into a string. You can easily convert a date into string format with the strftime method, for instance:
>>>datetime.now().strftime("%Y-%b-%d %H:%M:%S") '2020-Oct-25 20:35:54
And similarly, from string you can use strptime to convert a string object into a date object:
>>>datetime.strptime("Oct 25 2020 08:10:00", "%b %d %Y %H:%M:%S") datetime.datetime(2020, 10, 25, 8, 10)
You can check here for the full list of the format code supported by strftime and strptime. And do take note that strptime can be much slower than you expected if you are using it in a loop for a large set of data. For such case, you may consider to directly use datatime.datetime(year, month, day) to form the datetime object.
Create time zone aware date
Most of the methods in Python datetime module return time zone naive objects (which means it does not include any timezone info), in case you need some time zone aware objects, you can specify the time zone info when initializing a date/time object, for instance:
>>>singapore_tz = timezone(timedelta(hours=8), name="SGT") >>>sg_time_now = datetime.now(tz=singapore_tz) datetime.datetime(2020, 10, 24, 22, 31, 6, 554991, tzinfo=datetime.timezone(datetime.timedelta(seconds=28800), 'SGT'))
>>>import dateutil #time zone database name from IANA >>>sh_tz = dateutil.tz.gettz('Asia/Shanghai') >>>datetime(2020, 10, 24, 22, tzinfo = sh_tz) datetime.datetime(2020, 10, 24, 22, 0, tzinfo=tzfile('PRC')) #windows time zone names >>>cn_tz = dateutil.tz.gettz('China Standard Time') >>>datetime(2020, 10, 24, 22, tzinfo = cn_tz) datetime.datetime(2020, 10, 24, 22, 0, tzinfo=tzwin('China Standard Time'))
With the time zone database, you do not need to worry about the offset hours, and you only need to provide the name to get the correct date and time in the respective time zone.
Get a date by relative period
If you use timedelta to get a date from the current date plus a relative period such as 1 year or 1 month, you may sometimes run into problems during the leap years. For instance, the below returns Apr 30 as year 2020 is leap year, and the number of days shall be 366 rather than 365.
>>>datetime(2019, 5, 1) + timedelta(days=365) datetime.datetime(2020, 4, 30, 0, 0)
The simply way to get the correct result is to use the relativedelta from the dateutil package, e.g.:
>>>from dateutil.relativedelta import relativedelta >>>datetime(2019, 5, 1) + relativedelta(years=1) datetime.datetime(2020, 5, 1, 0, 0)
You can also specify the other arguments such as the months, days and hours:
>>>datetime.today() + relativedelta(years=1, months=1, days=10, hours=10) datetime.datetime(2021, 12, 5, 8, 49, 31, 386813)
To get the date of the next Sat from the current date, you can use :
>>>datetime.today() + relativedelta(weekday=calendar.SATURDAY) datetime.datetime(2020, 10, 24, 15, 16, 10, 502191)
Take note that if you are running it on Saturday before 23:59:59, it will just return the current date, so it is actually returning the nearest Saturday from your current date.
List out all the weekdays
In case you need to get all the weekdays starting from a particular date, you can make use of the recurrence rules from dateutil package.
For instance, the below rrule specifies to recur on daily basis for Mon to Fri with a start and end date:
>>>from dateutil.rrule import rrule, DAILY, MO, TU, WE, TH, FR >>>from dateutil.parser import parse >>>list(rrule(DAILY, interval=1, byweekday=[MO, TU, WE, TH, FR], dtstart=datetime.now().date(), until=datetime(2020, 11, 2))) [datetime.datetime(2020, 10, 26, 0, 0), datetime.datetime(2020, 10, 27, 0, 0), datetime.datetime(2020, 10, 28, 0, 0), datetime.datetime(2020, 10, 29, 0, 0), datetime.datetime(2020, 10, 30, 0, 0), datetime.datetime(2020, 11, 2, 0, 0)]
The frequency and interval arguments determine the frequency of the recurrence, and the byweekday and dtstart further constrain which are the dates to be selected.
Besides the weekday argument, you can also specify by year, month, hour, minute etc. You can check here for all the available arguments supported for instantiating the rrule object.
Another example, the below code returns a list of dates recurring on 9:15am every another day:
>>>list(rrule(DAILY, interval=2, byminute=15, count=4, dtstart=parse("20201024T090000"))) [datetime.datetime(2020, 10, 24, 9, 15), datetime.datetime(2020, 10, 26, 9, 15), datetime.datetime(2020, 10, 28, 9, 15), datetime.datetime(2020, 10, 30, 9, 15)]
Get a list of business days
Sometimes you would need to exclude the public holidays to get only the business days. To do so, you may first get a list of holidays from another 3rd party libraries like holidays or simply put all holidays into some config file, and then exclude these dates from rrule. For instance:
holidays = [ datetime(2020, 7, 10,), datetime(2020, 7, 31,), datetime(2020, 8, 10,) ] r = rrule(DAILY, interval=1, byweekday=[MO, TU, WE, TH, FR], dtstart=datetime(2020, 7, 10), until=datetime(2020, 8, 1)) rs = rrule.rruleset() rs.rrule(r) for d in holidays: rs.exdate(d) print(list(rs))
You can see the public holidays have been excluded from the return results:
Working with date sometimes can be tough especially when you need to manipulate the dates in different time zones or considering the daylight savings. Luckily with Python datetime and other 3rd party libraries like dateutil, things are getting easier. But you will still need to be very careful when handling dates with time zone or setting up recurrence rules in local time.
Thanks for reading, and you can find other Python related topics from here.