SQL Funções de Data

Na aula anterior, Datas e Horas, você viu que o SQLite guarda datas como texto, no formato '2026-10-14'. Agora a Ana quer trabalhar com essas datas: contar os pedidos de cada ano e de cada mês, descobrir em que dia da semana a loja vende mais, calcular o prazo de entrega ("7 dias depois do pedido"), achar o último dia do mês e mostrar a data no formato brasileiro. Para isso o SQLite tem seis funções de data e um jeito esperto de fazer contas com elas: os modificadores.

Você vai aprender
  • As seis funções: date, time, datetime, julianday, unixepoch e strftime.
  • Os códigos do strftime: %Y, %m, %d, %H, %M, %w, %j…
  • Agrupar pedidos por ano, por mês e por dia da semana.
  • Os modificadores: '+7 days', '-1 month', 'start of month', 'weekday 0'…
  • Calcular prazos, atrasos, diferenças em dias e idades.
  • As armadilhas: o strftime devolve texto, %M não é mês, o "31 de janeiro + 1 mês".

Os exemplos rodam de verdade no seu navegador, num SQLite com o banco da Loja Aurora. Todos usam datas fixas (as dos pedidos, ou '2026-10-14', o dia do último pedido da loja), para que o resultado seja o mesmo hoje e daqui a um ano. Com 'now' no lugar, cada função funciona igual, mas com a data de hoje.


A ideia: a calculadora de datas

Pense no calendário de parede da loja. Com ele, a Ana responde "que dia cai 7 dias depois de 14 de outubro?" contando os quadradinhos, "que dia da semana é dia 14?" olhando o topo da coluna, e "qual o último dia de outubro?" olhando o fim do mês. As funções de data são esse calendário dentro do SQL. Todas funcionam do mesmo jeito:

função( data , modificador , modificador , … )
  1. Você entrega uma data (texto ISO, como '2026-10-14', ou um número).
  2. Opcionalmente, um ou mais modificadores, que andam no calendário: '+7 days', 'start of month'… Eles são aplicados da esquerda para a direita.
  3. A função decide em que formato a resposta sai: só a data, só a hora, os dois, um número…

1. As seis funções

SELECT date('2026-10-14 16:45:10')      AS f_date,
       time('2026-10-14 16:45:10')      AS f_time,
       datetime('2026-10-14')           AS f_datetime,
       julianday('2026-10-14')          AS f_julianday,
       unixepoch('2026-10-14 16:45:10') AS f_unixepoch;
FunçãoDevolveExemplo de resposta
date(x)Só a data, AAAA-MM-DD2026-10-14 (a hora foi cortada)
time(x)Só a hora, HH:MM:SS16:45:10
datetime(x)Data e hora, AAAA-MM-DD HH:MM:SS2026-10-14 00:00:00 (sem hora na entrada, vira meia-noite)
julianday(x)O dia juliano, um número com fração2461327.5
unixepoch(x)Segundos desde 1º/01/1970 (UTC), inteiro1791996310
strftime(formato, x)Um texto no formato que você escolhera seção 2 inteira

Dia juliano e tempo Unix foram apresentados em Datas e Horas. As quatro primeiras funções também conferem a data: se não entendem o texto, devolvem NULL. E NULL entra, NULL sai: date(NULL) é NULL.


2. strftime: tirar pedaços da data

strftime vem do inglês string format time, "formatar o tempo como texto". O primeiro argumento é um molde, com códigos que começam por %; cada código é trocado por um pedaço da data:

SELECT strftime('%Y', '2026-10-14')        AS ano,
       strftime('%m', '2026-10-14')        AS mes,
       strftime('%d', '2026-10-14')        AS dia,
       strftime('%w', '2026-10-14')        AS dia_semana,
       strftime('%j', '2026-10-14')        AS dia_do_ano,
       strftime('%d/%m/%Y', '2026-10-14')  AS data_br;
'%Y'
O ano com 4 dígitos (Year): 2026.
'%m'
O mês, 01 a 12 (month, com m minúsculo): 10.
'%d'
O dia do mês, 01 a 31 (day): 14.
'%w'
O dia da semana (weekday), de 0 = domingo a 6 = sábado. 14/10/2026 é uma quarta-feira: 3.
'%j'
O dia do ano, 001 a 366: 14 de outubro é o dia 287.
'%d/%m/%Y'
Vários códigos e texto comum juntos: o que não é código (as barras) é copiado como está. Assim sai a data no formato brasileiro.

Os códigos mais usados

CódigoO que viraPara '2026-10-14 16:45:10'
%YAno (4 dígitos)2026
%mMês (01–12)10
%dDia do mês (01–31)14
%HHora (00–23)16
%MMinuto (00–59) — M maiúsculo45
%SSegundo (00–59)10
%wDia da semana (0 = domingo … 6 = sábado)3
%uDia da semana (1 = segunda … 7 = domingo)3
%jDia do ano (001–366)287
%WSemana do ano (00–53; a semana começa na segunda)41
%sSegundos desde 1970 (como unixepoch)1791996310
%%Um % de verdade%

Na verdade, as outras funções são atalhos do strftime: date(x) é o mesmo que strftime('%Y-%m-%d', x), e time(x), o mesmo que strftime('%H:%M:%S', x).

Agrupar por ano e por mês

Aqui o strftime brilha. Junto com o GROUP BY, ele responde "quantos pedidos por ano?":

SELECT strftime('%Y', data) AS ano,
       COUNT(*) AS pedidos
FROM pedidos
GROUP BY ano;

32 pedidos em 2024, 32 em 2025 e 26 em 2026 (até outubro). Para agrupar por mês, cuidado: strftime('%m', data) sozinho juntaria janeiro de 2024, de 2025 e de 2026 no mesmo grupo. Use ano e mês juntos, '%Y-%m':

SELECT strftime('%Y-%m', data) AS mes,
       COUNT(*) AS pedidos
FROM pedidos
WHERE data >= '2025-07-01' AND data < '2026-01-01'
GROUP BY mes;

O segundo semestre de 2025, mês a mês. Dezembro foi o mais movimentado, com 4 pedidos.

Experimente: a consulta conta os pedidos por ano. A gerente quer os de 2026 mês a mês. Mude o molde do strftime, troque o apelido da coluna para mes e filtre só 2026.

SELECT strftime('%Y', data) AS ano,
       COUNT(*) AS pedidos
FROM pedidos
GROUP BY ano;
SELECT strftime('%Y-%m', data) AS mes,
       COUNT(*) AS pedidos
FROM pedidos
WHERE data >= '2026-01-01'
GROUP BY mes;

O dia da semana

O %w dá um número; com um CASE, ele vira o nome do dia:

SELECT id, data,
       CASE strftime('%w', data)
         WHEN '0' THEN 'domingo'
         WHEN '1' THEN 'segunda'
         WHEN '2' THEN 'terça'
         WHEN '3' THEN 'quarta'
         WHEN '4' THEN 'quinta'
         WHEN '5' THEN 'sexta'
         ELSE 'sábado'
       END AS dia_semana
FROM pedidos
WHERE id >= 86;

Repare nas aspas em WHEN '0': o strftime devolve texto, então a comparação é com o texto '0'. Isso importa muito, e você vai ver por quê em Quando dá errado. E em que dia da semana a loja mais vende?

SELECT strftime('%w', data) AS dia_semana,
       COUNT(*) AS pedidos
FROM pedidos
GROUP BY dia_semana
ORDER BY pedidos DESC;

Terça-feira (2), com 23 pedidos. Domingo (0) teve só 1.


3. Modificadores: andar no calendário

Depois da data, as funções aceitam modificadores: textos que somam, subtraem ou "pulam" para um ponto do calendário.

SELECT date('2026-10-14', '+7 days')        AS mais_7_dias,
       date('2026-10-14', '-1 month')       AS menos_1_mes,
       date('2026-10-14', '+1 year')        AS mais_1_ano,
       date('2026-10-14', 'start of month') AS inicio_do_mes,
       date('2026-10-14', 'start of year')  AS inicio_do_ano,
       date('2026-10-14', 'weekday 0')      AS proximo_domingo;
ModificadorO que faz
'+N days' / '-N days'Soma ou subtrai N dias ('+7 days')
'+N months', '+N years'Soma meses ou anos
'+N hours', '+N minutes', '+N seconds'Soma horas, minutos ou segundos
'start of month'Volta para o dia 1 do mês
'start of year'Volta para 1º de janeiro
'start of day'Volta para a meia-noite do mesmo dia
'weekday N'Avança até o próximo dia da semana N (0 = domingo); se a data já é esse dia, fica nela
'unixepoch'Avisa que o número da entrada é tempo Unix
'localtime' / 'utc'Converte de UTC para a hora local, ou o contrário

O número pode vir no singular ou no plural ('+1 day' e '+1 days' funcionam), mas tem de ser em inglês: '+7 dias' devolve NULL.

Vários modificadores em fila

Os modificadores são aplicados da esquerda para a direita, cada um sobre o resultado do anterior. O clássico é o último dia do mês: vá para o dia 1, avance um mês e volte um dia.

SELECT date('2026-10-14', 'start of month', '+1 month', '-1 day') AS fim_de_outubro,
       date('2024-02-10', 'start of month', '+1 month', '-1 day') AS fim_de_fev_2024,
       datetime('2026-10-14 16:45:10', '+2 hours', '+30 minutes') AS duas_horas_e_meia_depois;
  1. '2026-10-14' com 'start of month' → 2026-10-01.
  2. '+1 month' → 2026-11-01.
  3. '-1 day' → 2026-10-31. Em 2024, ano bissexto, fevereiro termina no dia 29, e a conta acerta sozinha.

O prazo de entrega

A Loja Aurora promete entregar em até 7 dias. Com um modificador, cada pedido ganha o seu prazo, e o WHERE acha quem passou dele:

SELECT id, data, entregue_em,
       date(data, '+7 days') AS prazo
FROM pedidos
WHERE entregue_em > date(data, '+7 days')
  AND data >= '2026-01-01';

Em 2026, cinco pedidos chegaram 1 dia depois do prazo. A comparação entregue_em > date(…) funciona porque as duas datas estão em ISO (aula Datas e Horas).


4. Diferenças: quantos dias entre duas datas

Para contar os dias entre duas datas, subtraia os dias julianos:

SELECT id, data, entregue_em,
       julianday(entregue_em) - julianday(data) AS dias
FROM pedidos
WHERE id <= 4;

SELECT ROUND(AVG(julianday(entregue_em) - julianday(data)), 2) AS media_dias
FROM pedidos;

Em média, a loja leva 6 dias para entregar. A AVG (aula AVG()) ignorou sozinha os 5 pedidos ainda não entregues, porque julianday(NULL) é NULL. O resultado é REAL (5.0), porque o dia juliano tem fração. Em segundos, use unixepoch(b) - unixepoch(a), que dá um inteiro.

A idade de um cliente

Idade não é "dias ÷ 365": os anos bissextos atrapalham. O jeito certo é a conta que fazemos de cabeça: o ano de hoje menos o ano de nascimento, menos 1 se o aniversário deste ano ainda não chegou. Com a data fixa '2026-10-14' no lugar de "hoje":

SELECT nome, nascimento,
       strftime('%Y', '2026-10-14') - strftime('%Y', nascimento)
       - (strftime('%m-%d', '2026-10-14') < strftime('%m-%d', nascimento)) AS idade
FROM clientes
WHERE id IN (1, 3, 9, 13, 20);
strftime('%Y', '2026-10-14') - strftime('%Y', nascimento)
A diferença dos anos. Os dois são texto, mas a subtração (-) transforma texto em número sozinha: '2026' - '1985' dá 41.
strftime('%m-%d', …) < strftime('%m-%d', nascimento)
Compara "mês-dia" de hoje com o do aniversário. Se o aniversário ainda não chegou este ano, a comparação é verdadeira, e verdadeiro no SQLite vale 1.
- ( … )
Tira esse 1. O Tiago (5 de outubro) já fez aniversário: 26 anos. A Ana Beatriz (3 de novembro) ainda não: 27, e não 28.

O Antônio Carlos (id 9) não tem data de nascimento: a idade dele é NULL. Num sistema de verdade, troque '2026-10-14' por 'now' (ou date('now', 'localtime')).


5. Formas diferentes de fazer

Várias perguntas de data têm mais de uma resposta certa. Para "pedidos de outubro de 2026":

SELECT COUNT(*) AS por_intervalo FROM pedidos
WHERE data >= '2026-10-01' AND data < '2026-11-01';

SELECT COUNT(*) AS por_strftime FROM pedidos
WHERE strftime('%Y-%m', data) = '2026-10';

SELECT COUNT(*) AS por_start_of_month FROM pedidos
WHERE date(data, 'start of month') = '2026-10-01';
Para…UsePor quê
filtrar um período no WHEREdata >= '…' AND data < '…'Compara o valor guardado direto: pode usar índice (aula Índices).
agrupar por ano/mêsstrftime('%Y-%m', data)Dá um rótulo legível para cada grupo.
perguntas do calendário (dia da semana, dia do ano)strftime('%w', …), '%j'Não há outro jeito simples.
somar/subtrair tempomodificadores: date(data, '+7 days')Sabem quantos dias cada mês tem.
diferença em diasjulianday(b) - julianday(a)Número com fração; em segundos, unixepoch.
diferença "humana" (anos, meses, dias)timediff(b, a)Devolve texto como +0002-09-09 00:00:00.000: 2 anos, 9 meses e 9 dias.
mostrar no formato brasileirostrftime('%d/%m/%Y', data)Só na hora de exibir; guarde sempre em ISO.

A timediff é nova (chegou no SQLite 3.43) e só existe no SQLite.


Quando dá errado

As funções de data quase nunca dão erro: quando algo está errado, elas devolvem NULL ou um resultado inesperado. Conheça os casos.

Comparou o strftime com um número
SELECT COUNT(*) AS pedidos_2025
FROM pedidos
WHERE strftime('%Y', data) = 2025;

O que aconteceu: zero! E houve 32 pedidos em 2025. O strftime devolve o texto '2025', e no SQLite o texto '2025' não é igual ao número 2025 numa comparação com =. Nenhum erro, só uma resposta errada.

Como consertar: compare com texto, entre aspas: strftime('%Y', data) = '2025'. Ou transforme em número: CAST(strftime('%Y', data) AS INTEGER) = 2025.

Missão: conserte a consulta para ela contar os 32 pedidos de 2025.

SELECT COUNT(*) AS pedidos_2025
FROM pedidos
WHERE strftime('%Y', data) = 2025;
SELECT COUNT(*) AS pedidos_2025
FROM pedidos
WHERE strftime('%Y', data) = '2025';
%M não é mês
SELECT strftime('%d/%M/%Y', '2026-10-14') AS errado,
       strftime('%d/%m/%Y', '2026-10-14') AS certo;

O que aconteceu: 14/00/2026. O %M maiúsculo é o minuto, e a data não tinha hora: minuto zero. O mês é o %m minúsculo.

Como consertar: decore o par: %m = mês, %M = minuto.

Modificador em português, ou com unidade que não existe
SELECT date('2026-10-14', '+7 dias')   AS em_portugues,
       date('2026-10-14', '+1 week')   AS semana,
       date('2026-10-14', '+7')        AS sem_unidade,
       date('2026-10-14', '+7 days')   AS certo;

O que aconteceu: três NULL. O SQLite não entende dias, não tem a unidade week ("semana") e precisa da unidade depois do número. Quando um modificador não é entendido, a função inteira devolve NULL, sem erro.

Como consertar: unidades em inglês: days, months, years, hours, minutes, seconds. Uma semana é '+7 days'.

A data no formato brasileiro
SELECT date('14/10/2026')              AS data_br,
       strftime('%Y', '14/10/2026')    AS ano;

O que aconteceu: NULL nos dois. As funções só entendem o formato ISO ('2026-10-14'), números (dia juliano ou Unix) e 'now'.

Como consertar: guarde e escreva as datas em ISO (aula Datas e Horas).

31 de janeiro + 1 mês
SELECT date('2025-01-31', '+1 month')           AS normal,
       date('2025-01-31', '+1 month', 'floor')  AS com_floor,
       date('2024-02-29', '+1 year')            AS bissexto_mais_1_ano;

O que aconteceu: 31 de janeiro + 1 mês deu 3 de março! O SQLite soma 1 ao mês (fica "31 de fevereiro", que não existe) e empurra os dias que sobram para o mês seguinte. O mesmo acontece com 29 de fevereiro + 1 ano: 1º de março de 2025.

Como consertar: se você quer "o último dia possível do mês seguinte", acrescente o modificador 'floor' ("piso"), que segura no fim do mês: 2025-02-28. Ou trabalhe a partir do 'start of month', como no cálculo do último dia do mês (seção 3).

Usou uma função de outro banco
SELECT YEAR(data) AS ano
FROM pedidos;

O que aconteceu: no such function: YEAR, "não existe a função YEAR". O YEAR() é do MySQL e do SQL Server; no SQLite, use strftime('%Y', data). O mesmo vale para o DATEADD do SQL Server; nesse caso, curiosamente, o erro que aparece é outro, no such column: day, porque o SQLite lê o day de DATEADD(day, 7, data) como nome de coluna antes de reclamar da função.

Como consertar: veja a tabela da nota "Nos outros bancos", abaixo.

Nos outros bancos

Cada banco tem as suas funções de data, e esse é um dos pontos em que eles mais diferem. O strftime e os modificadores são do SQLite. Trechos ilustrativos, que não rodam no SQLite:

TarefaSQLitePostgreSQLMySQLSQL Server
Anostrftime('%Y', d)EXTRACT(YEAR FROM d)YEAR(d) ou EXTRACT(YEAR FROM d)YEAR(d) ou DATEPART(year, d)
Somar 7 diasdate(d, '+7 days')d + INTERVAL '7 days'DATE_ADD(d, INTERVAL 7 DAY)DATEADD(day, 7, d)
Dias entre datasjulianday(b) - julianday(a)b - a (com colunas DATE)DATEDIFF(b, a)DATEDIFF(day, a, b)
Formatarstrftime('%d/%m/%Y', d)to_char(d, 'DD/MM/YYYY')DATE_FORMAT(d, '%d/%m/%Y')FORMAT(d, 'dd/MM/yyyy')

Repare na ordem dos argumentos do DATEDIFF: no MySQL é (fim, início); no SQL Server é (unidade, início, fim). A aula As Diferenças entre os Bancos volta a esse assunto.


Referência rápida

EscritaResultado para '2026-10-14'
date(d)2026-10-14
time('2026-10-14 16:45:10')16:45:10
datetime(d)2026-10-14 00:00:00
julianday(d)2461327.5
unixepoch(d)1791936000
strftime('%Y', d) / '%m' / '%d'2026 / 10 / 14 (texto)
strftime('%Y-%m', d)2026-10 (bom para agrupar por mês)
strftime('%w', d)3 (quarta; 0 = domingo)
strftime('%d/%m/%Y', d)14/10/2026
date(d, '+7 days')2026-10-21
date(d, '-1 month')2026-09-14
date(d, 'start of month')2026-10-01
date(d, 'start of month', '+1 month', '-1 day')2026-10-31 (último dia do mês)
date(d, 'weekday 0')2026-10-18 (o domingo seguinte)
julianday(b) - julianday(a)Dias entre a e b
date('now'), datetime('now', 'localtime')Hoje em UTC / agora no fuso local (muda a cada dia)

Exercícios

1. O ano do pedido

Complete com o código do ano:

SELECT strftime('', data) FROM pedidos;

2. Uma semana depois

Complete o modificador para somar 7 dias:

SELECT date(data, '') FROM pedidos;

3. O que strftime('%w', '2026-10-18') devolve, sabendo que 18/10/2026 é um domingo?

4. Por que WHERE strftime('%Y', data) = 2025 não traz nenhum pedido?

5. Qual expressão dá o último dia do mês de data?

6. O que date('2026-10-14', '+2 semanas') devolve?

Missão final: os pedidos atrasados

Missão: a gerente quer a lista de todos os pedidos entregues depois do prazo de 7 dias. Mostre as colunas id, data, entregue_em e prazo (a data do pedido + 7 dias), só dos pedidos em que a entrega veio depois do prazo.

SELECT id, data, entregue_em
FROM pedidos
WHERE entregue_em IS NOT NULL;
SELECT id, data, entregue_em,
       date(data, '+7 days') AS prazo
FROM pedidos
WHERE entregue_em > date(data, '+7 days');

Foram 17 pedidos, todos com 1 dia de atraso. Os 5 pedidos não entregues não aparecem: NULL > … não é verdadeiro. Outra solução certa usa a diferença em dias: WHERE julianday(entregue_em) - julianday(data) > 7.


Resumo

  • Toda função de data segue o molde função(data, modificador, …): date (só a data), time (só a hora), datetime (as duas), julianday e unixepoch (números), strftime (o formato que você quiser).
  • strftime usa códigos: %Y ano, %m mês, %d dia, %H hora, %M minuto, %w dia da semana (0 = domingo), %j dia do ano. Ele devolve texto: compare com '2025', não com 2025.
  • Para agrupar por mês, use strftime('%Y-%m', data), com o ano junto.
  • Modificadores andam no calendário, da esquerda para a direita: '+7 days', '-1 month', 'start of month', 'weekday 0'. Em inglês; um modificador errado faz a função devolver NULL.
  • Dias entre duas datas: julianday(b) - julianday(a). Último dia do mês: 'start of month', '+1 month', '-1 day'.
  • Cuidado com 31/01 + 1 mês (vira 03/03; use 'floor') e com 'now' em resultados que precisam ser fixos.