From 89b58e65677571a5a124157390198fbd4b8af6df Mon Sep 17 00:00:00 2001 From: Maxime Guerreiro Date: Wed, 25 Apr 2018 01:56:56 +0200 Subject: [PATCH 01/11] Fix "the the" typo in test (#4578) --- src/test/moment/start_end_of.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/moment/start_end_of.js b/src/test/moment/start_end_of.js index 519c7641ee..4b47620d63 100644 --- a/src/test/moment/start_end_of.js +++ b/src/test/moment/start_end_of.js @@ -289,7 +289,7 @@ test('start of second', function (assert) { assert.equal(m.date(), 2, 'keep the day'); assert.equal(m.hours(), 3, 'keep the hours'); assert.equal(m.minutes(), 4, 'keep the minutes'); - assert.equal(m.seconds(), 5, 'keep the the seconds'); + assert.equal(m.seconds(), 5, 'keep the seconds'); assert.equal(m.milliseconds(), 0, 'strip out the milliseconds'); }); From 3062ca13cd1cfbcef1c4a0cd5543f5fcd329c1d9 Mon Sep 17 00:00:00 2001 From: Ash Date: Sun, 29 Apr 2018 21:42:28 +0100 Subject: [PATCH 02/11] [locale] Fix #4568: Insert punjabi for "next" (#4583) --- src/locale/pa-in.js | 2 +- src/test/locale/pa-in.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/locale/pa-in.js b/src/locale/pa-in.js index 50825e0a4e..6b85350943 100644 --- a/src/locale/pa-in.js +++ b/src/locale/pa-in.js @@ -47,7 +47,7 @@ export default moment.defineLocale('pa-in', { calendar : { sameDay : '[ਅਜ] LT', nextDay : '[ਕਲ] LT', - nextWeek : 'dddd, LT', + nextWeek : '[ਅਗਲਾ] dddd, LT', lastDay : '[ਕਲ] LT', lastWeek : '[ਪਿਛਲੇ] dddd, LT', sameElse : 'L' diff --git a/src/test/locale/pa-in.js b/src/test/locale/pa-in.js index 1c03a82914..594553a886 100644 --- a/src/test/locale/pa-in.js +++ b/src/test/locale/pa-in.js @@ -165,11 +165,11 @@ test('calendar next week', function (assert) { var i, m; for (i = 2; i < 7; i++) { m = moment().add({d: i}); - assert.equal(m.calendar(), m.format('dddd[,] LT'), 'Today + ' + i + ' days current time'); + assert.equal(m.calendar(), m.format('[ਅਗਲਾ] dddd[,] LT'), 'Today + ' + i + ' days current time'); m.hours(0).minutes(0).seconds(0).milliseconds(0); - assert.equal(m.calendar(), m.format('dddd[,] LT'), 'Today + ' + i + ' days beginning of day'); + assert.equal(m.calendar(), m.format('[ਅਗਲਾ] dddd[,] LT'), 'Today + ' + i + ' days beginning of day'); m.hours(23).minutes(59).seconds(59).milliseconds(999); - assert.equal(m.calendar(), m.format('dddd[,] LT'), 'Today + ' + i + ' days end of day'); + assert.equal(m.calendar(), m.format('[ਅਗਲਾ] dddd[,] LT'), 'Today + ' + i + ' days end of day'); } }); From 86ad2c364300e770e1527cfaf78064cf73df123c Mon Sep 17 00:00:00 2001 From: Takuya Sawada Date: Sat, 28 Apr 2018 01:01:44 +0900 Subject: [PATCH 03/11] Add era format functions --- src/lib/format/format.js | 2 +- src/lib/locale/en.js | 18 ++++++ src/lib/locale/prototype.js | 4 ++ src/lib/moment/prototype.js | 7 +++ src/lib/units/era.js | 106 ++++++++++++++++++++++++++++++++++++ 5 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 src/lib/units/era.js diff --git a/src/lib/format/format.js b/src/lib/format/format.js index 03f5c584fc..186c812196 100644 --- a/src/lib/format/format.js +++ b/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; diff --git a/src/lib/locale/en.js b/src/lib/locale/en.js index 4a7d250be8..ef95f1796c 100644 --- a/src/lib/locale/en.js +++ b/src/lib/locale/en.js @@ -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: 'After Christ', + narrow: 'AC', + abbr: 'AC' + }, + { + 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, diff --git a/src/lib/locale/prototype.js b/src/lib/locale/prototype.js index 24eef89f14..ae2048bc1d 100644 --- a/src/lib/locale/prototype.js +++ b/src/lib/locale/prototype.js @@ -20,6 +20,10 @@ proto.relativeTime = relativeTime; proto.pastFuture = pastFuture; proto.set = set; +// Eras +import { localeEras } from '../units/era'; +proto.eras = localeEras; + // Month import { localeMonthsParse, diff --git a/src/lib/moment/prototype.js b/src/lib/moment/prototype.js index bd8fff79c2..077716c174 100644 --- a/src/lib/moment/prototype.js +++ b/src/lib/moment/prototype.js @@ -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; diff --git a/src/lib/units/era.js b/src/lib/units/era.js new file mode 100644 index 0000000000..ffc9b74053 --- /dev/null +++ b/src/lib/units/era.js @@ -0,0 +1,106 @@ +import { addFormatToken } from '../format/format'; +import { hooks as moment } from '../utils/hooks'; +import { getLocale } from '../locale/locales'; + +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'); + +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 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(); +} From 9d2e59d53cbd493457dc83c4cc8166c19a810d97 Mon Sep 17 00:00:00 2001 From: Takuya Sawada Date: Sat, 28 Apr 2018 04:18:02 +0900 Subject: [PATCH 04/11] [locale] ja: add japanese era definitions --- src/locale/ja.js | 51 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/locale/ja.js b/src/locale/ja.js index 9812a73631..95cb126688 100644 --- a/src/locale/ja.js +++ b/src/locale/ja.js @@ -5,6 +5,55 @@ import moment from '../moment'; export default moment.defineLocale('ja', { + eras: [ + { + since: '1989-01-08', + offset: 1, + name: '平成', + narrow: '㍻', + abbr: 'H' + }, + { + since: '1926-12-25', + until: '1989-01-07', + offset: 1, + name: '昭和', + narrow: '㍼', + abbr: 'S' + }, + { + since: '1912-07-30', + until: '1926-12-24', + offset: 1, + name: '大正', + narrow: '㍽', + abbr: 'T' + }, + { + since: '1873-01-01', + until: '1912-07-29', + offset: 6, + name: '明治', + narrow: '㍾', + abbr: 'M' + }, + { + since: '0001-01-01', + until: '1873-12-31', + offset: 1, + name: '西暦', + narrow: 'AC', + abbr: 'AC' + }, + { + since: '0000-12-31', + until: -Infinity, + offset: 1, + name: '紀元前', + narrow: 'BC', + abbr: 'BC' + } + ], months : '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'), monthsShort : '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'), weekdays : '日曜日_月曜日_火曜日_水曜日_木曜日_金曜日_土曜日'.split('_'), @@ -56,6 +105,8 @@ export default moment.defineLocale('ja', { dayOfMonthOrdinalParse : /\d{1,2}日/, ordinal : function (number, period) { switch (period) { + case 'y': + return number === 1 ? '元年' : number + '年'; case 'd': case 'D': case 'DDD': From f1665741db0644b8f937a9ee6ef0caa9db286973 Mon Sep 17 00:00:00 2001 From: Takuya Sawada Date: Sun, 29 Apr 2018 15:58:09 +0900 Subject: [PATCH 05/11] [locale] en: add test code for era format --- src/test/locale/en.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/test/locale/en.js b/src/test/locale/en.js index b16d5536ce..e15213e83a 100644 --- a/src/test/locale/en.js +++ b/src/test/locale/en.js @@ -58,6 +58,30 @@ test('format', function (assert) { } }); +test('format era', function (assert) { + var a = [ + ['+000001-01-01', 'N, NN, NNN', 'AC, AC, AC'], + ['+000001-01-01', 'NNNN', 'After Christ'], + ['+000001-01-01', 'NNNNN', 'AC'], + ['+000001-01-01', 'y', '1'], + + ['+000000-12-31', 'N, NN, NNN', 'BC, BC, BC'], + ['+000000-12-31', 'NNNN', 'Before Christ'], + ['+000000-12-31', 'NNNNN', 'BC'], + ['+000000-12-31', 'y', '1'], + + ['-000001-12-31', 'N, NN, NNN', 'BC, BC, BC'], + ['-000001-12-31', 'NNNN', 'Before Christ'], + ['-000001-12-31', 'NNNNN', 'BC'], + ['-000001-12-31', 'y', '2'] + ], + i, l; + + for (i = 0, l = a.length; i < l; ++i) { + assert.equal(moment(a[i][0]).format(a[i][1]), a[i][2], a[i][0] + '; ' + a[i][1] + ' ---> ' + a[i][2]); + } +}); + test('format ordinal', function (assert) { assert.equal(moment([2011, 0, 1]).format('DDDo'), '1st', '1st'); assert.equal(moment([2011, 0, 2]).format('DDDo'), '2nd', '2nd'); From 7dc39277fd3abf4804e98aff4af4c66d0ce84a71 Mon Sep 17 00:00:00 2001 From: Takuya Sawada Date: Sun, 29 Apr 2018 16:17:09 +0900 Subject: [PATCH 06/11] [locale] ja: Add test code for japanese era format --- src/test/locale/ja.js | 80 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/src/test/locale/ja.js b/src/test/locale/ja.js index 1316724032..8d3001a132 100644 --- a/src/test/locale/ja.js +++ b/src/test/locale/ja.js @@ -54,6 +54,86 @@ test('format', function (assert) { } }); +test('format era', function (assert) { + var a = [ + /* First day of Heisei Era */ + ['+001989-01-08', 'N, NN, NNN', 'H, H, H'], + ['+001989-01-08', 'NNNN', '平成'], + ['+001989-01-08', 'NNNNN', '㍻'], + ['+001989-01-08', 'y yy yyy yyyy', '1 01 001 0001'], + ['+001989-01-08', 'yo', '元年'], + + /* Last day of Showa Era */ + ['+001989-01-07', 'N, NN, NNN', 'S, S, S'], + ['+001989-01-07', 'NNNN', '昭和'], + ['+001989-01-07', 'NNNNN', '㍼'], + ['+001989-01-07', 'y yy yyy yyyy', '64 64 064 0064'], + ['+001989-01-07', 'yo', '64年'], + + /* Last day of Showa Era */ + ['+001926-12-25', 'N, NN, NNN', 'S, S, S'], + ['+001926-12-25', 'NNNN', '昭和'], + ['+001926-12-25', 'NNNNN', '㍼'], + ['+001926-12-25', 'y yy yyy yyyy', '1 01 001 0001'], + ['+001926-12-25', 'yo', '元年'], + + /* Last day of Taisho Era */ + ['+001926-12-24', 'N, NN, NNN', 'T, T, T'], + ['+001926-12-24', 'NNNN', '大正'], + ['+001926-12-24', 'NNNNN', '㍽'], + ['+001926-12-24', 'y yy yyy yyyy', '15 15 015 0015'], + ['+001926-12-24', 'yo', '15年'], + + /* First day of Taisho Era */ + ['+001912-07-30', 'N, NN, NNN', 'T, T, T'], + ['+001912-07-30', 'NNNN', '大正'], + ['+001912-07-30', 'NNNNN', '㍽'], + ['+001912-07-30', 'y yy yyy yyyy', '1 01 001 0001'], + ['+001912-07-30', 'yo', '元年'], + + /* Last day of Meiji Era */ + ['+001912-07-29', 'N, NN, NNN', 'M, M, M'], + ['+001912-07-29', 'NNNN', '明治'], + ['+001912-07-29', 'NNNNN', '㍾'], + ['+001912-07-29', 'y yy yyy yyyy', '45 45 045 0045'], + ['+001912-07-29', 'yo', '45年'], + + /* The day the Japanese government had began using the Gregorian calendar */ + ['+001873-01-01', 'N, NN, NNN', 'M, M, M'], + ['+001873-01-01', 'NNNN', '明治'], + ['+001873-01-01', 'NNNNN', '㍾'], + ['+001873-01-01', 'y yy yyy yyyy', '6 06 006 0006'], + ['+001873-01-01', 'yo', '6年'], + + /* Christinan Era */ + ['+001872-12-31', 'N, NN, NNN', 'AC, AC, AC'], + ['+001872-12-31', 'NNNN', '西暦'], + ['+001872-12-31', 'NNNNN', 'AC'], + ['+001872-12-31', 'y yy yyy yyyy', '1872 1872 1872 1872'], + ['+001872-12-31', 'yo', '1872年'], + + ['+000001-01-01', 'N, NN, NNN', 'AC, AC, AC'], + ['+000001-01-01', 'NNNN', '西暦'], + ['+000001-01-01', 'NNNNN', 'AC'], + ['+000001-01-01', 'y', '1'], + + ['+000000-12-31', 'N, NN, NNN', 'BC, BC, BC'], + ['+000000-12-31', 'NNNN', '紀元前'], + ['+000000-12-31', 'NNNNN', 'BC'], + ['+000000-12-31', 'y', '1'], + + ['-000001-12-31', 'N, NN, NNN', 'BC, BC, BC'], + ['-000001-12-31', 'NNNN', '紀元前'], + ['-000001-12-31', 'NNNNN', 'BC'], + ['-000001-12-31', 'y', '2'] + ], + i, l; + + for (i = 0, l = a.length; i < l; ++i) { + assert.equal(moment(a[i][0]).format(a[i][1]), a[i][2], a[i][0] + '; ' + a[i][1] + ' ---> ' + a[i][2]); + } +}); + test('format month', function (assert) { var expected = '1月 1月_2月 2月_3月 3月_4月 4月_5月 5月_6月 6月_7月 7月_8月 8月_9月 9月_10月 10月_11月 11月_12月 12月'.split('_'), i; for (i = 0; i < expected.length; i++) { From 220c6b352d8215ebd982ca7ffa9f8207709bdf22 Mon Sep 17 00:00:00 2001 From: Takuya Sawada Date: Mon, 30 Apr 2018 19:06:57 +0900 Subject: [PATCH 07/11] Add era parsing functions --- src/lib/create/from-string-and-format.js | 8 +- src/lib/create/parsing-flags.js | 2 + src/lib/create/valid.js | 1 + src/lib/locale/prototype.js | 16 ++- src/lib/units/era.js | 144 +++++++++++++++++++++++ src/locale/ja.js | 4 + 6 files changed, 172 insertions(+), 3 deletions(-) diff --git a/src/lib/create/from-string-and-format.js b/src/lib/create/from-string-and-format.js index ed921d5b8e..482f1cc008 100644 --- a/src/lib/create/from-string-and-format.js +++ b/src/lib/create/from-string-and-format.js @@ -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'; @@ -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); } diff --git a/src/lib/create/parsing-flags.js b/src/lib/create/parsing-flags.js index c47173f0f9..a3625fcc6b 100644 --- a/src/lib/create/parsing-flags.js +++ b/src/lib/create/parsing-flags.js @@ -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 diff --git a/src/lib/create/valid.js b/src/lib/create/valid.js index d13f12f8b8..82ca341918 100644 --- a/src/lib/create/valid.js +++ b/src/lib/create/valid.js @@ -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 && diff --git a/src/lib/locale/prototype.js b/src/lib/locale/prototype.js index ae2048bc1d..a5ae132603 100644 --- a/src/lib/locale/prototype.js +++ b/src/lib/locale/prototype.js @@ -21,8 +21,20 @@ proto.pastFuture = pastFuture; proto.set = set; // Eras -import { localeEras } from '../units/era'; -proto.eras = localeEras; +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 { diff --git a/src/lib/units/era.js b/src/lib/units/era.js index ffc9b74053..98b04a262c 100644 --- a/src/lib/units/era.js +++ b/src/lib/units/era.js @@ -1,6 +1,11 @@ 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'); @@ -13,6 +18,41 @@ 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) { @@ -38,6 +78,53 @@ export function localeEras(m, format) { 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].includes(eraName)) { + 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) { @@ -104,3 +191,60 @@ export function getEraYear() { 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'); +} diff --git a/src/locale/ja.js b/src/locale/ja.js index 95cb126688..91d61e7ed7 100644 --- a/src/locale/ja.js +++ b/src/locale/ja.js @@ -54,6 +54,10 @@ export default moment.defineLocale('ja', { abbr: 'BC' } ], + eraYearOrdinalRegex: /(元|\d+)年/, + eraYearOrdinalParse: function (input, match) { + return match[1] === '元' ? 1 : parseInt(match[1] || input, 10); + }, months : '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'), monthsShort : '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'), weekdays : '日曜日_月曜日_火曜日_水曜日_木曜日_金曜日_土曜日'.split('_'), From e6831476814e5b5f2c05fe0bb5edb85469f0f450 Mon Sep 17 00:00:00 2001 From: Takuya Sawada Date: Tue, 1 May 2018 20:02:47 +0900 Subject: [PATCH 08/11] [locale] en: add test code for era parsing --- src/test/locale/en.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/test/locale/en.js b/src/test/locale/en.js index e15213e83a..169090fd00 100644 --- a/src/test/locale/en.js +++ b/src/test/locale/en.js @@ -58,6 +58,23 @@ test('format', function (assert) { } }); +test('parse era', function (assert) { + assert.equal(moment('2010 AC', 'y N', true).isValid(), true, '2010 AC'); + assert.equal(moment('2010 AC', 'y N', true).year(), 2010, '2010 AC'); + + assert.equal(moment('2010 After Christ', 'y N', true).isValid(), false, '2010 After Christ'); + assert.equal(moment('2010 After Christ', 'y N', false).isValid(), true, '2010 After Christ'); + assert.equal(moment('2010 After Christ', 'y NNNN', true).isValid(), true, '2010 After Christ'); + assert.equal(moment('2010 After Christ', 'y NNNN', true).year(), 2010, '2010 After Christ'); + assert.equal(moment('2010 After Christ', 'y N', false).year(), 2010, '2010 After Christ'); + + assert.equal(moment('469 BC', 'y N', true).isValid(), true, '469 BC'); + assert.equal(moment('469 BC', 'y N', true).year(), -468, '469 BC'); + + assert.equal(moment('469 Before Christ', 'y NNNN', true).isValid(), true, '469 Before Christ'); + assert.equal(moment('469 Before Christ', 'y NNNN', true).year(), -468, '469 Before Christ'); +}); + test('format era', function (assert) { var a = [ ['+000001-01-01', 'N, NN, NNN', 'AC, AC, AC'], From 96f5059aeca7e80d77a627d060ca5ec9450252f9 Mon Sep 17 00:00:00 2001 From: Takuya Sawada Date: Tue, 1 May 2018 18:16:24 +0900 Subject: [PATCH 09/11] [locale] ja: add test code for japanese era parsing --- src/test/locale/ja.js | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/test/locale/ja.js b/src/test/locale/ja.js index 8d3001a132..a2d82606bc 100644 --- a/src/test/locale/ja.js +++ b/src/test/locale/ja.js @@ -54,6 +54,42 @@ test('format', function (assert) { } }); +test('parse era', function (assert) { + // strict + assert.equal(moment('平成30年', 'NNNNy年', true).isValid(), true, '平成30年'); + assert.equal(moment('平成30年', 'NNNNy年', true).year(), 2018, '平成30年'); + assert.equal(moment('平成30年', 'NNNNyo', true).isValid(), true, '平成30年'); + assert.equal(moment('平成30年', 'NNNNyo', true).year(), 2018, '平成30年'); + + assert.equal(moment('平成30年', 'Ny年', true).isValid(), false, '平成30年'); + assert.equal(moment('平成30年', 'Ny年', false).isValid(), true, '平成30年'); + assert.equal(moment('㍻30年', 'Ny年', true).isValid(), false, '㍻30年'); + assert.equal(moment('㍻30年', 'Ny年', false).isValid(), true, '㍻30年'); + assert.equal(moment('H30年', 'Ny年', false).isValid(), true, 'H30年'); + + // abbrv + assert.equal(moment('H30年', 'Ny年', true).isValid(), true, 'H30年'); + assert.equal(moment('H30年', 'Ny年', true).year(), 2018, 'H30年'); + assert.equal(moment('H30年', 'NNNNy年', true).isValid(), false, 'H30年'); + assert.equal(moment('H30年', 'NNNNNy年', true).isValid(), false, 'H30年'); + + // narrow + assert.equal(moment('㍻30年', 'Ny年', true).isValid(), false, '㍻30年'); + assert.equal(moment('㍻30年', 'NNNNy年', true).isValid(), false, '㍻30年'); + assert.equal(moment('㍻30年', 'NNNNNy年', true).isValid(), true, '㍻30年'); + assert.equal(moment('㍻30年', 'NNNNNy年', true).year(), 2018, '㍻30年'); + + // ordinal year + assert.equal(moment('平成30年', 'NNNNyo', true).year(), 2018, '平成30年'); + assert.equal(moment('平成元年', 'NNNNyo', true).year(), 1989, '平成元年'); + + // old eras + assert.equal(moment('昭和64年', 'NNNNyo', true).year(), 1989, '昭和64年'); + assert.equal(moment('昭和元年', 'NNNNyo', true).year(), 1926, '昭和元年'); + assert.equal(moment('大正元年', 'NNNNyo', true).year(), 1912, '大正元年'); + assert.equal(moment('明治6年', 'NNNNyo', true).year(), 1873, '明治6年'); +}); + test('format era', function (assert) { var a = [ /* First day of Heisei Era */ From d38e1d86fa69e7c2ebadf60b7564746445fba5d1 Mon Sep 17 00:00:00 2001 From: Takuya Sawada Date: Tue, 1 May 2018 19:33:47 +0900 Subject: [PATCH 10/11] Use Array.prototype.indexOf() instead of Array.prototype.includes() --- src/lib/units/era.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/units/era.js b/src/lib/units/era.js index 98b04a262c..82f5cdd34b 100644 --- a/src/lib/units/era.js +++ b/src/lib/units/era.js @@ -110,7 +110,7 @@ export function localeErasParse(eraName, format, strict) { } break; } - } else if ([name, abbr, narrow].includes(eraName)) { + } else if ([name, abbr, narrow].indexOf(eraName) >= 0) { return eras[i]; } } From 753288cb1856b79a4e5d851009a0c965128cb788 Mon Sep 17 00:00:00 2001 From: Takuya Sawada Date: Thu, 26 Jul 2018 01:01:14 +0900 Subject: [PATCH 11/11] fix era definitions --- src/lib/locale/en.js | 6 +++--- src/locale/ja.js | 4 ++-- src/test/locale/en.js | 20 ++++++++++---------- src/test/locale/ja.js | 8 ++++---- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/lib/locale/en.js b/src/lib/locale/en.js index ef95f1796c..33b1c4b32c 100644 --- a/src/lib/locale/en.js +++ b/src/lib/locale/en.js @@ -8,9 +8,9 @@ getSetGlobalLocale('en', { since: '0001-01-01', until: +Infinity, offset: 1, - name: 'After Christ', - narrow: 'AC', - abbr: 'AC' + name: 'Anno Domini', + narrow: 'AD', + abbr: 'AD' }, { since: '0000-12-31', diff --git a/src/locale/ja.js b/src/locale/ja.js index 91d61e7ed7..c7bb682310 100644 --- a/src/locale/ja.js +++ b/src/locale/ja.js @@ -42,8 +42,8 @@ export default moment.defineLocale('ja', { until: '1873-12-31', offset: 1, name: '西暦', - narrow: 'AC', - abbr: 'AC' + narrow: 'AD', + abbr: 'AD' }, { since: '0000-12-31', diff --git a/src/test/locale/en.js b/src/test/locale/en.js index 169090fd00..56e0aadf64 100644 --- a/src/test/locale/en.js +++ b/src/test/locale/en.js @@ -59,14 +59,14 @@ test('format', function (assert) { }); test('parse era', function (assert) { - assert.equal(moment('2010 AC', 'y N', true).isValid(), true, '2010 AC'); - assert.equal(moment('2010 AC', 'y N', true).year(), 2010, '2010 AC'); + assert.equal(moment('2010 AD', 'y N', true).isValid(), true, '2010 AD'); + assert.equal(moment('2010 AD', 'y N', true).year(), 2010, '2010 AD'); - assert.equal(moment('2010 After Christ', 'y N', true).isValid(), false, '2010 After Christ'); - assert.equal(moment('2010 After Christ', 'y N', false).isValid(), true, '2010 After Christ'); - assert.equal(moment('2010 After Christ', 'y NNNN', true).isValid(), true, '2010 After Christ'); - assert.equal(moment('2010 After Christ', 'y NNNN', true).year(), 2010, '2010 After Christ'); - assert.equal(moment('2010 After Christ', 'y N', false).year(), 2010, '2010 After Christ'); + assert.equal(moment('2010 Anno Domini', 'y N', true).isValid(), false, '2010 Anno Domini'); + assert.equal(moment('2010 Anno Domini', 'y N', false).isValid(), true, '2010 Anno Domini'); + assert.equal(moment('2010 Anno Domini', 'y NNNN', true).isValid(), true, '2010 Anno Domini'); + assert.equal(moment('2010 Anno Domini', 'y NNNN', true).year(), 2010, '2010 Anno Domini'); + assert.equal(moment('2010 Anno Domini', 'y N', false).year(), 2010, '2010 Anno Domini'); assert.equal(moment('469 BC', 'y N', true).isValid(), true, '469 BC'); assert.equal(moment('469 BC', 'y N', true).year(), -468, '469 BC'); @@ -77,9 +77,9 @@ test('parse era', function (assert) { test('format era', function (assert) { var a = [ - ['+000001-01-01', 'N, NN, NNN', 'AC, AC, AC'], - ['+000001-01-01', 'NNNN', 'After Christ'], - ['+000001-01-01', 'NNNNN', 'AC'], + ['+000001-01-01', 'N, NN, NNN', 'AD, AD, AD'], + ['+000001-01-01', 'NNNN', 'Anno Domini'], + ['+000001-01-01', 'NNNNN', 'AD'], ['+000001-01-01', 'y', '1'], ['+000000-12-31', 'N, NN, NNN', 'BC, BC, BC'], diff --git a/src/test/locale/ja.js b/src/test/locale/ja.js index a2d82606bc..a30181d709 100644 --- a/src/test/locale/ja.js +++ b/src/test/locale/ja.js @@ -142,15 +142,15 @@ test('format era', function (assert) { ['+001873-01-01', 'yo', '6年'], /* Christinan Era */ - ['+001872-12-31', 'N, NN, NNN', 'AC, AC, AC'], + ['+001872-12-31', 'N, NN, NNN', 'AD, AD, AD'], ['+001872-12-31', 'NNNN', '西暦'], - ['+001872-12-31', 'NNNNN', 'AC'], + ['+001872-12-31', 'NNNNN', 'AD'], ['+001872-12-31', 'y yy yyy yyyy', '1872 1872 1872 1872'], ['+001872-12-31', 'yo', '1872年'], - ['+000001-01-01', 'N, NN, NNN', 'AC, AC, AC'], + ['+000001-01-01', 'N, NN, NNN', 'AD, AD, AD'], ['+000001-01-01', 'NNNN', '西暦'], - ['+000001-01-01', 'NNNNN', 'AC'], + ['+000001-01-01', 'NNNNN', 'AD'], ['+000001-01-01', 'y', '1'], ['+000000-12-31', 'N, NN, NNN', 'BC, BC, BC'],