Почему postgres показывает два разных формата для одного и того же значения интервала?

Я помогал с этим вопросом попытка изменить формат интервала.

from '01 day 22:10:37'  to  '46:10:37'

Я даю решение с манипуляциями со строками. Но потом я обнаружил, что postgres может показывать один и тот же интервал в двух разных форматах.

SELECT '2016-01-27 08:51:02'::timestamp - '2016-01-25 10:40:25'::timestamp end_date,
       '46:10:37'::interval interval_date;

Забавная вещь. Существует функция, выполняющая обратный процесс.

 justify_hours('46:10:37'::interval) --> '1 day 22:10:37'

Поэтому мне интересно, есть ли прямой способ решить эту проблему. И почему одно и то же значение интервала имеет два разных результата.

Вывод pgAdmin:

введите здесь описание изображения


person Juan Carlos Oropeza    schedule 29.06.2016    source источник


Ответы (2)


Когда интервал представляет собой разницу между двумя временными метками, он всегда выравнивается до часов (т. е. имеет стандартный формат). Примеры:

select
    '2015-01-01 13:0:0'::timestamp - '2014-01-01 23:0:0'::timestamp, --> 364 days 14:00:00
    '2015-01-01 13:0:0'::timestamp - '2014-01-01 03:0:0'::timestamp, --> 365 days 10:00:00
    '2015-01-01 13:0:0'::timestamp - '2015-01-01 03:0:0'::timestamp; --> 10:00:00

Вычисления на интервалах выполняются отдельно для части даты и части времени, поэтому они могут привести к странным форматам. Примеры:

select 
    '2 day 1:00:00'::interval- '1 day 2:00:00'::interval,    --> 1 day -01:00:00 (!!)
    '2 day 100:00:00'::interval+ '1 day 60:00:00'::interval, --> 3 days 160:00:00
    '2 day 100:00:00'::interval- '2 day 60:00:00'::interval; --> 40:00:00

Для таких случаев разработчики Postgres предусмотрели соответствующую функцию стандартизации формата:

select 
    justify_hours('1 day -01:00:00'),  --> 23:00:00
    justify_hours('3 days 160:00:00'), --> 9 days 16:00:00
    justify_hours('40:00:00');         --> 1 day 16:00:00

Однако они не думали, что потребуется обратная операция. В этом ответе я предложил функцию для преобразования части даты интервала в часы. Я думаю, что это может быть (с небольшими изменениями) какая-то обратная функция для justify_hours():

create or replace function unjustify_hours(interval)
returns interval language sql as $$
    select format('%s:%s',
        (extract (epoch from $1) / 3600)::int,
        to_char($1, 'mi:ss'))::interval;
$$;

select 
    unjustify_hours('23:00:00'),        --> 23:00:00
    unjustify_hours('9 days 16:00:00'), --> 232:00:00
    unjustify_hours('1 day 16:00:00');  --> 40:00:00

Функция to_char(interval, text) тут не поможет, т.к.

select 
    to_char(interval '23:00:00', 'hh24:mi:ss'),        --> 23:00:00
    to_char(interval '9 days 16:00:00', 'hh24:mi:ss'), --> 16:00:00 (!)
    to_char(interval '1 day 16:00:00',  'hh24:mi:ss'); --> 16:00:00 (!)

Обратите внимание, что интервал может быть правильно отформатирован разными способами:

select 
    justify_hours('100:00:00'),        --> 4 days 04:00:00
    justify_hours('1 days 76:00:00'),  --> 4 days 04:00:00
    justify_hours('2 days 52:00:00'),  --> 4 days 04:00:00
    justify_hours('5 days -20:00:00'); --> 4 days 04:00:00

Согласно документации:

В соответствии со стандартом SQL все поля значения интервала должны иметь один и тот же знак, поэтому ко всем полям применяется начальный отрицательный знак; например, отрицательный знак в литеральном интервале '-1 2:03:04' применяется как к дням, так и к часам/минутам/секундам. PostgreSQL позволяет полям иметь разные знаки и традиционно рассматривает каждое поле в текстовом представлении как независимо подписанное, так что в этом примере час/минута/секунда считается положительной. Если для IntervalStyle задано значение sql_standard, то считается, что ведущий знак применяется ко всем полям (но только в том случае, если не отображаются дополнительные знаки). В противном случае используется традиционная интерпретация PostgreSQL. Во избежание двусмысленности рекомендуется прикреплять явный знак к каждому полю, если какое-либо поле отрицательное.

а также

Внутренние значения интервалов хранятся в виде месяцев, дней и секунд. Это делается потому, что количество дней в месяце варьируется, и в сутках может быть 23 или 25 часов, если задействована корректировка летнего времени. Поля месяцев и дней представляют собой целые числа, а поле секунд может хранить дроби. Поскольку интервалы обычно создаются из строк-констант или вычитания временных меток, в большинстве случаев этот метод хранения работает хорошо. Функции justify_days и justify_hours доступны для настройки дней и часов, которые выходят за пределы их обычных диапазонов.

person klin    schedule 29.06.2016

Из https://www.postgresql.org/docs/9.5/static/functions-formatting.html

to_char(interval) форматирует HH и HH12, как показано на 12-часовом формате, т. е. ноль часов и 36 часов выводят как 12, а HH24 выводит полное значение часа, которое может превышать 23 для интервалов.

Из https://www.postgresql.org/docs/8.2/static/functions-formatting.html

to_char(interval) форматирует HH и HH12 как часы одного дня, а HH24 может выводить часы, превышающие один день, например. >24.

Я предполагаю, что первый использует неявный формат на основе результата вычитания, а второй использует явный формат на основе ввода.

person dalle    schedule 29.06.2016
comment
Но если вы проверите вывод pgAdmin, результатом будет interval, а не string. И HH12 означает 12:00/pm, поэтому диапазон от 00-12 до HH24 означает 00-23 - person Juan Carlos Oropeza; 29.06.2016