You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

修改dbt_date的convert_timezone宏支持从数据列读取时区参数

问题原因

原宏所有适配器实现中,都对target_tz、source_tz参数硬编码包裹了单引号,默认将传入值作为固定字符串常量(比如'Asia/Shanghai')解析。如果直接传入列名,会被SQL识别为固定字符串,不会读取行级的列值,自然无法实现动态时区转换。

改造方案

改造核心是增加参数标识传入的时区值是固定字符串还是列引用,同时100%兼容原有固定时区的传参习惯,不需要修改历史代码。

改造后完整宏代码

{# 顶层入口宏,新增两个布尔参数标识时区参数是否为列,默认False兼容旧逻辑 #}
{%- macro convert_timezone(column, target_tz=None, source_tz=None, target_tz_is_column=false, source_tz_is_column=false) -%}
{%- set source_tz = "UTC" if not source_tz else source_tz -%}
{%- set target_tz = var("dbt_date:time_zone") if not target_tz else target_tz -%}
{{ adapter.dispatch('convert_timezone', 'dbt_date') (column, target_tz, source_tz, target_tz_is_column, source_tz_is_column) }}
{%- endmacro -%}

{% macro default__convert_timezone(column, target_tz, source_tz, target_tz_is_column, source_tz_is_column) -%}
{# 根据标识决定是否给时区参数加单引号:列引用直接透传,固定字符串补单引号 #}
{%- set tz_src = source_tz if source_tz_is_column else "'" ~ source_tz ~ "'" -%}
{%- set tz_tgt = target_tz if target_tz_is_column else "'" ~ target_tz ~ "'" -%}
{%- if not source_tz -%}
cast(convert_timezone({{ tz_tgt }}, {{ column }}) as {{ dbt_utils.type_timestamp() }})
{%- else -%}
cast(convert_timezone({{ tz_src }}, {{ tz_tgt }}, {{ column }}) as {{ dbt_utils.type_timestamp() }})
{%- endif -%}
{%- endmacro -%}

{%- macro bigquery__convert_timezone(column, target_tz, source_tz, target_tz_is_column, source_tz_is_column) -%}
{%- set tz_tgt = target_tz if target_tz_is_column else "'" ~ target_tz ~ "'" -%}
timestamp(datetime({{ column }}, {{ tz_tgt }}))
{%- endmacro -%}

{%- macro spark__convert_timezone(column, target_tz, source_tz, target_tz_is_column, source_tz_is_column) -%}
{%- set tz_src = source_tz if source_tz_is_column else "'" ~ source_tz ~ "'" -%}
{%- set tz_tgt = target_tz if target_tz_is_column else "'" ~ target_tz ~ "'" -%}
from_utc_timestamp(
        to_utc_timestamp({{ column }}, {{ tz_src }}),
        {{ tz_tgt }}
        )
{%- endmacro -%}

{% macro postgres__convert_timezone(column, target_tz, source_tz, target_tz_is_column, source_tz_is_column) -%}
{%- set tz_src = source_tz if source_tz_is_column else "'" ~ source_tz ~ "'" -%}
{%- set tz_tgt = target_tz if target_tz_is_column else "'" ~ target_tz ~ "'" -%}
{%- if source_tz -%}
cast({{ column }} at time zone {{ tz_src }} at time zone {{ tz_tgt }} as {{ dbt_utils.type_timestamp() }})
{%- else -%}
cast({{ column }} at time zone {{ tz_tgt }} as {{ dbt_utils.type_timestamp() }})
{%- endif -%}
{%- endmacro -%}

{%- macro redshift__convert_timezone(column, target_tz, source_tz, target_tz_is_column, source_tz_is_column) -%}
{{ return(dbt_date.default__convert_timezone(column, target_tz, source_tz, target_tz_is_column, source_tz_is_column)) }}
{%- endmacro -%}

使用方式

  • 原有固定时区传参逻辑完全不变,不需要修改历史代码:
    select
      {{ dbt_date.convert_timezone('created_at', 'Asia/Shanghai', 'UTC') }} as created_at_cst
    from orders
    
  • 传入列作为动态时区参数时,将对应的*_is_column参数设为true即可:
    select
      {{ dbt_date.convert_timezone('created_at', 'user_timezone_col', 'UTC', target_tz_is_column=true) }} as created_at_local
    from user_events
    
    上述代码中user_timezone_col是表内存储每行对应时区的字段,SQL执行时会逐行读取该列值完成时区转换。
注意事项
  • 作为参数传入的时区列,存储值必须是对应数据平台支持的合法IANA时区格式(比如America/Los_Angeles、Asia/Tokyo),否则执行时会抛出时区无效错误。
  • 如果source_tz也需要使用动态列,同理设置source_tz_is_column=true即可。

内容的提问来源于stack exchange,提问作者Dug

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 06:31:11