The Semantic Layer needs a table with one row per day, the time spine. It uses it for running totals, for comparisons with earlier periods and to fill days that have no data. You build it as an ordinary dbt model and mark it with the time_spine property.

Copy and paste

The model (Snowflake SQL):

-- models/marts/time_spine_daily.sql
{{ config(materialized='table') }}

with days as (
    {{ dbt.date_spine('day', "to_date('2020-01-01')", "dateadd(day, 30, current_date)") }}
)

select cast(date_day as date) as date_day
from days

The configuration:

# models/marts/_time_spine.yml
version: 2
models:
  - name: time_spine_daily
    time_spine:
      standard_granularity_column: date_day
    columns:
      - name: date_day
        granularity: day
dbt build -s time_spine_daily

How it works

Which metrics need it

Simple metrics grouped by month do not read it, but the Semantic Layer expects a daily time spine in every project.

How far it should run

The table stays small, a few thousand rows, so table is the right materialization.

Other databases

dbt.date_spine works on every adapter; only the date expressions change. On DuckDB, for example: "cast('2020-01-01' as date)". For metrics at an hourly grain, add a second spine with an hourly column and granularity: hour.

The model and the configuration were tested with dbt Core 1.12 and MetricFlow 0.213 on DuckDB; the Snowflake date expressions follow the dbt documentation.

References