Add Months to a Date in DuckDB

DuckDB provides us with an easy way to add one or more months to date values, as well as subtract from them. This applies to the various date related data types, such as DATE and TIMESTAMP.

Actually, DuckDB provides us with a choice; We can use the + operator or the date_add() function.

Option 1: The + Operator

The + operator allows us to add time intervals to a date in DuckDB. This includes adding one or more months to a date.

Example:

SELECT DATE '2045-01-01' + INTERVAL 6 MONTH;

Result:

+-------------------------------------------------------------------------------------+
| (CAST('2045-01-01' AS DATE) + to_months(CAST(trunc(CAST(6 AS DOUBLE)) AS INTEGER))) |
+-------------------------------------------------------------------------------------+
| 2045-07-01 00:00:00 |
+-------------------------------------------------------------------------------------+

We can also use the plural form for the MONTH keyword (so that it becomes MONTHS):

SELECT DATE '2045-01-01' + INTERVAL 6 MONTHS;

Result:

+-------------------------------------------------------------------------------------+
| (CAST('2045-01-01' AS DATE) + to_months(CAST(trunc(CAST(6 AS DOUBLE)) AS INTEGER))) |
+-------------------------------------------------------------------------------------+
| 2045-07-01 00:00:00 |
+-------------------------------------------------------------------------------------+

We can also add to a TIMESTAMP value if needed:

SELECT TIMESTAMP '2045-01-01 15:27:05' + INTERVAL 6 MONTHS;

Result:

+---------------------------------------------------------------------------------------------------+
| (CAST('2045-01-01 15:27:05' AS TIMESTAMP) + to_months(CAST(trunc(CAST(6 AS DOUBLE)) AS INTEGER))) |
+---------------------------------------------------------------------------------------------------+
| 2045-07-01 15:27:05 |
+---------------------------------------------------------------------------------------------------+

We can also add more than just months:

SELECT DATE '2045-01-01' + INTERVAL '6 MONTHS 5 DAYS 8 HOURS 3 MINUTES 43 SECONDS';

Result:

+-------------------------------------------------------------------------------------------------+
| (CAST('2045-01-01' AS DATE) + CAST('6 MONTHS 5 DAYS 8 HOURS 3 MINUTES 43 SECONDS' AS INTERVAL)) |
+-------------------------------------------------------------------------------------------------+
| 2045-07-06 08:03:43 |
+-------------------------------------------------------------------------------------------------+

When we do this, we need to pass the interval as a string (i.e. enclosed in single quotes).

DuckDB knows how many days are in each month, so when we use MONTH to add a month to the last day of a given month that has more days than the following month, the result is the last day of the following month:

SELECT DATE '2045-01-31' + INTERVAL 1 MONTH;

Result:

+-------------------------------------------------------------------------------------+
| (CAST('2045-01-31' AS DATE) + to_months(CAST(trunc(CAST(1 AS DOUBLE)) AS INTEGER))) |
+-------------------------------------------------------------------------------------+
| 2045-02-28 00:00:00 |
+-------------------------------------------------------------------------------------+

So in this example, we started with the 31st day of January, and ended with the 28th February. DuckDB knows not to return 31st of February, given that date doesn’t exist.

However, this concept doesn’t apply if the resulting month has more days than the current month:

SELECT DATE '2045-02-28' + INTERVAL 1 MONTH;

Result:

+-------------------------------------------------------------------------------------+
| (CAST('2045-02-28' AS DATE) + to_months(CAST(trunc(CAST(1 AS DOUBLE)) AS INTEGER))) |
+-------------------------------------------------------------------------------------+
| 2045-03-28 00:00:00 |
+-------------------------------------------------------------------------------------+

In this case, we started at the 28th of February (its last day), and the result was the 28th of March – not the 31st of March. Even though March has 31 days, DuckDB didn’t just go ahead an assume that we wanted to move forward to the last day of March.

All of the above examples are returned as a TIMESTAMP. We can explicitly cast it to a DATE value if required:

SELECT cast(DATE '2045-01-01' + INTERVAL 6 MONTH AS DATE);

Result:

+---------------------------------------------------------------------------------------------------+
| CAST((CAST('2045-01-01' AS DATE) + to_months(CAST(trunc(CAST(6 AS DOUBLE)) AS INTEGER))) AS DATE) |
+---------------------------------------------------------------------------------------------------+
| 2045-07-01 |
+---------------------------------------------------------------------------------------------------+

Option 2: The DATE_ADD() Function

Alternatively, we can use the date_add() function to do the same thing that we did with the + operator:

SELECT date_add( DATE '2045-01-01', INTERVAL 6 MONTH );

Result:

+--------------------------------------------------------------------------------------------+
| date_add(CAST('2045-01-01' AS DATE), to_months(CAST(trunc(CAST(6 AS DOUBLE)) AS INTEGER))) |
+--------------------------------------------------------------------------------------------+
| 2045-07-01 00:00:00 |
+--------------------------------------------------------------------------------------------+

As with the + operator, we can use MONTHS instead of MONTH:

SELECT date_add( DATE '2045-01-01', INTERVAL 6 MONTHS );

Result:

+--------------------------------------------------------------------------------------------+
| date_add(CAST('2045-01-01' AS DATE), to_months(CAST(trunc(CAST(6 AS DOUBLE)) AS INTEGER))) |
+--------------------------------------------------------------------------------------------+
| 2045-07-01 00:00:00 |
+--------------------------------------------------------------------------------------------+

We can also add more than just days, just like with the + operator:

SELECT date_add( DATE '2045-01-01', INTERVAL '6 MONTHS 5 DAYS 8 HOURS 3 MINUTES 43 SECONDS');

Result:

+--------------------------------------------------------------------------------------------------------+
| date_add(CAST('2045-01-01' AS DATE), CAST('6 MONTHS 5 DAYS 8 HOURS 3 MINUTES 43 SECONDS' AS INTERVAL)) |
+--------------------------------------------------------------------------------------------------------+
| 2045-07-06 08:03:43 |
+--------------------------------------------------------------------------------------------------------+

We can also cast the result as a DATE:

SELECT cast(date_add( DATE '2045-01-01', INTERVAL 6 MONTH ) AS DATE);

Result:

+----------------------------------------------------------------------------------------------------------+
| CAST(date_add(CAST('2045-01-01' AS DATE), to_months(CAST(trunc(CAST(6 AS DOUBLE)) AS INTEGER))) AS DATE) |
+----------------------------------------------------------------------------------------------------------+
| 2045-07-01 |
+----------------------------------------------------------------------------------------------------------+