Après le dernier article sur les fonctions Window, on va maintenant faire un focus sur les dates. Pour moi, c'est un sujet indispensable à maîtriser car on s'en sert tout le temps, et c'est aussi un des endroits où on peut vite se tromper sans s'en rendre compte.
Dans cet article, on va aller droit au but avec les fonctions qu'on utilise tout le temps : DATE_TRUNC, DATE_PART / EXTRACT, DATEADD, DATEDIFF, etc., avec des cas concrets et du SQL.
USE DATABASE demo_db;
USE SCHEMA demo;
CREATE OR REPLACE TABLE demo_events (
event_id INTEGER,
user_id INTEGER,
event_ts TIMESTAMP_LTZ,
event_type VARCHAR,
amount NUMBER(10,2)
);
INSERT INTO demo_events (event_id, user_id, event_ts, event_type, amount) VALUES
(1, 101, '2025-01-31 23:50:00'::TIMESTAMP_LTZ, 'purchase', 120),
(2, 101, '2025-02-01 00:10:00'::TIMESTAMP_LTZ, 'purchase', 80),
(3, 101, '2025-02-15 14:05:00'::TIMESTAMP_LTZ, 'refund', -20),
(4, 102, '2025-02-03 09:00:00'::TIMESTAMP_LTZ, 'purchase', 60),
(5, 102, '2025-03-01 08:30:00'::TIMESTAMP_LTZ, 'purchase', 90),
(6, 103, '2025-03-15 18:20:00'::TIMESTAMP_LTZ, 'purchase', 200);
DATE vs TIMESTAMP
Je vais peut-être rien t'apprendre, mais sur Snowflake (et ailleurs), c'est simple DATE = uniquement la date, sans heure. TIMESTAMP = date + heure, mais avec plusieurs variantes (dont TIMESTAMP_LTZ, TIMESTAMP_NTZ, TIMESTAMP_TZ).
Le point important (qu'on oublie trop souvent) c'est que avec TIMESTAMP_LTZ, Snowflake stocke en interne en UTC, mais les affichages se font dans le fuseau horaire de la session (TIMEZONE).
Donc deux sessions peuvent voir différemment la même valeur si chacune utilise un fuseau horaire différent.
ALTER SESSION SET TIMEZONE = 'UTC';
SELECT '2025-02-01 00:30:00'::TIMESTAMP_LTZ AS ts_seen_in_utc;
ALTER SESSION SET TIMEZONE = 'Europe/Paris';
SELECT '2025-02-01 00:30:00'::TIMESTAMP_LTZ AS ts_seen_in_paris;
Scénario 1 : Regrouper par mois ou semaine avec DATE_TRUNC
DATE_TRUNC sert à ramener une date ou heure au début d'une période (début du mois, du trimestre, de l'heure, etc...)
ALTER SESSION SET WEEK_START = 1; -- 1 = lundi (dimanche = 7)
SELECT
event_ts,
DATE_TRUNC('week', event_ts) AS week_start,
DATE_TRUNC('month', event_ts) AS month_start,
DATE_TRUNC('quarter', event_ts) AS quarter_start,
DATE_TRUNC('year', event_ts) AS year_start
FROM demo_events
ORDER BY event_ts;

DATE_TRUNC/!\ Attention au jour de début de semaine DATE_TRUNC('week', …)
Quand tu tronques à la semaine, le résultat dépend du paramètre de session WEEK_START et donc ce paramètre influence aussi d'autres fonctions semaine (comme DATEDIFF en weeks).
Scénario 2 : Extraire une partie d'une date (année, mois, jour...) avec DATE_PART ou EXTRACT
Quand tu veux splitter la date sur plusieurs colonnes par exemple (mois = 1..12, année, jour de semaine…), l'utilisation de DATE_PART est très pratique (ou EXTRACT, qui est une alternative).
SELECT
event_ts,
DATE_PART('year', event_ts) AS y,
DATE_PART('month', event_ts) AS m,
DATE_PART('day', event_ts) AS d
FROM demo_events;

DATE_PARTVersion EXTRACT (même idée) :
SELECT
event_ts,
EXTRACT(year FROM event_ts) AS y,
EXTRACT(month FROM event_ts) AS m,
EXTRACT(week FROM event_ts) AS w,
EXTRACT(day FROM event_ts) AS d
FROM demo_events;
week, dayofweek, yearofweek, etc.) peuvent dépendre de WEEK_START et parfois de WEEK_OF_YEAR_POLICY.
EXTRACTScénario 3 : Ajouter ou soustraire du temps avec DATEADD
DATEADD(type, value, expr) permet d'ajouter (ou soustraire si value est négatif) une quantité à une date ou heure.
Nb : Il existe des alias qui font la même chose pour du time et timestamp (TIMEADD, TIMESTAMPADD).
SELECT
event_ts,
DATEADD('day', 7, event_ts) AS plus_7_days,
DATEADD('hour', -2, event_ts) AS minus_2_hours
FROM demo_events;

DATEADDScénario 4 : Calculer un écart entre deux dates avec DATEDIFF
DATEDIFF(<type>, 'exp1', 'exp2') calcule une différence entre deux dates selon une unité (jours, heures, mois etc...).
SELECT
event_id,
user_id,
event_ts,
DATEDIFF('day',
LAG(event_ts) OVER (PARTITION BY user_id ORDER BY event_ts),
event_ts
) AS days_since_prev,
DATEDIFF('week',
LAG(event_ts) OVER (PARTITION BY user_id ORDER BY event_ts),
event_ts
) AS week_since_prev
FROM demo_events
ORDER BY user_id, event_ts;

DATEDIFFScénario 5 : Fin de période ou début de période (LAST_DAY, NEXT_DAY, PREVIOUS_DAY)
Dernier jour d'une période
LAST_DAY(date, part) renvoie le dernier jour de la période demandée (souvent le dernier jour du mois).
SELECT
event_ts::DATE AS d,
LAST_DAY(event_ts::DATE) AS last_day_of_month,
LAST_DAY(event_ts::DATE, 'quarter') AS last_day_of_quarter
FROM demo_events;

LAST_DAY, NEXT_DAY, PREVIOUS_DAY)Prochain lundi / précédent lundi
NEXT_DAY = renvoie le prochain jour de semaine après une date (ex : le prochain lundi).
PREVIOUS_DAY = fait l'inverse et renvoie le dernier jour de semaine avant la date (ex : le lundi précédent).
SELECT
event_ts::DATE AS d,
NEXT_DAY(event_ts::DATE, 'MONDAY') AS next_monday,
PREVIOUS_DAY(event_ts::DATE, 'MONDAY') AS prev_monday
FROM demo_events;

LAST_DAY, NEXT_DAY, PREVIOUS_DAY)Scénario 6 : Parser proprement une date ou un timestamp (TO_DATE, TO_TIMESTAMP, TRY_*)
TO_DATEprend un timestamp (ou une string au format timestamp) et supprimer l'heure.TO_TIMESTAMPconvertit vers un timestamp et mappe versTO_TIMESTAMP_NTZ/LTZ/TZselon le paramètre de sessionTIMESTAMP_TYPE_MAPPING(par défautTIMESTAMP_NTZ).- Et pour éviter de faire planter un chargement quand le format varie (ou quand une valeur est invalide), les fonctions
TRY_*(TRY_TO_DATE, TRY_TO_TIMESTAMP etc...) sont super pratiques car au lieu d'erreur, elles renvoient NULL.
SELECT
raw_value,
TO_DATE('2025.02.01 09:30:00', 'YYYY.MM.DD HH24:MI:SS') AS d,
TO_TIMESTAMP('2025.02.01 09:30:00', 'YYYY.MM.DD HH24:MI:SS') AS ts,
-- Si ça matche, OK. Sinon => NULL
TRY_TO_DATE(raw_value) AS parsed_date_default,
-- Parsing avec format explicite
TRY_TO_DATE(raw_value, 'DD/MM/YYYY') AS parsed_date_fr,
-- Timestamp : default ou avec format explicite
TRY_TO_TIMESTAMP(raw_value) AS parsed_ts_default,
TRY_TO_TIMESTAMP(raw_value, 'YYYY-MM-DD HH24:MI:SS') AS parsed_ts_fmt
FROM (
SELECT column1 AS raw_value
FROM VALUES
('2025-02-03'),
('03/02/2025'),
('2025-02-03 10:15:00'),
('not_a_date'),
(NULL)
);

TO_DATE, TO_TIMESTAMP, TRY_*)Aller plus loin : Formation Snowflake
J'ai regroupé tous mes articles Snowflake dans un parcours complet.
👉 Accéder à la Formation Snowflake
Vous voulez que je vous accompagne sur votre projet data (Snowflake, ingestion, modélisation, performance, coûts, gouvernance) ?
👉 Réserver un appel de 30 minutes

