Skip to content

Commit

Permalink
Merge pull request #2266 from thostetler/fix-date-parsing-issue
Browse files Browse the repository at this point in the history
  • Loading branch information
thostetler committed May 23, 2023
2 parents c99ffec + dad89e2 commit 81f3c49
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 99 deletions.
11 changes: 9 additions & 2 deletions grunt/puppet.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ module.exports = function(grunt) {
});

// grab all the test files
let specs = grunt.file
const specs = grunt.file
.expand(
{
cwd: path.resolve('test/mocha/js/'),
Expand Down Expand Up @@ -209,17 +209,24 @@ module.exports = function(grunt) {
prod: {
options: {
env: 'production',
launchOptions: { headless: true, args: ['--no-sandbox'] },
},
},
dev: {
options: {
env: 'development',
launchOptions: { headless: true, args: ['--no-sandbox'] },
},
},
debug: {
options: {
env: 'development',
launchOptions: { headless: false, devtools: true, slowMo: 250 },
launchOptions: {
headless: false,
devtools: true,
slowMo: 250,
args: ['--no-sandbox'],
},
},
},
};
Expand Down
119 changes: 52 additions & 67 deletions src/js/mixins/papers_utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
define(['underscore', 'jquery-ui', 'jquery'], function(_, $ui, $) {
define(['underscore', 'jquery-ui', 'jquery', 'moment'], function(
_,
$ui,
$,
moment
) {
var Utils = {
/**
* Receives the ISO8601 date string (actually, browsers will be able to parse
Expand All @@ -11,78 +16,58 @@ define(['underscore', 'jquery-ui', 'jquery'], function(_, $ui, $) {
* string in ISO8601 format
* @param format
* string, jquery-ui datepicker for options
* @param foolsFormat
* array, jquery-ui datepicker format to use when we detect that
* hour == minute == second == 0 (this is ADS convention to mark
* unknown publication dates) or when a day or month are missing
* @param options
* @returns {*}
*/
formatDate: function(dateString, format) {
if (format && !_.isObject(format)) {
throw new Error('format must be an object of string formats');
}
format = _.defaults(format || {}, {
format: 'yy/mm/dd',
missing: { day: 'yy/mm', month: 'yy' },
separator: '-',
junk: '-00',
});

var fooIndex = ['day', 'month'];

var localDatePretendingToBeUtc;
var utc;
var formatToUse;
formatToUse = format.format;

utc = new Date(dateString);

if (_.isNaN(utc.getYear())) {
// we have to modify dateString, removing pattern until it parses
var i = 0;
var dateCopy = dateString;
var p = format.junk;
while (dateCopy.indexOf(p) > -1) {
dateCopy = dateCopy.substring(0, dateCopy.lastIndexOf(p));
try {
utc = new Date(dateCopy);
if (!_.isNaN(utc.getYear())) {
formatToUse = format.missing[fooIndex[i]];
if (!formatToUse) {
throw new Error('format is missing: ' + fooIndex[i]);
}
}
} catch (e) {
// pass
}
i += 1;
formatDate: function(dateString, options = {}) {
const { format = 'YYYY/MM', missing: missingOpts = {} } = options;

const missing = {
day: 'YYYY/MM',
month: 'YYYY',
dayAndMonth: 'YYYY',
...missingOpts,
};

// break apart date
const regex = /^(?<year>\d{4})-(?<month>\d{2}|00)-(?<day>\d{2}|00)$/;
const match = dateString.match(regex);

if (match) {
const { year, month, day } = match.groups;
const monthMissing = month === '00';
const dayMissing = day === '00';

const date = [
Number.parseInt(year, 10),

// months are zero-based, everything else is one-based
monthMissing ? 0 : Number.parseInt(month, 10) - 1,
dayMissing ? 1 : Number.parseInt(day, 10),
];

const utc = moment.utc(date);

if (!utc.isValid()) {
// if for some reason the parsed date is invalid, and assuming the year is always there, use that
return year;
}
if (_.isNaN(utc.getYear()))
throw new Error('Error parsing input: ' + dateString);
} else {
// it parsed well, but the string was too short
var s = format.separator;
if (dateString.indexOf(s) > -1) {
var parts = dateString.split(s);
if (parts.length == 2) {
formatToUse = format.missing[fooIndex[0]];
} else if (parts.length == 1) {
formatToUse = format.missing[fooIndex[1]];
}
if (!formatToUse) {
throw new Error('format is missing: missing.' + fooIndex[i]);
}

if (monthMissing && dayMissing) {
return utc.format(missing.dayAndMonth);
}
if (monthMissing) {
return utc.format(missing.month);
}
if (dayMissing) {
return utc.format(missing.day);
}
}

// the 'utc' contains UTC time, but it is displayed by browser in local time zone
// so we'll create another time, which will pretend to be UTC (but in reality it
// is just UTC+local offset); but it will display things as UTC; confused? ;-)
localDatePretendingToBeUtc = new Date(
utc.getTime() + utc.getTimezoneOffset() * 60000
);
return utc.format(format);
}

return $.datepicker.formatDate(formatToUse, localDatePretendingToBeUtc);
// if the regex doesn't match, return a null value
return null;
},

shortenAbstract: function(abs, maxLen) {
Expand Down
4 changes: 2 additions & 2 deletions src/js/widgets/abstract/widget.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,8 @@ define([

if (doc.pubdate) {
doc.formattedDate = PapersUtils.formatDate(doc.pubdate, {
format: 'MM d yy',
missing: { day: 'MM yy', month: 'yy' },
format: 'MMMM DD YYYY',
missing: { day: 'MMMM YYYY', month: 'YYYY' },
});
}

Expand Down
5 changes: 1 addition & 4 deletions src/js/widgets/library_list/widget.js
Original file line number Diff line number Diff line change
Expand Up @@ -413,10 +413,7 @@ define([
}

d.formattedDate = d.pubdate
? this.formatDate(d.pubdate, {
format: 'yy/mm',
missing: { day: 'yy/mm', month: 'yy' },
})
? this.formatDate(d.pubdate)
: undefined;
d.shortAbstract = d.abstract
? this.shortenAbstract(d.abstract)
Expand Down
5 changes: 1 addition & 4 deletions src/js/widgets/results/widget.js
Original file line number Diff line number Diff line change
Expand Up @@ -362,10 +362,7 @@ define([
}

d.formattedDate = d.pubdate
? self.formatDate(d.pubdate, {
format: 'yy/mm',
missing: { day: 'yy/mm', month: 'yy' },
})
? self.formatDate(d.pubdate)
: undefined;
d.shortAbstract = d.abstract
? self.shortenAbstract(d.abstract)
Expand Down
76 changes: 56 additions & 20 deletions test/mocha/js/mixins/papers_utils.spec.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,56 @@
define([
'js/mixins/papers_utils'
],
function(
PapersUtils
){

describe("Papers Utils Mixin (mixins/papers_utils.spec.js)", function(){

it("should know to format dates", function() {
expect(PapersUtils.formatDate('2011-01-01T00:00:00Z', {format: 'mm/yy'})).to.be.eql('01/2011');

expect(PapersUtils.formatDate('2011-12-01', {format: 'mm/yy', missing: {day: 'mm/yy', month: '--/yy'}})).to.be.eql('12/2011');
expect(PapersUtils.formatDate('2011-12-00', {format: 'mm/yy', missing: {day: 'mm/yy', month: '--/yy'}})).to.be.eql('12/2011');
expect(PapersUtils.formatDate('2011-00-00', {format: 'mm/yy', missing: {day: 'mm/yy', month: '--/yy'}})).to.be.eql('--/2011');

expect(function() {PapersUtils.formatDate('2011-12-0x', {format: 'mm/yy', missing: {day: 'mm/yy', month: '--/yy'}})}).to.throw.Exception;
});
})
});
define(['js/mixins/papers_utils'], function(Utils) {
describe('Papers Utils Mixin (mixins/papers_utils.spec.js)', function() {
it('returns formatted date with complete input', () => {
const dateString = '2023-05-22';
const format = 'YYYY/MM/DD';

const result = Utils.formatDate(dateString, { format });

expect(result).to.equal('2023/05/22');
});

it('returns formatted date with missing month', () => {
const dateString = '2023-00-22';
const missing = { month: 'YYYY', day: 'YYYY/MM' };

const result = Utils.formatDate(dateString, { missing });

expect(result).to.equal('2023');
});

it('returns formatted date with missing day', () => {
const dateString = '2023-05-00';
const missing = { day: 'YYYY/MM', month: 'YYYY' };

const result = Utils.formatDate(dateString, { missing });

expect(result).to.equal('2023/05');
});

it('returns formatted date with missing month and day', () => {
const dateString = '2023-00-00';
const missing = { dayAndMonth: 'YYYY' };

const result = Utils.formatDate(dateString, { missing });

expect(result).to.equal('2023');
});

it('returns year if parsed date is invalid', () => {
const dateString = '2023-13-45';
const format = 'YYYY/MM';

const result = Utils.formatDate(dateString, { format });

expect(result).to.equal('2023');
});

it('returns null if input does not match regex', () => {
const dateString = 'invalid-date';

const result = Utils.formatDate(dateString);

expect(result).to.equal(null);
});
});
});

0 comments on commit 81f3c49

Please sign in to comment.