Heroku TZ environment variable limits regarding database timestamp synchronization
0 reputation · 06 Oct 2022, 23:54 UTC
0 reputation · 06 Oct 2022, 23:54 UTC
TZ environment variable via application config alters how the runtime interprets relative date-time strings, this configuration is isolated to the dyno environment.
There is a potential synchronization discrepancy between the application's local time interpretation and the configuration of managed data services like PostgreSQL. If the application generates a timestamp based on a regional TZ setting, the database may still store or query that value using its internal UTC logic regardless of the dyno's offset.
How does the TZ variable affect the behavior of database drivers when persisting objects without explicit UTC offsets? Is there a risk of data drift when the application-level timezone is changed while the underlying managed database remains in default UTC mode?
33775 reputation · 07 Oct 2022, 06:01 UTC
The TZ config var on Heroku only changes the operating‑system timezone seen by processes. It does not automatically set the JVM default timezone, nor does it directly tell JDBC drivers to use that zone. Whether the driver interprets java.sql.Timestamp values in the dyno’s local time or in UTC depends on whether the JVM’s default timezone has been set to match TZ.
-Duser.timezone nor call TimeZone.setDefault in code – most Java JDBC drivers (PostgreSQL, MySQL, etc.) will convert timestamps to/from the database using UTC. In this case changing TZ has no effect on stored values and there is no drift risk.TZ (e.g., via -Duser.timezone=$TZ in the Procfile or by calling TimeZone.setDefault at startup), the driver will use that zone when constructing a java.sql.Timestamp without an explicit offset. The raw value sent to PostgreSQL’s TIMESTAMP WITHOUT TIME ZONE column will then reflect the dyno’s local wall‑clock time. Because the database session remains in UTC (Heroku‑managed Postgres defaults to UTC), the stored raw value will be offset from UTC, and a later change to TZ will cause new writes to use a different offset while existing rows keep the old offset – apparent data drift.TZ var only changes the OS timezone; it does not automatically set the JVM default timezone.java.sql.Timestamp values when no explicit timezone is supplied in the connection URL.TIMESTAMP WITHOUT TIME ZONE stores the raw wall‑clock value; if the driver applied the JVM timezone when building the timestamp, that raw value will reflect the dyno’s TZ.TZ after data has been written can cause apparent drift unless the driver or connection is forced to use UTC.-c timezone=UTC or Hibernate property hibernate.jdbc.time_zone=UTC) eliminates drift regardless of the dyno’s TZ.System.out.println(TimeZone.getDefault().getID());
If it prints UTC, the TZ var is not influencing the driver.heroku pg:psql):
SHOW timezone;
Expect UTC for a standard Heroku Postgres.Timestamp ts = new Timestamp(Instant.now().toEpochMilli());
// execute INSERT ... VALUES (?, ...)
Then query the raw column value and compare it to Instant.now().toEpochMilli(). If they match, the driver is using UTC; if they differ by the offset of your TZ, the driver is using the JVM timezone.-Duser.timezone or TimeZone.setDefault) and rely on the driver’s UTC conversion.-c timezone=UTC to the PostgreSQL connection URL) or set Hibernate’s hibernate.jdbc.time_zone=UTC.Do you currently set the JVM default timezone to match the TZ variable (via -Duser.timezone in your Procfile or by calling TimeZone.setDefault at startup)? If the answer is “no”, then the TZ var has no effect on JDBC timestamp handling and drift is not a risk; if “yes”, you must apply one of the UTC‑forcing steps above to prevent drift when TZ changes.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.