How to Format Dates in DuckDB

When working with SQL databases such as DuckDB, dates usually conform to a specific format. In particular, when we create dates in DuckDB, they must conform to the ISO 8601 format (YYYY-MM-DD). It’s the same with time (hh:mm:ss[.zzzzzz][+-TT[:tt]]) and timestamp values (YYYY-MM-DD hh:mm:ss[.zzzzzzzzz][+-TT[:tt]]).

But what if we have a requirement to present these dates or timestamps in a different format?

Fortunately, DuckDB provides us with tools to so. This article explains how to format date and timestamp values according to a specified format.

Quick Example

Let’s start with a quick example:

SELECT strftime(DATE '2050-12-20', '%A, %d %B %Y');

Result:

Tuesday, 20 December 2050

This is just one of any number of formats that we could use to format the date. We can also include timestamp values, and we can include the time portion in the formatted output.

Here, we used the strftime() function to format the date provided in the first argument. The second argument is our format string. The format string determines how the date will be formatted. Therefore, the format string we use will depend on the format that we’re trying to achieve. More about format strings later.

Initial Date/Time Formatting

Before getting into the details of formatting dates, let’s first look at how date/time values are initially formatted in DuckDB;

TypeDescriptionExample
DATERepresents a calendar date2025-10-05
TIMESTAMPRepresents a date and time (naive timestamp with microsecond precision)2025-10-05 14:30:00.123456
TIMESTAMP_NSNaive timestamp with nanosecond precision2025-10-05 14:30:00.123456789
TIMESTAMP_MSNaive timestamp with millisecond precision2025-10-05 14:30:00.123
TIMESTAMP_SNaive timestamp with second precision2025-10-05 14:30:00
TIMESTAMPTZTime zone aware timestamp with microsecond precision2025-10-05 14:30:00.123456+00:00
TIMERepresents a time of day, ignoring time zone14:30:00
TIMETZTime of day, using time zone14:30:00+05:30

We can see that each of these conform to a standardized format. But we can format them for display or export using DuckDB’s built-in functions.

While we’re at it, DuckDB also includes a few special date values that can be used on input:

Input stringDescriptionOutput
epoch1970-01-01 (Unix system day zero)1970-01-01
infinityLater than all other datesinfinity
-infinityEarlier than all other dates-infinity

Trying to format infinity and -infinity won’t do much, but it’s possible to format the output of epoch if required.

Using the strftime() Function for Date Formatting

As seen in the earlier example, the strftime() function allows us to convert a date or timestamp into a string representation based on a format string.

The syntax goes like this:

strftime(date, format)

Where:

  • date is the date or timestamp that you need to format.
  • format argument is a format string that specifies how the date or timestamp should be formatted. It uses placeholders to represent different components of the date, such as the year, month, day, hour, minute, and second.

Common Format Specifiers

The format string consists of one or more format specifiers. Here are some of the more commonly used format specifiers:

  • %Y: Four-digit year (e.g., 2023).
  • %y: Two-digit year (e.g., 23).
  • %m: Two-digit month (e.g., 10 for October).
  • %d: Two-digit day of the month (e.g., 05).
  • %H: Two-digit hour in 24-hour format (e.g., 14 for 2 PM).
  • %M: Two-digit minute (e.g., 30).
  • %S: Two-digit second (e.g., 00).
  • %A: Full weekday name (e.g., Thursday).
  • %a: Abbreviated weekday name (e.g., Thu).
  • %B: Full month name (e.g., October).
  • %b: Abbreviated month name (e.g., Oct).

These format specifiers are basically placeholders for the relevant date part. So if we use %Y, that will result in the four-digit year being displayed, and so on.

Here’s a full list of format specifiers that you can use to build format strings.

Examples of Date Formatting with strftime()

Let’s look at some simple examples of how to use strftime() to format dates in DuckDB.

Formatting a Date

This example illustrates how we can use different format specifiers to achieve different formats:

SELECT 
    strftime(DATE '2034-08-07', '%a, %d %b %y') AS short,
    strftime(DATE '2034-08-07', '%A, %d %B %Y') AS long,
    strftime(DATE '2034-08-07', '%A') AS day,
    strftime(DATE '2034-08-07', '%B') AS month,
    strftime(DATE '2034-08-07', '%Y') AS year;

Result:

+----------------+------------------------+--------+--------+------+
| short | long | day | month | year |
+----------------+------------------------+--------+--------+------+
| Mon, 07 Aug 34 | Monday, 07 August 2034 | Monday | August | 2034 |
+----------------+------------------------+--------+--------+------+

As shown here, we don’t need to include all date parts if we don’t want. For example, we can provide a single format specifier to output a single date part like we did for the day, month, and year columns.

Formatting a Timestamp

Here are some examples that use a TIMESTAMP value:

SELECT 
    strftime(TIMESTAMP '2034-08-07 12:35:15', '%d/%m/%Y %H:%M:%S') AS aus,
    strftime(TIMESTAMP '2034-08-07 12:35:15', '%m/%d/%Y %H:%M:%S') AS us,
    strftime(TIMESTAMP '2034-08-07 12:35:15', '%d/%m/%Y') AS aus_date,
    strftime(TIMESTAMP '2034-08-07 12:35:15', '%H:%M:%S') AS time,
    strftime(TIMESTAMP '2034-08-07 12:35:15', '%H:%M:%S %p') AS am_pm;

Result:

+---------------------+---------------------+------------+----------+-------------+
| aus | us | aus_date | time | am_pm |
+---------------------+---------------------+------------+----------+-------------+
| 07/08/2034 12:35:15 | 08/07/2034 12:35:15 | 07/08/2034 | 12:35:15 | 12:35:15 PM |
+---------------------+---------------------+------------+----------+-------------+

In Australia, dates are formatted with the day first, followed by the month (dd/mm/yyyy), whereas in the US, they’re formatted with the month first (mm/dd/yyyy). Format specifiers provide us with the flexibility to adjust our format string accordingly.

Including String Literals

We can include string literals in the formatted date. We already did this in the previous examples (by including / and : in our format strings).

Here’s an example that expands this idea further:

SELECT 
    strftime(
        DATE '2050-12-20', 
        'The day is %A on day number %d in the month of %B of the year %Y'
        ) AS result;

Result:

+-------------------------------------------------------------------------------+
| result |
+-------------------------------------------------------------------------------+
| The day is Tuesday on day number 20 in the month of December of the year 2050 |
+-------------------------------------------------------------------------------+

Extracting Date Components

We can see that the strftime() function is quite flexible with regards to how the various date parts are presented. We can even use the function to extract a single date part, such as the day or month or year:

SELECT 
    strftime(DATE '2034-08-07', '%-d') AS '%-d',
    strftime(DATE '2034-08-07', '%d') AS '%d',
    strftime(DATE '2034-08-07', '%-m') AS '%-m',
    strftime(DATE '2034-08-07', '%m') AS '%m',
    strftime(DATE '2034-08-07', '%A') AS '%A',
    strftime(DATE '2034-08-07', '%B') AS '%B',
    strftime(DATE '2034-08-07', '%Y') AS '%Y';

Result:

+-----+----+-----+----+--------+--------+------+
| %-d | %d | %-m | %m | %A | %B | %Y |
+-----+----+-----+----+--------+--------+------+
| 7 | 07 | 8 | 08 | Monday | August | 2034 |
+-----+----+-----+----+--------+--------+------+

DuckDB also provides several functions specifically for extracting date parts. Here are some of the more obvious ones:

  • year(): Extracts the year.
  • month(): Extracts the month.
  • day(): Extracts the day.
  • hour(): Extracts the hour.
  • minute(): Extracts the minute.
  • second(): Extracts the second.

Example:

SELECT 
    day(DATE '2034-08-07') AS 'day()',
    month(DATE '2034-08-07') AS 'month()',
    quarter(DATE '2034-08-07') AS 'quarter()',
    year(DATE '2034-08-07') AS 'year()',
    decade(DATE '2034-08-07') AS 'decade()',
    century(DATE '2034-08-07') AS 'century()';

Result:

+-------+---------+-----------+--------+----------+-----------+
| day() | month() | quarter() | year() | decade() | century() |
+-------+---------+-----------+--------+----------+-----------+
| 7 | 8 | 3 | 2034 | 203 | 21 |
+-------+---------+-----------+--------+----------+-----------+

Handling Time Zones

DuckDB also supports time zone-aware timestamps. We can use the AT TIME ZONE clause to convert a timestamp to a specific time zone before formatting:

SELECT 
    strftime(TIMESTAMP '2034-08-07 12:35:15', '%A, %d %B %Y %H:%M:%S') AS 'Local',
    strftime(TIMESTAMP '2034-08-07 12:35:15' AT TIME ZONE 'UTC', '%A, %d %B %Y %H:%M:%S') AS 'UTC';

Output:

+---------------------------------+---------------------------------+
| Local | UTC |
+---------------------------------+---------------------------------+
| Monday, 07 August 2034 12:35:15 | Monday, 07 August 2034 22:35:15 |
+---------------------------------+---------------------------------+

There’s also the timestamp() function that accepts TIMESTAMP and TIMESTAMPTZ values:

SELECT 
    timezone('UTC', TIMESTAMP '2034-08-07 12:35:15') AS 'timestamp',
    timezone('UTC', TIMESTAMPTZ '2034-08-07 12:35:15+00') AS 'timestamptz';

Output:

+------------------------+---------------------+
| timestamp | timestamptz |
+------------------------+---------------------+
| 2034-08-07 22:35:15+10 | 2034-08-07 12:35:15 |
+------------------------+---------------------+

We can pass this function to the strftime() function in order to format it the way we want:

SELECT 
    strftime(
        timezone(
            'UTC', TIMESTAMP '2034-08-07 12:35:15'), 
            '%A, %d %B %Y %H:%M:%S'
            ) AS 'timestamp',
    strftime(
        timezone(
            'UTC', TIMESTAMPTZ '2034-08-07 12:35:15+00'), 
            '%A, %d %B %Y %H:%M:%S'
            ) AS 'timestamptz';

Output:

+---------------------------------+---------------------------------+
| timestamp | timestamptz |
+---------------------------------+---------------------------------+
| Monday, 07 August 2034 22:35:15 | Monday, 07 August 2034 12:35:15 |
+---------------------------------+---------------------------------+

Using strptime() to Construct Dates

DuckDB also has a strptime() function, which enables us to convert a string to a valid timestamp value.

Example:

SELECT strptime('Monday, 7 August 2034 - 08:35:15 PM', '%A, %-d %B %Y - %I:%M:%S %p');

Result:

+--------------------------------------------------------------------------------+
| strptime('Monday, 7 August 2034 - 08:35:15 PM', '%A, %-d %B %Y - %I:%M:%S %p') |
+--------------------------------------------------------------------------------+
| 2034-08-07 20:35:15 |
+--------------------------------------------------------------------------------+

So it’s basically the opposite of strftime(), in that we pass a date that’s already been formatted as a string, so that the function will convert that string to a valid timestamp value.

Summary

Formatting dates in DuckDB is relatively straightforward thanks to the strftime() function and the various format specifiers, as well as other date-related functions. Whether we need to display dates in a specific format, extract individual components, handle time zones, or construct a timestamp value from a formatted date string, DuckDB provides plenty of tools that can help.