Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/spotty-buckets-flash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@powersync/mysql-zongji': minor
---

Export date and time values as structured fields.
3 changes: 0 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,6 @@ The `ZongJi` constructor accepts one argument of either:

If a `Connection` or `Pool` object is passed to the constructor, it will not be destroyed/ended by Zongji's `stop()` method.

If there is a `dateStrings` `mysql` configuration option in the connection details or connection, `ZongJi` will follow it.

Each instance includes the following methods:

| Method Name | Arguments | Description |
Expand Down Expand Up @@ -139,7 +137,6 @@ Neither method requires any arguments.
- :star2: [All types allowed by `mysql`](https://github.com/mysqljs/mysql#type-casting) are supported by this package.
- :speak_no_evil: 64-bit integer is supported via package big-integer(see #108). If an integer is within the safe range of JS number (-2^53, 2^53), a Number object will returned, otherwise, will return as String.
- :point_right: `TRUNCATE` statement does not cause corresponding `DeleteRows` event. Use unqualified `DELETE FROM` for same effect.
- When using fractional seconds with `DATETIME` and `TIMESTAMP` data types in MySQL > 5.6.4, only millisecond precision is available due to the limit of Javascript's `Date` object.

## Run Tests

Expand Down
99 changes: 40 additions & 59 deletions lib/common.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
const iconv = require('iconv-lite');
const decodeJson = require('./json_decode');
const dtDecode = require('./datetime_decode');
const bigInt = require('big-integer');

const MysqlTypes = (exports.MysqlTypes = {
Expand Down Expand Up @@ -332,27 +331,14 @@ const parseGeometryValue = function (buffer) {
// Returns false, or an object describing the fraction of a second part of a
// TIME, DATETIME, or TIMESTAMP.
const readTemporalFraction = function (parser, fractionPrecision) {
if (!fractionPrecision) return false;
if (!fractionPrecision) return undefined;
let fractionSize = Math.ceil(fractionPrecision / 2);
let fraction = readIntBE(parser._buffer, parser._offset, fractionSize);
parser._offset += fractionSize;
if (fractionPrecision % 2 !== 0) fraction /= 10; // Not using full space
if (fraction < 0) fraction *= -1; // Negative time, fraction not negative

let milliseconds;
if (fractionPrecision > 3) {
milliseconds = Math.floor(fraction / Math.pow(10, fractionPrecision - 3));
} else if (fractionPrecision < 3) {
milliseconds = fraction * Math.pow(10, 3 - fractionPrecision);
} else {
milliseconds = fraction;
}

return {
value: fraction, // the integer after the decimal place
precision: fractionPrecision, // the number of digits after the decimal
milliseconds: milliseconds // the unrounded 3 digits after the decimal
};
return { fraction, precision: fractionPrecision };
};

// This function is used to read and interpret non-null values from parser.
Expand Down Expand Up @@ -485,12 +471,12 @@ exports.readMysqlValue = function (
break;
case MysqlTypes.DATE:
raw = parseUInt24(parser);
result = dtDecode.getDate(
zongji.connection.config.dateStrings, // node-mysql dateStrings option
sliceBits(raw, 9, 24), // year
sliceBits(raw, 5, 9), // month
sliceBits(raw, 0, 5) // day
);

result = {
year: sliceBits(raw, 9, 24),
month: sliceBits(raw, 5, 9),
day: sliceBits(raw, 0, 5)
};
break;
case MysqlTypes.TIME:
raw = parseUInt24(parser);
Expand Down Expand Up @@ -523,35 +509,31 @@ exports.readMysqlValue = function (
minute = sliceBits(raw, 6, 12);
second = sliceBits(raw, 0, 6);

if (isNegative && (fraction === false || fraction.value === 0)) {
if (isNegative && (fraction === undefined || fraction.value === 0)) {
second++;
}

result =
(isNegative ? '-' : '') +
zeroPad(hour, hour > 99 ? 3 : 2) +
':' +
zeroPad(minute, 2) +
':' +
zeroPad(second, 2);
result = {
isNegative,
hour,
minute,
second,
fraction
};

if (fraction !== false) {
result += dtDecode.getFractionString(fraction);
}
break;
case MysqlTypes.DATETIME:
raw = parseUInt64(parser);
date = Math.floor(raw / 1000000);
time = raw % 1000000;
result = dtDecode.getDateTime(
zongji.connection.config.dateStrings, // node-mysql dateStrings option
Math.floor(date / 10000), // year
Math.floor((date % 10000) / 100), // month
date % 100, // day
Math.floor(time / 10000), // hour
Math.floor((time % 10000) / 100), // minutes
time % 100 // seconds
);
result = {
year: Math.floor(date / 10000),
month: Math.floor((date % 10000) / 100),
day: date % 100,
hour: Math.floor(time / 10000),
minute: Math.floor((time % 10000) / 100),
second: time % 100
};
break;
case MysqlTypes.DATETIME2: {
// Overlapping high-low to get all data in 32-bit numbers
Expand All @@ -561,31 +543,30 @@ exports.readMysqlValue = function (
fraction = readTemporalFraction(parser, column.metadata.decimals);

yearMonth = sliceBits(rawHigh, 14, 31);
result = dtDecode.getDateTime(
zongji.connection.config.dateStrings, // node-mysql dateStrings option
Math.floor(yearMonth / 13), // year
yearMonth % 13, // month
sliceBits(rawLow, 17, 22), // day
sliceBits(rawLow, 12, 17), // hour
sliceBits(rawLow, 6, 12), // minutes
sliceBits(rawLow, 0, 6), // seconds
fraction // fraction of a second object
);
result = {
year: Math.floor(yearMonth / 13),
month: yearMonth % 13,
day: sliceBits(rawLow, 17, 22),
hour: sliceBits(rawLow, 12, 17),
minute: sliceBits(rawLow, 6, 12),
second: sliceBits(rawLow, 0, 6),
fraction
};
break;
}
case MysqlTypes.TIMESTAMP:
raw = parser.parseUnsignedNumber(4);
result = dtDecode.getTimeStamp(zongji.connection.config.dateStrings, raw);
result = {
secondsFromEpoch: raw
};
break;
case MysqlTypes.TIMESTAMP2:
raw = readIntBE(parser._buffer, parser._offset, 4);
parser._offset += 4;
fraction = readTemporalFraction(parser, column.metadata.decimals);
result = dtDecode.getTimeStamp(
zongji.connection.config.dateStrings,
raw, // seconds from epoch
fraction
); // fraction of a second object
result = {
secondsFromEpoch: raw,
fraction: readTemporalFraction(parser, column.metadata.decimals)
};
break;
case MysqlTypes.YEAR:
raw = parser.parseUnsignedNumber(1);
Expand Down
112 changes: 0 additions & 112 deletions lib/datetime_decode.js

This file was deleted.

Loading