Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix newDate() and option for reverted back Now() date format #1534

Merged
merged 6 commits into from
Jun 10, 2023
Merged
Show file tree
Hide file tree
Changes from 5 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
98 changes: 41 additions & 57 deletions src/17alasql.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,79 +53,63 @@ alasql.databasenum = 0;
/**
Alasql options object
*/
alasql.options = {
/** Log or throw error */
errorlog: false,
alasql.options = {};
alasql.options.errorlog = false; // Log or throw error
alasql.options.valueof = false; // Use valueof in orderfn
alasql.options.dropifnotexists = false; // DROP database in any case
alasql.options.datetimeformat = 'sql'; // How to handle DATE and DATETIME types
// Another value is 'javascript'
alasql.options.casesensitive = true; // Table and column names are case sensitive and converted to lower-case
alasql.options.logtarget = 'output'; // target for log. Values: 'console', 'output', 'id' of html tag
alasql.options.logprompt = true; // Print SQL at log

/** Use valueof in orderfn */
valueof: true,
alasql.options.progress = false; // Callback for async queries progress
alasql.options.nowdateseparator = '-'; // Date separator for Now() function
alasql.options.nowdatetimeseparator = ' '; // 'T'; // Separator between date and time for Now() function

/** DROP database in any case */
dropifnotexists: false,
// Default modifier
// values: RECORDSET, VALUE, ROW, COLUMN, MATRIX, TEXTSTRING, INDEX
alasql.options.modifier = undefined;
// How many rows to lookup to define columns
alasql.options.columnlookup = 10;
// Create vertex if not found
alasql.options.autovertex = true;

/** How to handle DATE and DATETIME types */
datetimeformat: 'sql',
// Use dbo as current database (for partial T-SQL comaptibility)
alasql.options.usedbo = true;

/** Table and column names are case sensitive and converted to lower-case */
casesensitive: true,
// AUTOCOMMIT ON | OFF
alasql.options.autocommit = true;

/** target for log. Values: 'console', 'output', 'id' of html tag */
logtarget: 'output',
// Use cache
alasql.options.cache = true;

/** Print SQL at log */
logprompt: true,
// Compatibility flags
alasql.options.tsql = true;

/** Callback for async queries progress */
progress: false,
alasql.options.mysql = true;

/**
* Default modifier
* values: RECORDSET, VALUE, ROW, COLUMN, MATRIX, TEXTSTRING, INDEX
* @type {'RECORDSET'|'VALUE'|'ROW'|'COLUMN'|'MATRIX'|'TEXTSTRING'|'INDEX'|undefined}
*/
modifier: undefined,
alasql.options.postgres = true;

/** How many rows to lookup to define columns */
columnlookup: 10,
alasql.options.oracle = true;

/** Create vertex if not found */
autovertex: true,
alasql.options.sqlite = true;

/** Use dbo as current database (for partial T-SQL comaptibility) */
usedbo: true,
alasql.options.orientdb = true;

/** AUTOCOMMIT ON | OFF */
autocommit: true,
// for SET NOCOUNT OFF
alasql.options.nocount = false;

/** Use cache */
cache: true,
// Check for NaN and convert it to undefined
alasql.options.nan = false;

/** Compatibility flags */
tsql: true,
alasql.options.excel = {cellDates: true};

mysql: true,
alasql.options.joinstar = 'overwrite'; // Option for SELECT * FROM a,b

postgres: true,
alasql.options.loopbreak = 100000;

oracle: true,

sqlite: true,

orientdb: true,

/** for SET NOCOUNT OFF */
nocount: false,

/** Check for NaN and convert it to undefined */
nan: false,

excel: {cellDates: true},

/** Option for SELECT * FROM a,b */
joinstar: 'overwrite',

loopbreak: 100000,
};
alasql.options;

//alasql.options.worker = false;

Expand Down Expand Up @@ -285,7 +269,7 @@ alasql.dexec = function (databaseid, sql, params, cb, scope) {
var res = (alasql.res = statement(params, cb, scope));
return res;
} else {
// console.log(ast.statements[0]);
// console.log(ast.statements[0]);
alasql.precompile(ast.statements[0], alasql.useid, params);
var res = (alasql.res = ast.statements[0].execute(databaseid, params, cb, scope));
return res;
Expand Down
72 changes: 61 additions & 11 deletions src/61date.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ stdfn.ASCII = function (a) {
return a.charCodeAt(0);
};

/**
/**
Return first non-null argument
See https://msdn.microsoft.com/en-us/library/ms190349.aspx
*/
Expand All @@ -43,20 +43,24 @@ stdfn.OBJECT_ID = function (objid) {
};

stdfn.DATE = function (d) {
if (/\d{8}/.test(d)) return new Date(+d.substr(0, 4), +d.substr(4, 2) - 1, +d.substr(6, 2));
if (/^\d{8}$/.test(d.toString()))
return new Date(+d.substr(0, 4), +d.substr(4, 2) - 1, +d.substr(6, 2));
return newDate(d);
};

stdfn.NOW = function () {
var d = new Date();
stdfn.NOW = function (param) {
var d;
if (param) d = stdfn.DATE(param);
else d = new Date();
var separator = alasql.options.nowdateseparator;
var s =
d.getFullYear() +
'-' +
separator +
('0' + (d.getMonth() + 1)).substr(-2) +
'-' +
separator +
('0' + d.getDate()).substr(-2);
s +=
' ' +
alasql.options.nowdatetimeseparator +
('0' + d.getHours()).substr(-2) +
':' +
('0' + d.getMinutes()).substr(-2) +
Expand Down Expand Up @@ -166,12 +170,58 @@ alasql.stdfn.DATE_SUB = alasql.stdfn.SUBDATE = function (d, interval) {
return new Date(nd);
};

var dateRegexp = /^\d{4}\.\d{2}\.\d{2} \d{2}:\d{2}:\d{2}/;
var dateRegexp =
// /^(?<year>\d{4})[-\.](?<month>\d{1,2})[-\.](?<day>\d{1,2})( (?<hours>\d{2}):(?<minutes>\d{2})(:(?<seconds>\d{2})(\.(?<milliseconds>)\d{3})?)?)?/;
/^\W*(?<year>\d{4})[-\/\.](?<month>[0-1]?\d)[-\/\.](?<day>[0-3]?\d)([T ](?<hour>[0-2][0-9]):(?<min>[0-5][0-9])(:(?<sec>[0-5][0-9])(\.(?<msec>\d{3}))?\d*)?(?<tz>Z|(?<tz_dir>[-+])(?<tz_HH>\d\d):(?<tz_mm>\d\d))?)?/i;
function newDate(d) {
// Read https://stackoverflow.com/questions/2587345/why-does-date-parse-give-incorrect-results/20463521#20463521 to understand
// why we need to rely on our own parsing to ensure consistency across runtimes.
// Also: YYYY-MM-DD defaults to UTC is required by ECMA-262 but YYYY-MM-DD HH:ss is not.
// AlaSQL will treat _anything_ with no timezone as local time.

var date;

if (typeof d === 'interger') {
return new Date(d);
}

if (typeof d === 'string') {
if (dateRegexp.test(d)) {
d = d.replace('.', '-').replace('.', '-');
const match = d.match(dateRegexp);
if (match) {
const {year, month, day, hour, min, sec, msec, tz, tz_dir, tz_HH, tz_mm} = match.groups;

if (tz) {
var offset_HH = 0;
var offset_mm = 0;

if ('Z' != tz) {
offset_HH = -1 * +(tz_dir + tz_HH);
offset_mm = -1 * +(tz_dir + tz_mm);
}
date = new Date(
Date.UTC(+year, +month - 1, +day, +hour + offset_HH, +min + offset_mm, +sec, +msec)
);
} else {
const dateArrguments = [year, month - 1, day];
if (hour) {
dateArrguments.push(hour, min, sec || 0, msec || 0);
}
date = new Date(...dateArrguments);
}
} else {
date = new Date(Date.parse(d));
}
}
return new Date(d);

if (isNaN(date)) {
date = new Date(d);
}

// https://github.com/AlaSQL/alasql/pull/1534#discussion_r1051623032
//if (isNaN(date)) {
// throw 'The value could not be converted to a date: ' + JSON.stringify(d);
// //console.warn(`d:${d}, date:${date}`)
//}

return date;
}
120 changes: 120 additions & 0 deletions test/test845.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,124 @@ describe('Test ' + test + ' - use NOW() function', function () {
//02/25/22
assert(/\d{2}\/\d{2}\/\d{2}/.test(res[0].conv));
});

it('3. NOW() with point', function () {
var currentSeparator = alasql.options.nowdateseparator;
alasql.options.nowdateseparator = '.';
var res = alasql('SELECT NOW() AS now');
//2022.02.25 19:21:27.839
assert(/\d{4}.\d{2}.\d{2} \d{2}:\d{2}:\d{2}.\d{3}/.test(res[0].now));
alasql.options.nowdateseparator = currentSeparator;
});

it('4. CONVERT with NOW() with point as an argument', function () {
var currentSeparator = alasql.options.nowdateseparator;
alasql.options.nowdateseparator = '.';
var res = alasql('SELECT CONVERT(STRING,NOW(),1) AS conv');
//02/25/22
assert(/\d{2}\/\d{2}\/\d{2}/.test(res[0].conv));
alasql.options.nowdateseparator = currentSeparator;
});

it('5. Reads multiple formats of date strings', function () {
assert.equal(
alasql('VALUE OF SELECT DATE("2023-06-10 16:27:36.224Z")').toISOString(),
'2023-06-10T16:27:36.224Z'
);

// Despite ECMA-262 where YYYY-MM-DD must be set in UTC and YYYY-MM-DD HH:ss must not AlaSQL will treat _anything_ with no timezone as local time.

assert.equal(
alasql('VALUE OF SELECT DATE("2022-01-10T00:00:00")').toISOString(),
alasql('VALUE OF SELECT DATE("2022-01-10")').toISOString()
);

assert.equal(
alasql('VALUE OF SELECT DATE("2022-01-20T12:01:02.123456789Z")').toISOString(),
'2022-01-20T12:01:02.123Z'
);

assert.equal(
alasql('VALUE OF SELECT DATE("2022-01.20 12:00:00.123456789-10:30")').toISOString(),
'2022-01-20T22:30:00.123Z'
);

assert.equal(
alasql('VALUE OF SELECT DATE("2022-01.20 12:00:00.123456789+10:30")').toISOString(),
'2022-01-20T01:30:00.123Z'
);

assert.equal(
alasql('VALUE OF SELECT DATE("2022-01.20 12:00+10:30")').toISOString(),
'2022-01-20T01:30:00.000Z'
);

assert.equal(
alasql('VALUE OF SELECT DATE("2022-01-20T12:00:00.123456789+03:30")').toISOString(),
'2022-01-20T08:30:00.123Z'
);

assert.equal(
alasql('VALUE OF SELECT DATE("2022-01-20T12:00:00.123456789+03:30")').toISOString(),
'2022-01-20T08:30:00.123Z'
);

/*
Hmmm. How best to test that this gets converted correclty to locala time (including DST)?
"2022.01.10 04:10",
"2022.01.10 04:10:11",
"2022.01.10 04:10:11.123",
"2022-01-10 04:10",
"2022-01-10 04:10:11",
"2022-01-10 04:10:11.123",
"2022.1.10",
"2022-1-1"
"2022/1/1"

For now: Lets make sure that it converts to a valid date.
*/

assert(!isNaN(alasql('VALUE OF SELECT DATE("2012.01.10 04:10")')));
assert(!isNaN(alasql('VALUE OF SELECT DATE("2022.01.10 04:10:11")')));
assert(!isNaN(alasql('VALUE OF SELECT DATE("2022.01.10 04:10:11.123")')));
assert(!isNaN(alasql('VALUE OF SELECT DATE("2022.01.10T04:10:11.123")')));
assert(!isNaN(alasql('VALUE OF SELECT DATE("2022-01-10 04:10")')));
assert(!isNaN(alasql('VALUE OF SELECT DATE("2022-01-10 04:10:11")')));
assert(!isNaN(alasql('VALUE OF SELECT DATE("2022-01-10 04:10:11.123")')));
assert(!isNaN(alasql('VALUE OF SELECT DATE("2022.1.10")')));
assert(!isNaN(alasql('VALUE OF SELECT DATE("2022-1-1")')));
assert(!isNaN(alasql('VALUE OF SELECT DATE("2022/1/1")')));
});

it('6. Will seek to trim value', function () {
assert.deepEqual(
alasql('VALUE OF SELECT DATE(" 2022-01-10T00:00:01")'),
alasql('VALUE OF SELECT DATE("2022-01-10T00:00:01")')
);

assert.deepEqual(
alasql('VALUE OF SELECT DATE(" 2022-01-10T00:00:02")'),
alasql('VALUE OF SELECT DATE("2022-01-10T00:00:02")')
);

assert.notDeepEqual(
alasql('VALUE OF SELECT DATE("xyz 2022-01-10T00:00:03")'),
alasql('VALUE OF SELECT DATE("2022-01-10T00:00:03")')
);

assert.deepEqual(
alasql('VALUE OF SELECT DATE("€%&/()\'2022-01-10T00:00:04")'),
alasql('VALUE OF SELECT DATE("2022-01-10T00:00:04")')
);

assert.deepEqual(
alasql('VALUE OF SELECT DATE("2022-01-10T00:00:05 XXYYYZZZ5678&/(")'),
alasql('VALUE OF SELECT DATE("2022-01-10T00:00:05")')
);

assert.deepEqual(
alasql('VALUE OF SELECT DATE("2022-01-10T00:00:06 2000-02-20T20:20:20")'),
alasql('VALUE OF SELECT DATE("2022-01-10T00:00:06")')
);
});
});
mathiasrw marked this conversation as resolved.
Show resolved Hide resolved