Skip to content

Commit

Permalink
feat(bc-dates): Use extended ISO strings throughout the application
Browse files Browse the repository at this point in the history
Format as (-)yyyy for display and inputs, but represent underlying date as full ISO string (±yyyyyy)
  • Loading branch information
MonkeyDo committed Jun 21, 2019
1 parent fc47de6 commit d83332c
Show file tree
Hide file tree
Showing 11 changed files with 105 additions and 50 deletions.
13 changes: 3 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"babel-runtime": "^6.23.0",
"bluebird": "^3.5.1",
"body-parser": "^1.14.1",
"bookbrainz-data": "^2.2.1",
"bookbrainz-data": "^2.3.0",
"browserify": "^16.2.3",
"classnames": "^2.2.5",
"compression": "^1.7.1",
Expand Down
7 changes: 4 additions & 3 deletions src/client/components/pages/entities/author.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ import React from 'react';
import {kebabCase as _kebabCase} from 'lodash';
import {labelsForAuthor} from '../../../helpers/utils';


const {deletedEntityMessage, extractAttribute, getTypeAttribute, getEntityUrl,
ENTITY_TYPE_ICONS, getSortNameOfDefaultAlias} = entityHelper;
ENTITY_TYPE_ICONS, getSortNameOfDefaultAlias, transformISODateForDisplay} = entityHelper;
const {Button, Col, Row} = bootstrap;

function AuthorAttributes({author}) {
Expand All @@ -41,8 +42,8 @@ function AuthorAttributes({author}) {
const gender = extractAttribute(author.gender, 'name');
const beginArea = extractAttribute(author.beginArea, 'name');
const endArea = extractAttribute(author.endArea, 'name');
const beginDate = extractAttribute(author.beginDate);
const endDate = extractAttribute(author.endDate);
const beginDate = transformISODateForDisplay(extractAttribute(author.beginDate));
const endDate = transformISODateForDisplay(extractAttribute(author.endDate));
const sortNameOfDefaultAlias = getSortNameOfDefaultAlias(author);

const isGroup = type === 'Group';
Expand Down
6 changes: 3 additions & 3 deletions src/client/components/pages/entities/publisher.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import PropTypes from 'prop-types';
import React from 'react';

const {deletedEntityMessage, extractAttribute, getTypeAttribute, getEntityUrl,
ENTITY_TYPE_ICONS, getSortNameOfDefaultAlias} = entityHelper;
ENTITY_TYPE_ICONS, getSortNameOfDefaultAlias, transformISODateForDisplay} = entityHelper;
const {Col, Row} = bootstrap;

function PublisherAttributes({publisher}) {
Expand All @@ -39,8 +39,8 @@ function PublisherAttributes({publisher}) {
}
const type = getTypeAttribute(publisher.publisherType).data;
const area = extractAttribute(publisher.area, 'name');
const beginDate = extractAttribute(publisher.beginDate);
const endDate = extractAttribute(publisher.endDate);
const beginDate = transformISODateForDisplay(extractAttribute(publisher.beginDate));
const endDate = transformISODateForDisplay(extractAttribute(publisher.endDate));
const sortNameOfDefaultAlias = getSortNameOfDefaultAlias(publisher);
return (
<div>
Expand Down
25 changes: 16 additions & 9 deletions src/client/entity-editor/common/new-date-field.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as _ from 'lodash';
import {Button, FormControl, InputGroup} from 'react-bootstrap';
import {dateObjectToString, getTodayDate} from '../../helpers/utils';
import {dateObjectToISOString, getTodayDate} from '../../helpers/utils';
import {isValid, parseISO} from 'date-fns';
import CustomInput from '../../input';
import DatePicker from 'react-datepicker';
Expand All @@ -10,6 +10,7 @@ import React from 'react';
import ValidationLabel from './validation-label';
import classNames from 'classnames';
import {dateIsBefore} from '../validators/base';
import {transformISODateForDisplay} from '../../helpers/entity';


class DateField extends React.Component {
Expand All @@ -21,7 +22,7 @@ class DateField extends React.Component {
day: !day ? '' : this.padMonthOrDay(day),
month: !month ? '' : this.padMonthOrDay(month),
warn: dateIsBefore(getTodayDate(), {day, month, year}),
year: !year ? '' : this.padYear(year)
year: !year ? '' : this.formatYearForDisplay(year)
};
}

Expand Down Expand Up @@ -63,14 +64,20 @@ class DateField extends React.Component {
);
};

padYear = (year) => {
// If year is a number, pad it for clarity ('84' -> '0084' to clarify it isn't '1984')
/**
* If year is a number, pad it for clarity ('84' -> '0084' to clarify it isn't '1984')
* If it is too long (eg. extended ISO format ±YYYYYY), trim it
* @function formatYearForDisplay
* @param {string|number} year - The year string or number to format
* @returns {string} a short ISO date string (YYYY-MM-DD)
*/
formatYearForDisplay = (year) => {
if (isNaN(Number(year)) || year === '') {
return year;
}
const isCommonEraDate = Math.sign(year) === 1;
return isCommonEraDate ? _.padStart(year, 4, 0) :
`-${_.padStart(Math.abs(year), 4, 0)}`;
const ISOyear = `${isCommonEraDate ? '+' : '-'}${_.padStart(Math.abs(year), 6, 0)}`;
return transformISODateForDisplay(ISOyear);
};

padMonthOrDay = (num) => {
Expand All @@ -83,7 +90,7 @@ class DateField extends React.Component {

handleYearInputBlur = (event) => {
const year = event.target.value;
this.setState({year: this.padYear(year)});
this.setState({year: this.formatYearForDisplay(year)});
};

handleMonthInputBlur = (event) => {
Expand All @@ -102,7 +109,7 @@ class DateField extends React.Component {
const month = (date.getMonth() + 1).toString();
const day = date.getDate().toString();
this.setState(
{day: this.padMonthOrDay(day), month: this.padMonthOrDay(month), year: this.padYear(year)},
{day: this.padMonthOrDay(day), month: this.padMonthOrDay(month), year: this.formatYearForDisplay(year)},
this.setStateCallback
);
};
Expand All @@ -118,7 +125,7 @@ class DateField extends React.Component {
{this.props.label}
</ValidationLabel>
);
const dateString = dateObjectToString({
const dateString = dateObjectToISOString({
day: this.state.day,
month: this.state.month,
year: this.state.year
Expand Down
38 changes: 36 additions & 2 deletions src/client/helpers/entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import * as bootstrap from 'react-bootstrap';

import {get as _get, kebabCase as _kebabCase} from 'lodash';

import {format, isValid, parseISO} from 'date-fns';
import FontAwesome from 'react-fontawesome';
import React from 'react';

Expand Down Expand Up @@ -64,6 +64,40 @@ export function getDateAttributes(entity) {
return attributes;
}


/**
* Transforms an extended ISO 8601-2004 string to a more human-firendly result
* @function transformISODateForDisplay
* @param {string} ISODateString - an extended ISO date string (±YYYYYY-MM-DD)
* @returns {string} A date string with less padding zeros
*/
export function transformISODateForDisplay(ISODateString) {
const dateStringWithoutSign = ISODateString.slice(1);
const parts = dateStringWithoutSign.split('-');
let formatting;
switch (parts.length) {
case 1:
formatting = 'uuuu';
break;
case 2:
formatting = 'uuuu-MM';
break;
case 3:
formatting = 'uuuu-MM-dd';
break;
default:
return ISODateString;
}
const parsedDate = parseISO(ISODateString, {additionalDigits: 2});
if (!isValid(parsedDate)) {
return ISODateString;
}
return format(
parsedDate,
formatting
);
}

export function showEntityEditions(entity) {
return (
<div>
Expand Down Expand Up @@ -143,7 +177,7 @@ export function getEditionReleaseDate(edition) {
edition.releaseEventSet.releaseEvents.length;

if (hasReleaseEvents) {
return edition.releaseEventSet.releaseEvents[0].date;
return transformISODateForDisplay(edition.releaseEventSet.releaseEvents[0].date);
}

return '?';
Expand Down
9 changes: 8 additions & 1 deletion src/client/helpers/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,14 @@ export function getTodayDate() {
return {day, month, year};
}

export function dateObjectToString(value) {
/**
* Format a {day, month, year} object into an ISO 8601-2004 string (±YYYYYY-MM-DD)
* This is a duplicate of a util that cannot be direcly imported from src/server/helpers/entityRouteUtils.js
* @function dateObjectToISOString
* @param {string} value - a {day, month, year} object
* @returns {string} ISO 8601-2004 string (±YYYYYY-MM-DD)
*/
export function dateObjectToISOString(value) {
const isCommonEraDate = Math.sign(value.year) > -1;
// Convert to ISO 8601:2004 extended for BCE years (±YYYYYY)
let date = `${isCommonEraDate ? '+' : '-'}${_.padStart(Math.abs(value.year).toString(), 6, '0')}`;
Expand Down
17 changes: 15 additions & 2 deletions src/server/helpers/entityRouteUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,14 @@ export function addInitialRelationship(props, relationshipTypeId, relationshipIn
return props;
}

export function separateDateInObject(value: string) {
/**
* Parse an ISO 8601-2004 string and return an object with separate day, month and year, if they exist.
* If any of the values don't exist, the default is an empty string.
* @function ISODateStringToObject
* @param {string} value - relationshipId number for initaial relationship
* @returns {object} a {day, month, year} object
*/
export function ISODateStringToObject(value: string) {
const date = value ? value.split('-') : [];
// A leading minus sign denotes a BC date
// This creates an empty first array item that needs to be removed,
Expand All @@ -253,7 +260,13 @@ export function separateDateInObject(value: string) {
};
}

export function dateObjectToString(value) {
/**
* Format a {day, month, year} object into an ISO 8601-2004 string (±YYYYYY-MM-DD)
* @function dateObjectToISOString
* @param {string} value - a {day, month, year} object
* @returns {string} ISO 8601-2004 string (±YYYYYY-MM-DD)
*/
export function dateObjectToISOString(value) {
const isCommonEraDate = Math.sign(value.year) > -1;
// Convert to ISO 8601:2004 extended for BCE years (±YYYYYY)
let date = `${isCommonEraDate ? '+' : '-'}${_.padStart(Math.abs(value.year).toString(), 6, '0')}`;
Expand Down
14 changes: 7 additions & 7 deletions src/server/routes/entity/author.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ import * as middleware from '../../helpers/middleware';
import * as utils from '../../helpers/utils';

import {
dateObjectToString,
ISODateStringToObject,
dateObjectToISOString,
entityEditorMarkup,
generateEntityProps,
makeEntityCreateOrEditHandler,
separateDateInObject
makeEntityCreateOrEditHandler
} from '../../helpers/entityRouteUtils';

import _ from 'lodash';
Expand Down Expand Up @@ -144,9 +144,9 @@ function authorToFormState(author) {

const authorSection = {
beginArea: entityRoutes.areaToOption(author.beginArea),
beginDate: separateDateInObject(author.beginDate),
beginDate: ISODateStringToObject(author.beginDate),
endArea: entityRoutes.areaToOption(author.endArea),
endDate: separateDateInObject(author.endDate),
endDate: ISODateStringToObject(author.endDate),
ended: author.ended,
gender: author.gender && author.gender.id,
type: author.authorType && author.authorType.id
Expand Down Expand Up @@ -223,11 +223,11 @@ function transformNewForm(data) {
aliases,
beginAreaId: data.authorSection.beginArea &&
data.authorSection.beginArea.id,
beginDate: dateObjectToString(data.authorSection.beginDate),
beginDate: dateObjectToISOString(data.authorSection.beginDate),
disambiguation: data.nameSection.disambiguation,
endAreaId: data.authorSection.endArea &&
data.authorSection.endArea.id,
endDate: data.authorSection.ended ? dateObjectToString(data.authorSection.endDate) : '',
endDate: data.authorSection.ended ? dateObjectToISOString(data.authorSection.endDate) : '',
ended: data.authorSection.ended,
genderId: data.authorSection.gender,
identifiers,
Expand Down
10 changes: 5 additions & 5 deletions src/server/routes/entity/edition.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ import * as middleware from '../../helpers/middleware';
import * as utils from '../../helpers/utils';

import {
ISODateStringToObject,
addInitialRelationship,
dateObjectToString,
dateObjectToISOString,
entityEditorMarkup,
generateEntityProps,
makeEntityCreateOrEditHandler,
separateDateInObject
makeEntityCreateOrEditHandler
} from '../../helpers/entityRouteUtils';

import Promise from 'bluebird';
Expand Down Expand Up @@ -250,7 +250,7 @@ function editionToFormState(edition) {
);

const releaseDate = edition.releaseEventSetId ?
separateDateInObject(edition.releaseEventSet.releaseEvents[0].date) :
ISODateStringToObject(edition.releaseEventSet.releaseEvents[0].date) :
{day: '', month: '', year: ''};

const publisher = edition.publisherSet && (
Expand Down Expand Up @@ -339,7 +339,7 @@ function transformNewForm(data) {

let releaseEvents = [];
if (data.editionSection.releaseDate.year) {
releaseEvents = [{date: dateObjectToString(data.editionSection.releaseDate)}];
releaseEvents = [{date: dateObjectToISOString(data.editionSection.releaseDate)}];
}

const languages = _.map(
Expand Down
14 changes: 7 additions & 7 deletions src/server/routes/entity/publisher.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ import * as entityRoutes from './entity';
import * as middleware from '../../helpers/middleware';
import * as utils from '../../helpers/utils';
import {
dateObjectToString,
ISODateStringToObject,
dateObjectToISOString,
entityEditorMarkup,
generateEntityProps,
makeEntityCreateOrEditHandler,
separateDateInObject
makeEntityCreateOrEditHandler
} from '../../helpers/entityRouteUtils';
import _ from 'lodash';
import {escapeProps} from '../../helpers/props';
Expand Down Expand Up @@ -161,8 +161,8 @@ function publisherToFormState(publisher) {

const publisherSection = {
area: entityRoutes.areaToOption(publisher.area),
beginDate: separateDateInObject(publisher.beginDate),
endDate: separateDateInObject(publisher.endDate),
beginDate: ISODateStringToObject(publisher.beginDate),
endDate: ISODateStringToObject(publisher.endDate),
ended: publisher.ended,
type: publisher.publisherType && publisher.publisherType.id
};
Expand Down Expand Up @@ -228,10 +228,10 @@ function transformNewForm(data) {
return {
aliases,
areaId: data.publisherSection.area && data.publisherSection.area.id,
beginDate: dateObjectToString(data.publisherSection.beginDate),
beginDate: dateObjectToISOString(data.publisherSection.beginDate),
disambiguation: data.nameSection.disambiguation,
endDate: data.publisherSection.ended ?
dateObjectToString(data.publisherSection.endDate) : '',
dateObjectToISOString(data.publisherSection.endDate) : '',
ended: data.publisherSection.ended,
identifiers,
note: data.submissionSection.note,
Expand Down

0 comments on commit d83332c

Please sign in to comment.