diff --git a/README.md b/README.md index a907aa4..ea2dc39 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,18 @@ For example, use `America/New_York` for East Coast Time. ## Macros +### Date Dimension + +#### get_date_dimension ([source](macros/get_date_dimension.sql)) +Returns query to build date dimension from/to specified dates, including a number of useful columns based on each date. +See the [example model](models/examples/dim_date.sql) for details. + +Usage: + +```python +{{ dbt_date.get_date_dimension('2015-01-01', '2022-12-31') }} +``` + ### Date #### convert_timezone ([source](macros/date/convert_timezone.sql)) @@ -50,19 +62,47 @@ Using named parameters, we can also specify the source only and rely on the conf {{ dbt_date.convert_timezone('my_column', source_tz='UTC') }} ``` -#### now ([source](macros/date/now.sql)) -Gets time based on local timezone (specified). Default is "America/Los_Angeles". +#### date_part ([source](macros/date/date_part.sql)) +Extracts date parts from date. Usage: ```python -{{ dbt_date.now() }} +{{ dbt_date.date_part('dayofweek', 'date_day') }} as day_of_week ``` -or, specify a timezone: +#### day_name ([source](macros/date/day_name.sql)) +Extracts name of weekday from date. + +Usage: ```python -{{ dbt_date.now('America/New_York') }} +{{ dbt_date.day_name('date_day', short=true) }} as day_of_week_short_name, +{{ dbt_date.day_name('date_day', short=false) }} as day_of_week_long_name +``` + +#### last_week ([source](macros/date/last_week.sql)) +Convenience function to get the start date of last week + +Wraps: +```python +{{ dbt_date.n_weeks_ago(1, tz) }} +``` + +Usage: +```python +{{ dbt_date.last_week()) }} +``` +```python +{{ dbt_date.last_week(tz='America/New_York)) }} +``` + +#### month_name ([source](macros/date/month_name.sql)) +Extracts name of month from date. + +```python +{{ dbt_date.month_name('date_day', short=true) }} as month_short_name, +{{ dbt_date.month_name('date_day', short=false) }} as month_long_name ``` #### n_days_ago ([source](macros/date/n_days_ago.sql)) @@ -119,6 +159,21 @@ Usage: {{ dbt_date.n_weeks_away(4) }} ``` +#### now ([source](macros/date/now.sql)) +Gets time based on local timezone (specified). Default is "America/Los_Angeles". + +Usage: + +```python +{{ dbt_date.now() }} +``` + +or, specify a timezone: + +```python +{{ dbt_date.now('America/New_York') }} +``` + #### periods_since ([source](macros/date/periods_since.sql)) Returns the number of periods since a specified date. diff --git a/dbt_project.yml b/dbt_project.yml index 50334b3..ff49e7a 100644 --- a/dbt_project.yml +++ b/dbt_project.yml @@ -7,7 +7,7 @@ macro-paths: ["macros"] log-path: "logs" require-dbt-version: ">=0.14.0" - +profile: dev quoting: identifier: false schema: false diff --git a/macros/date/convert_timezone.sql b/macros/date/convert_timezone.sql index 955a042..261cd01 100644 --- a/macros/date/convert_timezone.sql +++ b/macros/date/convert_timezone.sql @@ -3,7 +3,7 @@ {{ adapter_macro('dbt_date.convert_timezone', column, target_tz, source_tz) }} {% endmacro %} -{% macro default__convert_timezone(column, target_tz=None, source_tz=None) %} +{% macro default__convert_timezone(column, target_tz, source_tz) %} {%- if not source_tz -%} cast(convert_timezone('{{ target_tz }}', {{ column }}) as {{ dbt_utils.type_timestamp() }}) {%- else -%} @@ -11,6 +11,6 @@ {%- endif -%} {% endmacro %} -{% macro bigquery__convert_timezone(column, target_tz=None, source_tz=None) %} +{% macro bigquery__convert_timezone(column, target_tz, source_tz=None) %} datetime({{ column }}, '{{ target_tz}}') {% endmacro %} diff --git a/macros/date/date_part.sql b/macros/date/date_part.sql new file mode 100644 index 0000000..5f9b07c --- /dev/null +++ b/macros/date/date_part.sql @@ -0,0 +1,11 @@ +{% macro date_part(datepart, date) -%} + {{ adapter_macro('dbt_date.date_part', datepart, date) }} +{%- endmacro %} + +{% macro default__date_part(datepart, date) -%} + date_part('{{ datepart }}', {{ date }}) +{%- endmacro %} + +{% macro bigquery__date_part(datepart, date) -%} + extract({{ datepart }} from {{ date }}) +{%- endmacro %} diff --git a/macros/date/day_name.sql b/macros/date/day_name.sql new file mode 100644 index 0000000..75e3eb9 --- /dev/null +++ b/macros/date/day_name.sql @@ -0,0 +1,13 @@ +{% macro day_name(date, short=True) -%} + {{ adapter_macro('dbt_date.day_name', date, short) }} +{%- endmacro %} + +{% macro default__day_name(date, short) -%} +{% set f = 'Dy' if short else 'Day' %} + to_char({{ date }}, '{{ f }}') +{%- endmacro %} + +{% macro bigquery__day_name(date, short) -%} +{% set f = '%a' if short else '%A' %} + format_date('{{ f }}', cast({{ date }} as date)) +{%- endmacro %} diff --git a/macros/date/month_name.sql b/macros/date/month_name.sql new file mode 100644 index 0000000..dc5ac50 --- /dev/null +++ b/macros/date/month_name.sql @@ -0,0 +1,13 @@ +{% macro month_name(date, short=True) -%} + {{ adapter_macro('dbt_date.month_name', date, short) }} +{%- endmacro %} + +{% macro default__month_name(date, short) -%} +{% set f = 'MON' if short else 'MONTH' %} + to_char({{ date }}, '{{ f }}') +{%- endmacro %} + +{% macro bigquery__month_name(date, short) -%} +{% set f = '%b' if short else '%B' %} + format_date('{{ f }}', cast({{ date }} as date)) +{%- endmacro %} diff --git a/macros/get_base_dates.sql b/macros/get_base_dates.sql new file mode 100644 index 0000000..bc4dee2 --- /dev/null +++ b/macros/get_base_dates.sql @@ -0,0 +1,15 @@ +{% macro get_base_dates(start_date, end_date) %} +with dates as +( + {{ dbt_utils.date_spine( + datepart="day", + start_date="cast('" ~ start_date ~ "' as datetime)", + end_date="cast('" ~ end_date ~ "' as datetime)", + ) + }} +) +select + d.date_day +from + dates d +{% endmacro %} \ No newline at end of file diff --git a/macros/get_date_dimension.sql b/macros/get_date_dimension.sql new file mode 100644 index 0000000..d25eaa6 --- /dev/null +++ b/macros/get_date_dimension.sql @@ -0,0 +1,63 @@ +{% macro get_date_dimension(start_date, end_date) %} +{{ adapter_macro('dbt_date.get_date_dimension', start_date, end_date) }} +{% endmacro %} + +{% macro default__get_date_dimension(start_date, end_date) %} +with base_dates as ( + {{ dbt_date.get_base_dates(start_date, end_date) }} +), +dates_with_prior_year_dates as ( + select + cast(d.date_day as date) as date_day, + cast({{ dbt_utils.dateadd('year', -1 , 'd.date_day') }} as date) as prior_year_date_day, + cast({{ dbt_utils.dateadd('day', -364 , 'd.date_day') }} as date) as prior_year_over_year_date_day + from + base_dates d +) +select + d.date_day, + cast({{ dbt_utils.dateadd('day', -1 , 'd.date_day') }} as date) as prior_date_day, + cast({{ dbt_utils.dateadd('day', 1 , 'd.date_day') }} as date) as next_date_day, + d.prior_year_date_day as prior_year_date_day, + d.prior_year_over_year_date_day, + cast( + case + when {{ dbt_date.date_part('dayofweek', 'd.date_day') }} = 0 then 7 + else {{ dbt_date.date_part('dayofweek', 'd.date_day') }} + end + as {{ dbt_utils.type_int() }} + ) as day_of_week, + + {{ dbt_date.day_name('d.date_day', short=false) }} as day_of_week_name, + {{ dbt_date.day_name('d.date_day', short=true) }} as day_of_week_name_short, + cast({{ dbt_date.date_part('day', 'd.date_day') }} as {{ dbt_utils.type_int() }}) as day_of_month, + cast({{ dbt_date.date_part('dayofyear', 'd.date_day') }} as {{ dbt_utils.type_int() }}) as day_of_year, + + cast({{ dbt_utils.date_trunc('week', 'd.date_day') }} as date) as week_start_date, + cast({{ dbt_utils.last_day('d.date_day', 'week') }} as date) as week_end_date, + cast({{ dbt_date.date_part('week', 'd.date_day') }} as {{ dbt_utils.type_int() }}) as week_of_year, + cast({{ dbt_utils.date_trunc('week', 'd.prior_year_over_year_date_day') }} as date) as prior_year_week_start_date, + cast({{ dbt_utils.last_day('d.prior_year_over_year_date_day', 'week') }} as date) as prior_year_week_end_date, + cast({{ dbt_date.date_part('week', 'd.prior_year_over_year_date_day') }} as {{ dbt_utils.type_int() }}) as prior_year_week_of_year, + + cast({{ dbt_date.date_part('month', 'd.date_day') }} as {{ dbt_utils.type_int() }}) as month_of_year, + {{ dbt_date.month_name('d.date_day', short=false) }} as month_name, + {{ dbt_date.month_name('d.date_day', short=true) }} as month_name_short, + + cast({{ dbt_utils.date_trunc('month', 'd.date_day') }} as date) as month_start_date, + cast({{ dbt_utils.last_day('d.date_day', 'month') }} as date) as month_end_date, + + cast({{ dbt_utils.date_trunc('month', 'd.prior_year_date_day') }} as date) as prior_year_month_start_date, + cast({{ dbt_utils.last_day('d.prior_year_date_day', 'month') }} as date) as prior_year_month_end_date, + + cast({{ dbt_date.date_part('quarter', 'd.date_day') }} as {{ dbt_utils.type_int() }}) as quarter_of_year, + cast({{ dbt_utils.date_trunc('quarter', 'd.date_day') }} as date) as quarter_start_date, + cast({{ dbt_utils.last_day('d.date_day', 'quarter') }} as date) as quarter_end_date, + + cast({{ dbt_date.date_part('year', 'd.date_day') }} as {{ dbt_utils.type_int() }}) as year_number, + cast({{ dbt_utils.date_trunc('year', 'd.date_day') }} as date) as year_start_date, + cast({{ dbt_utils.last_day('d.date_day', 'year') }} as date) as year_end_date +from + dates_with_prior_year_dates d +order by 1 +{% endmacro %} \ No newline at end of file diff --git a/models/examples/dim_date.sql b/models/examples/dim_date.sql new file mode 100644 index 0000000..75b1969 --- /dev/null +++ b/models/examples/dim_date.sql @@ -0,0 +1,6 @@ +{{ + config( + materialized = 'ephemeral' + ) +}} +{{ dbt_date.get_date_dimension('2015-01-01', '2022-12-31') }} \ No newline at end of file