diff --git a/NEWS.md b/NEWS.md index 021ddbbb6..47c915141 100644 --- a/NEWS.md +++ b/NEWS.md @@ -50,6 +50,8 @@ 9. `fread()` no longer replaces a literal header column name `"NA"` with an auto-generated `Vn` name when `na.strings` includes `"NA"`, [#5124](https://github.com/Rdatatable/data.table/issues/5124). Data rows still continue to parse `"NA"` as missing. Thanks @Mashin6 for the report and @shrektan for the fix. +10. `fwrite` with a `POSIXct` column backed by integer storage (e.g. created via `seq()`) would write epoch integers instead of ISO datetime strings, [#3535](https://github.com/Rdatatable/data.table/issues/3535). Thanks to @alistaire47 for reporting and @tjc234 and @rmwood0908 for fixing. + ### Notes 1. {data.table} now depends on R 3.5.0 (2018). diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index 443487c6a..a880c3065 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -21585,3 +21585,20 @@ close(con) file.create(f <- tempfile()) test(2367.6, fread(file(f)), data.table(), warning="Connection has size 0.") unlink(f) +# fwrite ignores dateTimeAs for integer-backed POSIXct created by seq() #3535 +oldtz = Sys.getenv('TZ') +Sys.setenv(TZ = 'UTC') +tmp <- tempfile() +on.exit(Sys.setenv(TZ = oldtz), add = TRUE) +DT <- data.table(x = seq(as.POSIXct('1970-01-01'), by = '1 sec', length = 6)) +fwrite(DT, tmp) +test(2368.1, capture.output(cat(readLines(tmp), sep = '\n')), c( + "x", + "1970-01-01T00:00:00Z", + "1970-01-01T00:00:01Z", + "1970-01-01T00:00:02Z", + "1970-01-01T00:00:03Z", + "1970-01-01T00:00:04Z", + "1970-01-01T00:00:05Z" +)) +rm(DT, tmp, oldtz) diff --git a/src/fwrite.c b/src/fwrite.c index 670936423..e8f181c1d 100644 --- a/src/fwrite.c +++ b/src/fwrite.c @@ -480,6 +480,16 @@ void writePOSIXct(const void *col, int64_t row, char **pch) *pch = ch; } +void writePOSIXctInt(const void *col, int64_t row, char **pch) +{ + // integer-backed POSIXct + // before formatting, since writePOSIXct expects double storage + int32_t xi = ((const int32_t*)col)[row]; + double x = (xi == INT32_MIN) ? NAN : (double)xi; + const void *tmp = &x; + writePOSIXct(tmp, 0, pch); +} + // # nocov start. Covered in other.Rraw test 22, not the main suite. void writeNanotime(const void *col, int64_t row, char **pch) { diff --git a/src/fwrite.h b/src/fwrite.h index 01b795b6f..412510e53 100644 --- a/src/fwrite.h +++ b/src/fwrite.h @@ -23,6 +23,7 @@ writer_fun_t writeITime; writer_fun_t writeDateInt32; writer_fun_t writeDateFloat64; writer_fun_t writePOSIXct; +writer_fun_t writePOSIXctInt; writer_fun_t writeNanotime; writer_fun_t writeString; writer_fun_t writeCategString; @@ -42,6 +43,7 @@ typedef enum { // same order as fun[] above WF_DateInt32, WF_DateFloat64, WF_POSIXct, + WF_POSIXctInt, WF_Nanotime, WF_String, WF_CategString, @@ -60,6 +62,7 @@ static const int writerMaxLen[] = { // same order as fun[] and WFs above; max f 16, //&writeDateInt32 16, //&writeDateFloat64 32, //&writePOSIXct + 32, //&writePOSIXctInt 48, //&writeNanotime 0, //&writeString 0, //&writeCategString diff --git a/src/fwriteR.c b/src/fwriteR.c index b2d631fb3..f10b26aaa 100644 --- a/src/fwriteR.c +++ b/src/fwriteR.c @@ -65,6 +65,7 @@ writer_fun_t *funs[] = { &writeDateInt32, &writeDateFloat64, &writePOSIXct, + &writePOSIXctInt, &writeNanotime, &writeString, &writeCategString, @@ -128,6 +129,7 @@ static int32_t whichWriter(SEXP column) { if (dateTimeAs == DATETIMEAS_EPOCH) return WF_Int32; if (INHERITS(column, char_ITime)) return WF_ITime; if (INHERITS(column, char_Date)) return WF_DateInt32; + if (INHERITS(column, char_POSIXct)) return WF_POSIXctInt; return WF_Int32; case REALSXP: if (INHERITS(column, char_nanotime)