UNPKG

@beisen/ethos

Version:

beisencloud pc react components

819 lines (778 loc) • 21 kB
'use strict'; /** * @ignore * DateTimeFormat for * Inspired by DateTimeFormat from JDK. * @author yiminghe@gmail.com */ var GregorianCalendar = require('gregorian-calendar'); var enUsLocale = require('./en_US'); var MAX_VALUE = Number.MAX_VALUE; var warning = require('warning'); /** * date or time style enum * @enum {Number} Date.Formatter.Style */ var DateTimeStyle = { /** * full style */ FULL: 0, /** * long style */ LONG: 1, /** * medium style */ MEDIUM: 2, /** * short style */ SHORT: 3 }; /* Letter Date or Time Component Presentation Examples G Era designator Text AD y Year Year 1996; 96 Y WeekYear WeekYear 1996; 96 M Month in year Month July; Jul; 07 w Week in year Number 27 W Week in month Number 2 D Day in year Number 189 d Day in month Number 10 F Day of week in month Number 2 E Day in week Text Tuesday; Tue a Am/pm marker Text PM H Hour in day (0-23) Number 0 k Hour in day (1-24) Number 24 K Hour in am/pm (0-11) Number 0 h Hour in am/pm (1-12) Number 12 m Minute in hour Number 30 s Second in minute Number 55 S Millisecond Number 978 x z Time zone General time zone Pacific Standard Time; PST; GMT-08:00 Z Time zone RFC 822 time zone -0800 */ var patternChars = new Array(GregorianCalendar.DAY_OF_WEEK_IN_MONTH + 2).join('1'); var ERA = 0; var calendarIndexMap = {}; patternChars = patternChars.split(''); patternChars[ERA] = 'G'; patternChars[GregorianCalendar.YEAR] = 'y'; patternChars[GregorianCalendar.MONTH] = 'M'; patternChars[GregorianCalendar.DAY_OF_MONTH] = 'd'; patternChars[GregorianCalendar.HOUR_OF_DAY] = 'H'; patternChars[GregorianCalendar.MINUTES] = 'm'; patternChars[GregorianCalendar.SECONDS] = 's'; patternChars[GregorianCalendar.MILLISECONDS] = 'S'; patternChars[GregorianCalendar.WEEK_OF_YEAR] = 'w'; patternChars[GregorianCalendar.WEEK_OF_MONTH] = 'W'; patternChars[GregorianCalendar.DAY_OF_YEAR] = 'D'; patternChars[GregorianCalendar.DAY_OF_WEEK_IN_MONTH] = 'F'; patternChars.push('Y'); patternChars.forEach(function (v, key) { var k = key; if (v === 'Y') { k = GregorianCalendar.YEAR; } if (v) { calendarIndexMap[v] = k; } }); function mix(t, s) { for (var p in s) { if (s.hasOwnProperty(p)) { t[p] = s[p]; } } } var SUBSTITUTE_REG = /\\?\{([^{}]+)\}/g; var EMPTY = ''; function substitute(str, o, regexp) { if (typeof str !== 'string' || !o) { return str; } return str.replace(regexp || SUBSTITUTE_REG, function (match, name) { if (match.charAt(0) === '\\') { return match.slice(1); } return o[name] === undefined ? EMPTY : o[name]; }); } patternChars = patternChars.join('') + 'ahkKZE'; function encode(lastField, count, compiledPattern) { compiledPattern.push({ field: lastField, count: count }); } function compile(pattern) { var length = pattern.length; var inQuote = false; var compiledPattern = []; var tmpBuffer = null; var count = 0; var lastField = -1; for (var i = 0; i < length; i++) { var c = pattern.charAt(i); if (c === '\'') { // '' is treated as a single quote regardless of being // in a quoted section. if (i + 1 < length) { c = pattern.charAt(i + 1); if (c === '\'') { i++; if (count !== 0) { encode(lastField, count, compiledPattern); lastField = -1; count = 0; } if (inQuote) { tmpBuffer += c; } continue; } } if (!inQuote) { if (count !== 0) { encode(lastField, count, compiledPattern); lastField = -1; count = 0; } tmpBuffer = ''; inQuote = true; } else { compiledPattern.push({ text: tmpBuffer }); inQuote = false; } continue; } if (inQuote) { tmpBuffer += c; continue; } if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) { if (count !== 0) { encode(lastField, count, compiledPattern); lastField = -1; count = 0; } compiledPattern.push({ text: c }); continue; } if (patternChars.indexOf(c) === -1) { throw new Error('Illegal pattern character "' + c + '"'); } if (lastField === -1 || lastField === c) { lastField = c; count++; continue; } encode(lastField, count, compiledPattern); lastField = c; count = 1; } if (inQuote) { throw new Error('Unterminated quote'); } if (count !== 0) { encode(lastField, count, compiledPattern); } return compiledPattern; } var zeroDigit = '0'; // TODO zeroDigit localization?? function zeroPaddingNumber(value, minDigits, maxDigits_, b) { // Optimization for 1, 2 and 4 digit numbers. This should // cover most cases of formatting date/time related items. // Note: This optimization code assumes that maxDigits is // either 2 or Integer.MAX_VALUE (maxIntCount in format()). var buffer = b || []; var maxDigits = maxDigits_ || MAX_VALUE; if (value >= 0) { if (value < 100 && minDigits >= 1 && minDigits <= 2) { if (value < 10 && minDigits === 2) { buffer.push(zeroDigit); } buffer.push(value); return buffer.join(''); } else if (value >= 1000 && value < 10000) { if (minDigits === 4) { buffer.push(value); return buffer.join(''); } if (minDigits === 2 && maxDigits === 2) { return zeroPaddingNumber(value % 100, 2, 2, buffer); } } } buffer.push(value + ''); return buffer.join(''); } /** * * date time formatter for GregorianCalendar * * @example * * const calendar = new GregorianCalendar(2013,9,24); * // ' to escape * const formatter = new GregorianCalendarFormat("'today is' ''yyyy/MM/dd a''"); * document.write(formatter.format(calendar)); * * @class GregorianCalendarFormat * @param {String} pattern patter string of date formatter * * <table border="1"> * <thead valign="bottom"> * <tr><th class="head">Letter</th> * <th class="head">Date or Time Component</th> * <th class="head">Presentation</th> * <th class="head">Examples</th> * </tr> * </thead> * <tbody valign="top"> * <tr><td>G</td> * <td>Era designator</td> * <td>Text</td> * <td>AD</td> * </tr> * <tr><td>y</td> * <td>Year</td> * <td>Year</td> * <td>1996; 96</td> * </tr> * <tr><td>M</td> * <td>Month in year</td> * <td>Month</td> * <td>July; Jul; 07</td> * </tr> * <tr><td>w</td> * <td>Week in year</td> * <td>Number</td> * <td>27</td> * </tr> * <tr><td>W</td> * <td>Week in month</td> * <td>Number</td> * <td>2</td> * </tr> * <tr><td>D</td> * <td>Day in year</td> * <td>Number</td> * <td>189</td> * </tr> * <tr><td>d</td> * <td>Day in month</td> * <td>Number</td> * <td>10</td> * </tr> * <tr><td>F</td> * <td>Day of week in month</td> * <td>Number</td> * <td>2</td> * </tr> * <tr><td>E</td> * <td>Day in week</td> * <td>Text</td> * <td>Tuesday; Tue</td> * </tr> * <tr><td>a</td> * <td>Am/pm marker</td> * <td>Text</td> * <td>PM</td> * </tr> * <tr><td>H</td> * <td>Hour in day (0-23)</td> * <td>Number</td> * <td>0</td> * </tr> * <tr><td>k</td> * <td>Hour in day (1-24)</td> * <td>Number</td> * <td>24</td> * </tr> * <tr><td>K</td> * <td>Hour in am/pm (0-11)</td> * <td>Number</td> * <td>0</td> * </tr> * <tr><td>h</td> * <td>Hour in am/pm (1-12)</td> * <td>Number</td> * <td>12</td> * </tr> * <tr><td>m</td> * <td>Minute in hour</td> * <td>Number</td> * <td>30</td> * </tr> * <tr><td>s</td> * <td>Second in minute</td> * <td>Number</td> * <td>55</td> * </tr> * <tr><td>S</td> * <td>Millisecond</td> * <td>Number</td> * <td>978</td> * </tr> * <tr><td>x/z</td> * <td>Time zone</td> * <td>General time zone</td> * <td>Pacific Standard Time; PST; GMT-08:00</td> * </tr> * <tr><td>Z</td> * <td>Time zone</td> * <td>RFC 822 time zone</td> * <td>-0800</td> * </tr> * </tbody> * </table> * @param {Object} locale format locales */ function DateTimeFormat(pattern, locale) { this.locale = locale || enUsLocale; this.originalPattern = pattern; this.pattern = compile(pattern); } function formatField(field, count, locale, calendar) { var current = void 0; var value = void 0; switch (field) { case 'G': value = calendar.getYear() > 0 ? 1 : 0; current = locale.eras[value]; break; case 'Y': value = calendar.getWeekYear(); if (value <= 0) { value = 1 - value; } current = zeroPaddingNumber(value, 2, count !== 2 ? MAX_VALUE : 2); break; case 'y': value = calendar.getYear(); if (value <= 0) { value = 1 - value; } current = zeroPaddingNumber(value, 2, count !== 2 ? MAX_VALUE : 2); break; case 'M': value = calendar.getMonth(); if (count >= 4) { current = locale.months[value]; } else if (count === 3) { current = locale.shortMonths[value]; } else { current = zeroPaddingNumber(value + 1, count); } break; case 'k': current = zeroPaddingNumber(calendar.getHourOfDay() || 24, count); break; case 'E': value = calendar.getDayOfWeek(); current = count >= 4 ? locale.weekdays[value] : locale.shortWeekdays[value]; break; case 'a': current = locale.ampms[calendar.getHourOfDay() >= 12 ? 1 : 0]; break; case 'h': current = zeroPaddingNumber(calendar.getHourOfDay() % 12 || 12, count); break; case 'K': current = zeroPaddingNumber(calendar.getHourOfDay() % 12, count); break; case 'Z': var offset = calendar.getTimezoneOffset(); var parts = [offset < 0 ? '-' : '+']; offset = Math.abs(offset); parts.push(zeroPaddingNumber(Math.floor(offset / 60) % 100, 2), zeroPaddingNumber(offset % 60, 2)); current = parts.join(''); break; default: // case 'd': // case 'H': // case 'm': // case 's': // case 'S': // case 'D': // case 'F': // case 'w': // case 'W': var index = calendarIndexMap[field]; value = calendar.get(index); current = zeroPaddingNumber(value, count); } return current; } function matchPartString(dateStr, startIndex, match, mLen) { for (var i = 0; i < mLen; i++) { if (dateStr.charAt(startIndex + i) !== match.charAt(i)) { return false; } } return true; } function matchField(dateStr, startIndex, matches) { var matchedLen = -1; var index = -1; var i = void 0; var len = matches.length; for (i = 0; i < len; i++) { var m = matches[i]; var mLen = m.length; if (mLen > matchedLen && matchPartString(dateStr, startIndex, m, mLen)) { matchedLen = mLen; index = i; } } return index >= 0 ? { value: index, startIndex: startIndex + matchedLen } : null; } function getLeadingNumberLen(str) { var i = void 0; var c = void 0; var len = str.length; for (i = 0; i < len; i++) { c = str.charAt(i); if (c < '0' || c > '9') { break; } } return i; } function matchNumber(dateStr, startIndex, count, obeyCount) { var str = dateStr; var n = void 0; if (obeyCount) { if (dateStr.length < startIndex + count) { return null; } str = dateStr.slice(startIndex, startIndex + count); if (!str.match(/^\d+$/)) { throw new Error('GregorianCalendarFormat parse error, dateStr: ' + dateStr + ', patter: ' + this.originalPattern); } } else { str = str.slice(startIndex); } n = parseInt(str, 10); if (isNaN(n)) { throw new Error('GregorianCalendarFormat parse error, dateStr: ' + dateStr + ', patter: ' + this.originalPattern); } return { value: n, startIndex: startIndex + getLeadingNumberLen(str) }; } function parseField(calendar, dateStr, startIndex_, field, count, obeyCount, tmp) { var match = void 0; var year = void 0; var hour = void 0; var startIndex = startIndex_; if (dateStr.length <= startIndex) { return startIndex; } var locale = this.locale; switch (field) { case 'G': match = matchField(dateStr, startIndex, locale.eras); if (match) { if (calendar.isSetYear()) { if (match.value === 0) { year = calendar.getYear(); calendar.setYear(1 - year); } } else { tmp.era = match.value; } } break; case 'y': match = matchNumber.call(this, dateStr, startIndex, count, obeyCount); if (match) { year = match.value; if ('era' in tmp) { if (tmp.era === 0) { year = 1 - year; } } calendar.setYear(year); } break; case 'M': var month = void 0; if (count >= 3) { match = matchField(dateStr, startIndex, locale[count === 3 ? 'shortMonths' : 'months']); if (match) { month = match.value; } } else { match = matchNumber.call(this, dateStr, startIndex, count, obeyCount); if (match) { month = match.value - 1; } } if (match) { calendar.setMonth(month); } break; case 'k': match = matchNumber.call(this, dateStr, startIndex, count, obeyCount); if (match) { calendar.setHourOfDay(match.value % 24); } break; case 'E': match = matchField(dateStr, startIndex, locale[count > 3 ? 'weekdays' : 'shortWeekdays']); if (match) { calendar.setDayOfWeek(match.value); } break; case 'a': match = matchField(dateStr, startIndex, locale.ampms); if (match) { if (calendar.isSetHourOfDay()) { if (match.value) { hour = calendar.getHourOfDay(); if (hour < 12) { calendar.setHourOfDay((hour + 12) % 24); } } } else { tmp.ampm = match.value; } } break; case 'h': match = matchNumber.call(this, dateStr, startIndex, count, obeyCount); if (match) { hour = match.value %= 12; if (tmp.ampm) { hour += 12; } calendar.setHourOfDay(hour); } break; case 'K': match = matchNumber.call(this, dateStr, startIndex, count, obeyCount); if (match) { hour = match.value; if (tmp.ampm) { hour += 12; } calendar.setHourOfDay(hour); } break; case 'Z': // let sign = 1; var zoneChar = dateStr.charAt(startIndex); if (zoneChar === '-') { // sign = -1; startIndex++; } else if (zoneChar === '+') { startIndex++; } else { break; } match = matchNumber.call(this, dateStr, startIndex, 2, true); if (match) { var zoneOffset = match.value * 60; startIndex = match.startIndex; match = matchNumber.call(this, dateStr, startIndex, 2, true); if (match) { zoneOffset += match.value; } calendar.setTimezoneOffset(zoneOffset); } break; default: // case 'd': // case 'H': // case 'm': // case 's': // case 'S': // case 'D': // case 'F': // case 'w': // case 'W' match = matchNumber.call(this, dateStr, startIndex, count, obeyCount); if (match) { var index = calendarIndexMap[field]; calendar.set(index, match.value); } } if (match) { startIndex = match.startIndex; } return startIndex; } mix(DateTimeFormat.prototype, { /* * format a GregorianDate instance according to specified pattern * @param {GregorianCalendar} calendar GregorianDate instance * @returns {string} formatted string of GregorianDate instance */ format: function format(calendar) { if (!calendar.isGregorianCalendar) { throw new Error('calendar must be type of GregorianCalendar'); } var i = void 0; var ret = []; var pattern = this.pattern; var len = pattern.length; for (i = 0; i < len; i++) { var comp = pattern[i]; if (comp.text) { ret.push(comp.text); } else if ('field' in comp) { ret.push(formatField(comp.field, comp.count, this.locale, calendar)); } } return ret.join(''); }, /* * parse a formatted string of GregorianDate instance according to specified pattern * @param {String} dateStr formatted string of GregorianDate * @returns {GregorianCalendar} */ parse: function parse(dateStr, option_) { var option = option_ || {}; var calendarLocale = option.locale; var calendar = new GregorianCalendar(calendarLocale); var i = void 0; var j = void 0; var tmp = {}; var obeyCount = option.obeyCount || false; var dateStrLen = dateStr.length; var errorIndex = -1; var startIndex = 0; var oldStartIndex = 0; var pattern = this.pattern; var len = pattern.length; /* eslint no-labels: 0 no-empty-label:0 */ loopPattern: { for (i = 0; errorIndex < 0 && i < len; i++) { var comp = pattern[i]; var text = void 0; var textLen = void 0; oldStartIndex = startIndex; text = comp.text; if (text) { textLen = text.length; if (textLen + startIndex > dateStrLen) { errorIndex = startIndex; } else { for (j = 0; j < textLen; j++) { if (text.charAt(j) !== dateStr.charAt(j + startIndex)) { errorIndex = startIndex; break loopPattern; } } startIndex += textLen; } } else if ('field' in comp) { if (!option.obeyCount) { var nextComp = pattern[i + 1]; obeyCount = false; if (nextComp) { if ('field' in nextComp) { obeyCount = true; } else { var c = nextComp.text.charAt(0); if (c >= '0' && c <= '9') { obeyCount = true; } } } } startIndex = parseField.call(this, calendar, dateStr, startIndex, comp.field, comp.count, obeyCount, tmp); if (startIndex === oldStartIndex) { errorIndex = startIndex; } } } } if (errorIndex >= 0) { // warning(false, 'error when parsing date: ' + dateStr + ', position: ' + dateStr.slice(0, errorIndex) + '^'); return undefined; } return calendar; } }); mix(DateTimeFormat, { Style: DateTimeStyle, /* * get a formatter instance of short style pattern. * en-us: M/d/yy h:mm a * zh-cn: yy-M-d ah:mm * @param {Object} locales locales object * @returns {GregorianCalendar} * @static */ getInstance: function getInstance(locale) { return this.getDateTimeInstance(DateTimeStyle.SHORT, DateTimeStyle.SHORT, locale); }, /* * get a formatter instance of specified date style. * @param {Date.Formatter.Style} dateStyle date format style * @param {Object} locales * @returns {GregorianCalendar} * @static */ getDateInstance: function getDateInstance(dateStyle, locale) { return this.getDateTimeInstance(dateStyle, undefined, locale); }, /* * get a formatter instance of specified date style and time style. * @param {Date.Formatter.Style} dateStyle date format style * @param {Date.Formatter.Style} timeStyle time format style * @param {Object} locales * @returns {GregorianCalendar} * @static */ getDateTimeInstance: function getDateTimeInstance(dateStyle, timeStyle, locale_) { var locale = locale_ || enUsLocale; var datePattern = ''; if (dateStyle !== undefined) { datePattern = locale.datePatterns[dateStyle]; } var timePattern = ''; if (timeStyle !== undefined) { timePattern = locale.timePatterns[timeStyle]; } var pattern = datePattern; if (timePattern) { if (datePattern) { pattern = substitute(locale.dateTimePattern, { date: datePattern, time: timePattern }); } else { pattern = timePattern; } } return new DateTimeFormat(pattern, locale); }, /* * get a formatter instance of specified time style. * @param {Date.Formatter.Style} timeStyle time format style * @param {Object} locales * @returns {GregorianCalendar} * @static */ getTimeInstance: function getTimeInstance(timeStyle, locale) { return this.getDateTimeInstance(undefined, timeStyle, locale); } }); module.exports = DateTimeFormat; DateTimeFormat.version = '@VERSION@'; // gc_format@163.com