Как обрабатывать дату, записанную в формате числа с плавающей точкой?

Каждый раз, когда я пытаюсь вручную писать SQL-запросы, у меня возникают трудности с работой с датами и временем. Я знаю, что в базе данных существуют разные типы полей для этого: DATETIME и TIMESTAMP, а также DATE и TIME - их достаточно много. <br/><br/> У меня меньше опыта с PostgreSQL, чем с MySQL, и недавно я столкнулся с базой данных PostgreSQL, где для хранения даты и времени используется поле типа с плавающей точкой double precision. Я уже несколько дней пытаюсь понять, как с этим работать. Например, если я вывожу содержимое поля, то получаю число 45672.38114849537. <br/> Я обратился к нейросети за помощью, и она посоветовала использовать функцию to_timestamp, в результате чего я выполнил запрос: <br/> SELECT to_timestamp(start_time) FROM TReportAppUsage; <br/> Однако вывод оказался 1970-01-01 12:41:13.617443+00, хотя на самом деле это должно быть в 2025 году. При этом в описании структуры базы данных указано, что отсчет даты начинается не с 01.01.1970 (эпоха Unix), а с 30.12.1899. <br/> Могли бы вы помочь разобраться в этих SQL-вопросах и объяснить, как корректно вывести дату и время из этого поля? Кроме того, что нужно писать в условии WHERE для фильтрации по такой дате?
  • 28 февраля 2025 г. 10:54
Ответы на вопрос 2
Ваша ситуация действительно требует немного специфического подхода к работе с датами. Основная проблема здесь заключается в том, что значение, которое вы видите в вашем поле (число с плавающей точкой), вероятно, представляет собой количество дней с определенной начальной даты.

Судя по вашему описанию, начальная дата в данном случае — это 30 декабря 1899 года. В PostgreSQL вы можете преобразовать это значение в корректный формат даты и времени с помощью операций с числовыми значениями.

### 1. Преобразование числа с плавающей точкой в дату

Для преобразования числа с плавающей точкой (days) в дату, вы можете использовать функцию `to_timestamp`, но сначала надо привести число дней к точному значению, добавив его к начальной дате с помощью типа `interval`. 

Вот пример SQL-запроса, который делает это:

```sql
SELECT 
    '1899-12-30'::date + (start_time * interval '1 day') AS converted_date
FROM 
    TReportAppUsage;
```

В этом запросе мы добавляем количество дней (умноженное на интервал в 1 день) к дате `1899-12-30`.

### 2. Фильтрация по дате в условии WHERE

Если вы хотите фильтровать записи по дате, вы также выполните преобразование в условии. Например, если вы хотите получить все записи с датой после 1 января 2025 года, вы можете написать следующий запрос:

```sql
SELECT *
FROM 
    TReportAppUsage
WHERE 
    '1899-12-30'::date + (start_time * interval '1 day') > '2025-01-01';
```

Таким образом, вы гарантируете, что SQL-сервер правильно интерпретирует и сравнивает даты.

### Итоги

- Чтобы преобразовать дни с плавающей точкой в дату, добавьте их к дате 30 декабря 1899 года, используя интервал в 1 день.
- Для фильтрации по дате повторите преобразование в условии `WHERE`.

Этот подход должен помочь вам правильно обрабатывать даты в PostgreSQL с использованием числа с плавающей точкой.
Этот способ хранения даты тянется ещё с Lotus 1-2-3. Когда Microsoft добавляла дату/время в Excel, то для совместимости использовали тот же формат. 
Отсчёт ведётся от 30.12.1899. Целая часть - количество дней, прошедших с этой даты, дробная часть - время. Если дробную часть умножить на 24*60*60, то получим количество секунд с начала дня.
Встроенных функция для преобразования в PostgreSQL нет, так что, видимо, это прямой перенос из чего-то типа MSAccess.
45672.38114849537 соответствует 2025-01-15 09:08:51.230
Получить можно, например, так:
SELECT
  date_lotus,
  '1889-12-30'::date + MAKE_INTERVAL(days => date_lotus::int, secs => ((date_lotus - date_lotus::int) * 24*60*60)::int) AS datetime
  FROM test;

| date_lotus        | datetime            |
| ----------------- | ------------------- |
| 45672.38114849537 | 2015-01-16 09:08:51 |
Похожие вопросы