Every connection anyone has made to a PostgreSQL server since 8.0 has opened with the server announcing, unprompted, the value of integer_datetimes. Since PostgreSQL 10, the value it announces is the only value a PostgreSQL server can be built with, and the current documentation disposes of the entire subject in one sentence: “As of PostgreSQL 10, this is always on.” So this is a post about why a parameter with exactly one possible value is still introduced to every client at the start of every conversation.

integer_datetimes is a read-only preset, a sibling of block_size and in_hot_standby; the context is internal, and nothing you put in postgresql.conf or SET will touch it. It reports the internal representation of the date/time types. For timestamp and timestamptz, that representation is a 64-bit integer counting microseconds from midnight, 2000-01-01, with the other time types using the same scheme for their seconds fields. An int64 of microseconds gives exact microsecond precision over the entire range, which ends late in the year 294276 (the very top and bottom of the integer range are reserved for infinity and -infinity).

The alternative, and the reason the parameter exists, was the original format: a double-precision float counting seconds from the same epoch. Floats and timekeeping are a bad marriage. Precision is microseconds near the epoch and degrades as you move away from it, and most decimal fractions of a second have no exact binary representation at all, so values shifted subtly in arithmetic and equality comparisons between timestamps computed by different routes were a gamble.

The integer format arrived as a compile-time option in the 7.x era, became the build default in 8.4 (listed first among that release’s incompatibilities), and became the only option in PostgreSQL 10, when Tom Lane removed --disable-integer-datetimes. The commit message is a small classic of the genre: the replication protocol defines its timestamp fields as integers, the code translating them on float builds turned out to contain multiple bugs, with internal floats leaking into wire fields, and the fact that nobody had noticed demonstrated that nobody was running float builds. Rather than maintain a wall between the two formats for the benefit of no one, the project shot the option. It died of neglect, formally.

Which leaves the question of why every client is still told about it. The answer is the binary wire format. In text mode, a timestamp is a string, and nobody cares how the server stores it. In binary mode, the eight bytes on the wire are the storage format: an int64 of microseconds, or, once upon a time, a float8 of seconds. A client that guesses wrong gets no error; it gets a confidently wrong timestamp. (A 2010 report on pgsql-general involved an application writing float-format binary timestamps into an 8.4 server, yielding dates around the year 15000.) So the server declares integer_datetimes in the ParameterStatus messages it sends at connection start, before the client has said anything, and binary-capable drivers read it to select their decoder. This is the same reporting machinery in_hot_standby rides, except that this value can never change after startup; the protocol documentation shrugs and calls it a pseudo-parameter.

The one place the old format can still bite is pg_upgrade, which compares the “Date/time type storage” line of pg_controldata between the two clusters and refuses to run on a mismatch. Every cluster you can build today is integer, so a surviving float-built cluster (hand-compiled before 8.4, or built with the escape hatch before 10) cannot be pg_upgraded at all. Dump and restore is the only exit, and if you have one of these in a closet, that fact is worth knowing before the closet becomes urgent.

There is nothing here to set and nothing to monitor. integer_datetimes persists because the protocol promised it in 8.0 and drivers still check it before decoding a binary timestamp; removing it would break working clients to save a few bytes of startup traffic. So every connection still opens with the server solemnly reporting the outcome of an argument that ended in 2017. The answer is on. It was always going to be on.