Skip to content

Commit 2202e14

Browse files
committed
fix!: use UNIX epoch differences for portable date and timestamp conversion
1 parent e35f303 commit 2202e14

File tree

1 file changed

+22
-40
lines changed

1 file changed

+22
-40
lines changed

src/duckdb_read_stat.c

Lines changed: 22 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
#include <string.h>
2-
#include <time.h>
32
#if defined(_MSC_VER)
43

5-
#define timegm _mkgmtime
64
#define strncasecmp _strnicmp
75
#define strcasecmp _stricmp
86

@@ -193,20 +191,15 @@ void duckdb_read_stat_init(duckdb_init_info info)
193191
duckdb_init_set_init_data(info, data, duckdb_free);
194192
}
195193

196-
const struct tm sas_epoch = {.tm_year = 60, .tm_mon = 0, .tm_mday = 1, .tm_isdst = 0};
197-
const struct tm spss_epoch = {.tm_year = -318, .tm_mon = 9, .tm_mday = 14, .tm_isdst = 0};
198-
const struct tm stata_epoch = {.tm_year = 60, .tm_mon = 0, .tm_mday = 1, .tm_isdst = 0};
194+
const int days_between_unix_epoch_and_sas_epoch = -3653;
195+
const int days_between_unix_epoch_and_spss_epoch = -141428;
196+
const int days_between_unix_epoch_and_stata_epoch = -3653;
199197

200-
duckdb_date duckdb_read_stat_convert_date(int days, int seconds, struct tm epoch)
198+
duckdb_date duckdb_read_stat_convert_date(int days, int seconds, int days_between_epochs)
201199
{
202-
struct tm datetime_struct = {.tm_year = epoch.tm_year, .tm_mon = epoch.tm_mon, .tm_mday = epoch.tm_mday + days, .tm_sec = epoch.tm_sec + seconds, .tm_isdst = 0};
203-
time_t datetime = timegm(&datetime_struct);
204-
struct tm *datetime_struct_epoch = gmtime(&datetime);
205-
duckdb_date_struct date_struct;
206-
date_struct.year = datetime_struct_epoch->tm_year + 1900;
207-
date_struct.month = datetime_struct_epoch->tm_mon + 1;
208-
date_struct.day = datetime_struct_epoch->tm_mday;
209-
return duckdb_to_date(date_struct);
200+
duckdb_date date;
201+
date.days = days_between_epochs + days + (seconds / (3600 * 24));
202+
return date;
210203
}
211204

212205
double duckdb_read_stat_modulo(double dividend, double divisor)
@@ -234,58 +227,47 @@ duckdb_date duckdb_read_stat_to_date(double timestamp, duckdb_read_stat_file_for
234227
switch (file_format)
235228
{
236229
case DUCKDB_READ_STAT_FILE_FORMAT_SAS:
237-
return duckdb_read_stat_convert_date(days, seconds, sas_epoch);
230+
return duckdb_read_stat_convert_date(days, seconds, days_between_unix_epoch_and_sas_epoch);
238231
case DUCKDB_READ_STAT_FILE_FORMAT_SPSS:
239-
return duckdb_read_stat_convert_date(days, seconds, spss_epoch);
232+
return duckdb_read_stat_convert_date(days, seconds, days_between_unix_epoch_and_spss_epoch);
240233
case DUCKDB_READ_STAT_FILE_FORMAT_STATA:
241-
return duckdb_read_stat_convert_date(days, seconds, stata_epoch);
234+
return duckdb_read_stat_convert_date(days, seconds, days_between_unix_epoch_and_stata_epoch);
242235
}
243236
}
244237

245-
duckdb_timestamp duckdb_read_stat_convert_timestamp(int days, int seconds, struct tm epoch)
238+
duckdb_timestamp duckdb_read_stat_convert_timestamp(int days, int seconds, int days_between_epochs)
246239
{
247-
struct tm datetime_struct = {.tm_year = epoch.tm_year, .tm_mon = epoch.tm_mon, .tm_mday = epoch.tm_mday + days, .tm_sec = epoch.tm_sec + seconds, .tm_isdst = 0};
248-
time_t datetime = timegm(&datetime_struct);
249-
struct tm *datetime_struct_epoch = gmtime(&datetime);
250-
duckdb_timestamp_struct timestamp_struct;
251-
252-
timestamp_struct.date.year = datetime_struct_epoch->tm_year + 1900;
253-
timestamp_struct.date.month = datetime_struct_epoch->tm_mon + 1;
254-
timestamp_struct.date.day = datetime_struct_epoch->tm_mday;
255-
timestamp_struct.time.hour = datetime_struct_epoch->tm_hour;
256-
timestamp_struct.time.min = datetime_struct_epoch->tm_min;
257-
timestamp_struct.time.sec = datetime_struct_epoch->tm_sec;
258-
timestamp_struct.time.micros = 0;
259-
260-
return duckdb_to_timestamp(timestamp_struct);
240+
duckdb_timestamp timestamp;
241+
timestamp.micros = ((((int64_t)days_between_epochs + (int64_t)days) * 24L * 3600L) + (int64_t)seconds) * 1000000L;
242+
return timestamp;
261243
}
262244

263245
duckdb_timestamp duckdb_read_stat_to_timestamp(double timestamp, duckdb_read_stat_file_format file_format)
264246
{
265247
int days = 0;
266-
double msecs = 0;
248+
double milliseconds = 0;
267249
int seconds = 0;
268-
int usecs = 0;
250+
269251
if (file_format == DUCKDB_READ_STAT_FILE_FORMAT_STATA)
270252
{
271253
days = (int)(floor(timestamp / 86400000.0));
272-
msecs = duckdb_read_stat_modulo(timestamp, 86400000.0);
273-
seconds = (int)(msecs / 1000.0);
274-
usecs = (int)duckdb_read_stat_modulo(msecs, 1000.0);
254+
milliseconds = duckdb_read_stat_modulo(timestamp, 86400000.0);
255+
seconds = (int)(milliseconds / 1000.0);
275256
}
276257
else
277258
{
278259
days = (int)(floor(timestamp / 86400.0));
279260
seconds = (int)duckdb_read_stat_modulo(timestamp, 86400.0);
280261
}
262+
281263
switch (file_format)
282264
{
283265
case DUCKDB_READ_STAT_FILE_FORMAT_SAS:
284-
return duckdb_read_stat_convert_timestamp(days, seconds, sas_epoch);
266+
return duckdb_read_stat_convert_timestamp(days, seconds, days_between_unix_epoch_and_sas_epoch);
285267
case DUCKDB_READ_STAT_FILE_FORMAT_SPSS:
286-
return duckdb_read_stat_convert_timestamp(days, seconds, spss_epoch);
268+
return duckdb_read_stat_convert_timestamp(days, seconds, days_between_unix_epoch_and_spss_epoch);
287269
case DUCKDB_READ_STAT_FILE_FORMAT_STATA:
288-
return duckdb_read_stat_convert_timestamp(days, seconds, stata_epoch);
270+
return duckdb_read_stat_convert_timestamp(days, seconds, days_between_unix_epoch_and_stata_epoch);
289271
}
290272
}
291273

0 commit comments

Comments
 (0)