diff --git a/LDK/resources/web/LDK/ConvertUtils.js b/LDK/resources/web/LDK/ConvertUtils.js index b0d638c8..5773f14b 100644 --- a/LDK/resources/web/LDK/ConvertUtils.js +++ b/LDK/resources/web/LDK/ConvertUtils.js @@ -20,14 +20,30 @@ LDK.ConvertUtils = new function(){ var result = null, parsedDate; + if (value instanceof Date) { + return value; + } + useStrict = Ext4.isDefined(useStrict) ? useStrict : false; if (Ext4.Date.formatContainsHourInfo(format)) { // if parse format contains hour information, no DST adjustment is necessary result = Ext4.Date.parse(value, format, useStrict); } else { - // set time to 12 noon, then clear the time - parsedDate = Ext4.Date.parse(value + ' ' + 12, format + ' ' + 'H', useStrict); + // The core of the parsing problem comes from JS Date.parse() treating ISO 8601 short differently from other date formats: + // https://www.w3.org/TR/NOTE-datetime + // Example: + // new Date('2024-01-01') + // Sun Dec 31 2023 16:00:00 GMT-0800 (Pacific Standard Time) + // new Date('1/1/2024') + // Mon Jan 01 2024 00:00:00 GMT-0800 (Pacific Standard Time) + + // Therefore special case this format and append the browser's time zone: + if (format === 'c' && value.length === 10) { + format = 'Y-m-d'; + } + + parsedDate = Ext4.Date.parse(value, format, useStrict); if (parsedDate) { result = Ext4.Date.clearTime(parsedDate); } @@ -58,9 +74,22 @@ LDK.ConvertUtils = new function(){ formats.push(format); formats = formats.concat(DATEFORMATS); + // See 'c' format: https://docs.sencha.com/extjs/4.2.1/#!/api/Ext.Date + // And http://www.w3.org/TR/NOTE-datetime + // The issue is that this treats any date beginning with YYYY-MM-DD as ISO8601, + // and assumes GMT as the date/time. In general, we want the string '2024-01-01' to be treated as the browser's timezone. + // Therefore push this to the lowest priority, which will allow other formats to preferentially parse date-only values + if (format !== 'c' && formats.indexOf('c') > -1) { + formats = Ext4.Array.remove(formats, 'c') + formats.push('c') + } + var val; - for (var i=0; i < formats.length && !val; ++i) { + for (var i=0; i < formats.length; ++i) { val = safeParseDate(value, formats[i]); + if (val) { + break; + } } // two digit years tend to get parsed as 1900, rather than 2000s, so we make assumptions about dates more than 90 in the past @@ -76,7 +105,7 @@ LDK.ConvertUtils = new function(){ } Ext4.Array.forEach(results.metaData.fields, function(field){ - if (field.jsonType == 'date'){ + if (field.jsonType === 'date'){ Ext4.Array.forEach(results.rows, function(row){ if (row[field.name]){ row[field.name] = LDK.ConvertUtils.parseDate(row[field.name]); diff --git a/LDK/resources/web/LDK/StoreUtils.js b/LDK/resources/web/LDK/StoreUtils.js index 60342944..c18059f9 100644 --- a/LDK/resources/web/LDK/StoreUtils.js +++ b/LDK/resources/web/LDK/StoreUtils.js @@ -115,15 +115,19 @@ LDK.StoreUtils = new function(){ var fields = store.model.getFields(); Ext4.each(fields, function(field){ + var extType = field.extType || LABKEY.ext4.Util.EXT_TYPE_MAP[field.jsonType]; + var val; if (!Ext4.isEmpty(data[field.name])){ val = data[field.name]; - var type = Ext4.data.Types[field.extType]; + var type = Ext4.data.Types[extType]; if (type && type.convert){ - if (field.extType == LABKEY.ext4.Util.EXT_TYPE_MAP.date) + if (extType === LABKEY.ext4.Util.EXT_TYPE_MAP.date) { val = LDK.ConvertUtils.parseDate(val); - else + } + else { val = type.convert(val); + } } model.set(field.name, val); @@ -193,59 +197,6 @@ LDK.StoreUtils = new function(){ return map; }, - /** - * Parses a string into a date, normalizing for the current timezone. Date.parse() - * will make different timezone assumptions, depending on date format. For example, - * 2010-02-04 is assumed to be GMT, while 2/4/2010 is assume to match the local machine. - * This method tries to infer the date format, and if an ISO date is provided, it will convert the - * date object to the current timezone. - * @param val - */ - normalizeDateString: function(val){ - if (!val || Ext4.isDate(val)){ - return val; - } - else if (Ext4.isNumber(val)){ - return new Date(val); - } - - else if (Ext4.isString(val)) { - //try to guess format: - var date; - //ISO dates are assumed to be GMT, so we convert to local time, letting Ext normalize timezone - if (Ext4.Date.parse(val, Date.patterns.ISO8601Long)){ - date = Ext4.Date.parse(val, Date.patterns.ISO8601Long); - } - else if (Ext4.Date.parse(val, Date.patterns.ISO8601Short)){ - date = Ext4.Date.parse(val, Date.patterns.ISO8601Short); - } - else if (val.indexOf('Z') != -1) - { - var parsed = Date.parse(val); - if (parsed) - date = new Date(parsed); - } - else { - //with non ISO dates, browsers seem to accept tacking the timezone to the end - var parsed = Date.parse(val + ' ' + Ext4.Date.getTimezone(new Date())); - if (parsed) - date = new Date(parsed); - } - - if (date){ - var mills = Date.parse(Ext4.Date.format(date, 'm/d/Y H:i')); - if (!mills == date.getTime()){ - console.error('Date doesn\'t match: ' + val + '/' + date.toString()); - return null; - } - else { - return date; - } - } - } - return val; - }, - sortStoreByFieldNames: function(store, fieldNames){ var fields = []; Ext4.each(fieldNames, function(fn){ @@ -262,7 +213,7 @@ LDK.StoreUtils = new function(){ /** * A sorter function that can be used to sort an Ext store based on one or more fields. The primary advantage is that this sorter uses the column * metadata to sort on the displayValue, instead of rawValue for lookup columns, which is usually what the user expects. - * @param {array} fieldList An ordered array of field metadata objects. + * @param {array} fields An ordered array of field metadata objects. * @returns {function} The sorter function that can be passed to the sort() method of an Ext.data.Store. */ getStoreSortFn: function(fields){ @@ -298,12 +249,12 @@ LDK.StoreUtils = new function(){ var rec1; var rec2; rec1 = store.findExact(item.valueField, a.get(item.term)); - if(rec1 != -1){ + if(rec1 !== -1){ rec1 = store.getAt(rec1); val1 = rec1.get(item.displayField) || ''; } rec2 = store.findExact(item.valueField, b.get(item.term)); - if(rec2 != -1){ + if(rec2 !== -1){ rec2 = store.getAt(rec2); val2 = rec2.get(item.displayField) || ''; } diff --git a/LDK/test/src/org/labkey/test/tests/external/labModules/LabModulesTest.java b/LDK/test/src/org/labkey/test/tests/external/labModules/LabModulesTest.java index 59f673a4..e31b1780 100644 --- a/LDK/test/src/org/labkey/test/tests/external/labModules/LabModulesTest.java +++ b/LDK/test/src/org/labkey/test/tests/external/labModules/LabModulesTest.java @@ -304,6 +304,9 @@ private void dateParseTest() throws ParseException String dateFormat3 = "MM/dd/yy"; checkDate("02/20/11", dateFormat3); checkDate("3/5/99", dateFormat3); + + String clientFormattedString = (String)executeScript("return Ext4.Date.format(LDK.ConvertUtils.parseDate('2024-01-01', 'c'), 'Y-m-d');"); + assertEquals("Incorrect date parsing", clientFormattedString, "2024-01-01"); } private void checkDate(String dateStr, String javaFormatStr) throws ParseException diff --git a/laboratory/resources/web/laboratory/panel/AssayTemplatePanel.js b/laboratory/resources/web/laboratory/panel/AssayTemplatePanel.js index a2f4d60d..3c226370 100644 --- a/laboratory/resources/web/laboratory/panel/AssayTemplatePanel.js +++ b/laboratory/resources/web/laboratory/panel/AssayTemplatePanel.js @@ -112,7 +112,7 @@ Ext4.define('Laboratory.panel.AssayTemplatePanel', { fields.push(col.editor.dataIndex || col.editor.name); } - if (col.dataIndex == 'category'){ + if (col.dataIndex === 'category'){ categoryCol = col; } }, this);