Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature] Add era support for en,ja (closes #4591) #4599

Closed
wants to merge 11 commits into from
8 changes: 7 additions & 1 deletion src/lib/create/from-string-and-format.js
Expand Up @@ -4,7 +4,7 @@ import { getParseRegexForToken } from '../parse/regex';
import { addTimeToArrayFromToken } from '../parse/token';
import { expandFormat, formatTokenFunctions, formattingTokens } from '../format/format';
import checkOverflow from './check-overflow';
import { HOUR } from '../units/constants';
import { YEAR, HOUR } from '../units/constants';
import { hooks } from '../utils/hooks';
import getParsingFlags from './parsing-flags';

Expand Down Expand Up @@ -82,6 +82,12 @@ export function configFromStringAndFormat(config) {
// handle meridiem
config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem);

// handle era
var era = getParsingFlags(config).era;
if (era !== null) {
config._a[YEAR] = config._locale.erasConvertYear(era, config._a[YEAR]);
}

configFromArray(config);
checkOverflow(config);
}
Expand Down
2 changes: 2 additions & 0 deletions src/lib/create/parsing-flags.js
Expand Up @@ -7,11 +7,13 @@ function defaultParsingFlags() {
overflow : -2,
charsLeftOver : 0,
nullInput : false,
invalidEra : null,
invalidMonth : null,
invalidFormat : false,
userInvalidated : false,
iso : false,
parsedDateParts : [],
era : null,
meridiem : null,
rfc2822 : false,
weekdayMismatch : false
Expand Down
1 change: 1 addition & 0 deletions src/lib/create/valid.js
Expand Up @@ -12,6 +12,7 @@ export function isValid(m) {
var isNowValid = !isNaN(m._d.getTime()) &&
flags.overflow < 0 &&
!flags.empty &&
!flags.invalidEra &&
!flags.invalidMonth &&
!flags.invalidWeekday &&
!flags.weekdayMismatch &&
Expand Down
2 changes: 1 addition & 1 deletion src/lib/format/format.js
@@ -1,7 +1,7 @@
import zeroFill from '../utils/zero-fill';
import isFunction from '../utils/is-function';

export var formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g;
export var formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|N{1,5}|YYYYYY|YYYYY|YYYY|YY|y{2,4}|yo?|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g;

var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g;

Expand Down
18 changes: 18 additions & 0 deletions src/lib/locale/en.js
Expand Up @@ -3,6 +3,24 @@ import { getSetGlobalLocale } from './locales';
import toInt from '../utils/to-int';

getSetGlobalLocale('en', {
eras: [
{
since: '0001-01-01',
until: +Infinity,
offset: 1,
name: 'Anno Domini',
narrow: 'AD',
abbr: 'AD'
},
{
since: '0000-12-31',
until: -Infinity,
offset: 1,
name: 'Before Christ',
narrow: 'BC',
abbr: 'BC'
}
],
dayOfMonthOrdinalParse: /\d{1,2}(th|st|nd|rd)/,
ordinal : function (number) {
var b = number % 10,
Expand Down
16 changes: 16 additions & 0 deletions src/lib/locale/prototype.js
Expand Up @@ -20,6 +20,22 @@ proto.relativeTime = relativeTime;
proto.pastFuture = pastFuture;
proto.set = set;

// Eras
import {
localeEras,
localeErasParse,
localeErasConvertYear,
erasAbbrRegex,
erasNameRegex,
erasNarrowRegex,
} from '../units/era';
proto.eras = localeEras;
proto.erasParse = localeErasParse;
proto.erasConvertYear = localeErasConvertYear;
proto.erasAbbrRegex = erasAbbrRegex;
proto.erasNameRegex = erasNameRegex;
proto.erasNarrowRegex = erasNarrowRegex;

// Month
import {
localeMonthsParse,
Expand Down
7 changes: 7 additions & 0 deletions src/lib/moment/prototype.js
Expand Up @@ -57,6 +57,13 @@ proto.unix = unix;
proto.valueOf = valueOf;
proto.creationData = creationData;

// Era
import { getEraName, getEraNarrow, getEraAbbr, getEraYear } from '../units/era';
proto.eraName = getEraName;
proto.eraNarrow = getEraNarrow;
proto.eraAbbr = getEraAbbr;
proto.eraYear = getEraYear;

// Year
import { getSetYear, getIsLeapYear } from '../units/year';
proto.year = getSetYear;
Expand Down
250 changes: 250 additions & 0 deletions src/lib/units/era.js
@@ -0,0 +1,250 @@
import { addFormatToken } from '../format/format';
import { addRegexToken, matchUnsigned, regexEscape } from '../parse/regex';
import { addParseToken } from '../parse/token';
import { YEAR } from './constants';
import { hooks as moment } from '../utils/hooks';
import { getLocale } from '../locale/locales';
import getParsingFlags from '../create/parsing-flags';
import hasOwnProp from '../utils/has-own-prop';

addFormatToken('N', 0, 0, 'eraAbbr');
addFormatToken('NN', 0, 0, 'eraAbbr');
addFormatToken('NNN', 0, 0, 'eraAbbr');
addFormatToken('NNNN', 0, 0, 'eraName');
addFormatToken('NNNNN', 0, 0, 'eraNarrow');

addFormatToken('y', ['y', 1], 'yo', 'eraYear');
addFormatToken('y', ['yy', 2], 0, 'eraYear');
addFormatToken('y', ['yyy', 3], 0, 'eraYear');
addFormatToken('y', ['yyyy', 4], 0, 'eraYear');

addRegexToken('N', matchEraAbbr);
addRegexToken('NN', matchEraAbbr);
addRegexToken('NNN', matchEraAbbr);
addRegexToken('NNNN', matchEraName);
addRegexToken('NNNNN', matchEraNarrow);

addParseToken(['N', 'NN', 'NNN', 'NNNN', 'NNNNN'], function (input, array, config, token) {
var era = config._locale.erasParse(input, token, config._strict);
if (era) {
getParsingFlags(config).era = era;
} else {
getParsingFlags(config).invalidEra = input;
}
});

addRegexToken('y', matchUnsigned);
addRegexToken('yy', matchUnsigned);
addRegexToken('yyy', matchUnsigned);
addRegexToken('yyyy', matchUnsigned);
addRegexToken('yo', matchEraYearOrdinal);

addParseToken(['y', 'yy', 'yyy', 'yyyy'], YEAR);
addParseToken(['yo'], function (input, array, config, token) {
var match;
if (config._locale._eraYearOrdinalRegex) {
match = input.match(config._locale._eraYearOrdinalRegex);
}

if (config._locale.eraYearOrdinalParse) {
array[YEAR] = config._locale.eraYearOrdinalParse(input, match);
} else {
array[YEAR] = parseInt(input, 10);
}
});

export function localeEras(m, format) {
var i, l, date, eras = this._eras || getLocale('en')._eras;
for (i = 0, l = eras.length; i < l; ++i) {
switch (typeof eras[i].since) {
case 'string':
// truncate time
date = moment(eras[i].since).startOf('day');
eras[i].since = date.valueOf();
break;
}

switch (typeof eras[i].until) {
case 'undefined':
eras[i].until = +Infinity;
break;
case 'string':
// truncate time
date = moment(eras[i].until).startOf('day').valueOf();
eras[i].until = date.valueOf();
break;
}
}
return eras;
}

export function localeErasParse(eraName, format, strict) {
var i, l, eras = this.eras(),
name, abbr, narrow;
eraName = eraName.toUpperCase();

for (i = 0, l = eras.length; i < l; ++i) {
name = eras[i].name.toUpperCase();
abbr = eras[i].abbr.toUpperCase();
narrow = eras[i].narrow.toUpperCase();

if (strict) {
switch (format) {
case 'N':
case 'NN':
case 'NNN':
if (abbr === eraName) {
return eras[i];
}
break;

case 'NNNN':
if (name === eraName) {
return eras[i];
}
break;

case 'NNNNN':
if (narrow === eraName) {
return eras[i];
}
break;
}
} else if ([name, abbr, narrow].indexOf(eraName) >= 0) {
return eras[i];
}
}
}

export function localeErasConvertYear(era, year) {
var dir = era.since <= era.until ? +1 : -1;
if (year === undefined) {
return moment(era.since).year();
} else {
return moment(era.since).year() + (year - era.offset) * dir;
}
}

export function getEraName() {
var i, l, val, eras = this.localeData().eras();
for (i = 0, l = eras.length; i < l; ++i) {
// truncate time
val = this.startOf('day').valueOf();

if (eras[i].since <= val && val <= eras[i].until) {
return eras[i].name;
}
if (eras[i].until <= val && val <= eras[i].since) {
return eras[i].name;
}
}

return '';
}

export function getEraNarrow() {
var i, l, val, eras = this.localeData().eras();
for (i = 0, l = eras.length; i < l; ++i) {
// truncate time
val = this.startOf('day').valueOf();

if (eras[i].since <= val && val <= eras[i].until) {
return eras[i].narrow;
}
if (eras[i].until <= val && val <= eras[i].since) {
return eras[i].narrow;
}
}

return '';
}

export function getEraAbbr() {
var i, l, val, eras = this.localeData().eras();
for (i = 0, l = eras.length; i < l; ++i) {
// truncate time
val = this.startOf('day').valueOf();

if (eras[i].since <= val && val <= eras[i].until) {
return eras[i].abbr;
}
if (eras[i].until <= val && val <= eras[i].since) {
return eras[i].abbr;
}
}

return '';
}

export function getEraYear() {
var i, l, dir, val, eras = this.localeData().eras();
for (i = 0, l = eras.length; i < l; ++i) {
dir = eras[i].since <= eras[i].until ? +1 : -1;

// truncate time
val = this.startOf('day').valueOf();

if ((eras[i].since <= val && val <= eras[i].until) || (eras[i].until <= val && val <= eras[i].since)) {
return (this.year() - moment(eras[i].since).year()) * dir + eras[i].offset;
}
}

return this.year();
}

export function erasNameRegex(isStrict) {
if (!hasOwnProp(this, '_erasNameRegex')) {
computeErasParse.call(this);
}
return isStrict ? this._erasNameRegex : this._erasRegex;
}

export function erasAbbrRegex(isStrict) {
if (!hasOwnProp(this, '_erasAbbrRegex')) {
computeErasParse.call(this);
}
return isStrict ? this._erasAbbrRegex : this._erasRegex;
}

export function erasNarrowRegex(isStrict) {
if (!hasOwnProp(this, '_erasNarrowRegex')) {
computeErasParse.call(this);
}
return isStrict ? this._erasNarrowRegex : this._erasRegex;
}

function matchEraAbbr(isStrict, locale) {
return locale.erasAbbrRegex(isStrict);
}

function matchEraName(isStrict, locale) {
return locale.erasNameRegex(isStrict);
}

function matchEraNarrow(isStrict, locale) {
return locale.erasNarrowRegex(isStrict);
}

function matchEraYearOrdinal(isStrict, locale) {
return locale._eraYearOrdinalRegex || matchUnsigned;
}

function computeErasParse() {
var abbrPieces = [], namePieces = [], narrowPieces = [], mixedPieces = [],
i, l, eras = this.eras();

for (i = 0, l = eras.length; i < l; ++i) {
namePieces.push(regexEscape(eras[i].name));
abbrPieces.push(regexEscape(eras[i].abbr));
narrowPieces.push(regexEscape(eras[i].narrow));

mixedPieces.push(regexEscape(eras[i].name));
mixedPieces.push(regexEscape(eras[i].abbr));
mixedPieces.push(regexEscape(eras[i].narrow));
}

this._erasRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
this._erasNameRegex = new RegExp('^(' + namePieces.join('|') + ')', 'i');
this._erasAbbrRegex = new RegExp('^(' + abbrPieces.join('|') + ')', 'i');
this._erasNarrowRegex = new RegExp('^(' + narrowPieces.join('|') + ')', 'i');
}