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.
- As seis funções:
date,time,datetime,julianday,unixepochestrftime. - 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
strftimedevolve texto,%Mnã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 , … )
- Você entrega uma data (texto ISO, como
'2026-10-14', ou um número). - Opcionalmente, um ou mais modificadores, que andam no calendário:
'+7 days','start of month'… Eles são aplicados da esquerda para a direita. - 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ção | Devolve | Exemplo de resposta |
|---|---|---|
date(x) | Só a data, AAAA-MM-DD | 2026-10-14 (a hora foi cortada) |
time(x) | Só a hora, HH:MM:SS | 16:45:10 |
datetime(x) | Data e hora, AAAA-MM-DD HH:MM:SS | 2026-10-14 00:00:00 (sem hora na entrada, vira meia-noite) |
julianday(x) | O dia juliano, um número com fração | 2461327.5 |
unixepoch(x) | Segundos desde 1º/01/1970 (UTC), inteiro | 1791996310 |
strftime(formato, x) | Um texto no formato que você escolher | a 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ódigo | O que vira | Para '2026-10-14 16:45:10' |
|---|---|---|
%Y | Ano (4 dígitos) | 2026 |
%m | Mês (01–12) | 10 |
%d | Dia do mês (01–31) | 14 |
%H | Hora (00–23) | 16 |
%M | Minuto (00–59) — M maiúsculo | 45 |
%S | Segundo (00–59) | 10 |
%w | Dia da semana (0 = domingo … 6 = sábado) | 3 |
%u | Dia da semana (1 = segunda … 7 = domingo) | 3 |
%j | Dia do ano (001–366) | 287 |
%W | Semana do ano (00–53; a semana começa na segunda) | 41 |
%s | Segundos 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;
| Modificador | O 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;
'2026-10-14'com'start of month'→2026-10-01.'+1 month'→2026-11-01.'-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… | Use | Por quê |
|---|---|---|
filtrar um período no WHERE | data >= '…' AND data < '…' | Compara o valor guardado direto: pode usar índice (aula Índices). |
| agrupar por ano/mês | strftime('%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 tempo | modificadores: date(data, '+7 days') | Sabem quantos dias cada mês tem. |
| diferença em dias | julianday(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 brasileiro | strftime('%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.
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.
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'.
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).
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).
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.
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:
| Tarefa | SQLite | PostgreSQL | MySQL | SQL Server |
|---|---|---|---|---|
| Ano | strftime('%Y', d) | EXTRACT(YEAR FROM d) | YEAR(d) ou EXTRACT(YEAR FROM d) | YEAR(d) ou DATEPART(year, d) |
| Somar 7 dias | date(d, '+7 days') | d + INTERVAL '7 days' | DATE_ADD(d, INTERVAL 7 DAY) | DATEADD(day, 7, d) |
| Dias entre datas | julianday(b) - julianday(a) | b - a (com colunas DATE) | DATEDIFF(b, a) | DATEDIFF(day, a, b) |
| Formatar | strftime('%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
| Escrita | Resultado 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:
2. Uma semana depois
Complete o modificador para somar 7 dias:
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),juliandayeunixepoch(números),strftime(o formato que você quiser). strftimeusa códigos:%Yano,%mmês,%ddia,%Hhora,%Mminuto,%wdia da semana (0 = domingo),%jdia do ano. Ele devolve texto: compare com'2025', não com2025.- 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.