elliptical-datetime
Version:
Elliptical phrases to handle natural language dates and times
531 lines (470 loc) • 16.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.InternalDate = exports.Date = exports.Day = exports.InternalDay = undefined;
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _elliptical = require('elliptical');
var _moment = require('moment');
var _moment2 = _interopRequireDefault(_moment);
var _duration = require('./duration');
var _ellipticalNumber = require('elliptical-number');
var _time = require('./time');
var _month = require('./month');
var _year = require('./year');
var _weekday = require('./weekday');
var _helpers = require('./helpers');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/** @jsx createElement */
const InternalDay = exports.InternalDay = {
defaultProps: {
recurse: true,
label: 'day'
},
describe({ props }) {
if (props.nullify) return;
return (0, _elliptical.createElement)(
'choice',
null,
props.recurse ? (0, _elliptical.createElement)(
'placeholder',
{
label: props.label,
arguments: props.phraseArguments || (props.phraseArguments ? [props.phraseArgument] : [props.label]) },
(0, _elliptical.createElement)(RecursiveDay, { label: props.label })
) : null,
(0, _elliptical.createElement)(
'sequence',
null,
props.prepositions ? (0, _elliptical.createElement)('literal', { text: 'on ', decorate: true }) : null,
(0, _elliptical.createElement)(
'placeholder',
{
label: props.label,
arguments: props.phraseArguments || (props.phraseArguments ? [props.phraseArgument] : [props.label]),
merge: true },
(0, _elliptical.createElement)(
'choice',
null,
(0, _elliptical.createElement)(DayAlone, null),
(0, _elliptical.createElement)(AmbiguousAbsoluteDay, null),
(0, _elliptical.createElement)(AmbiguousAbsoluteNamedMonth, null)
)
)
)
);
}
};
const Day = exports.Day = {
id: 'elliptical-datetime:Day',
defaultProps: {
recurse: true,
label: 'day'
},
describe({ props }) {
return (0, _elliptical.createElement)(InternalDay, props);
}
};
function* mapDate(option) {
for (let result of (0, _helpers.possibleDates)(option.result)) {
yield _lodash2.default.assign({}, option, { result });
}
}
const Date = exports.Date = {
id: 'elliptical-datetime:Date',
defaultProps: {
recurse: true,
prepositions: false,
nullify: false,
past: true,
future: true,
label: 'date'
},
filterResult(result, { props }) {
if (!props.past && (0, _moment2.default)(result).isBefore((0, _moment2.default)())) {
return false;
}
if (!props.future && (0, _moment2.default)(result).isAfter((0, _moment2.default)())) {
return false;
}
// if (result._ambiguousMonth) {
// return false
// }
return true;
},
describe({ props }) {
return (0, _elliptical.createElement)(
'map',
{ outbound: mapDate, skipIncomplete: true, limit: 1 },
(0, _elliptical.createElement)(InternalDate, props)
);
}
};
const InternalDate = exports.InternalDate = {
defaultProps: {
recurse: true,
prepositions: false,
nullify: false,
past: true,
future: true,
label: 'date'
},
describe({ props }) {
if (props.nullify) return;
return (0, _elliptical.createElement)(
'choice',
null,
(0, _elliptical.createElement)(
'placeholder',
{
label: props.label,
arguments: props.phraseArguments || (props.phraseArguments ? [props.phraseArgument] : [props.label]) },
(0, _elliptical.createElement)(
'choice',
null,
(0, _elliptical.createElement)(RelativeNamed, null),
(0, _elliptical.createElement)(RelativeNumbered, { prepositions: props.prepositions }),
(0, _elliptical.createElement)(DayWithYear, { prepositions: props.prepositions }),
(0, _elliptical.createElement)(RelativeAdjacent, null),
props.recurse ? (0, _elliptical.createElement)(RecursiveDate, { label: props.label }) : null
)
),
(0, _elliptical.createElement)(
'sequence',
null,
props.prepositions ? (0, _elliptical.createElement)('literal', { text: 'on ', decorate: true }) : null,
(0, _elliptical.createElement)(
'placeholder',
{
label: props.label,
arguments: props.phraseArguments || (props.phraseArguments ? [props.phraseArgument] : [props.label]),
merge: true },
(0, _elliptical.createElement)(
'choice',
null,
(0, _elliptical.createElement)(RelativeWeekday, null),
(0, _elliptical.createElement)(AbsoluteDay, null)
)
)
)
);
}
};
const DayWithYear = {
mapResult(result) {
const day = result.day;
const year = result.year;
if (year) {
const date = (0, _helpers.absoluteDate)(_lodash2.default.assign({ year: year.year }, day));
return { date, _ambiguousCentury: year._ambiguousCentury };
} else {
return {
date: (0, _helpers.absoluteDate)(day),
_ambiguousMonth: day._ambiguousMonth,
_ambiguousYear: true
};
}
},
describe({ props }) {
return (0, _elliptical.createElement)(
'sequence',
null,
(0, _elliptical.createElement)(Day, { prepositions: props.prepositions, id: 'day', recurse: false, ellipsis: true }),
(0, _elliptical.createElement)(
'sequence',
{ merge: true },
(0, _elliptical.createElement)('list', { items: [', ', ' in ', ' '], limit: 1 }),
(0, _elliptical.createElement)(_year.Year, { id: 'year' })
)
);
}
};
const ExtraDateDuration = {
mapResult(result) {
return { [result.type]: result.multiplier || 1 };
},
describe() {
return (0, _elliptical.createElement)(
'sequence',
null,
(0, _elliptical.createElement)(
'placeholder',
{ label: 'number' },
(0, _elliptical.createElement)('literal', { text: 'the ' })
),
(0, _elliptical.createElement)(
'placeholder',
{ label: 'time period', merge: true },
(0, _elliptical.createElement)('list', { items: [{ text: 'day', value: { type: 'days' } }, { text: 'fortnight', value: { type: 'days', multiplier: 14 } }, { text: 'week', value: { type: 'days', multiplier: 7 } }, { text: 'month', value: { type: 'months' } }, { text: 'year', value: { type: 'years' } }] })
)
);
}
};
const RecursiveDay = {
mapResult(result) {
const duration = result.direction === -1 ? (0, _helpers.negateDuration)(result.duration) : result.duration;
return (0, _helpers.relativeDay)(duration, result.day);
},
describe({ props }) {
return (0, _elliptical.createElement)(
'sequence',
null,
(0, _elliptical.createElement)(
'placeholder',
{ label: 'offset', merge: true },
(0, _elliptical.createElement)(
'sequence',
null,
(0, _elliptical.createElement)(
'choice',
{ id: 'duration' },
(0, _elliptical.createElement)(ExtraDateDuration, null),
(0, _elliptical.createElement)(_duration.DateDuration, null)
),
(0, _elliptical.createElement)('list', { merge: true, id: 'direction', items: [{ text: ' before', value: -1 }, { text: ' after', value: 1 }, { text: ' from', value: 1 }], limit: 2 })
)
),
(0, _elliptical.createElement)('literal', { text: ' ' }),
(0, _elliptical.createElement)(
'placeholder',
{ label: 'day', id: 'day' },
(0, _elliptical.createElement)(Day, { recurse: false, prepositions: false, label: props.label })
)
);
}
};
const RecursiveDate = {
mapResult(result) {
const duration = result.direction === -1 ? (0, _helpers.negateDuration)(result.duration) : result.duration;
return _lodash2.default.assign({}, result.date, {
date: (0, _helpers.relativeDate)(duration, result.date.date)
});
},
describe({ props }) {
return (0, _elliptical.createElement)(
'sequence',
null,
(0, _elliptical.createElement)(
'placeholder',
{ label: 'offset', merge: true },
(0, _elliptical.createElement)(
'sequence',
null,
(0, _elliptical.createElement)(
'choice',
{ id: 'duration' },
(0, _elliptical.createElement)(ExtraDateDuration, null),
(0, _elliptical.createElement)(_duration.DateDuration, null)
),
(0, _elliptical.createElement)('list', { merge: true, id: 'direction', items: [{ text: ' before', value: -1 }, { text: ' after', value: 1 }, { text: ' from', value: 1 }], limit: 2 })
)
),
(0, _elliptical.createElement)('literal', { text: ' ' }),
(0, _elliptical.createElement)(InternalDate, { id: 'date', label: props.label, recurse: false, prepositions: false })
);
}
};
const RelativeNamed = {
mapResult(result) {
return { date: (0, _helpers.relativeDate)(result) };
},
describe() {
return (0, _elliptical.createElement)('list', { items: [{ text: 'today', value: { days: 0 } }, { text: 'tomorrow', value: { days: 1 } }, { text: 'yesterday', value: { days: -1 } }, { text: 'now', value: { days: 0 } }, { text: 'right now', value: { days: 0 } }], limit: 3 });
}
};
const RelativeNumbered = {
mapResult(result) {
const duration = result.direction === -1 ? (0, _helpers.negateDuration)(result.duration) : result.duration;
return { date: (0, _helpers.relativeDate)(duration) };
},
describe({ props }) {
return (0, _elliptical.createElement)(
'choice',
null,
props.prepositions ? (0, _elliptical.createElement)(
'sequence',
null,
(0, _elliptical.createElement)('literal', { text: 'in ', id: 'direction', value: 1 }),
(0, _elliptical.createElement)(_duration.DateDuration, { id: 'duration' })
) : null,
(0, _elliptical.createElement)(
'sequence',
null,
(0, _elliptical.createElement)(_duration.DateDuration, { id: 'duration' }),
(0, _elliptical.createElement)('literal', { text: ' ago', id: 'direction', value: -1 })
)
);
}
};
const RelativeAdjacent = {
mapResult(result) {
const duration = {
[result.type]: result.num * (result.multiplier || 1)
};
return { date: (0, _helpers.relativeDate)(duration) };
},
describe() {
return (0, _elliptical.createElement)(
'placeholder',
{ label: 'time period' },
(0, _elliptical.createElement)(
'sequence',
null,
(0, _elliptical.createElement)('list', { id: 'num', items: [{ text: 'next ', value: 1 }, { text: 'last ', value: -1 }] }),
(0, _elliptical.createElement)(
'placeholder',
{ label: 'time period', merge: true },
(0, _elliptical.createElement)('list', { items: [{ text: 'week', value: { type: 'days', multiplier: 7 } }, { text: 'month', value: { type: 'months' } }, { text: 'year', value: { type: 'years' } }] })
)
)
);
}
};
function dateFromRelative(distance, weekday) {
const day = distance * 7 + weekday;
return (0, _moment2.default)().day(day).toDate();
}
const RelativeWeekday = {
mapResult(result) {
const date = dateFromRelative(result.distance || 0, result.weekday);
if (result.distance == null) {
return { date, _ambiguousWeek: true };
} else {
return { date };
}
},
describe() {
return (0, _elliptical.createElement)(
'choice',
null,
(0, _elliptical.createElement)(
'sequence',
null,
(0, _elliptical.createElement)('list', { optional: true, id: 'distance', items: [{ text: 'last ', value: -1 }, { text: 'this ', value: 0 }, { text: 'next ', value: 1 }, { text: 'this upcoming ', value: 1 }], limit: 1 }),
(0, _elliptical.createElement)(_weekday.Weekday, { id: 'weekday' })
),
(0, _elliptical.createElement)(
'sequence',
null,
(0, _elliptical.createElement)('literal', { text: 'the ' }),
(0, _elliptical.createElement)(
'placeholder',
{ label: 'relative weekday', merge: true },
(0, _elliptical.createElement)(
'sequence',
null,
(0, _elliptical.createElement)(_weekday.Weekday, { id: 'weekday' }),
(0, _elliptical.createElement)('list', { id: 'distance', items: [{ text: ' after next', value: 2 }, { text: ' after this', value: 1 }, { text: ' before this', value: -1 }, { text: ' before last', value: -2 }] })
)
)
)
);
}
};
function leapYear(year) {
return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
}
const MonthNumber = {
mapResult: result => parseInt(result, 10) - 1,
describe: () => (0, _elliptical.createElement)(_ellipticalNumber.DigitString, { maxLength: 2, max: 12, min: 1, label: 'mm' })
};
const DayNumber = {
mapResult: result => parseInt(result, 10),
describe: () => (0, _elliptical.createElement)(_ellipticalNumber.DigitString, { maxLength: 2, max: 31, min: 1, label: 'dd' })
};
const AbsoluteDay = {
mapResult(result) {
const date = (0, _helpers.absoluteDate)(_lodash2.default.assign({}, result, { year: result.year.year }));
return { date, _ambiguousCentury: result.year._ambiguousCentury };
},
filterResult(result) {
let years = result._ambiguousCentury ? [0, 100, -100] : [0];
return _lodash2.default.some(years, year => {
const date = _lodash2.default.assign({}, result, { year: result.date.getFullYear() + year });
return (0, _helpers.validateDay)(date);
});
},
describe() {
return (0, _elliptical.createElement)(
'sequence',
null,
(0, _elliptical.createElement)(AmbiguousAbsoluteDay, { merge: true }),
(0, _elliptical.createElement)('list', { items: ['/'], limit: 1 }),
(0, _elliptical.createElement)(_year.Year, { id: 'year' })
);
}
};
const DayAlone = {
mapResult(result) {
return {
month: (0, _moment2.default)().month(),
day: result,
_ambiguousMonth: true
};
},
describe() {
return (0, _elliptical.createElement)(
'sequence',
null,
(0, _elliptical.createElement)('literal', { text: 'the ', optional: true, preferred: true, limited: true }),
(0, _elliptical.createElement)(
'choice',
{ limit: 1, merge: true },
(0, _elliptical.createElement)(_ellipticalNumber.Integer, { allowWordForm: true, max: 31, min: 1, limit: 1, allowLeadingZero: false }),
(0, _elliptical.createElement)(_ellipticalNumber.Ordinal, { allowWordForm: true, max: 31, limit: 1 })
)
);
}
};
const AmbiguousAbsoluteDay = {
describe() {
return (0, _elliptical.createElement)(
'filter',
{ 'function': _helpers.validateDay },
(0, _elliptical.createElement)(
'sequence',
null,
(0, _elliptical.createElement)(MonthNumber, { id: 'month' }),
(0, _elliptical.createElement)('list', { items: ['/'], limit: 1 }),
(0, _elliptical.createElement)(DayNumber, { id: 'day' })
)
);
}
};
const AmbiguousAbsoluteNamedMonth = {
filterResult(result) {
return (0, _helpers.validateDay)(result);
},
describe() {
return (0, _elliptical.createElement)(
'choice',
null,
(0, _elliptical.createElement)(
'sequence',
null,
(0, _elliptical.createElement)(_month.Month, { id: 'month' }),
(0, _elliptical.createElement)('list', { items: [' ', ' the '], limit: 1 }),
(0, _elliptical.createElement)(
'choice',
{ id: 'day', limit: 1 },
(0, _elliptical.createElement)(_ellipticalNumber.Integer, { allowWordForm: true, max: 31, min: 1, limit: 1 }),
(0, _elliptical.createElement)(_ellipticalNumber.Ordinal, { allowWordForm: true, max: 31, limit: 1 })
)
),
(0, _elliptical.createElement)(
'sequence',
null,
(0, _elliptical.createElement)('literal', { text: 'the ' }),
(0, _elliptical.createElement)(
'choice',
{ id: 'day', limit: 1 },
(0, _elliptical.createElement)(_ellipticalNumber.Integer, { allowWordForm: true, max: 31, min: 1, limit: 1 }),
(0, _elliptical.createElement)(_ellipticalNumber.Ordinal, { allowWordForm: true, max: 31, limit: 1 })
),
(0, _elliptical.createElement)('list', { items: [' of ', ' '], limit: 1 }),
(0, _elliptical.createElement)(_month.Month, { id: 'month' })
)
);
}
};