2.11. Tipos de Dados para Data e Hora

2.11.1. Representação

SELECT TIMESTAMP WITH TIME ZONE '2022-10-11 06:15:00';
SELECT TIMESTAMP '2022-10-11 06:15:00';
SELECT TIMESTAMP WITH TIME ZONE '2022-10-11 06:15:00-03';
SELECT TIMESTAMP '2022-10-11 06:15:00-03';

Nota

Veja que o PostgreSQL descarta o time zone nesse último caso.

SELECT TIMESTAMP WITH TIME ZONE 'epoch';
SELECT DATE 'tomorrow';
SELECT TIME 'now';

2.11.2. Extraindo Partes de um tipo temporal

Sintaxe:

EXTRACT(field FROM source)

Exemplos:

SELECT EXTRACT(DAY FROM TIMESTAMP '2022-10-10 06:15:00');
SELECT EXTRACT(DOY FROM TIMESTAMP '2022-10-10 06:15:00');
SELECT EXTRACT(MONTH FROM TIMESTAMP '2022-10-10 06:15:00');
SELECT EXTRACT(YEAR FROM TIMESTAMP '2022-10-10 06:15:00');

2.11.3. Operadores

Adicionando um número de dias a uma data:

SELECT DATE '2022-10-10' + 30;

Calculando o número de dias entre duas datas:

SELECT DATE '2022-11-09' - DATE '2022-10-10';

Intervalo de tempo entre dois instantes de tempo:

SELECT TIMESTAMP '2022-11-09 06:15' - TIMESTAMP '2022-10-10 06:10';

Dica

O padrão SQL define algumas funções para fornecer valores específicos de data e hora:

SELECT CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP, LOCALTIME, LOCALTIMESTAMP;

Dica

Para converter um valor do tipo TIMESTAMP para TIME faça:

SELECT (TIMESTAMP '2022-11-09 06:15')::time;

O operador :: permite fazer a coerção explícita de tipos.

2.11.4. Exercícios

Exercício 1. Crie a tabela de focos abaixo com uma coluna do tipo data e hora sem fuso horário.

CREATE TABLE foco_v2
(
    latitude            DOUBLE PRECISION,
    longitude           DOUBLE PRECISION,
    num_dias_sem_chuva  INTEGER,
    frp                 NUMERIC(4,1),
    data                TIMESTAMP WITHOUT TIME ZONE,
    satelite            VARCHAR(30)
);

Vamos incluir alguns valores na tabela acima:

INSERT INTO foco_v2 VALUES(-9.41792, -51.65176, 30, 89, '2022-09-01 16:30:00', 'AQUA'),
                          (-6.63565,-51.73956, 28, 103, '2022-08-30 14:30:00', 'TERRA'),
                          (-5.40959,-51.04929, 31, 31.9, '2022-09-03 08:30:00', 'NOAA-20'),
                          (-7.04535,-52.24566, 27, 44.2, '2022-09-03 07:30:00', 'GOES-16'),
                          (-5.67382,-51.89861, 32, 97.8, '2022-08-03 17:30:00', 'GOES-16');

Exercício 2. Apresente os dados de focos com duas colunas extras, uma apresentando o ano e outra o mês.

Solução:
SELECT *, extract(YEAR FROM data) AS ano, extract(MONTH FROM data) AS mes
  FROM foco_v2;

Saída:

 latitude | longitude | num_dias_sem_chuva |  frp  |        data         | satelite | ano  | mes
----------+-----------+--------------------+-------+---------------------+----------+------+-----
 -9.41792 | -51.65176 |                 30 |  89.0 | 2022-09-01 16:30:00 | AQUA     | 2022 |   9
 -6.63565 | -51.73956 |                 28 | 103.0 | 2022-08-30 14:30:00 | TERRA    | 2022 |   8
 -5.40959 | -51.04929 |                 31 |  31.9 | 2022-09-03 08:30:00 | NOAA-20  | 2022 |   9
 -7.04535 | -52.24566 |                 27 |  44.2 | 2022-09-03 07:30:00 | GOES-16  | 2022 |   9
 -5.67382 | -51.89861 |                 32 |  97.8 | 2022-08-03 17:30:00 | GOES-16  | 2022 |   8
(5 rows)

Exercício 3. Quais os focos detectados no período da manhã?

Solução:

Para solução desse exercício, você deverá extrair apenas a parte da hora do dia da coluna data que é do tipo TIMESTAMP. Para isso, devemos realizar uma conversão de tipos, que pode ser feita usando a notação específica do PostgreSQL com o operador ::tipo ou a função padrão SQL cast.

Na primeira solução, usaremos o operador ::tipo:

SELECT *
  FROM foco_v2
 WHERE (data::time >= '06:00'::time) AND (data::time < '12:00'::time);

Saída:

 latitude | longitude | num_dias_sem_chuva | frp  |        data         | satelite
----------+-----------+--------------------+------+---------------------+----------
 -5.40959 | -51.04929 |                 31 | 31.9 | 2022-09-03 08:30:00 | NOAA-20
 -7.04535 | -52.24566 |                 27 | 44.2 | 2022-09-03 07:30:00 | GOES-16
(2 rows)

Na segunda solução, usaremos a função SQL cast(expressão AS tipo):

SELECT *
  FROM foco_v2
 WHERE ( cast(data AS TIME) >= TIME '06:00' ) AND ( cast(data AS TIME) < TIME '12:00' );

Saída:

 latitude | longitude | num_dias_sem_chuva | frp  |        data         | satelite
----------+-----------+--------------------+------+---------------------+----------
 -5.40959 | -51.04929 |                 31 | 31.9 | 2022-09-03 08:30:00 | NOAA-20
 -7.04535 | -52.24566 |                 27 | 44.2 | 2022-09-03 07:30:00 | GOES-16
(2 rows)

Exercício 4. Quais os focos detectados no período da tarde?

Solução:
SELECT *
  FROM foco_v2
 WHERE ( cast(data AS TIME) >= TIME '12:00' ) AND  ( cast(data AS TIME) < TIME '18:00' );

Saída:

 latitude | longitude | num_dias_sem_chuva |  frp  |        data         | satelite
----------+-----------+--------------------+-------+---------------------+----------
 -9.41792 | -51.65176 |                 30 |  89.0 | 2022-09-01 16:30:00 | AQUA
 -6.63565 | -51.73956 |                 28 | 103.0 | 2022-08-30 14:30:00 | TERRA
 -5.67382 | -51.89861 |                 32 |  97.8 | 2022-08-03 17:30:00 | GOES-16
(3 rows)

Exercício 5. Quais os focos detectados no mês de agosto?

Solução:
SELECT *
  FROM foco_v2
 WHERE extract(MONTH FROM data) = 8;

Saída:

 latitude | longitude | num_dias_sem_chuva |  frp  |        data         | satelite
----------+-----------+--------------------+-------+---------------------+----------
 -6.63565 | -51.73956 |                 28 | 103.0 | 2022-08-30 14:30:00 | TERRA
 -5.67382 | -51.89861 |                 32 |  97.8 | 2022-08-03 17:30:00 | GOES-16
(2 rows)

Exercício 6. Qual o número de focos reportado por cada satélite?

Solução:
  SELECT satelite, COUNT(*) AS num_focos
    FROM foco_v2
GROUP BY satelite
ORDER BY num_focos DESC;

Saída:

 satelite | num_focos
----------+-----------
 GOES-16  |         2
 NOAA-20  |         1
 AQUA     |         1
 TERRA    |         1
(4 rows)

Exercício 7. Qual o número de focos reportado mensalmente?

Solução:

Vamos apresentar duas soluções para esse exercício. A primeira solução irá extrair a parte do mês do campo data associado ao foco para realizar um agrupamento e então contar o total de focos no grupo:

  SELECT extract(MONTH FROM data) AS mes, COUNT(*) AS num_focos
    FROM foco_v2
GROUP BY extract(MONTH FROM data)
ORDER BY num_focos DESC;

Saída:

 mes | num_focos
-----+-----------
   9 |         3
   8 |         2
(2 rows)

A segunda solução irá utilizar a função to_char para extrair o mês na forma textual, para então realizar o agrupamento dos focos pelo mês e depois realizar a contagem de linhas de cada grupo:

  SELECT to_char(data, 'month') AS mes, COUNT(*) AS num_focos
    FROM foco_v2
GROUP BY to_char(data, 'month')
ORDER BY num_focos DESC;

Saída:

    mes    | num_focos
-----------+-----------
 september |         3
 august    |         2
(2 rows)

Dica

Veja o resultado da consulta abaixo:

SELECT latitude, longitude, data, to_char(data, 'Month'), to_char(data, 'MONTH'), to_char(data, 'month')
  FROM foco_v2;

Dica

Para uma lista completa das funções e operações com tipos associados a data, hora e intervalos, consulte o manual do PostgreSQL na Seção 9.9. Date/Time Functions and Operators. Veja também as funções de formatção de tipos na Seção 9.8. Data Type Formatting Functions.