Mismatch in Date precision causes odd behavior #1200

Open
Carbonyte opened this Issue Jan 19, 2017 · 6 comments

Comments

5 participants
@Carbonyte

Carbonyte commented Jan 19, 2017

The PostgreSQL TIMESTAMP type has a maximum precision of 1 microsecond, but the JavaScript Date type only has a maximum precision of 1 millisecond. This causes information to be silently discarded, leading to bizarre behavior:

client.query("SELECT '2017-01-01 00:00:00.00001'::timestamp as d", (err, res) => {
let date = res.rows[0].d;

client.query("SELECT '2017-01-01 00:00:00.00001'::timestamp = $1 as eq,\
'2017-01-01 00:00:00.00001'::timestamp > $1 as gt", [date], (err, res) => {
console.log(res.rows[0].eq); //false
console.log(res.rows[0].gt); //true
});
});

I'm not sure this a bug that could be fixed without using a non-standard Date object, but even if it isn't fixed it should be at least noted in the documentation.

@vitaly-t

This comment has been minimized.

Show comment
Hide comment
@vitaly-t

vitaly-t Jan 21, 2017

Contributor

JavaScript doesn't support microseconds, so wouldn't this be self-implying for a JavaScript library?

No point documenting the obvious, imo.

And there is nothing bizarre in your example, because:

let date = res.rows[0].d;
//=> 2017-01-01T00:00:00.000Z

which makes the rest logically apparent:

2017-01-01 00:00:00.00001 = 2017-01-01T00:00:00.000Z => false
2017-01-01 00:00:00.00001 > 2017-01-01T00:00:00.000Z => true
Contributor

vitaly-t commented Jan 21, 2017

JavaScript doesn't support microseconds, so wouldn't this be self-implying for a JavaScript library?

No point documenting the obvious, imo.

And there is nothing bizarre in your example, because:

let date = res.rows[0].d;
//=> 2017-01-01T00:00:00.000Z

which makes the rest logically apparent:

2017-01-01 00:00:00.00001 = 2017-01-01T00:00:00.000Z => false
2017-01-01 00:00:00.00001 > 2017-01-01T00:00:00.000Z => true
@Carbonyte

This comment has been minimized.

Show comment
Hide comment
@Carbonyte

Carbonyte Jan 22, 2017

The behavior isn't bizarre once you know the cause, but it isn't a particularly easy thing to debug if you don't. How the various Postgres datatypes map to JS types is itself not well documented, which is part of the problem.

Carbonyte commented Jan 22, 2017

The behavior isn't bizarre once you know the cause, but it isn't a particularly easy thing to debug if you don't. How the various Postgres datatypes map to JS types is itself not well documented, which is part of the problem.

@williamstein

This comment has been minimized.

Show comment
Hide comment
@williamstein

williamstein Feb 7, 2017

For what it's worth, I probably wasted about 60 hours last week, and cause a lot of pain to users, because there wasn't a sentence about this in the main README.md page. I did systematically read through the fine README before starting to use the driver, and if there was a warning about timestamps silently rounding by "textually deleting the last 3 digits" instead of rounding, it would have helped. (And other than this, I really like this driver!)

I think the behavior of the driver is perhaps more surprising than your examples above suggest. What happens with the driver is completely different than what happens using postgresql's ::timestamp(3) explicit cast, which rounds to the nearest timestamp, rather than just textually removing the last 3 digits.

williamstein commented Feb 7, 2017

For what it's worth, I probably wasted about 60 hours last week, and cause a lot of pain to users, because there wasn't a sentence about this in the main README.md page. I did systematically read through the fine README before starting to use the driver, and if there was a warning about timestamps silently rounding by "textually deleting the last 3 digits" instead of rounding, it would have helped. (And other than this, I really like this driver!)

I think the behavior of the driver is perhaps more surprising than your examples above suggest. What happens with the driver is completely different than what happens using postgresql's ::timestamp(3) explicit cast, which rounds to the nearest timestamp, rather than just textually removing the last 3 digits.

@rpedela

This comment has been minimized.

Show comment
Hide comment
@rpedela

rpedela Feb 21, 2017

Contributor

A workaround is to override the type parser. That doesn't prevent confusion or fix the documentation, but at least you could handle microseconds with custom code.

var pg = require('pg');

function pgToString(value) {
    return value.toString();
}

pg.types.setTypeParser(1082, pgToString); // date
pg.types.setTypeParser(1083, pgToString); // time
pg.types.setTypeParser(1114, pgToString); // timestamp
pg.types.setTypeParser(1184, pgToString); // timestamptz
pg.types.setTypeParser(1266, pgToString); // timetz
Contributor

rpedela commented Feb 21, 2017

A workaround is to override the type parser. That doesn't prevent confusion or fix the documentation, but at least you could handle microseconds with custom code.

var pg = require('pg');

function pgToString(value) {
    return value.toString();
}

pg.types.setTypeParser(1082, pgToString); // date
pg.types.setTypeParser(1083, pgToString); // time
pg.types.setTypeParser(1114, pgToString); // timestamp
pg.types.setTypeParser(1184, pgToString); // timestamptz
pg.types.setTypeParser(1266, pgToString); // timetz
@williamstein

This comment has been minimized.

Show comment
Hide comment

@rpedela THANKS!

@brianc brianc added this to the docs milestone May 24, 2017

@brianc

This comment has been minimized.

Show comment
Hide comment
@brianc

brianc May 24, 2017

Owner

Added this to the docs milestone - I'll include a note around this when I discuss the type parsing there.

Owner

brianc commented May 24, 2017

Added this to the docs milestone - I'll include a note around this when I discuss the type parsing there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment