diff --git a/src/lib/create/from-string-and-format.js b/src/lib/create/from-string-and-format.js index b6c5082ea5..df55f17050 100644 --- a/src/lib/create/from-string-and-format.js +++ b/src/lib/create/from-string-and-format.js @@ -8,7 +8,7 @@ import { 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'; @@ -40,7 +40,8 @@ export function configFromStringAndFormat(config) { token, skipped, stringLength = string.length, - totalParsedInputLength = 0; + totalParsedInputLength = 0, + era; tokens = expandFormat(config._f, config._locale).match(formattingTokens) || []; @@ -99,6 +100,12 @@ export function configFromStringAndFormat(config) { config._meridiem ); + // handle era + 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 4c75977187..65a377db1d 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 38be121659..b25c7d1161 100644 --- a/src/lib/create/valid.js +++ b/src/lib/create/valid.js @@ -13,6 +13,7 @@ export function isValid(m) { !isNaN(m._d.getTime()) && flags.overflow < 0 && !flags.empty && + !flags.invalidEra && !flags.invalidMonth && !flags.invalidWeekday && !flags.weekdayMismatch && diff --git a/src/lib/format/format.js b/src/lib/format/format.js index 53b00d687c..177788f2ad 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'; -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, +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, localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, formatFunctions = {}, formatTokenFunctions = {}; diff --git a/src/lib/locale/en.js b/src/lib/locale/en.js index c548661591..470bd27a7c 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: '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, diff --git a/src/lib/locale/prototype.js b/src/lib/locale/prototype.js index eb523d033f..2057f69526 100644 --- a/src/lib/locale/prototype.js +++ b/src/lib/locale/prototype.js @@ -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, diff --git a/src/lib/moment/prototype.js b/src/lib/moment/prototype.js index 46ff54ab96..bbea60146d 100644 --- a/src/lib/moment/prototype.js +++ b/src/lib/moment/prototype.js @@ -69,6 +69,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..28c71495d0 --- /dev/null +++ b/src/lib/units/era.js @@ -0,0 +1,289 @@ +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' + ); +} diff --git a/src/locale/ja.js b/src/locale/ja.js index 827d42d8b0..612a369842 100644 --- a/src/locale/ja.js +++ b/src/locale/ja.js @@ -5,6 +5,59 @@ 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: 'AD', + abbr: 'AD', + }, + { + since: '0000-12-31', + until: -Infinity, + offset: 1, + name: '紀元前', + narrow: 'BC', + 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( '_' @@ -58,6 +111,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': diff --git a/src/test/locale/en.js b/src/test/locale/en.js index 561efed599..afc8621d06 100644 --- a/src/test/locale/en.js +++ b/src/test/locale/en.js @@ -67,6 +67,80 @@ test('format', function (assert) { } }); +test('parse era', function (assert) { + 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 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'); + + 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', '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'], + ['+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'); diff --git a/src/test/locale/ja.js b/src/test/locale/ja.js index 1ceea04e5f..7f0ca5ab41 100644 --- a/src/test/locale/ja.js +++ b/src/test/locale/ja.js @@ -64,6 +64,135 @@ 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 */ + ['+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', 'AD, AD, AD'], + ['+001872-12-31', 'NNNN', '西暦'], + ['+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', 'AD, AD, AD'], + ['+000001-01-01', 'NNNN', '西暦'], + ['+000001-01-01', 'NNNNN', 'AD'], + ['+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( '_'