31. Dates and Times#

Dates and times exist in a vast number of different applications. At a minimum, log messages have datetime values to show when an event occurred.

Datetime representation and functionality seems to be a problem with straightforward solutions, but underneath the surface, implementing datetime has been surprisingly problematic to get correct.

One problem stems from how we represent dates in our daily lives -

  • October 5, 2005

  • Oct. 5, 2005

  • 5 Oct 2005

  • 5/10/2005

  • 10/5/2005

  • 20051005

So many formats? Which comes first - the day or the month? Do we start counting at 0 or 1? What about time zones? Does Arizona still not follow daylight savings time? You can not confuse coyotes. When did the cows in Indiana start following daylight savings time? Leap years occur every four years, except at the century mark, unless divisible by 400. What about leap seconds? When are those applied? When was the last one applied? Western civilization follows the Gregorian calendar, but what about the dates before its introduction in 1582? What do other societies use?

Most computer systems track datetime based upon Unix Epoch, the number of seconds since January 1st, 1970 at 00:00:00 UTC. Some implementations will also use finer-grained representations such as milliseconds and nanoseconds. Due to the size of most computer systems when this approach was adopted, systems represented the DateTime type with a signed 32-bit integer, limiting the acceptable date range from December 13, 1901 to January 19, 2038. Using 64-bit numbers in modern systems means these limits are no longer a practical concern.

Reference: View current Epoch time and perform conversions: https://currentmillis.com/

For string representations of dates and times, you should strive to follow ISO 8601. The format expresses dates and times from the largest to the smallest unit. Python’s datetime module uses the following format for ISO (though several alternatives exist): 2005-10-05T22:40:32.123456+00:00 (YYYY-MM-DDTHH:mm:SS.sssuuu+00:00) where

  • YYYY: 4-digit year

  • MM: 2-digit month from 01 to 12

  • DD: 2-digit day from 01 to 31

  • T: separator of the date and time portions. Time portions can also be separated by :

  • HH: 2-digit hours from 00 to 23

  • mm: 2-digit minutes from 00 to 59

  • SS: 2-digit seconds from 00 to 59

  • sssuuu: 3-digits for milliseconds and 3-digits for microseconds.

  • +00:00: offset from the GMT/UTC timezone. This value may be negative. Expressed in hours and then minutes.

Advantages of the ISO 8601 Representation:

  • Standardization: ISO 8601 provides a consistent and unambiguous way to represent dates and times, which is crucial for communication and data interchange across different systems and cultures.

  • Readability: The format is intuitive and easy to read for humans, with components arranged from the largest to smallest unit (year, month, day, hour, minute, second).

  • Sorting: Dates and times represented in ISO 8601 format can be easily sorted alphabetically, which simplifies data organization and retrieval.

  • Compatibility: Most programming languages and database systems support parsing and formatting ISO 8601 dates and times, making it widely adopted in software development.

  • Flexibility: ISO 8601 allows for optional components such as milliseconds, time zone offsets, and durations, providing flexibility to accommodate various use cases while maintaining consistency.

31.1. The datetime Module#

Python’s datetime module provides functionality to support dates and times. The module defines four primary classes:

  • date: represents just dates

  • time: represents time, but no dates.

  • datetime: represents the date and time together

  • timedelta: used for the difference/interval between dates and/or times

1import datetime as dt
1# run a command to see what's available within the datetime/dt module

You should have seen at least the four main classes listed in the output. Ideally, you can see the online information available from within Python on these classes.

31.1.1. date#

Several different methods exist to create a date object:

 1from datetime import date
 2
 3# From the current date
 4today = date.today()
 5print(today)
 6
 7# From a specific year, month and date
 8someday = date(2005,10,5)
 9print(someday)
10
11# From Unix epoch time value
12anotherday = date.fromtimestamp(1656549504)
13print(anotherday)
14
15# From an isoformat string
16aday = date.fromisoformat("2021-12-02")
17print(aday)
18
19# from the number of days since January 1st, 0001.
20oday = date.fromordinal(738340)
21print(oday)
22
23# Show the min and max values possibles for date
24print("Minimum date:",date.min)
25print("Maximum date:",date.max)
2024-04-28
2005-10-05
2022-06-29
2021-12-02
2022-07-04
Minimum date: 0001-01-01
Maximum date: 9999-12-31

The date object supports the comparison operators: ==, !=, <, <=, >=, >

You can subtract two date objects to get a timedelta object representing the difference between dates.

1x = today-oday
2print(type(x))
3print(x)
<class 'datetime.timedelta'>
664 days, 0:00:00

31.1.2. time#

The time object represents time. As with the date object, we have several ways to construct a time object.

 1from datetime import time
 2
 3# from the current datetime, extract the time portion 
 4#(can do this with any datetime object)
 5t = dt.datetime.now().time()
 6print(type(t))
 7print("Current time:",t)
 8
 9# from a specific time
10spec_time = time(23,4,30)
11print(spec_time)
12
13# from a specific time, using microseconds
14spec_time = time(11,5,24,123456)
15print(spec_time)
16
17# display the minimum and maximum values
18print("Minimum time:", time.min)
19print("Maximum time:", time.max)
20
21# Access the time attributes
22print("hour:", t.hour)
23print("minute:", t.minute)
24print("second:", t.second)
25print("microsecond:", t.microsecond)
<class 'datetime.time'>
Current time: 21:45:47.245874
23:04:30
11:05:24.123456
Minimum time: 00:00:00
Maximum time: 23:59:59.999999
hour: 21
minute: 45
second: 47
microsecond: 245874

The time object supports the comparison operators: ==, !=, <, <=, >=, >

The time object does not support the subtraction operator.

The time object does support time zones, covered in the next section.

31.1.3. datetime#

The datetime class inherits from the date class, adding the capabilities to represent time.

 1# main constructor from
 2a = dt.datetime(2014, 11, 28, 17, 10, 30, 342380)
 3print("year =", a.year)
 4print("month =", a.month)
 5print("hour =", a.hour)
 6print("minute =", a.minute)
 7print("timestamp =", a.timestamp())
 8print()
 9
10# create datetime from the current date and time
11now = dt.datetime.now()
12utcnow = dt.datetime.utcnow()
13print(now.isoformat())
14print(utcnow.isoformat())
15print()
16
17# Construct date from an iso formatted string
18iso = dt.datetime.fromisoformat("2022-06-30T20:19:56+05:00")
19print(iso.isoformat())
year = 2014
month = 11
hour = 17
minute = 10
timestamp = 1417212630.34238

2024-04-28T21:45:47.250263
2024-04-29T01:45:47.250301

2022-06-30T20:19:56+05:00
/var/folders/f0/69wncpqd02s3j1r_z5sfddd00000gq/T/ipykernel_25203/2957373874.py:12: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
  utcnow = dt.datetime.utcnow()

If you notice in the above code, the datetime objects do not have an associated timezone unless you explicitly specify one. While datetime.now() returns the current time in the local timezone, the datetime object is unaware of that timezone by default. Python considers these two types of datetime objects as aware and naive. Aware datetime objects include timezone information. Naive objects do not have timezone information. Aware objects can unambiguously define a specific moment in time, and the interpreter can compute a difference from another aware datetime object. A naive datetime object has neither of those two properties - the program and its logic determine the exact meaning of time. The lesson - always use timezones when you work with datetime objects.

The following code returns the local timezone as a datetime.timezone object.

1LOCAL_TIMEZONE = dt.datetime.now().astimezone().tzinfo
2print("Time Zone:",LOCAL_TIMEZONE)
3print("Type:",type(LOCAL_TIMEZONE))
Time Zone: EDT
Type: <class 'datetime.timezone'>

A recommended approach to creating UTC current datetime objects:

1utc = dt.datetime.now(dt.timezone.utc)
2print(utc)
2024-04-29 01:45:47.256862+00:00

The next block defines a function that converts a datetime to a specific timezone.

1def utc_to_timezone(utc_dt,time_zone=None):
2    """Converts a datetime assumed to be in UTC timezone to a specific time zone. 
3       If the timezone is not defined, then the local timezone is assumed."""
4    return utc_dt.replace(tzinfo=dt.timezone.utc).astimezone(tz=time_zone)
1# get the current time and convert to the local timezone.
2print(utc_to_timezone(dt.datetime.utcnow()))
2024-04-28 21:45:47.262597-04:00
/var/folders/f0/69wncpqd02s3j1r_z5sfddd00000gq/T/ipykernel_25203/1863282035.py:2: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
  print(utc_to_timezone(dt.datetime.utcnow()))

In the following code, we use the zoneinfo package (available in Python versions 3.9 and later).

With this module, we can instantiate timezone objects based upon a time zone name - the available ones for Python can be found through zoneinfo.available_timezones().

1import zoneinfo
2
3# produces a set of timezone names
4zones = zoneinfo.available_timezones()
5
6# convert the curent datetime to Hong Kong's timezone
7hk_tz = zoneinfo.ZoneInfo('HongKong')
8print(utc_to_timezone(dt.datetime.utcnow(),hk_tz))
2024-04-29 09:45:47.270830+08:00
/var/folders/f0/69wncpqd02s3j1r_z5sfddd00000gq/T/ipykernel_25203/353357187.py:8: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
  print(utc_to_timezone(dt.datetime.utcnow(),hk_tz))

31.2. DateTime Arithmetic#

Python supports some DateTime arithmetic operations. For example, we can subtract two dates to find the period (interval) between them. However, we cannot add two datetime objects. If you reflect on the addition, what does it actually mean to add two dates? What is July 17, 2022 plus August 15, 2022? We can add and subtract periods (intervals) from datetime objects. Python uses the timedelta to represent periods.

1t4 = dt.datetime(year = 2019, month = 7, day = 12, hour = 7, minute = 9, second = 33)
2t5 = dt.datetime(year = 2019, month = 6, day = 10, hour = 5, minute = 55, second = 13)
3difference = t4 - t5
4print("difference =", difference)
5
6print("Tomorrow: ", dt.datetime.now(dt.timezone.utc) + dt.timedelta(days=1))
7print("Tomorrow: ", dt.datetime.now(dt.timezone.utc) + dt.timedelta(hours=24))
difference = 32 days, 1:14:20
Tomorrow:  2024-04-30 01:45:47.273919+00:00
Tomorrow:  2024-04-30 01:45:47.273947+00:00

31.3. Formatting and Parsing#

As with many other programming languages, Python allows you to define custom formats to display datetime objects and parse strings for those formats.

Use the strftime function to convert datetime into a specific formatted string. The format string defines a series of format codes that correspond to the different parts of a date as well as different ways to display that part.

1utcnow = dt.datetime.utcnow().replace(tzinfo=dt.timezone.utc)
2print(utcnow.strftime("%m/%d/%Y, %H:%M:%S %Z"))
04/29/2024, 01:45:47 UTC
/var/folders/f0/69wncpqd02s3j1r_z5sfddd00000gq/T/ipykernel_25203/1183752382.py:1: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
  utcnow = dt.datetime.utcnow().replace(tzinfo=dt.timezone.utc)

Directive

Meaning

Example

%y

Year without century as a zero-padded decimal number.

00, 01, …, 99

%Y

Year with century as a decimal number.

0001, 0002, …, 2013, 2014, …, 9998, 9999

%b

Month as locale’s abbreviated name.

Jan, Feb, …, Dec (en_US);
Jan, Feb, …, Dez (de_DE)

%B

Month as locale’s full name.

January, February, …, December (en_US);
Januar, Februar, …, Dezember (de_DE)

%m

Month as a zero-padded decimal number.

01, 02, …, 12

%d

Day of the month as a zero-padded decimal number.

01, 02, …, 31

%H

Hour (24-hour clock) as a zero-padded decimal number.

00, 01, …, 23

%I

Hour (12-hour clock) as a zero-padded decimal number.

01, 02, …, 12

%p

Locale’s equivalent of either AM or PM.

AM, PM (en_US);
am, pm (de_DE)

%M

Minute as a zero-padded decimal number.

00, 01, …, 59

%S

Second as a zero-padded decimal number.

00, 01, …, 59

%z

UTC offset in the form ±HHMM[SS[.ffffff]] (empty string if the object is naive).

(empty), +0000, -0400, +1030, +063415, -030712.345216

%Z

Time zone name (empty string if the object is naive).

(empty), UTC, GMT

%%

A literal '%' character.

%

Source: https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes
The above table only shows the more commonly used formats. Python adopted these codes from the 1989 C Standard.

Similarly, we can use strptime to parse datetime objects from a string.

1str = '07/01/2022, 12:20:04 UTC'
2mydate = dt.datetime.strptime(str,"%m/%d/%Y, %H:%M:%S %Z")
3print(mydate, mydate.tzinfo)    ## Notice that mydate is not an aware datetime
2022-07-01 12:20:04 None

31.4. Pendulum#

Several other developers and groups have written alternate libraries for datetime functionality within Python

Pendulum is a drop-in replacement for the datetime library. Besides a cleaner API, the Pendulum library creates timezone-aware objects by default.

1# Tests to see if we are running in the Google Colaboratory environment
2# If so, ensure pendulum is installed
3try:
4  import google.colab
5  %pip install pendulum
6except:
7  pass
1import pendulum
2now = pendulum.now()
3print(now)
4print(now.timezone)
5print(pendulum.now('UTC'))
2024-04-28 21:45:47.295070-04:00
America/New_York
2024-04-29 01:45:47.295169+00:00

Using the method resolution order function mro(), we can see the inheritance hierarchy and notice that Pendulum’s classes extend Python’s datetime objects.

1print(now.__class__.mro())
2print()
3print(now.timezone.__class__.mro())
[<class 'pendulum.datetime.DateTime'>, <class 'datetime.datetime'>, <class 'pendulum.date.Date'>, <class 'pendulum.mixins.default.FormattableMixin'>, <class 'datetime.date'>, <class 'object'>]

[<class 'pendulum.tz.timezone.Timezone'>, <class 'zoneinfo.ZoneInfo'>, <class 'datetime.tzinfo'>, <class 'pendulum.tz.timezone.PendulumTimezone'>, <class 'abc.ABC'>, <class 'object'>]
 1# Converting to iso8601 format
 2print(now.to_iso8601_string())
 3
 4# Date arithimetic 
 5print(now.add(days=2))
 6
 7# Definining a duration  (length of time)
 8dur = pendulum.duration(days=10, hours=5)
 9print(dur)
10print("dur.weeks:",dur.weeks)
11print("dur.days:",dur.days)
12print("dur.hours:",dur.hours)
13print(dur.in_hours())
14print(dur.in_words(locale="zh"))
15
16#Using the duration in datetime arithmetic
17print(now - dur)
18
19# A period is the difference between 2 datetime instances.
20# it maintains a reference to those datetimes
21period = now - now.subtract(days=3)
22print(period)
23print(period.in_seconds())
24print(period.in_days())
25print(now - period - period)
2024-04-28T21:45:47.295070-04:00
2024-04-30 21:45:47.295070-04:00
1 week 3 days 5 hours
dur.weeks: 1
dur.days: 10
dur.hours: 5
245
1周 3天 5小时
2024-04-18 16:45:47.295070-04:00
<Interval [2024-04-25 21:45:47.295070-04:00 -> 2024-04-28 21:45:47.295070-04:00]>
259200
3
2024-04-22 21:45:47.295070-04:00

Pendulum also supports an alternative format to convert date time values to objects. Many other languages and libraries use this formatting approach.

Further details and available tokens

1now = pendulum.now()
2print(now)
3now.format('YYYY-MM-DD HH:mm:ssZ')
2024-04-28 21:45:47.303792-04:00
'2024-04-28 21:45:47-04:00'
1parsed = pendulum.from_format('2022-07-01 08:44:39-04:00','YYYY-MM-DD HH:mm:ssZ')
2print(parsed)
2022-07-01 08:44:39-04:00

Arrow is another popular alternative to Python’s datetime module.

31.5. Best Practices#

Generally speaking, you should process datetime objects with the UTC timezone and then display datetime values to users in their local timezone (and show that timezone). For web applications, the timezone conversion should occur within the client’s browser.

Storing datetime objects can be a bit more complicated. UTC with the timezone offset is the most appropriate for dates that have already occurred. For datetime values in the future, the situation is not necessarily unambiguous - the problem arises when changes are made to timezone rules after a datetime has been saved. Saving datetimes for the future You’ll want to store the timezone name in addition to the datetime in UTC/offset. By having the timezone name, you can check if the offset has subsequently changed. Provided you can map the event location back to a timezone, the event location suffices.

For data science applications, most datetime values will have occurred in the past so storing those dates with the relevant timezone is appropriate. That relevant timezone is where the record occurred (or where the given user was). However, you will also want to create additional features for either visualization or machine learning purposes. This includes -

  • day of the week

  • weekend flag

  • holiday flag

  • local date and time

  • local hour

  • time before an event

And last but certainly not least, use a library to process dates and times.

31.6. Studying the Python Source Code#

One way to improve your program is to study other people’s source code. The difficulty with this approach, though, is finding good examples. The various application programming interfaces (APIs) for programming languages are good places to look for examples. Typically these APIs have been implemented by multiple developers and undergone a rigorous review process to be included in the language’s distribution. In the following code block, we have copied portions of Python’s datetime module to examine how the class implementation. Within this class, the developers made many noteworthy choices.

  1. The datetime class is immutable. The class uses __new__ not __init__ to initialize the object’s state.

  2. The class validates parameters to __new__. The code checks that the individual data fields are within the expected bounds (e.g., the month between 1 and 12). The call to _index() enforces that the parameters are integers.

  3. The days_in_months array has been offset by 1. In other words, the array has a dummy value at index 0. This offset avoids having to subtract one for every access to the array.

  4. The function _days_in_month has an assertion that used as a sanity check to ensure the month value is in the correct range. Based on the code’s logic, this check does occur before any call to _days_in_month. The assertion provides an additional safety net.

  5. The class lazily initializes the hash value. By lazily initialized, the class does not calculate the hash value until the first time it is requested. After that, the computed value is stored and used for subsequent calls.

  6. The class defines property methods to access the various attributes

  7. The class defines static constructors. These constructors call each other. This pattern reduces the amount of code needed to support multiple ways to define an object. This pattern also allows for constructors with different arguments to be used.

  8. The developers used docstrings for the class itself and then for the different methods within the class.

  9. The __sub__ method checks the type of arguments passed and alters its behavior based on those types.

Source: python/cpython

  1from operator import index as _index
  2class date:
  3    """Concrete date type.
  4    Constructors:
  5    __new__()
  6    fromtimestamp()
  7    today()
  8    fromordinal()
  9    Operators:
 10    __repr__, __str__
 11    __eq__, __le__, __lt__, __ge__, __gt__, __hash__
 12    __add__, __radd__, __sub__ (add/radd only with timedelta arg)
 13    Methods:
 14    timetuple(), toordinal(), weekday(),  isoweekday(), isocalendar(), isoformat()
 15    ctime(), strftime()
 16    Properties (readonly):
 17    year, month, day
 18    """
 19    __slots__ = '_year', '_month', '_day', '_hashcode'
 20
 21    def __new__(cls, year, month=None, day=None):
 22        """Constructor.
 23        Arguments:
 24        year, month, day (required, base 1)
 25        """
 26        ### removed "pickle code" ...
 27        year, month, day = _check_date_fields(year, month, day)
 28        self = object.__new__(cls)
 29        self._year = year
 30        self._month = month
 31        self._day = day
 32        self._hashcode = -1
 33        return self
 34
 35    # Additional constructors
 36    @classmethod
 37    def fromtimestamp(cls, t):
 38        "Construct a date from a POSIX timestamp (like time.time())."
 39        y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)
 40        return cls(y, m, d)
 41
 42    @classmethod
 43    def today(cls):
 44        "Construct a date from time.time()."
 45        t = _time.time()
 46        return cls.fromtimestamp(t)
 47    
 48    
 49    def isoformat(self):
 50        """Return the date formatted according to ISO.  This is 'YYYY-MM-DD'."""
 51        return "%04d-%02d-%02d" % (self._year, self._month, self._day)
 52        
 53    # Read-only field accessors
 54    @property
 55    def year(self):
 56        """year (1-9999)"""
 57        return self._year   
 58        
 59    def __sub__(self, other):
 60        """Subtract two dates, or a date and a timedelta."""
 61        if isinstance(other, timedelta):
 62            return self + timedelta(-other.days)
 63        if isinstance(other, date):
 64            days1 = self.toordinal()
 65            days2 = other.toordinal()
 66            return timedelta(days1 - days2)
 67        return NotImplemented
 68
 69    def __hash__(self):
 70        "Hash."
 71        if self._hashcode == -1:
 72            self._hashcode = hash(self._getstate())
 73        return self._hashcode
 74        
 75    def _getstate(self):
 76        yhi, ylo = divmod(self._year, 256)
 77        return bytes([yhi, ylo, self._month, self._day])
 78        
 79def _check_date_fields(year, month, day):
 80    year = _index(year)
 81    month = _index(month)
 82    day = _index(day)
 83    if not MINYEAR <= year <= MAXYEAR:
 84        raise ValueError('year must be in %d..%d' % (MINYEAR, MAXYEAR), year)
 85    if not 1 <= month <= 12:
 86        raise ValueError('month must be in 1..12', month)
 87    dim = _days_in_month(year, month)
 88    if not 1 <= day <= dim:
 89        raise ValueError('day must be in 1..%d' % dim, day)
 90        
 91def _is_leap(year):
 92    "year -> 1 if leap year, else 0."
 93    return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
 94
 95# -1 is a placeholder for indexing purposes.
 96_DAYS_IN_MONTH = [-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
 97
 98def _days_in_month(year, month):
 99    "year, month -> number of days in that month in that year."
100    assert 1 <= month <= 12, month
101    if month == 2 and _is_leap(year):
102        return 29
103    return _DAYS_IN_MONTH[month]

31.7. Datetime Myths#

Processing dates correctly is much easier said than done. Many “gotchas” exist and relying upon a well implemented, test and used library is critical. The following two pages should many of the “myths” programmers naively believe when looking at dates and times:

Falsehoods Programmers Believe about Time

More Falsehood Programmers Believe about Time

31.8. Suggested LLM Prompts#

  • Provide a detailed description of how dates are typically represented in computers. Discuss the difficulties of date and time such as the complexities with different timezones and calendaring systems.

  • Provide a detailed explanation and examples of how to convert different string formats representing dates and times into datetime objects in Python. Cover various string formats, including ISO, Unix timestamps, and custom formats. Discuss the use of the strptime() method and its counterpart strftime().

  • Explain the ISO-8601 representation for dates and times. Show example strings. Break down the different components of the representation. What are the advantages of this representation?

  • Explain how to perform arithmetic operations on datetime objects, such as adding or subtracting time intervals. Provide examples of adding or subtracting days, hours, minutes, and seconds. Discuss the timedelta class and its usage.

  • Demonstrate how to format datetime objects into human-readable strings using the strftime() method. Provide a comprehensive list of format codes and examples of their usage. Explain how to handle different locales and time zones.

  • Explain the concept of time zones and how to handle them in Python’s datetime module. Discuss the tzinfo class and its subclasses. Provide examples of converting datetime objects between different time zones and handling daylight saving time. Include discussion of timezone aware datetime object.s

  • Introduce the pendulum library, which provides a more user-friendly and intuitive interface for working with dates and times in Python. Provide examples of how to use pendulum for common datetime operations, such as parsing, formatting, and manipulating datetime objects.

  • Discuss the importance of handling datetime objects correctly in web applications. Provide examples of common use cases, such as storing and retrieving dates and times from databases, displaying datetime information in different time zones, and validating user input for datetime fields.

31.9. Review Questions#

  1. What is the difference between the datetime and date classes in Python’s datetime module?

  2. How would you create a datetime object representing the current date and time?

  3. Explain the concept of time zones in Python’s datetime module and how to handle them.

  4. Explain the difference between aware and naive datetime objects in Python.

  5. Explain the difference between the strftime() and strptime() methods in the datetime module.

  6. How would you calculate the number of days between two date objects?

  7. What is the purpose of the pendulum library in Python, and how does it differ from the built-in datetime module?

  8. Compare and contrast the different representations of a date and time value by using a Unix Epoch value versus ISO 8601?

answers

31.10. Exercises#

  1. Parse Date: Implement the following function -

    def parse_date(date_str):
        """
        Parse a date string in the format "YYYY-MM-DD" and return the corresponding datetime.date object.
     
        Parameters:
            date_str (str): Date string in the format "YYYY-MM-DD".
         
        Returns:
            datetime.date: Corresponding date object.
        Raises:
            ValueError: invalid date format
        """
    
  2. Calculate Business Days: Implement the following function -

    def count_business_days(start_date, end_date):
        """
        Calculate the number of business days (excluding weekends) between two given dates.
     
        Parameters:
            start_date (datetime.date): The start date.
            end_date (datetime.date): The end date.
         
        Returns:
            int: The number of business days between start_date and end_date (inclusive).
        """
    
  3. Calculate the Number of Years until Retirement: Write a Python script that prompts the user to enter their date of birth and retirement age. Then, calculate the number of years until they reach retirement age (67 years) based on the current date.

    To provide for better code reuse and abstraction, you solution needs to implement these two functions:

    def get_date_input(prompt):
        """
        Prompt the user to enter a date in the format "YYYY-MM-DD" and return the corresponding date object.
     
        Parameters:
            prompt (str): The prompt message to display.
         
        Returns:
            datetime.date: Date object entered by the user.
        """
    
    def calculate_years_until_retirement(date_of_birth, retirement_age=67):
        """
        Calculate the number of years until retirement based on the date of birth and retirement age.
     
        Parameters:
            date_of_birth (datetime.date): Date of birth.
            retirement_age (int): Retirement age. Default is 67.
         
        Returns:
            int: Number of years until retirement.
        """
    
    
  4. Determine the Time Remaining until Bond Maturity: Write a Python function that takes the issue date and maturity date of a bond and determines the time remaining until maturity based on the current date. The function should two values where the first is the percentage of time passed for maturity and the second is the number of years remaining.

    def remaining_time_to_maturity(issue_date, maturity_date):
        """
        Calculate the time remaining until maturity based on the issue date and maturity date of a bond.
     
        Parameters:
           issue_date (datetime.date): The issue date of the bond.
           maturity_date (datetime.date): The maturity date of the bond.
         
        Returns:
            tuple: A tuple containing two values:
                - Percentage of time passed for maturity
                - Number of years remaining until maturity
        """
    

    Answers to exercises