cancel
Showing results for 
Search instead for 
Did you mean: 
Data Engineering
Join discussions on data engineering best practices, architectures, and optimization strategies within the Databricks Community. Exchange insights and solutions with fellow data engineers.
cancel
Showing results for 
Search instead for 
Did you mean: 

Databricks Unity Catalog Foreign Catalog – Timezone Mismatch with AWS RDS PostgreSQL Affecting Query

vkondepati
Databricks Partner

Hi Databricks Community,

I'm facing a timezone-related issue with a Unity Catalog Foreign Catalog connected to AWS RDS PostgreSQL and would appreciate some guidance on the best way to handle it centrally.

Current Setup:

  • Source: AWS RDS PostgreSQL (configured in Central Time / CST)

  • Databricks: Unity Catalog Foreign Catalog using Lakehouse Federation

  • Foreign Catalog: Appears to interpret timestamps in UTC by default

Issue:

When querying the foreign catalog from Databricks, I've noticed discrepancies in query results involving date filters and aggregations compared to running equivalent queries directly against PostgreSQL.

For example, queries using date ranges, daily aggregations, or timestamp-based filtering return different counts or totals.

Even after setting the session timezone in the Databricks SQL Editor, the results remain inconsistent.

The only workaround I've found is to explicitly convert or cast timestamps to Central Time within each SQL query, which produces the expected results.

However, this isn't scalable, especially when multiple users, dashboards, and downstream applications consume the same foreign catalog.

What I'm Trying to Achieve:

I'd like to handle timezone interpretation or conversion centrally so that consumers get consistent results without needing to remember timezone conversions in every query.

Questions for the Community:

  1. Is there a supported way to configure the timezone at the Unity Catalog Foreign Catalog, connection, or federation level?

  2. Does Lakehouse Federation support passing a timezone setting to the underlying PostgreSQL session?

  3. Would a Databricks SQL warehouse-level configuration help, or does federated query execution behave differently?

  4. Is the recommended approach to use governed views that standardize timestamp conversions, or is there a better native solution?

Ideally, I'd like to preserve the correct source timestamps while ensuring consistent business-date filtering and aggregation across all consumers.

Has anyone encountered this behavior with AWS RDS PostgreSQL and Databricks Lakehouse Federation? How did you resolve it without requiring query-level timezone conversions?

Thanks in advance for your insights!

Tags: Databricks SQL, Unity Catalog, Lakehouse Federation, PostgreSQL, Timezone, AWS RDS, Data Governance

Venkat
4 REPLIES 4

DoTA
Valued Contributor III

Short version: I could not find a documented timezone setting at the foreign catalog, connection or warehouse level, so I would plan on fixing it once in a governed layer rather than per query. Treat the details below as things to verify on your side.

1) Check the Postgres column type first. The federation docs map 'timestamp without time zone' to either TIMESTAMP or TIMESTAMP_NTZ depending on a preferTimestampNTZ flag (default false, so TIMESTAMP), and I did not find a documented way to set that flag on a foreign catalog. A 'timestamp without time zone' column that holds Central wall-clock values will then be read as if it were an instant, which is where date-filter and daily-aggregation drift comes from. A 'timestamptz' column carries an absolute instant and is usually fine once the session time zone is set.

2) Quick diagnosis on one known row: compare SELECT ts, ts::text in Postgres against SELECT ts, CAST(ts AS STRING), typeof(ts) in Databricks (via the foreign catalog). If the wall-clock text is the same but the type is TIMESTAMP, the session time zone setting will not change how the value is read, which would explain why SET TIME ZONE did not help.

3) Fix it centrally with a thin view layer. For each federated table, create a view in a curated schema that does the conversion once, e.g. to_utc_timestamp(ts, 'America/Chicago') as event_ts_utc (treats the stored wall-clock value as Central and gives you the true instant) and CAST(ts AS DATE) as business_date computed from the original wall-clock value. Point dashboards and downstream apps at the views only, and grant no access to the raw foreign catalog. Using the IANA zone name rather than a fixed CST offset keeps daylight saving correct.

4) If the data is queried a lot, a scheduled materialized copy of those views avoids both repeated conversion and repeated hits on RDS, and the business_date column then becomes a plain stored value everyone filters on.

One more check: confirm the RDS parameter group time zone and the session timezone Databricks opens its connection with, since a mismatch there changes how timestamptz values are rendered on the way out. If you can share the column type and one example row from each side, it will be easy to say which case you are in.

balajij8
Esteemed Contributor II
There is no supported configuration at the Foreign Catalog, connection or federation level to override this. The options for PostgreSQL cover host, port, user, password, database and SSL settings - no timezone option exists. Similarly, there is no mechanism to pass remote session variables (Postgres timezone) through the connection or catalog. Because a Postgres timestamp without time zone maps to Spark TimestampType by default, CST values are normalized as UTC. Per-query cast is currently the only direct control during live federation.

Applying a Databricks SQL warehouse-level configuration will only partially help here. While you can set the TIMEZONE parameter globally via SQL configuration parameters or the SQL Warehouses API (defaults to UTC), it only affects session rendering and parsing within that specific warehouse. It does not change the underlying source type mapping, nor does it apply to notebook or job compute, which use spark.sql.session.timeZone instead. Changing the warehouse parameter shifts timestamp interpretation for all consumers on that warehouse, risking breakage at other sides.

To standardize this without forcing users to write manual casts, you can try dynamic views or materialized views that centralize the CST conversion as they preserve the raw source timestamps while enforcing one consistent business-date logic for all consumers. The durable fix is at the source - using timestamptz in RDS instead of a timestamp holding CST wall times. For recurring dashboard and downstream consumption, you can ingest the data into Delta instead of relying on federation and it gives you full control to handle the conversion once and use it without additional work.

data_pulse
Contributor

@vkondepati 

I spent some time reproducing a similar scenario using Neon PostgreSQL connected to Databricks through Unity Catalog Lakehouse Federation and found a few observations that might help explain the timezone/date-aggregation mismatch.

1. PostgreSQL setup and baseline validation

I created a PostgreSQL table containing both timestamp types:

CREATE TABLE timezone_test.events (
    id INTEGER PRIMARY KEY,
    event_description TEXT,
    local_timestamp TIMESTAMP WITHOUT TIME ZONE,
    utc_timestamp TIMESTAMP WITH TIME ZONE
);

 Inserted six test records around America/Chicago midnight boundaries and daylight-saving transitions (March 8 and November 1, 2026).

PostgreSQL correctly handled both UTC and Chicago timezone conversions. For example:

Source timestamp (Chicago) UTC instant Chicago date

2026-03-07 23:302026-03-08 05:30 UTCMar 7
2026-03-08 00:302026-03-08 06:30 UTCMar 8
2026-03-08 03:302026-03-08 08:30 UTCMar 8

This established the source-side baseline before testing federation.

2. Unity Catalog federation observations

I created a PostgreSQL connection and foreign catalog in Databricks, then queried the same six records.

One important observation: both PostgreSQL timestamp types were exposed as Databricks TIMESTAMP by default.

With:

SET TIME ZONE 'America/Chicago';

the PostgreSQLtimestamp without time zone values  were displayed differently in Databricks.

For example:

  • PostgreSQL source: 2026-03-08 00:30:00
  • Databricks federated value: 2026-03-07 18:30:00

The underlying epoch remained unchanged across Databricks UTC and Chicago sessions, confirming that the mapped value was being treated as an instant rather than preserving the original local wall-clock representation.

3. Query-plan investigation - Aggregate pushdown

I compared the same date aggregation against:

  • The Unity Catalog foreign table.
  • A native Delta copy of the same records
  • remote_query() with aggregate/filter pushdown disabled.

Interestingly, the foreign catalog returned different date groupings from the native Delta table.

EXPLAIN FORMATTED confirmed that the foreign-catalog query pushed the date casts and aggregation directly to PostgreSQL:

SELECT
    CAST("local_timestamp" AS DATE),
    CAST("utc_timestamp" AS DATE),
    COUNT(*)
FROM "timezone_test"."events"
GROUP BY
    CAST("local_timestamp" AS DATE),
    CAST("utc_timestamp" AS DATE)

For example, three records were grouped under March 8 for the PostgreSQL local timestamp, whereas Databricks-local evaluation grouped those same records under March 7.

This demonstrates how timestamp mapping combined with aggregate pushdown can produce different business-day groupings.

An additional observation regarding timezone configuration: although SET TIME ZONE 'America/Chicago'  changed the Databricks session's timestamp interpretation, it did not ensure equivalent date-casting semantics when aggregation was pushed to PostgreSQL.

EXPLAIN FORMATTED confirmed that date casts and aggregation executed remotely in PostgreSQL. When I disabled aggregate pushdown through reomte_query(), the date conversions and grouping executed locally in Photon, producing results consistent with the native Delta table.

This suggests that configuring the Databricks SQL session or warehouse timezone alone may not resolve discrepancies involving remote execution.

4. Workarounds tested

A. Disable aggregate pushdown

SELECT
    CAST(local_timestamp AS DATE) AS local_date,
    COUNT(*) AS records
FROM remote_query(
    'neon_postgres_connection',
    database => 'neondb',
    query => 'SELECT local_timestamp FROM timezone_test.events',
    `pushdown.aggregates.enabled` => 'false',
    `pushdown.filters.enabled` => 'false'
)
GROUP BY CAST(local_timestamp AS DATE)

This successfully produced results consistent with the native Delta table.

EXPLAIN FORMATTED also confirmed that Photon performed the date conversion and aggregation locally, rather than pushing them to PostgreSQL.

This is a useful workaround, although it may affect performance and does not necessarily preserve the original PostgreSQL business-date semantics.

B. Preserve source timestamps using TIMESTAMP_NTZ

An explicit CAST(local_timestamp AS TIMESTAMP_NTZ) after federation did not restore the original PostgreSQL wall-clock values.

However, converting the timestamp to text in PostgreSQL first, then casting to TIMESTAMP_NTZ in Databricks, worked:

SELECT
    id,
    CAST(local_ts_text AS TIMESTAMP_NTZ) AS local_ntz
FROM remote_query(
    'neon_postgres_connection',
    database => 'neondb',
    query => '
        SELECT id,
               local_timestamp::text AS local_ts_text
        FROM timezone_test.events
    '
)

This preserved the original PostgreSQL values, including 2026-03-08 00:30:00 without the earlier Chicago display shift.

This workaround is specifically relevant to PostgreSQL timestamp without time zone columns representing local wall-clock values, not timestamptz columns representing actual instants.

5. What about preferTimestampNTZ?

The Databricks PostgreSQL federation documentation mentions preferTimestampNTZ=true for mapping PostgreSQL timezone-naive timestamps to  TimestampNTZType.

I checked foreign catalog connection and couldn't find a way to specify it through the connection.

Conclusion

Based on this controlled reproduction, the interaction between PostgreSQL timestamp type mapping and federated aggregate pushdown can lead to different date-aggregation results, even under the same Databricks session timezone.

The tests demonstrated two practical approaches depending on the intended semantics:

  • Disable aggregate pushdown when Databricks-local timestamp interpretation is required.
  • Preserve PostgreSQL timezone-naive wall-clock values through source-side text conversion followed by TIMESTAMP_NTZ.

Neither should be treated as a universal fix without validating the source timestamp semantics.

Also another community post highlighting the same issue and narrowed down to the same governed view approach to encapsulate remote_query or source-side text-to-TIMESTAMP_NTZ conversion

It would also be valuable for Databricks to clarify timezone handling and document the availability of preferTimestampNTZ for Unity Catalog federation. I tried Altering the foreign catalog to set this property but it complains does not support the following option(s): preferTimestampNTZ.

suranga
Databricks Partner

Agree there's no timezone option on the connection or foreign catalog. Rather than casting in every query, I'd fix it once in a governed view:

CREATE VIEW main.gold.orders_v AS
SELECT
order_id,
to_utc_timestamp(created_at, 'America/Chicago') AS created_at_utc,
CAST(created_at AS TIMESTAMP_NTZ) AS created_at_local
FROM pg_catalog.public.orders;

Grant SELECT on the view only, not the foreign catalog. That way every consumer (SQL, dashboards, Genie) gets the same answer.

Three things to watch:
- Use 'America/Chicago', not a fixed -06:00 offset, so daylight saving time is handled. DST ends Sunday 1 Nov, so the 01:00–02:00 hour that day happens twice and can't be told apart in a timestamp-without-time-zone column.
- Wrapping the column in a function can stop the filter from being pushed down to Postgres. Check EXPLAIN. If the filter isn't pushed down, also filter on the raw column with a one-day buffer.
- If you can change the source, timestamptz in Postgres avoids the whole problem.