From d8cd62ed215bb6acd9d92bfd0e124879b2de0a53 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Thu, 26 Jul 2018 16:59:50 -0400 Subject: [PATCH 1/2] Parse date into ISO 8601 format instead of seconds since UTC epoch Fix #14 by not converting to local or UTC date, instead slightly modifying DateTime strings into easy-to-parse ISO 8601 format. When a DateTime string follows the EXIF spec, we map some colons to dashes to conform to ISO 8601. The other supported format was ISO 8601, so we don't do anything in this case. --- lib/date.js | 86 ++++++---------------------------------------------- package.json | 3 +- 2 files changed, 12 insertions(+), 77 deletions(-) diff --git a/lib/date.js b/lib/date.js index fb72673..96b6366 100644 --- a/lib/date.js +++ b/lib/date.js @@ -1,84 +1,18 @@ -function parseNumber(s) { - return parseInt(s, 10); -} - -//in seconds -var hours = 3600; -var minutes = 60; - -//take date (year, month, day) and time (hour, minutes, seconds) digits in UTC -//and return a timestamp in seconds -function parseDateTimeParts(dateParts, timeParts) { - dateParts = dateParts.map(parseNumber); - timeParts = timeParts.map(parseNumber); - var year = dateParts[0]; - var month = dateParts[1] - 1; - var day = dateParts[2]; - var hours = timeParts[0]; - var minutes = timeParts[1]; - var seconds = timeParts[2]; - var date = Date.UTC(year, month, day, hours, minutes, seconds, 0); - var timestamp = date / 1000; - return timestamp; -} - -//parse date with "2004-09-04T23:39:06-08:00" format, -//one of the formats supported by ISO 8601, and -//convert to utc timestamp in seconds -function parseDateWithTimezoneFormat(dateTimeStr) { - - var dateParts = dateTimeStr.substr(0, 10).split('-'); - var timeParts = dateTimeStr.substr(11, 8).split(':'); - var timezoneStr = dateTimeStr.substr(19, 6); - var timezoneParts = timezoneStr.split(':').map(parseNumber); - var timezoneOffset = (timezoneParts[0] * hours) + - (timezoneParts[1] * minutes); - - var timestamp = parseDateTimeParts(dateParts, timeParts); - //minus because the timezoneOffset describes - //how much the described time is ahead of UTC - timestamp -= timezoneOffset; - - if(typeof timestamp === 'number' && !isNaN(timestamp)) { - return timestamp; - } -} - -//parse date with "YYYY:MM:DD hh:mm:ss" format, convert to utc timestamp in seconds -function parseDateWithSpecFormat(dateTimeStr) { - var parts = dateTimeStr.split(' '), - dateParts = parts[0].split(':'), - timeParts = parts[1].split(':'); - - var timestamp = parseDateTimeParts(dateParts, timeParts); - - if(typeof timestamp === 'number' && !isNaN(timestamp)) { - return timestamp; - } -} - function parseExifDate(dateTimeStr) { - //some easy checks to determine two common date formats + //some easy checks to determine common date formats //is the date in the standard "YYYY:MM:DD hh:mm:ss" format? - var isSpecFormat = dateTimeStr.length === 19 && - dateTimeStr.charAt(4) === ':'; - //is the date in the non-standard format, - //"2004-09-04T23:39:06-08:00" to include a timezone? - var isTimezoneFormat = dateTimeStr.length === 25 && - dateTimeStr.charAt(10) === 'T'; - var timestamp; - - if(isTimezoneFormat) { - return parseDateWithTimezoneFormat(dateTimeStr); - } - else if(isSpecFormat) { - return parseDateWithSpecFormat(dateTimeStr); - } + //if so, convert to ISO 8601 by replacing first two ':'s with '-'s + if (dateTimeStr.length === 19 && + dateTimeStr.charAt(4) === ':' && + dateTimeStr.charAt(7) === ':') { + dateTimeStr = dateTimeStr.slice(0, 4) + '-' + + dateTimeStr.slice(5, 7) + '-' + dateTimeStr.slice(8); + } + + return dateTimeStr; } module.exports = { - parseDateWithSpecFormat: parseDateWithSpecFormat, - parseDateWithTimezoneFormat: parseDateWithTimezoneFormat, parseExifDate: parseExifDate }; diff --git a/package.json b/package.json index 11dfcd0..9558f75 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ }, "devDependencies": { "browserify": "^7.0.0", - "uglify-js": "^2.4.15" + "uglify-js": "^2.4.15", + "nodeunit": "^0.11.3" } } From 79b44d31c6f83fc586b13bd9f32ce2dfe22ca8ab Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Thu, 26 Jul 2018 17:15:37 -0400 Subject: [PATCH 2/2] Fix tests --- test/test-date.js | 60 ------------------------------------------- test/test-simplify.js | 17 +++++++++--- 2 files changed, 13 insertions(+), 64 deletions(-) delete mode 100644 test/test-date.js diff --git a/test/test-date.js b/test/test-date.js deleted file mode 100644 index c0a6025..0000000 --- a/test/test-date.js +++ /dev/null @@ -1,60 +0,0 @@ -var date = require('../lib/date'); - -var minutes = 60; -var hours = minutes * 60; -var days = hours * 24; -var years = days * 365; -var leapYears = days * 366; - -module.exports = { - 'test parse unix epoch without timezone': function(test) { - var dateStr = '1970:01:01 00:00:00'; - var timestamp = date.parseDateWithSpecFormat(dateStr); - test.strictEqual(timestamp, 0); - test.done(); - }, - 'test parse given date without timezone': function(test) { - var dateStr = '1990:02:14 14:30:14'; - var timestamp = date.parseDateWithSpecFormat(dateStr); - //between 1970 and 1990 there were 5 leap years: 1972, 1976, 1980, 1984, 1988 - var expectedTimestamp = (15 * years) + (5 * leapYears) + - ((31 + 13) * days) + (14 * hours) + (30 * minutes) + 14; - test.strictEqual(timestamp, expectedTimestamp); - test.done(); - }, - 'test parse invalid date without timezone should not return anything': function(test) { - var dateStr = '1990:AA:14 14:30:14'; - var timestamp = date.parseDateWithSpecFormat(dateStr); - test.strictEqual(timestamp, undefined); - test.done(); - }, - 'test parse given date with timezone': function(test) { - var dateStr = '2004-09-04T23:39:06-08:00'; - var timestamp = date.parseDateWithTimezoneFormat(dateStr); - var yearsFromEpoch = 2004 - 1970; - //1972, 1976, 1980, 1984, 1988, 1992, 1996, 2000 - var leapYearsCount = 8; - //2004 is a leap year as well, hence 29 days for february - var dayCount = 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 3; - var expectedTimestamp = (yearsFromEpoch - leapYearsCount) * years + - leapYearsCount * leapYears + - dayCount * days + - 23 * hours + - 39 * minutes + - 6 + - 8 * hours; //for timezone - test.strictEqual(timestamp, expectedTimestamp); - test.done(); - }, - 'test parse invalid date with timezone': function(test) { - var dateStr = '2004-09-04T23:39:06A08:00'; - var timestamp = date.parseDateWithTimezoneFormat(dateStr); - test.strictEqual(timestamp, undefined); - test.done(); - }, - 'test parseExifDate': function(test) { - test.strictEqual(date.parseExifDate('1970:01:01 00:00:00'), 0); - test.strictEqual(date.parseExifDate('1970-01-01T00:00:00-01:00'), 3600); - test.done(); - } -} \ No newline at end of file diff --git a/test/test-simplify.js b/test/test-simplify.js index b68db75..611199e 100644 --- a/test/test-simplify.js +++ b/test/test-simplify.js @@ -1,5 +1,14 @@ var simplify = require('../lib/simplify'); +function parseDate(date) { + // Force UTC time for testing purposes. + if (date.indexOf(':') >= 0 && + (date.indexOf('Z') < 0 && date.indexOf('T') < 0)) { + date += 'Z'; + } + return new Date(date).getTime(); +} + module.exports = { 'test castDateValues': function(test) { var values = { @@ -16,9 +25,9 @@ module.exports = { } simplify.castDateValues(getTagValue, setTagValue); test.strictEqual(Object.keys(setValues).length, 3); - test.strictEqual(setValues.DateTimeOriginal, 0); - test.strictEqual(setValues.CreateDate, 5 * 3600); - test.strictEqual(setValues.ModifyDate, 5 * 3600); + test.strictEqual(parseDate(setValues.DateTimeOriginal), 0); + test.strictEqual(parseDate(setValues.CreateDate), 5 * 3600 * 1000); + test.strictEqual(parseDate(setValues.ModifyDate), 5 * 3600 * 1000); test.done(); } -} \ No newline at end of file +}