Skip to content

Commit

Permalink
Allow setting connection timezone and honor UTC For Date/DateTime
Browse files Browse the repository at this point in the history
This resolves sidorares#262

if timezone is set, we change the timezone of the connection upon opening.

If timezone is set to utc and not using dateStrings, we create Date objects
using UTC mode instead.

Also discovered missing config keys that affect packet parsers not
being included in the cache key for packet parser cache, so added them.
  • Loading branch information
aikar committed Feb 20, 2018
1 parent 415959b commit b3f4540
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 8 deletions.
13 changes: 12 additions & 1 deletion lib/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,18 @@ function Connection(opts) {
}
connection._handshakePacket = handshakeCommand.handshake;
connection.threadId = handshakeCommand.handshake.connectionId;
connection.emit('connect', handshakeCommand.handshake);
if (connection.config.timezone && connection.config.timezone !== 'local') {
this.query("SET time_zone = ?", [connection.config.timezone], function (err) {
if (err) {
connection.end();
connection.emit("error", err);
} else {
connection.emit('connect', handshakeCommand.handshake);
}
});
} else {
connection.emit('connect', handshakeCommand.handshake);
}
});
handshakeCommand.on('error', function(err) {
connection._closing = true;
Expand Down
2 changes: 1 addition & 1 deletion lib/connection_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ function ConnectionConfig(options) {
this.debug = options.debug;
this.trace = options.trace !== false;
this.stringifyObjects = options.stringifyObjects || false;
this.timezone = options.timezone || 'local';
this.timezone = (options.timezone || 'local').toLowerCase();
this.queryFormat = options.queryFormat;
this.pool = options.pool || undefined;
this.ssl = typeof options.ssl === 'string'
Expand Down
58 changes: 58 additions & 0 deletions lib/packets/packet.js
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,37 @@ Packet.prototype.readDateTime = function() {
return new Date(y, m - 1, d, H, M, S, ms);
};

Packet.prototype.readUTCDateTime = function() {
var length = this.readInt8();
if (length == 0xfb) {
return null;
}
var y = 0;
var m = 0;
var d = 0;
var H = 0;
var M = 0;
var S = 0;
var ms = 0;
if (length > 3) {
y = this.readInt16();
m = this.readInt8();
d = this.readInt8();
}
if (length > 6) {
H = this.readInt8();
M = this.readInt8();
S = this.readInt8();
}
if (length > 10) {
ms = this.readInt32() / 1000;
}
if (y + m + d + H + M + S + ms === 0) {
return INVALID_DATE;
}
return new Date(Date.UTC(y, m - 1, d, H, M, S, ms));
};

// this is nearly duplicate of previous function so generated code is not slower
// due to "if (dateStrings)" branching
var pad = '000000000000';
Expand Down Expand Up @@ -613,6 +644,25 @@ Packet.prototype.parseDate = function() {
return new Date(y, m - 1, d);
};

Packet.prototype.parseUTCDate = function() {
var strLen = this.readLengthCodedNumber();
if (strLen === null) {
return null;
}

if (strLen != 10) {
// we expect only YYYY-MM-DD here.
// if for some reason it's not the case return invalid date
return new Date(NaN);
}
var y = this.parseInt(4);
this.offset++; // -
var m = this.parseInt(2);
this.offset++; // -
var d = this.parseInt(2);
return new Date(Date.UTC(y, m - 1, d));
};

Packet.prototype.parseDateTime = function() {
var str = this.readLengthCodedString('binary');
if (str === null) {
Expand All @@ -621,6 +671,14 @@ Packet.prototype.parseDateTime = function() {
return new Date(str);
};

Packet.prototype.parseUTCDateTime = function() {
var str = this.readLengthCodedString('binary');
if (str === null) {
return null;
}
return new Date(Date.UTC(str));
};

// TODO: handle E notation
var dot = '.'.charCodeAt(0);
var exponent = 'e'.charCodeAt(0);
Expand Down
3 changes: 3 additions & 0 deletions lib/parsers/binary_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ function readCodeFor(field, config, options, fieldNum) {
if (config.dateStrings) {
return 'packet.readDateTimeString(' + field.decimals + ');';
}
if (config.timezone === 'utc') {
return 'packet.readUTCDateTime();';
}
return 'packet.readDateTime();';
case Types.TIME:
return 'packet.readTimeString()';
Expand Down
19 changes: 13 additions & 6 deletions lib/parsers/parser_cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ var parserCache = new LRU({
max: 15000
});

function keyFromFields(type, fields, options) {
function keyFromFields(type, fields, options, config) {
var res =
type +
'/' +
Expand All @@ -13,20 +13,27 @@ function keyFromFields(type, fields, options) {
options.nestTables +
'/' +
options.rowsAsArray +
options.supportBigNumbers +
(options.supportBigNumbers || config.supportBigNumbers) +
'/' +
options.bigNumberStrings +
(options.bigNumberStrings || config.bigNumberStrings) +
'/' +
typeof options.typeCast;
typeof options.typeCast +
'/' +
options.timezone +
'/' +
options.decimalNumbers +
'/' +
options.dateStrings;
for (var i = 0; i < fields.length; ++i) {
var field = fields[i];
res +=
'/' + fields[i].name + ':' + fields[i].columnType + ':' + fields[i].flags;
'/' + field.name + ':' + field.columnType + ':' + field.flags + ':' + field.characterSet;
}
return res;
}

function getParser(type, fields, options, config, compiler) {
var key = keyFromFields(type, fields, options);
var key = keyFromFields(type, fields, options, config);
var parser = parserCache.get(key);

if (parser) {
Expand Down
6 changes: 6 additions & 0 deletions lib/parsers/text_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,12 +165,18 @@ function readCodeFor(type, charset, encodingExpr, config, options) {
if (config.dateStrings) {
return 'packet.readLengthCodedString("ascii")';
}
if (config.timezone === 'utc') {
return 'packet.parseUTCDate()';
}
return 'packet.parseDate()';
case Types.DATETIME:
case Types.TIMESTAMP:
if (config.dateStrings) {
return 'packet.readLengthCodedString("ascii")';
}
if (config.timezone === 'utc') {
return 'packet.parseUTCDateTime();';
}
return 'packet.parseDateTime()';
case Types.TIME:
return 'packet.readLengthCodedString("ascii")';
Expand Down

0 comments on commit b3f4540

Please sign in to comment.