Date/Time Types

PostgreSQL supports the full set of SQL date and time types.

Table 2-8. Date/Time Types

TypeDescriptionStorageEarliestLatestResolution
datedates only4 bytes4713 BCAD 327671 day
time [(p)] [without time zone]times of day only8 bytes00:00:00.0023:59:59.991 microsecond
time [(p)] with time zonetimes of day only12 bytes00:00:00.00+1223:59:59.99-121 microsecond
timestamp [(p)] [with time zone]date and time with time zone8 bytes4713 ADAD 14650011 microsecond / 14 digits
timestamp [(p)] [without time zone]both date and time8 bytes4713 BCAD 14650011 microsecond / 14 digits
interval [(p)]for time intervals12 bytes-178000000 years178000000 years1 microsecond

time, timestamp, and interval accept an optional precision value p which specifies the number of fractional digits retained in the seconds field. By default, there is no explicit bound on precision. The effective limit of precision is determined by the underlying double precision floating-point number used to store values (in seconds for interval and in seconds since 2000-01-01 for timestamp). The useful range of p is from 0 to about 6 for timestamp, but may be more for interval. The system will accept p ranging from 0 to 13.

Time zones, and time-zone conventions, are influenced by political decisions, not just earth geometry. Time zones around the world became somewhat standardized during the 1900's, but continue to be prone to arbitrary changes. PostgreSQL uses Linux's underlying features to provide output time-zone support, and these systems usually contain information for only the time period 1902 through 2038 (corresponding to the full range of conventional Unix system time). timestamp with time zone and time with time zone will use time zone information only within that year range, and assume that times outside that range are in UTC.

Note

To ensure compatibility with earlier versions of PostgreSQL, we also continue to provide datetime (equivalent to timestamp) and timespan (equivalent to interval). However, support for these types is now restricted to having an implicit translation to timestamp and interval. You should move any old types over when appropriate and you should not use any of these types in new applications.

The types abstime and reltime are lower precision types that are used internally. You are discouraged from using these types.

Date/Time Input

Date and time input is accepted in almost any reasonable format, including ISO 8601, SQL-compatible, traditional PostgreSQL, and others. In the SQL style, the ordering of month and day in date input can be ambiguous, therefore a setting exists to specify how it should be interpreted in ambiguous cases. The command SET DateStyle TO 'US' or SET DateStyle TO 'NonEuropean' specifies the variant "month before day"; the command SET DateStyle TO 'European' sets the variant "day before month". The ISO style is the default, but this default can be changed at compile time or at run time.

PostgreSQL is more flexible in handling date/time than the SQL standard requires.

Remember that any date or time input needs to be enclosed into single quotes, like text strings. Refer to the Section called Constants of Other Types in Chapter 1 for more information. SQL9x requires the following syntax
type [ (p) ] 'value'
where p in the optional precision specification is an integer corresponding to the number of fractional digits in the seconds field. Precision can be specified for time, timestamp, and interval types.

date

The following are possible inputs for the date type.

Date/Time Keywords

Note

The month May has no explicit abbreviation, for obvious reasons.

time [ ( p ) ] [ without time zone ]

This type can be referenced as time and as time without time zone, as per SQL99. The optional precision p should be between 0 and 13, and defaults to the precision of the input time literal.

The following are valid time inputs.

time [ ( p ) ] with time zone

This type is defined by SQL92, but the definition exhibits fundamental deficiencies that render the type nearly useless. In most cases, a combination of date, time, and timestamp should provide a complete range of date/time functionality required by any application.

The optional precision p should be between 0 and 13, and defaults to the precision of the input time literal.

time with time zone accepts all input also legal for the time type, appended with a legal time zone, as follows:

Refer to Table 2-14 for more examples of time zones.

timestamp [ (p) ] with time zone

Valid input for the timestamp type consists of a concatenation of a date and a time, followed by an optional AD or BC, followed by an optional time zone. (See below.) Thus:
1999-01-08 04:05:06 -8:00
is a valid timestamp value that is ISO-compliant. In addition, the wide-spread format:
January 8 04:05:06 1999 PST
is supported.

The optional precision p should be between 0 and 13, and defaults to the precision of the input timestamp literal.

timestamp [ (p) ] without time zone

Valid input for the timestamp without time zone type consists of a concatenation of a date and a time, followed by an optional AD or BC, followed by an optional time zone. (See below.) Thus:
2002-01-08 04:05:06 
is a valid timestamp without time zone value that is ISO-compliant. In addition, the wide-spread format:
January 8 04:05:06 2002 PST
is supported.

The optional precision p should be between 0 and 13, and defaults to the precision of the input timestamp literal.

For timestamp without time zone, any explicit time zone specified in the input is silently swallowed. That is, the resulting date/time value is derived from the explicit date/time fields in the input value, and is not adjusted for time zone.

interval [ ( p ) ]

The interval type can be specified with the following syntax:
  Quantity Unit [Quantity Unit...] [Direction]
@ Quantity Unit [Quantity Unit...] [Direction]
where: Quantity is a number (possibly signed), Unit is second, minute, hour, day, week, month, year, decade, century, millennium, or abbreviations or plurals of these units; Direction can be ago or empty. The at sign (@) is optional noise. The amounts of different units are implicitly added up with appropriate sign accounting.

Quantities of days, hours, minutes, and seconds can be specified without explicit unit markings. For example, '1 12:59:10' is read the same as '1 day 12 hours 59 min 10 sec'.

The optional precision p should be between 0 and 13, and defaults to the precision of the input literal.

Special Values

The following SQL-compatible functions can be used as date or time input for the corresponding data type: CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP. The latter two accept an optional precision specification.

PostgreSQL also supports several special constants for convenience.

'now' is evaluated when the value is first interpreted.

Note

As of PostgreSQL version 7.2, 'current' is no longer supported as a date/time constant. Previously, 'current' was stored as a special value, and evaluated to 'now' only when used in an expression or type conversion.

Date/Time Input Interpretation

The date/time types are all decoded using a common set of routines.

Date/Time Decoding

  1. Break the input string into tokens and categorize each token as a string, time, time zone, or number.

    1. If the token contains a colon (":"), this is a time string.

    2. If the token contains a dash ("-"), slash ("/"), or dot ("."), this is a date string which may have a text month.

    3. If the token is numeric only, then it is either a single field or an ISO 8601 concatenated date (for example, "19990113" for January 13, 1999) or time (for example, 141516 for 14:15:16).

    4. If the token starts with a plus ("+") or minus ("-"), then it is either a time zone or a special field.

  2. If the token is a text string, match up with possible strings.

    1. Do a binary-search table lookup for the token as either a special string (for example, today), day (for example, Thursday), month (for example, January), or optional word (for example, on).

      Set field values and bit mask for fields. For example, set year, month, day for today, and additionally hour, minute, second for now.

    2. If not found, do a similar binary-search table lookup to match the token with a time zone.

    3. If not found, throw an error.

  3. The token is a number or number field.

    1. If there are more than 4 digits, and if no other date fields have been previously read, then interpret as a "concatenated date" (for example, 19990118). 8 and 6 digits are interpreted as year, month, and day, while 7 and 5 digits are interpreted as year, day of year, respectively.

    2. If the token is three digits and a year has already been decoded, then interpret as day of year.

    3. If four or more digits, then interpret as a year.

    4. If in European date mode, and if the day field has not yet been read, and if the value is less than or equal to 31, then interpret as a day.

    5. If the month field has not yet been read, and if the value is less than or equal to 12, then interpret as a month.

    6. If the day field has not yet been read, and if the value is less than or equal to 31, then interpret as a day.

    7. If two digits or four or more digits, then interpret as a year.

    8. Otherwise, throw an error.

  4. If BC has been specified, negate the year and offset by one for internal storage (there is no year zero in the Gregorian calendar, so numerically 1BC becomes year zero).

  5. If BC was not specified, and if the year field was two digits in length, then adjust the year to 4 digits. If the field was less than 70, then add 2000; otherwise, add 1900.

    Tip

    Gregorian years 1-99AD may be entered by using 4 digits with leading zeros (for example, 0099 is 99AD). Previous versions of PostgreSQL accepted years with three digits and with single digits, but as of version 7.0 the rules have been tightened up to reduce the possibility of ambiguity.

Date/Time Output

Output formats can be set to one of the four styles ISO 8601, SQL (Ingres), traditional PostgreSQL, and German, using the SET DateStyle. The default is the ISO format.

The output of the date and time styles is the date or time part, in accordance with the above examples.

The SQL style has European and non-European (US) variants, which determines whether month follows day or vice versa. (See also the Section called Date/Time Input for how this setting affects interpretation of input values.)

interval output looks like the input format, except that units like week or century are converted to years and days. In ISO mode the output looks like:
[ Quantity Units [ ... ] ] [ Days ] Hours:Minutes [ ago ]

There are several ways to affect the appearance of date/time types:

  • The PGDATESTYLE environment variable used by the backend directly on postmaster start-up.

  • The PGDATESTYLE environment variable used by the frontend libpq on session start-up.

  • SET DATESTYLE SQL command.

Date/Time Calculations

PostgreSQL uses Julian dates for all date/time calculations. They have the nice property of correctly predicting/calculating any date more recent than 4713BC to far into the future, using the assumption that the length of the year is 365.2425 days.

The Julian Day was invented by the French scholar Joseph Justus Scaliger (1540-1609) and probably takes its name from the Scaliger's father, the Italian scholar Julius Caesar Scaliger (1484-1558). Astronomers have used the Julian period to assign a unique number to every day since 1 January 4713 BC. This is the so-called Julian Day (JD). JD 0 designates the 24 hours from noon UTC on 1 January 4713 BC to noon UTC on 2 January 4713 BC.

"Julian Day" is different from "Julian Date". The Julian calendar was introduced by Julius Caesar in 45 BC. It was in common use until the 1582, when countries started changing to the Gregorian calendar. In the Julian calendar, the tropical year is approximated as 365 1/4 days = 365.25 days. This gives an error of about 1 day in 128 years. The accumulating calendar error prompted Pope Gregory XIII to reform the calendar in accordance with instructions from the Council of Trent.

In the Gregorian calendar, the tropical year is approximated as 365 + 97 / 400 days = 365.2425 days. Thus it takes approximately 3300 years for the tropical year to shift one day with respect to the Gregorian calendar.

The approximation 365+97/400 is achieved by having 97 leap years every 400 years, using the following rules:

Every year divisible by 4 is a leap year.
However, every year divisible by 100 is not a leap year.
However, every year divisible by 400 is a leap year after all.

So, 1700, 1800, 1900, 2100, and 2200 are not leap years. But 1600, 2000, and 2400 are leap years. By contrast, in the older Julian calendar only years divisible by 4 are leap years.

The Papal bull of February 1582 decreed that 10 days should be dropped from October 1582 so that 15 October should follow immediately after 4 October. This was observed in Italy, Poland, Portugal, and Spain. Other Catholic countries followed shortly after, but Protestant countries were reluctant to change, and the Greek orthodox countries did not change until the start of this century. The reform was observed by Great Britain and Dominions (including what is now the USA) in 1752. Thus 2 Sep 1752 was followed by 14 Sep 1752. This is why UNIX systems have cal produce the following:
% cal 9 1752
   September 1752
 S  M Tu  W Th  F  S
       1  2 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30

Note

The SQL92 standard states that "Within the definition of a datetime literal, the datetime values are constrained by the natural rules for dates and times according to the Gregorian calendar". Dates between 1752-09-03 and 1752-09-13, although eliminated in some countries by Papal fiat, conform to "natural rules" and are hence valid dates.

Different calendars have been developed in various parts of the world, many predating the Gregorian system. For example, the beginnings of the Chinese calendar can be traced back to the 14th century BC. Legend has it that the Emperor Huangdi invented the calendar in 2637 BC. The People's Republic of China uses the Gregorian calendar for civil purposes. Chinese calendar is used for determining festivals.

Time Zones

PostgreSQL endeavors to be compatible with SQL92 definitions for default usage. There are two exceptions:

To address these difficulties, we recommend using date/time types that contain both date and time when using time zones. We recommend not using the SQL92 type TIME WITH TIME ZONE (although it is supported by PostgreSQL for legacy applications and for compatibility with other RDBMS implementations). PostgreSQL assumes local time for any type containing only date or time. Further, time zone support is derived from the underlying operating system time zone capabilities, and hence can handle daylight savings time and other expected behavior.

PostgreSQL obtains time zone support from the underlying operating system for dates between 1902 and 2038 (near the typical date limits for UNIX-style systems). Outside of this range, all dates are assumed to be specified and used in Universal Coordinated Time (UTC).

All dates and times are stored internally in UTC, traditionally known as Greenwich Mean Time (GMT). Times are converted to local time on the database server before being sent to the client frontend, hence by default are in the server time zone.

There are several ways to affect the time zone behavior:

  • The TZ environment variable is used by the backend directly on postmaster start-up as the default time zone.

  • The PGTZ environment variable, if set at the client, is used by libpq to send a SET TIME ZONE command to the backend upon connection.

  • The SQL command SET TIME ZONE sets the time zone for the session.

  • The SQL92 qualifier on
    timestamp AT TIME ZONE 'zone'
    where zone can be specified as a text time zone (for example. 'PST') or as an interval (for example, INTERVAL '-08:00').

Note

If an invalid time zone is specified, the time zone becomes GMT.

Note

If the runtime option AUSTRALIAN_TIMEZONES is set then CST and EST refer to Australian time zones, not American ones.

PostgreSQL must have internal tabular information for time zone decoding, since there is no Unix standard system interface to provide access to general, cross-timezone information. The underlying OS is used to provide time zone information for output.

The following table of time zones recognized by PostgreSQL is organized by time zone offset from UTC, rather than alphabetically; this is intended to facilitate matching local usage with recognized abbreviations for cases where these might differ.

Table 2-18. PostgreSQL Recognized Time Zones

Time ZoneOffset from UTCDescription
NZDT+13:00New Zealand Daylight Time
IDLE+12:00International Date Line, East
NZST+12:00New Zealand Std Time
NZT+12:00New Zealand Time
AESST+11:00 Australia Eastern Summer Std Time
ACSST+10:30 Central Australia Summer Std Time
CADT+10:30 Central Australia Daylight Savings Time
SADT+10:30South Australian Daylight Time
AEST+10:00 Australia Eastern Std Time
EAST+10:00 East Australian Std Time
GST+10:00Guam Std Time, USSR Zone 9
LIGT+10:00Melbourne, Australia
ACST+09:30 Central Australia Std Time
SAST+09:30 South Australia Std Time
CAST+09:30 Central Australia Std Time
AWSST+9:00 Australia Western Summer Std Time
JST+9:00Japan Std Time,USSR Zone 8
KST+9:00Korea Standard Time
WDT+9:00West Australian Daylight Time
MT+8:30Moluccas Time
AWST+8:00 Australia Western Std Time
CCT+8:00 China Coastal Time
WADT+8:00West Australian Daylight Time
WST+8:00West Australian Std Time
JT+7:30Java Time
WAST+7:00West Australian Std Time
IT+3:30Iran Time
BT+3:00 Baghdad Time
EETDST+3:00 Eastern Europe Daylight Savings Time
CETDST+2:00 Central European Daylight Savings Time
EET+2:00 Eastern Europe, USSR Zone 1
FWT+2:00French Winter Time
IST+2:00Israel Std Time
MEST+2:00Middle Europe Summer Time
METDST+2:00Middle Europe Daylight Time
SST+2:00Swedish Summer Time
BST+1:00 British Summer Time
CET+1:00 Central European Time
DNT+1:00 Dansk Normal Tid
FST+1:00 French Summer Time
MET+1:00Middle Europe Time
MEWT+1:00Middle Europe Winter Time
MEZ+1:00Middle Europe Zone
NOR+1:00Norway Standard Time
SET+1:00Seychelles Time
SWT+1:00Swedish Winter Time
WETDST+1:00Western Europe Daylight Savings Time
GMT0:00Greenwich Mean Time
WET0:00Western Europe
WAT-1:00West Africa Time
NDT-2:30Newfoundland Daylight Time
ADT-03:00 Atlantic Daylight Time
NFT-3:30Newfoundland Standard Time
NST-3:30Newfoundland Standard Time
AST-4:00 Atlantic Std Time (Canada)
EDT-4:00 Eastern Daylight Time
CDT-5:00 Central Daylight Time
EST-5:00 Eastern Standard Time
CST-6:00 Central Std Time
MDT-6:00Mountain Daylight Time
MST-7:00Mountain Standard Time
PDT-7:00Pacific Daylight Time
PST-8:00Pacific Std Time
YDT-8:00Yukon Daylight Time
HDT-9:00Hawaii/Alaska Daylight Time
YST-9:00Yukon Standard Time
AHST-10:00 Alaska-Hawaii Std Time
CAT-10:00 Central Alaska Time
NT-11:00Nome Time
IDLW-12:00International Date Line, West