MySQL vs. PostgreSQL: Date and Time Data Types

Comments 0

Share to social media

Date and time data types are important because they help track and manage time-based information in databases. This includes things like recording when orders are placed, scheduling events, or calculating the time between two activities. In this article, we will look at differences between how PostgreSQL and MySQL stores and utilizes date and time datatypes.

MySQL and PostgreSQL offer several data types that can be used for handling dates and times. These data types provide the tools to store and manage information like dates of a particular event, timestamps, and even time durations. While they both share some similarities on how they handle date and time, there are key differences in how they handle precision, time zones, and date/time calculations.

Getting date and time data right is key for keeping databases accurate and useful. In this article, we will compare how MySQL and PostgreSQL handle date and time data, their differences, strengths, and which one might work better for your needs. By the end, you’ll have a clearer idea of which database to choose for managing date and time information.

MySQL Date and Time Data Types

MySQL is quite flexible when it comes to accepting date and time values. It provides five main date and time data types, which are, DATE, DATETIME, TIMESTAMP, TIME, and YEAR. However, these data types must follow a specific accepted format to ensure the values are correctly stored in the database. Here’s a breakdown of the format MySQL expects for its following date and time values.

Data Type

Expected Format

Example Values

Storage size

DATE

YYYY-MM-DD

2025-02-14

3 bytes

DATETIME

YYYY-MM-DD HH:MM:SS

2025-02-14 10:30:45

8 bytes

TIMESTAMP

YYYY-MM-DD HH:MM:SS

2025-02-14 10:30:45

4 bytes

TIME

HH:MM:SS

10:30:45

3 bytes

YEAR

YYYY

2025

1 byte

You are also allowed to include fractional seconds (up to 6 digits) into the DATETIME and TIMESTAMP values. Values are enclosed in quoted strings during insertion. Here is an example of what this would look like:

Then create a value using an INSERT statement:

If you insert a value that doesn’t follow the expected format, MySQL handles it based on the sql_mode setting. In the STRICT_TRANS_TABLES mode, dates are checked strictly. You can check the setting value using:

By default, this will look something like this:

And if there is not a value ‘STRICT_TRANS_TABLES’ (which there is in my example) then you can set it for your connection using the following statement (which will preserve all other settings):

Note: Beware that sql_mode pertains to more than just date format handling, so check out this article in the MySQL docs before using.

When MySQL encounters an invalid date format, it does the following:

Strict Mode (STRICT_TRANS_TABLES): If strict mode is enabled (default in newer MySQL versions), MySQL rejects invalid dates and throws an error. For example:

MySQL will throw the following error:

SQL Error [1292] [22001]: Data truncation: Incorrect datetime value: ‘2025-14-02 10:30:45’ for column ‘event_time’ at row 1

The error occurred because ‘2025-14-02’ is an invalid date format. In MySQL, a valid DATE format follows YYYY-MM-DD, and the month (14) exceeds the valid range (1-12).

Non-strict mode: If strict mode is disabled, MySQL does not throw an error for invalid dates. Instead, it stores '0000-00-00‘ for invalid dates instead of rejecting them. Here’s an example:

Then execute the statement:

You should get back no error message and the data is seemingly stored. But let’s run a SELECT statement to see how it stored our date value.

This will return something like:

The second row is the one where we had strict mode turned off.

Note that MySQL also allows inserting partial or unknown dates by setting the day or month to 00. This is controlled by the NO_ZERO_IN_DATE sql mode. (When setting strict mode earlier, you may have seen a warning about a few settings that are going to be a part of strict mode in MySQL. As such, NO_ZERO_IN_DATE is deprecated and will be rolled into strict mode in the future.)

This can be useful for cases where only part of the date is known. Here’s an example of how this works:

You can see two new rows, each with 00 for a day or month value for event_id = 3 and 4:

Some MySQL functions do not work correctly with 0 values. For example, adding an interval to a 0 date can produce unexpected results. For example:

This returns a NULL value. So, it is not advisable to use this instead, you can enforce valid dates and disallow 0 values, by enabling strict mode and NO_ZERO_DATE in sql_mode.

This ensures that MySQL rejects incomplete dates instead of storing them as 0000-00-00 or allowing 0 for days and months and leaves any other settings intact.

When to Use DATETIME vs. TIMESTAMP

The DATETIME and TIMESTAMP data types in MySQL both store date and time values, however, they have distinct differences in their storage and range of values. The DATETIME data type takes 8 bytes to store a value while TIMESTAMP uses only 4 bytes, making it more space-efficient.

DATETIME can store dates from 1000-01-01 to 9999-12-31, making it suitable for historical records and long-term data while TIMESTAMP only works from 1970-01-01 to 2038-01-19, due to its Unix timestamp limitation. If you need to store dates beyond 2038, use the DATETIME data type.

Note: 2038 is not that far in the future, and if there will be a simple fix is not obvious (at best, if the TIMESTAMP is redefined to include later dates, you may have to convert all of your data, so it may be advised to use DATETIME in future development, especially where future dates may be needed.)

Below is a breakdown of the major differences between the DATETIME and TIMESTAMP data type in MySQL:

Feature

DATETIME

TIMESTAMP

Stores Date & Time?

Yes

Yes

Time Zone Affected?

No (Stored as-is)

Yes (Stored in UTC, converted to session time zone)

Storage size

8 bytes

4 bytes

Range

1000-01-01 00:00:00.000000′ to ‘9999-12-31 23:59:59.499999

1970-01-01 00:00:01.000000′ to ‘2038-01-19 03:14:07.499999

Best Use Case

When time zone conversion is not needed

When you need timezone-aware timestamps

Fractional Seconds?

Yes (supports up to 6 decimal places)

Yes (supports up to 6 decimal places)

Here’s an example with both Data Types:

This returns:

In the output, the event_time column shows the exact date and time you entered while the created_at column records when the event was added, storing it in UTC and adjusting automatically based on your time zone when retrieved.

So, if you care about time zone conversions, use TIMESTAMP and if you want a fixed value across all time zones, use DATETIME.

Handling 2 Digits Years in MySQL

MySQL lets you insert years using just two digits, but it automatically expands them to four digits based on a specific rule:

  • If the value is between 00 and 69, MySQL treats it as 2000–2069.
  • If the value is between 70 and 99, MySQL treats it as 1970–1999.

This automatic conversion may cause confusion, especially with old or future dates. To avoid mistakes, always use four-digit years (YYYY-MM-DD). Here’s a practical example using our product table created earlier:

MySQL treated 25-02-14 as ‘2025-02-14’ and also 89-12-31 as ‘1989-12-31’

So far, we have covered quite a bit about the Date and Time Data Types in MySQL, but definitely could not cover every detail. For more, check the official documentation on date and time types in the MySQL Documentation.

PostgreSQL Date and Time Data Types

PostgreSQL provides a range of date and time data types for storing simple dates, handling time zones, and managing time intervals. It supports DATE, TIME (with time zone), TIME (without time zone), TIMESTAMP (with time zone), TIMESTAMP (without time zone) which can include fractional seconds (up to 6 digits), and INTERVAL. Here’s a breakdown of the range, storage size, and expected format of the following date and time values.

DataType

Range

Expected Format

Description

Storage Size

DATE

4713 BC to 5874897 AD

YYYY:MM:DD

Stores only the calendar date (year, month, day) without a time component.

4 bytes

TIME [(p)] WITHOUT TIME ZONE

00:00:00 to 24:00:00 (24:00:00 is a special case that represents the start of the next day)

HH:MM:SS

Stores only the time of day without any time zone information.

8 bytes

TIME [(p)] WITH TIME ZONE

00:00:00+1559 to 24:00:00-1559 (24:00:00 values represent the start of the next day)

‘HH:MI:SS+TZ’

Stores the time of day with an associated time zone offset.

12 bytes

TIMESTAMP [(p)] WITHOUT TIME ZONE

4713 BC to 294276 AD

YYYY-MM-DD HH:MI:SS

Stores both date and time, but does not include time zone information.

8 bytes

TIMESTAMP [(p)] WITH TIME ZONE

4713 BC to 294276 AD

YYYY-MM-DD HH:MI:SS+TZ

Stores both date and time, adjusting for time zones automatically based on session settings.

8 bytes

INTERVAL [(p)]16 bytes

-178 million to +178 million years

quantity unit(e.g., ‘2 days’)

Represents a duration of time, allowing operations like date/time arithmetic.

16 bytes

Note: In PostgreSQL, you can control how dates are interpreted by setting the `DateStyle` parameter. The available formats include MDY (month-day-year), DMY (day-month-year), and YMD(year-month-day). This ensures flexibility in handling different regional date formats.

When inserting date and time values in PostgreSQL, they must be enclosed in single quotes. Here’s an example on how you can create a table and insert date and time values in PostgreSQL:

The output will be as follows:

If you try to insert the date without enclosing it in a single quote, it will throw an error. Let’s try that:

Then you will get an error like the following:

SQL Error [42804]: ERROR: column “event_date” is of type date but expression is of type integer
Hint: You will need to rewrite or cast the expression.
Position: 82

This is because the date expression now looks like an integer mathematical expression of 2024-2-14.

TIMESTAMP WITH vs. WITHOUT TIME ZONE

One of PostgreSQL’s strengths is its ability to handle time zones properly. It offers two types of timestamps. TIMESTAMP WITHOUT TIME ZONE stores the exact date and time as entered and no automatic time zone conversion, and TIMESTAMP WITH TIME ZONE converts the stored value to UTC and adjusts it when retrieved based on the session’s time zone. Here’s an example of how both timestamps work:

The output should look something like the following:

Note that even though the time entered into the event_timestamptz and event_timestamp columns looks the same, event_timestamp is at 10:30, but event_timestamptz adds the time zone information, so it could be interpreted as 10:30 in the executor’s time zone, or at 5:30 UTC.

Every time you run the INSERT query, it automatically sets the time stamp and adjusts it based on the session’s time zone when retrieved. Meanwhile, event_timestamp will always stay as the base time 2025-02-14 10:30:00, no matter the session time zone.

If you don’t need time zone adjustments, use TIMESTAMP WITHOUT TIME ZONE and if your application handles users in different time zones use TIMESTAMP WITH TIME ZONE.

Special Date And Time Values

PostgreSQL provides predefined constants that make working with dates and times easier. These constants help in retrieving system-generated values without requiring manual input. Here are some of the special date and time values:

Special value

Description

CURRENT_DATE

Returns only the current date (YYYY-MM-DD).

CURRENT_TIME

Returns only the current time (HH:MI:SS with optional fractional seconds).

today

Used in place of a specific date value. Represents the current date (similar to CURRENT_DATE). For example:

select cast(‘today’ as date);

tomorrow

Represents the next day (current date + 1).

yesterday

Represents the previous day (current date – 1).

allballs

Represents midnight (00:00:00). Equivalent to ’00:00:00′::time.

SELECT 'allballs'::time;

CURRENT_TIMESTAMP

Returns the current date and time.

NOW()

Works the same as CURRENT_TIMESTAMP, returning the current date and time.

infinity

Represents a future date that never ends.

-infinity

Represents a past date that has no beginning.

Note: The CURRENT_DATE(), CURRENT_TIME(), CURRENT_TIMESTAMP(), and NOW() special date and time values are also available in MySQL and work the same way. But, there are no built-in values for today, tomorrow, yesterday, allballs, and infinity like in PostgreSQL.

However, you can work around this by using the largest and smallest possible date values:

  • Simulating infinity → Use ‘9999-12-31’, the highest possible date.
  • Simulating -infinity → Use ‘0000-00-00’ (if allowed) or ‘1000-01-01’, the lowest supported date. For relative date calculations:
  • Today → CURRENT_DATE()
  • Tomorrow → CURRENT_DATE() + 1
  • Yesterday → CURRENT_DATE() - 1

Here are some examples on using some of these special date and time values in PostgreSQL:

This will return something similar to:

A screenshot of a video

AI-generated content may be incorrect.

Key difference: CURRENT_TIME only shows the time, while CURRENT_TIMESTAMP and NOW() include both date and time.

The infinity values are useful when you don’t know the exact start or end date of an event. Here’s an example:

This will return:

The typical use case for the ‘infinity’ and ‘-infinity’ are useful for setting lifelong memberships, permanent bans, or open-ended job contracts.

Let’s try out another example:

Events on today’s date:

Which as I am finalizing this article is April 28:

For earlier than today:

This returned:

And for events in the future:

Beyond these common built in intervals, you can use the INTERVAL construct for more flexibility

Knowing this is the twenty ninth of April, you can see the different values from the INSERT statement in the following output:

For more information on the date and time data type, please visit the official PostgreSQL documentation.

MySQL vs PostgreSQL: Comparison of Date and Time Data Types

MySQL and PostgreSQL both offer date and time data types, but there are subtle differences in their flexibility and behaviour. While both databases support DATE, TIME, and TIMESTAMP, MySQL has YEAR as a separate data type, while PostgreSQL does not. PostgreSQL also includes INTERVAL for handling time spans, which MySQL lacks. Here’s a detailed comparison of date and time data type in both databases:

Data Type

MySQL

Range (MySQL)

PostgreSQL

Range (PostgreSQL)

Differences

Date types

‘DATE’

Range: 1000-01-01′ to ‘9999-12-31’

‘DATE’

Range:

4713 BC’ to ‘5874897 AD’

Both databases offer DATE types for storing dates (year, month, day).

Datetime types

‘DATETIME’

‘TIMESTAMP’

Datetime range: ‘1000-01-01 00:00:00’ to ‘9999-12-31 23:59:59’

Timestamp range: ‘1970-01-01 00:00:01′ to ‘2038-01-19 03:14:07’

‘TIMESTAMP’

Range: ‘4713 BC’ to ‘294276 AD’

PostgreSQL’s TIMESTAMP supports a broader range and higher precision than MySQL’s DATETIME.

Time types

‘TIME’

Range: ‘-838:59:59’ to ‘838:59:59’

TIME’

Range: ’00:00:00′ to ’24:00:00′

PostgreSQL’s TIME type has a more conventional range compared to MySQL’s.

Year types

‘YEAR’

Range: ‘1901’ to ‘2155’

MySQL offers a YEAR type specifically for storing year values using 1 byte. PostgreSQL does not have this data type.

Interval types

-

-

‘INTERVAL’

Range: Can represent large periods from years to microsecond

PostgreSQL supports INTERVAL for storing periods of time, which is not natively available in MySQL.

Note: MySQL has an interval type which is more of an expression/keyword and not a data type. We will see how this works in a bit.

MySQL allows storing invalid dates like ‘0000-00-00’ in non-strict mode, which can lead to unexpected results. But, PostgreSQL enforces strict date validation and does not allow invalid dates. Let’s see this in action:

This input will fail since that is not an allowable value for the DATE datatype:

SQL Error [22008]: ERROR: date/time field value out of range: “0000-00-00”
Position: 44
Error position: line: 8 pos: 43

One of the differences in date and time data types is how each database handles time zone. PostgreSQL provides TIMESTAMP WITH TIME ZONE (TIMESTAMPTZ), which automatically converts values to UTC and adjusts them based on the session’s time zone.

MySQL’s TIMESTAMP also supports automatic time zone conversion, but DATETIME does not. Instead, it stores the values as inserted.

Date and Time Functions in MySQL and PostgreSQL

Another major difference between both databases is how they handle special dates and time values. Here’s a well detailed table comparing date and time functions in both databases:

Special Value

MySQL Equivalent

PostgreSQL

CURRENT_DATE

✅ CURRENT_DATE()

✅ CURRENT_DATE

CURRENT_TIME

✅ CURRENT_TIME()

✅ CURRENT_TIME

CURRENT_TIMESTAMP

✅ CURRENT_TIMESTAMP()

✅ CURRENT_TIMESTAMP

'today', 'tomorrow', 'yesterday'

❌ No direct equivalent

✅ Supported

'infinity', '-infinity'

❌ No direct equivalent

✅Used for open-ended ranges

'allballs' (00:00:00)

❌ No equivalent

✅ Used as a midnight shorthand

While in some cases there are not specific shorthand methods that work across systems ,there are relatively simple ways to get the job done when needed. For example, if you want to store values like ‘today’, ‘tomorrow’, and ‘yesterday’ in MySQL, you can take advantage of the already supported CURRENT_DATE() function. Here’s how to do this in MySQL:

And querying the data in this table, you should get a similar result just like PostgreSQL

Working with Intervals (PostgreSQL vs. MySQL alternative)

PostgreSQL supports INTERVAL which adds more flexibility and allows you to perform date/time arithmetic directly. It Is a data type that can store time spans. You can add or subtract intervals to dates and timestamps easily.

In MySQL, INTERVAL is not a data type—it is an expression used for date and time calculations but cannot be stored as a data type. It is used within date arithmetic operations and works with functions like DATE_ADD() and DATE_SUB(), or in direct arithmetic expressions. It allows specifying time units (e.g., DAY, HOUR, MONTH).

Here’s an example of how the Interval data type works in PostgreSQL and how it can be handled in MySQL:

PostgreSQL

Using interval, you can add one day to the current timestamp

The output of this will be:

You can also subtract 3 hours from the current timestamp

Wich will show you 3 hours ago

You can also add 2 weeks to the current timestamp:

It also allows you to add values to a specific date too

This, as you can see now gives us 1 month after 2/14:

MySQL:

MySQL does not have a built-in INTERVAL type. Instead, it provides the DATE_ADD() and DATE_SUB() functions to achieve similar results. Here’s how we can do this in MySQL. To add one day to the current timestamp using DATE_ADD():

This will return the following (again, this is mid afternoon on 4/29):

To subtract 3 hours from the current timestamp using DATE_SUB():

To add two weeks to the current timestamp:

87weaTo add one month to a particular date:

Just like in the last section:

For more detailed information on INTERVAL in MySQL, refer to the MySQL 8.4 Reference Manual on Expression

Key Takeaway

Both MySQL and PostgreSQL provide similar core data types: DATE, TIME, TIMESTAMP, and DATETIME (MySQL) or TIMESTAMP WITH/WITHOUT TIME ZONE (PostgreSQL).

However, PostgreSQL includes an additional INTERVAL as a data type, allowing direct arithmetic operations for storing time spans, while MySQL uses INTERVAL only as an expression inside DATE_ADD() and DATE_SUB() functions.

PostgreSQL also has built-in time zone support with TIMESTAMP WITH TIME ZONE, automatically adjusting for session settings while MySQL lacks true time zone-aware timestamps, its TIMESTAMP type stores values in UTC but requires manual conversion when retrieving data.

PostgreSQL supports special values like ‘infinity’, ‘-infinity’, ‘today’, ‘tomorrow’, ‘yesterday’, and ‘allballs’ (00:00:00). MySQL does not support the following but can use maximum and minimum date values as a workaround.

If your application relies heavily on time zones and intervals, PostgreSQL is more robust. If you need simpler date-time storage and don’t require complex time zone logic, MySQL works fine. PostgreSQL is also stricter in enforcing valid date formats, while MySQL allows more flexibility (sometimes at the cost of unexpected behavior).

Article tags

Load comments

About the author

Aisha is a skilled software engineer with a passion for demystifying complex technical concepts. She firmly believes in the power of technology to transform lives and aims to share this knowledge with as many people as possible. With 7 years of experience studying technical concepts and a background in computer science, she has become an expert at breaking down complex ideas and making them accessible to everyone.