Python: 2 Ways to Check if a Date String is Valid

Updated: June 15, 2023 By: Khue Post a comment

Overview

In general (and in the context of this article), a valid date string can be considered a date string in the ISO 8601 format. The ISO 8601 format is an internationally recognized standard for representing dates and times in consistent and unambiguous ways.

By considering a valid date string as one that adheres to the ISO 8601 format, you ensure that the date is represented correctly and can be easily understood by others who are familiar with the standard. This format is widely used in various industries and systems, making it a reliable choice for date representation.

In Python, an invalid date string cannot be parsed into a date or a datetime object by the datetime module. An invalid date string can be very similar to a valid one and easy to fool our eyes:

  • The string contains ambiguous or conflicting information, such as 02/03/04 (could be February 3rd 2004, March 2nd 2004, or April 3rd 2002).
  • The string does not match any of the expected formats.
  • The string contains invalid values for year, month, day, hour, minute, second, microsecond, or timezone.

Some examples of invalid date strings are:

"2023-13-01" # month cannot be 13
"2021-02-29" # February 29th does not exist in 2021
"2024-01-32" # day cannot be 32
"2023-01-01 25:00:00" # hour cannot be 25
"2025-01-01 12:00:00 PM UTC+0100" # conflicting timezone information
"02/03/04" # ambiguous format

Checking if a Date String is Valid

Some different ways to know whether a given string is a true representation of a date object.

Using datetime.fromisoformat()

The main idea of this approach is to use the datetime.fromisoformat() class method of the datetime module to parse a date string in ISO 8601 format and return a datetime object. If the string is not a valid ISO 8601 date string, it raises a ValueError exception.

Example:

from datetime import datetime

def is_valid_date(date_str):
  # Checks if a date string is valid ISO 8601 format
  # Returns True if valid, False otherwise
  try:
    datetime.fromisoformat(date_str)
    return True
  except ValueError:
    return False

# Test cases
print(is_valid_date("2023-06-15")) # True
print(is_valid_date("2024-06-15T10:44:50")) # True
print(is_valid_date("2023-06-15T10:44:50.123456")) # True
print(is_valid_date("2024-06-15T10:44:50+07:00")) # True
print(is_valid_date("2024-02-29")) # True (2024 is a leap year
print(is_valid_date("2025-02-29")) # False (2025 is not a leap year)
print(is_valid_date("2024-06-15T10:44:50Z")) # False
print(is_valid_date("2021-13-15")) # False

This technique has some pros and cons, as shown below:

  • Pros: This solution is simple and does not require any external libraries. It can handle different variations of ISO 8601 format, such as YYYY-MM-DD, YYYY-MM-DDTHH:MM:SS, YYYY-MM-DDTHH:MM:SS.mmmmmm, YYYY-MM-DDTHH:MM:SS+HH:MM, etc.
  • Cons: This solution requires Python 3.7 or higher. It does not support the Z suffix for UTC timezone, such as YYYY-MM-DDTHH:MM:SSZ. It may also raise other exceptions, such as TypeError or OverflowError, if the input is not a string or has invalid values.

In case you have to deal with the Z suffix for UTC timezone, see the next solution.

Using dateutil.parse.isoparse()

In this approach, we’ll use the isoparse() function from the dateutil.parser module to parse a date string in ISO 8601 format and return a datetime object. If the string is not a valid ISO 8601 date string, it will raise a ValueError exception.

dateutil is not ready to use by default. You need to install it first:

pip install python-dateutil

Code example:

from dateutil.parser import isoparse

def is_valid_date(date_str):
  # Checks if a date string is valid ISO 8601 format
  # Returns True if valid, False otherwise
  try:
    isoparse(date_str)
    return True
  except ValueError:
    return False

# Test cases
print(is_valid_date("2024-06-15")) # True
print(is_valid_date("2025-06-15T10:44:50")) # True
print(is_valid_date("2023-06-15T10:44:50.123456")) # True
print(is_valid_date("2025-06-15T10:44:50+07:00")) # True
print(is_valid_date("2023-06-15T10:44:50Z")) # True
print(is_valid_date("2024-13-15")) # False (invalid month)
print(is_valid_date("2025-02-29")) # False (2025 is not a leap year)
print(is_valid_date("2023-06-15T10:44:50.1234567")) # True

Some considerations about this technique:

  • Advantages: This solution can handle different variations of ISO 8601 format, such as YYYY-MM-DD, YYYY-MM-DDTHH:MM:SS, YYYY-MM-DDTHH:MM:SS.mmmmmm, YYYY-MM-DDTHH:MM:SS+HH:MM, etc. It also supports the Z suffix for UTC timezone, such as YYYY-MM-DDTHH:MM:SSZ so it is more reliable than using datetime.fromisoformat().
  • Disadvantages: This solution requires installing an external library. It may also raise other exceptions, such as TypeError or OverflowError, if the input is not a string or has invalid values.

Conclusion

You’ve learned a couple of different ways to check the validity of a date string in Python. If you don’t care about the timezone (such as when you build software specifically for a certain city), the first one is good enough. In other situations, the second one is safer.