Вопрос о конвертации времени из формата TDateTime, используемого в Delphi, в Unix-эпоху является актуальным для разработчиков, работающих с несколькими языками программирования. В Delphi, TDateTime представлен как двойное число, где целочисленная часть указывает количество дней, прошедших с 30 декабря 1899 года, а дробная часть - время суток в миллисекундах.
Проблема
Необходимо корректно конвертировать значение TDateTime в Unix-эпоху. Существующий код для конвертации приводит к неверным результатам, например, для значения TDateTime37838.001388888886 (5 августа 2003 года, 02:00) код возвращает Unix-время 1060041719, что соответствует 5 августа 2003 года, 00:01:59, что явно неверно.
Решение
В компонентах RTL Delphi/C++Builder есть функция DateTimeToUnix(), предназначенная именно для такой конвертации. Важно понимать, что TDateTime имеет точность до миллисекунд, и для корректной конвертации необходимо учитывать это. В современных версиях RTL используется алгоритм, который избегает потери точности, связанной с плавающей точкой, и вместо этого использует TTimeStamp.
Алгоритм конвертации
Для конвертации TDateTime в Unix-время можно использовать следующий алгоритм, адаптированный для C++:
#include <cmath>
#include <cstdint>
#define HoursPerDay 24
#define MinsPerHour 60
#define SecsPerMin 60
#define MSecsPerSec 1000
#define MinsPerDay (HoursPerDay * MinsPerHour)
#define SecsPerDay (MinsPerDay * SecsPerMin)
#define SecsPerHour (SecsPerMin * MinsPerHour)
#define MSecsPerDay (SecsPerDay * MSecsPerSec)
#define UnixDateDelta 25569 // Количество дней между базой TDateTime (30.12.1899) и Unix time_t (1.1.1970)
#define DateDelta 693594 // Количество дней между 1.1.0001 и 30.12.1899
const float FMSecsPerDay = MSecsPerDay;
const int IMSecsPerDay = MSecsPerDay;
struct TTimeStamp
{
int Time; // Количество миллисекунд с начала суток
int Date; // Количество дней, прошедших с 1.1.0001
};
typedef double TDateTime;
TTimeStamp DateTimeToTimeStamp(TDateTime DateTime)
{
__int64 LTemp = std::round(DateTime * FMSecsPerDay);
__int64 LTemp2 = LTemp / IMSecsPerDay;
TTimeStamp Result;
Result.Date = DateDelta + LTemp2;
Result.Time = std::abs(LTemp) % IMSecsPerDay;
return Result;
}
__int64 DateTimeToMilliseconds(const TDateTime ADateTime)
{
TTimeStamp LTimeStamp = DateTimeToTimeStamp(ADateTime);
return (__int64(LTimeStamp.Date) * MSecsPerDay) + LTimeStamp.Time;
}
__int64 SecondsBetween(const TDateTime ANow, const TDateTime AThen)
{
return std::abs(DateTimeToMilliseconds(ANow) - DateTimeToMilliseconds(AThen)) / MSecsPerSec;
}
__int64 DateTimeToUnix(const TDateTime AValue)
{
__int64 Result = SecondsBetween(UnixDateDelta, AValue);
if (AValue < UnixDateDelta)
Result = -Result;
return Result;
}
Альтернативный способ
Использование библиотеки Howard Hinnant для C++20, которая предоставляет удобные средства для работы с датой и временем:
#include "date/date.h"
#include <iostream>
date::sys_seconds convert(double d)
{
using namespace date;
using namespace std::chrono;
using ddays = duration<double, days::period>;
return round<seconds>(sys_days{1899_y/12/30} + ddays{d});
}
int main()
{
using namespace date;
using namespace std;
using namespace std::chrono;
auto tp = convert(37838.001388888886);
cout << tp << " = " << (tp-sys_seconds{})/1s << '\n';
}
Вывод программы будет следующим:
2003-08-05 00:02:00 = 1060041720
Обратите внимание, что для использования данной библиотеки необходимо добавить определение NOMINMAX перед подключением заголовочных файлов, чтобы избежать конфликтов с макросами, определенными в Windows.h.
Используя предложенные алгоритмы, можно корректно конвертировать значения TDateTime в Unix-время для дальнейшего использования в C++ коде.
Описание алгоритма конвертации даты и времени из формата `TDateTime`, используемого в Delphi, в Unix-эпоху для программирования на C++.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.