/** * Globalize Runtime v1.7.0 * * https://github.com/globalizejs/globalize * * Copyright OpenJS Foundation and other contributors * Released under the MIT license * https://jquery.org/license * * Date: 2021-08-02T11:53Z */ /*! * Globalize Runtime v1.7.0 2021-08-02T11:53Z Released under the MIT license * http://git.io/TrdQbw */ (function( root, factory ) { "use strict"; // UMD returnExports if ( typeof define === "function" && define.amd ) { // AMD define([ "../globalize-runtime", "./number" ], factory ); } else if ( typeof exports === "object" ) { // Node, CommonJS module.exports = factory( require( "../globalize-runtime" ), require( "./number" ) ); } else { // Extend global factory( root.Globalize ); } }(this, function( Globalize ) { var createErrorUnsupportedFeature = Globalize._createErrorUnsupportedFeature, looseMatching = Globalize._looseMatching, partsJoin = Globalize._partsJoin, partsPush = Globalize._partsPush, regexpEscape = Globalize._regexpEscape, removeLiteralQuotes = Globalize._removeLiteralQuotes, runtimeKey = Globalize._runtimeKey, stringPad = Globalize._stringPad, validateParameterPresence = Globalize._validateParameterPresence, validateParameterType = Globalize._validateParameterType, validateParameterTypeString = Globalize._validateParameterTypeString; var validateParameterTypeDate = function( value, name ) { validateParameterType( value, name, value === undefined || value instanceof Date, "Date" ); }; var ZonedDateTime = (function() { function definePrivateProperty(object, property, value) { Object.defineProperty(object, property, { value: value }); } function getUntilsIndex(original, untils) { var index = 0; var originalTime = original.getTime(); // TODO Should we do binary search for improved performance? while (index < untils.length - 1 && originalTime >= untils[index]) { index++; } return index; } function setWrap(fn) { var offset1 = this.getTimezoneOffset(); var ret = fn(); this.original.setTime(new Date(this.getTime())); var offset2 = this.getTimezoneOffset(); if (offset2 - offset1) { this.original.setMinutes(this.original.getMinutes() + offset2 - offset1); } return ret; } var ZonedDateTime = function(date, timeZoneData) { definePrivateProperty(this, "original", new Date(date.getTime())); definePrivateProperty(this, "local", new Date(date.getTime())); definePrivateProperty(this, "timeZoneData", timeZoneData); definePrivateProperty(this, "setWrap", setWrap); if (!(timeZoneData.untils && timeZoneData.offsets && timeZoneData.isdsts)) { throw new Error("Invalid IANA data"); } this.setTime(this.local.getTime() - this.getTimezoneOffset() * 60 * 1000); }; ZonedDateTime.prototype.clone = function() { return new ZonedDateTime(this.original, this.timeZoneData); }; // Date field getters. ["getFullYear", "getMonth", "getDate", "getDay", "getHours", "getMinutes", "getSeconds", "getMilliseconds"].forEach(function(method) { // Corresponding UTC method, e.g., "getUTCFullYear" if method === "getFullYear". var utcMethod = "getUTC" + method.substr(3); ZonedDateTime.prototype[method] = function() { return this.local[utcMethod](); }; }); // Note: Define .valueOf = .getTime for arithmetic operations like date1 - date2. ZonedDateTime.prototype.valueOf = ZonedDateTime.prototype.getTime = function() { return this.local.getTime() + this.getTimezoneOffset() * 60 * 1000; }; ZonedDateTime.prototype.getTimezoneOffset = function() { var index = getUntilsIndex(this.original, this.timeZoneData.untils); return this.timeZoneData.offsets[index]; }; // Date field setters. ["setFullYear", "setMonth", "setDate", "setHours", "setMinutes", "setSeconds", "setMilliseconds"].forEach(function(method) { // Corresponding UTC method, e.g., "setUTCFullYear" if method === "setFullYear". var utcMethod = "setUTC" + method.substr(3); ZonedDateTime.prototype[method] = function(value) { var local = this.local; // Note setWrap is needed for seconds and milliseconds just because // abs(value) could be >= a minute. return this.setWrap(function() { return local[utcMethod](value); }); }; }); ZonedDateTime.prototype.setTime = function(time) { return this.local.setTime(time); }; ZonedDateTime.prototype.isDST = function() { var index = getUntilsIndex(this.original, this.timeZoneData.untils); return Boolean(this.timeZoneData.isdsts[index]); }; ZonedDateTime.prototype.inspect = function() { var index = getUntilsIndex(this.original, this.timeZoneData.untils); var abbrs = this.timeZoneData.abbrs; return this.local.toISOString().replace(/Z$/, "") + " " + (abbrs && abbrs[index] + " " || (this.getTimezoneOffset() * -1) + " ") + (this.isDST() ? "(daylight savings)" : ""); }; ZonedDateTime.prototype.toDate = function() { return new Date(this.getTime()); }; // Type cast getters. ["toISOString", "toJSON", "toUTCString"].forEach(function(method) { ZonedDateTime.prototype[method] = function() { return this.toDate()[method](); }; }); return ZonedDateTime; }()); /** * dayOfWeek( date, firstDay ) * * @date * * @firstDay the result of `dateFirstDayOfWeek( cldr )` * * Return the day of the week normalized by the territory's firstDay [0-6]. * Eg for "mon": * - return 0 if territory is GB, or BR, or DE, or FR (week starts on "mon"); * - return 1 if territory is US (week starts on "sun"); * - return 2 if territory is EG (week starts on "sat"); */ var dateDayOfWeek = function( date, firstDay ) { return ( date.getDay() - firstDay + 7 ) % 7; }; /** * distanceInDays( from, to ) * * Return the distance in days between from and to Dates. */ var dateDistanceInDays = function( from, to ) { var inDays = 864e5; return ( to.getTime() - from.getTime() ) / inDays; }; /** * startOf changes the input to the beginning of the given unit. * * For example, starting at the start of a day, resets hours, minutes * seconds and milliseconds to 0. Starting at the month does the same, but * also sets the date to 1. * * Returns the modified date */ var dateStartOf = function( date, unit ) { date = date instanceof ZonedDateTime ? date.clone() : new Date( date.getTime() ); switch ( unit ) { case "year": date.setMonth( 0 ); /* falls through */ case "month": date.setDate( 1 ); /* falls through */ case "day": date.setHours( 0 ); /* falls through */ case "hour": date.setMinutes( 0 ); /* falls through */ case "minute": date.setSeconds( 0 ); /* falls through */ case "second": date.setMilliseconds( 0 ); } return date; }; /** * dayOfYear * * Return the distance in days of the date to the begin of the year [0-d]. */ var dateDayOfYear = function( date ) { return Math.floor( dateDistanceInDays( dateStartOf( date, "year" ), date ) ); }; /** * Returns a new object created by using `object`'s values as keys, and the keys as values. */ var objectInvert = function( object, fn ) { fn = fn || function( object, key, value ) { object[ value ] = key; return object; }; return Object.keys( object ).reduce(function( newObject, key ) { return fn( newObject, key, object[ key ] ); }, {}); }; // Invert key and values, e.g., {"year": "yY"} ==> {"y": "year", "Y": "year"} var dateFieldsMap = objectInvert({ "era": "G", "year": "yY", "quarter": "qQ", "month": "ML", "week": "wW", "day": "dDF", "weekday": "ecE", "dayperiod": "a", "hour": "hHkK", "minute": "m", "second": "sSA", "zone": "zvVOxX" }, function( object, key, value ) { value.split( "" ).forEach(function( symbol ) { object[ symbol ] = key; }); return object; }); /** * millisecondsInDay */ var dateMillisecondsInDay = function( date ) { // TODO Handle daylight savings discontinuities return date - dateStartOf( date, "day" ); }; var datePatternRe = ( /([a-z])\1*|'([^']|'')+'|''|./ig ); /** * hourFormat( date, format, timeSeparator, formatNumber ) * * Return date's timezone offset according to the format passed. * Eg for format when timezone offset is 180: * - "+H;-H": -3 * - "+HHmm;-HHmm": -0300 * - "+HH:mm;-HH:mm": -03:00 * - "+HH:mm:ss;-HH:mm:ss": -03:00:00 */ var dateTimezoneHourFormat = function( date, format, timeSeparator, formatNumber ) { var absOffset, offset = date.getTimezoneOffset(); absOffset = Math.abs( offset ); formatNumber = formatNumber || { 1: function( value ) { return stringPad( value, 1 ); }, 2: function( value ) { return stringPad( value, 2 ); } }; return format // Pick the correct sign side (+ or -). .split( ";" )[ offset > 0 ? 1 : 0 ] // Localize time separator .replace( ":", timeSeparator ) // Update hours offset. .replace( /HH?/, function( match ) { return formatNumber[ match.length ]( Math.floor( absOffset / 60 ) ); }) // Update minutes offset and return. .replace( /mm/, function() { return formatNumber[ 2 ]( Math.floor( absOffset % 60 ) ); }) // Update minutes offset and return. .replace( /ss/, function() { return formatNumber[ 2 ]( Math.floor( absOffset % 1 * 60 ) ); }); }; var dateWeekDays = [ "sun", "mon", "tue", "wed", "thu", "fri", "sat" ]; /** * format( date, properties ) * * @date [Date instance]. * * @properties * * TODO Support other calendar types. * * Disclosure: this function borrows excerpts of dojo/date/locale. */ var dateFormat = function( date, numberFormatters, properties ) { var parts = []; var timeSeparator = properties.timeSeparator; // create globalize date with given timezone data if ( properties.timeZoneData ) { date = new ZonedDateTime( date, properties.timeZoneData() ); } properties.pattern.replace( datePatternRe, function( current ) { var aux, dateField, type, value, chr = current.charAt( 0 ), length = current.length; if ( chr === "j" ) { // Locale preferred hHKk. // http://www.unicode.org/reports/tr35/tr35-dates.html#Time_Data chr = properties.preferredTime; } if ( chr === "Z" ) { // Z..ZZZ: same as "xxxx". if ( length < 4 ) { chr = "x"; length = 4; // ZZZZ: same as "OOOO". } else if ( length < 5 ) { chr = "O"; length = 4; // ZZZZZ: same as "XXXXX" } else { chr = "X"; length = 5; } } // z...zzz: "{shortRegion}", e.g., "PST" or "PDT". // zzzz: "{regionName} {Standard Time}" or "{regionName} {Daylight Time}", // e.g., "Pacific Standard Time" or "Pacific Daylight Time". if ( chr === "z" ) { if ( date.isDST ) { value = date.isDST() ? properties.daylightTzName : properties.standardTzName; } // Fall back to "O" format. if ( !value ) { chr = "O"; if ( length < 4 ) { length = 1; } } } switch ( chr ) { // Era case "G": value = properties.eras[ date.getFullYear() < 0 ? 0 : 1 ]; break; // Year case "y": // Plain year. // The length specifies the padding, but for two letters it also specifies the // maximum length. value = date.getFullYear(); if ( length === 2 ) { value = String( value ); value = +value.substr( value.length - 2 ); } break; case "Y": // Year in "Week of Year" // The length specifies the padding, but for two letters it also specifies the // maximum length. // yearInWeekofYear = date + DaysInAWeek - (dayOfWeek - firstDay) - minDays value = new Date( date.getTime() ); value.setDate( value.getDate() + 7 - dateDayOfWeek( date, properties.firstDay ) - properties.firstDay - properties.minDays ); value = value.getFullYear(); if ( length === 2 ) { value = String( value ); value = +value.substr( value.length - 2 ); } break; // Quarter case "Q": case "q": value = Math.ceil( ( date.getMonth() + 1 ) / 3 ); if ( length > 2 ) { value = properties.quarters[ chr ][ length ][ value ]; } break; // Month case "M": case "L": value = date.getMonth() + 1; if ( length > 2 ) { value = properties.months[ chr ][ length ][ value ]; } break; // Week case "w": // Week of Year. // woy = ceil( ( doy + dow of 1/1 ) / 7 ) - minDaysStuff ? 1 : 0. // TODO should pad on ww? Not documented, but I guess so. value = dateDayOfWeek( dateStartOf( date, "year" ), properties.firstDay ); value = Math.ceil( ( dateDayOfYear( date ) + value ) / 7 ) - ( 7 - value >= properties.minDays ? 0 : 1 ); break; case "W": // Week of Month. // wom = ceil( ( dom + dow of `1/month` ) / 7 ) - minDaysStuff ? 1 : 0. value = dateDayOfWeek( dateStartOf( date, "month" ), properties.firstDay ); value = Math.ceil( ( date.getDate() + value ) / 7 ) - ( 7 - value >= properties.minDays ? 0 : 1 ); break; // Day case "d": value = date.getDate(); break; case "D": value = dateDayOfYear( date ) + 1; break; case "F": // Day of Week in month. eg. 2nd Wed in July. value = Math.floor( date.getDate() / 7 ) + 1; break; // Week day case "e": case "c": if ( length <= 2 ) { // Range is [1-7] (deduced by example provided on documentation) // TODO Should pad with zeros (not specified in the docs)? value = dateDayOfWeek( date, properties.firstDay ) + 1; break; } /* falls through */ case "E": value = dateWeekDays[ date.getDay() ]; value = properties.days[ chr ][ length ][ value ]; break; // Period (AM or PM) case "a": value = properties.dayPeriods[ date.getHours() < 12 ? "am" : "pm" ]; break; // Hour case "h": // 1-12 value = ( date.getHours() % 12 ) || 12; break; case "H": // 0-23 value = date.getHours(); break; case "K": // 0-11 value = date.getHours() % 12; break; case "k": // 1-24 value = date.getHours() || 24; break; // Minute case "m": value = date.getMinutes(); break; // Second case "s": value = date.getSeconds(); break; case "S": value = Math.round( date.getMilliseconds() * Math.pow( 10, length - 3 ) ); break; case "A": value = Math.round( dateMillisecondsInDay( date ) * Math.pow( 10, length - 3 ) ); break; // Zone case "z": break; case "v": // v...vvv: "{shortRegion}", eg. "PT". // vvvv: "{regionName} {Time}", // e.g., "Pacific Time". if ( properties.genericTzName ) { value = properties.genericTzName; break; } /* falls through */ case "V": //VVVV: "{explarCity} {Time}", e.g., "Los Angeles Time" if ( properties.timeZoneName ) { value = properties.timeZoneName; break; } if ( current === "v" ) { length = 1; } /* falls through */ case "O": // O: "{gmtFormat}+H;{gmtFormat}-H" or "{gmtZeroFormat}", eg. "GMT-8" or "GMT". // OOOO: "{gmtFormat}{hourFormat}" or "{gmtZeroFormat}", eg. "GMT-08:00" or "GMT". if ( date.getTimezoneOffset() === 0 ) { value = properties.gmtZeroFormat; } else { // If O..OOO and timezone offset has non-zero minutes, show minutes. if ( length < 4 ) { aux = date.getTimezoneOffset(); aux = properties.hourFormat[ aux % 60 - aux % 1 === 0 ? 0 : 1 ]; } else { aux = properties.hourFormat; } value = dateTimezoneHourFormat( date, aux, timeSeparator, numberFormatters ); value = properties.gmtFormat.replace( /\{0\}/, value ); } break; case "X": // Same as x*, except it uses "Z" for zero offset. if ( date.getTimezoneOffset() === 0 ) { value = "Z"; break; } /* falls through */ case "x": // x: hourFormat("+HH[mm];-HH[mm]") // xx: hourFormat("+HHmm;-HHmm") // xxx: hourFormat("+HH:mm;-HH:mm") // xxxx: hourFormat("+HHmm[ss];-HHmm[ss]") // xxxxx: hourFormat("+HH:mm[:ss];-HH:mm[:ss]") aux = date.getTimezoneOffset(); // If x and timezone offset has non-zero minutes, use xx (i.e., show minutes). if ( length === 1 && aux % 60 - aux % 1 !== 0 ) { length += 1; } // If (xxxx or xxxxx) and timezone offset has zero seconds, use xx or xxx // respectively (i.e., don't show optional seconds). if ( ( length === 4 || length === 5 ) && aux % 1 === 0 ) { length -= 2; } value = [ "+HH;-HH", "+HHmm;-HHmm", "+HH:mm;-HH:mm", "+HHmmss;-HHmmss", "+HH:mm:ss;-HH:mm:ss" ][ length - 1 ]; value = dateTimezoneHourFormat( date, value, ":" ); break; // timeSeparator case ":": value = timeSeparator; break; // ' literals. case "'": value = removeLiteralQuotes( current ); break; // Anything else is considered a literal, including [ ,:/.@#], chinese, japonese, and // arabic characters. default: value = current; } if ( typeof value === "number" ) { value = numberFormatters[ length ]( value ); } dateField = dateFieldsMap[ chr ]; type = dateField ? dateField : "literal"; partsPush( parts, type, value ); }); return parts; }; var dateFormatterFn = function( dateToPartsFormatter ) { return function dateFormatter( value ) { return partsJoin( dateToPartsFormatter( value )); }; }; /** * isLeapYear( year ) * * @year [Number] * * Returns an indication whether the specified year is a leap year. */ var dateIsLeapYear = function( year ) { return new Date( year, 1, 29 ).getMonth() === 1; }; /** * lastDayOfMonth( date ) * * @date [Date] * * Return the last day of the given date's month */ var dateLastDayOfMonth = function( date ) { return new Date( date.getFullYear(), date.getMonth() + 1, 0 ).getDate(); }; /** * Differently from native date.setDate(), this function returns a date whose * day remains inside the month boundaries. For example: * * setDate( FebDate, 31 ): a "Feb 28" date. * setDate( SepDate, 31 ): a "Sep 30" date. */ var dateSetDate = function( date, day ) { var lastDay = new Date( date.getFullYear(), date.getMonth() + 1, 0 ).getDate(); date.setDate( day < 1 ? 1 : day < lastDay ? day : lastDay ); }; /** * Differently from native date.setMonth(), this function adjusts date if * needed, so final month is always the one set. * * setMonth( Jan31Date, 1 ): a "Feb 28" date. * setDate( Jan31Date, 8 ): a "Sep 30" date. */ var dateSetMonth = function( date, month ) { var originalDate = date.getDate(); date.setDate( 1 ); date.setMonth( month ); dateSetDate( date, originalDate ); }; var outOfRange = function( value, low, high ) { return value < low || value > high; }; /** * parse( value, tokens, properties ) * * @value [String] string date. * * @tokens [Object] tokens returned by date/tokenizer. * * @properties [Object] output returned by date/tokenizer-properties. * * ref: http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns */ var dateParse = function( _value, tokens, properties ) { var amPm, day, daysOfYear, month, era, hour, hour12, timezoneOffset, valid, YEAR = 0, MONTH = 1, DAY = 2, HOUR = 3, MINUTE = 4, SECOND = 5, MILLISECONDS = 6, date = new Date(), truncateAt = [], units = [ "year", "month", "day", "hour", "minute", "second", "milliseconds" ]; // Create globalize date with given timezone data. if ( properties.timeZoneData ) { date = new ZonedDateTime( date, properties.timeZoneData() ); } if ( !tokens.length ) { return null; } valid = tokens.every(function( token ) { var century, chr, value, length; if ( token.type === "literal" ) { // continue return true; } chr = token.type.charAt( 0 ); length = token.type.length; if ( chr === "j" ) { // Locale preferred hHKk. // http://www.unicode.org/reports/tr35/tr35-dates.html#Time_Data chr = properties.preferredTimeData; } switch ( chr ) { // Era case "G": truncateAt.push( YEAR ); era = +token.value; break; // Year case "y": value = token.value; if ( length === 2 ) { if ( outOfRange( value, 0, 99 ) ) { return false; } // mimic dojo/date/locale: choose century to apply, according to a sliding // window of 80 years before and 20 years after present year. century = Math.floor( date.getFullYear() / 100 ) * 100; value += century; if ( value > date.getFullYear() + 20 ) { value -= 100; } } date.setFullYear( value ); truncateAt.push( YEAR ); break; case "Y": // Year in "Week of Year" throw createErrorUnsupportedFeature({ feature: "year pattern `" + chr + "`" }); // Quarter (skip) case "Q": case "q": break; // Month case "M": case "L": if ( length <= 2 ) { value = token.value; } else { value = +token.value; } if ( outOfRange( value, 1, 12 ) ) { return false; } // Setting the month later so that we have the correct year and can determine // the correct last day of February in case of leap year. month = value; truncateAt.push( MONTH ); break; // Week (skip) case "w": // Week of Year. case "W": // Week of Month. break; // Day case "d": day = token.value; truncateAt.push( DAY ); break; case "D": daysOfYear = token.value; truncateAt.push( DAY ); break; case "F": // Day of Week in month. eg. 2nd Wed in July. // Skip break; // Week day case "e": case "c": case "E": // Skip. // value = arrayIndexOf( dateWeekDays, token.value ); break; // Period (AM or PM) case "a": amPm = token.value; break; // Hour case "h": // 1-12 value = token.value; if ( outOfRange( value, 1, 12 ) ) { return false; } hour = hour12 = true; date.setHours( value === 12 ? 0 : value ); truncateAt.push( HOUR ); break; case "K": // 0-11 value = token.value; if ( outOfRange( value, 0, 11 ) ) { return false; } hour = hour12 = true; date.setHours( value ); truncateAt.push( HOUR ); break; case "k": // 1-24 value = token.value; if ( outOfRange( value, 1, 24 ) ) { return false; } hour = true; date.setHours( value === 24 ? 0 : value ); truncateAt.push( HOUR ); break; case "H": // 0-23 value = token.value; if ( outOfRange( value, 0, 23 ) ) { return false; } hour = true; date.setHours( value ); truncateAt.push( HOUR ); break; // Minute case "m": value = token.value; if ( outOfRange( value, 0, 59 ) ) { return false; } date.setMinutes( value ); truncateAt.push( MINUTE ); break; // Second case "s": value = token.value; if ( outOfRange( value, 0, 59 ) ) { return false; } date.setSeconds( value ); truncateAt.push( SECOND ); break; case "A": date.setHours( 0 ); date.setMinutes( 0 ); date.setSeconds( 0 ); /* falls through */ case "S": value = Math.round( token.value * Math.pow( 10, 3 - length ) ); date.setMilliseconds( value ); truncateAt.push( MILLISECONDS ); break; // Zone case "z": case "Z": case "O": case "v": case "V": case "X": case "x": if ( typeof token.value === "number" ) { timezoneOffset = token.value; } break; } return true; }); if ( !valid ) { return null; } // 12-hour format needs AM or PM, 24-hour format doesn't, ie. return null // if amPm && !hour12 || !amPm && hour12. if ( hour && !( !amPm ^ hour12 ) ) { return null; } if ( era === 0 ) { // 1 BC = year 0 date.setFullYear( date.getFullYear() * -1 + 1 ); } if ( month !== undefined ) { dateSetMonth( date, month - 1 ); } if ( day !== undefined ) { if ( outOfRange( day, 1, dateLastDayOfMonth( date ) ) ) { return null; } date.setDate( day ); } else if ( daysOfYear !== undefined ) { if ( outOfRange( daysOfYear, 1, dateIsLeapYear( date.getFullYear() ) ? 366 : 365 ) ) { return null; } date.setMonth( 0 ); date.setDate( daysOfYear ); } if ( hour12 && amPm === "pm" ) { date.setHours( date.getHours() + 12 ); } if ( timezoneOffset !== undefined ) { date.setMinutes( date.getMinutes() + timezoneOffset - date.getTimezoneOffset() ); } // Truncate date at the most precise unit defined. Eg. // If value is "12/31", and pattern is "MM/dd": // => new Date( , 12, 31, 0, 0, 0, 0 ); truncateAt = Math.max.apply( null, truncateAt ); date = dateStartOf( date, units[ truncateAt ] ); // Get date back from globalize date. if ( date instanceof ZonedDateTime ) { date = date.toDate(); } return date; }; /* eslint-disable no-unused-expressions */ /** * tokenizer( value, numberParser, properties ) * * @value [String] string date. * * @numberParser [Function] * * @properties [Object] output returned by date/tokenizer-properties. * * Returns an Array of tokens, eg. value "5 o'clock PM", pattern "h 'o''clock' a": * [{ * type: "h", * lexeme: "5" * }, { * type: "literal", * lexeme: " " * }, { * type: "literal", * lexeme: "o'clock" * }, { * type: "literal", * lexeme: " " * }, { * type: "a", * lexeme: "PM", * value: "pm" * }] * * OBS: lexeme's are always String and may return invalid ranges depending of the token type. * Eg. "99" for month number. * * Return an empty Array when not successfully parsed. */ var dateTokenizer = function( value, numberParser, properties ) { var digitsRe, valid, tokens = [], widths = [ "abbreviated", "wide", "narrow" ]; digitsRe = properties.digitsRe; value = looseMatching( value ); valid = properties.pattern.match( datePatternRe ).every(function( current ) { var aux, chr, length, numeric, tokenRe, token = {}; function hourFormatParse( tokenRe, numberParser ) { var aux, isPositive, match = value.match( tokenRe ); numberParser = numberParser || function( value ) { return +value; }; if ( !match ) { return false; } isPositive = match[ 1 ]; // hourFormat containing H only, e.g., `+H;-H` if ( match.length < 6 ) { aux = isPositive ? 1 : 3; token.value = numberParser( match[ aux ] ) * 60; // hourFormat containing H and m, e.g., `+HHmm;-HHmm` } else if ( match.length < 10 ) { aux = isPositive ? [ 1, 3 ] : [ 5, 7 ]; token.value = numberParser( match[ aux[ 0 ] ] ) * 60 + numberParser( match[ aux[ 1 ] ] ); // hourFormat containing H, m, and s e.g., `+HHmmss;-HHmmss` } else { aux = isPositive ? [ 1, 3, 5 ] : [ 7, 9, 11 ]; token.value = numberParser( match[ aux[ 0 ] ] ) * 60 + numberParser( match[ aux[ 1 ] ] ) + numberParser( match[ aux[ 2 ] ] ) / 60; } if ( isPositive ) { token.value *= -1; } return true; } function oneDigitIfLengthOne() { if ( length === 1 ) { // Unicode equivalent to /\d/ numeric = true; return tokenRe = digitsRe; } } function oneOrTwoDigitsIfLengthOne() { if ( length === 1 ) { // Unicode equivalent to /\d\d?/ numeric = true; return tokenRe = new RegExp( "^(" + digitsRe.source + "){1,2}" ); } } function oneOrTwoDigitsIfLengthOneOrTwo() { if ( length === 1 || length === 2 ) { // Unicode equivalent to /\d\d?/ numeric = true; return tokenRe = new RegExp( "^(" + digitsRe.source + "){1,2}" ); } } function twoDigitsIfLengthTwo() { if ( length === 2 ) { // Unicode equivalent to /\d\d/ numeric = true; return tokenRe = new RegExp( "^(" + digitsRe.source + "){2}" ); } } // Brute-force test every locale entry in an attempt to match the given value. // Return the first found one (and set token accordingly), or null. function lookup( path ) { var array = properties[ path.join( "/" ) ]; if ( !array ) { return null; } // array of pairs [key, value] sorted by desc value length. array.some(function( item ) { var valueRe = item[ 1 ]; if ( valueRe.test( value ) ) { token.value = item[ 0 ]; tokenRe = item[ 1 ]; return true; } }); return null; } token.type = current; chr = current.charAt( 0 ); length = current.length; if ( chr === "Z" ) { // Z..ZZZ: same as "xxxx". if ( length < 4 ) { chr = "x"; length = 4; // ZZZZ: same as "OOOO". } else if ( length < 5 ) { chr = "O"; length = 4; // ZZZZZ: same as "XXXXX" } else { chr = "X"; length = 5; } } if ( chr === "z" ) { if ( properties.standardOrDaylightTzName ) { token.value = null; tokenRe = properties.standardOrDaylightTzName; } } // v...vvv: "{shortRegion}", eg. "PT". // vvvv: "{regionName} {Time}" or "{regionName} {Time}", // e.g., "Pacific Time" // http://unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns if ( chr === "v" ) { if ( properties.genericTzName ) { token.value = null; tokenRe = properties.genericTzName; // Fall back to "V" format. } else { chr = "V"; length = 4; } } if ( chr === "V" && properties.timeZoneName ) { token.value = length === 2 ? properties.timeZoneName : null; tokenRe = properties.timeZoneNameRe; } switch ( chr ) { // Era case "G": lookup([ "gregorian/eras", length <= 3 ? "eraAbbr" : ( length === 4 ? "eraNames" : "eraNarrow" ) ]); break; // Year case "y": case "Y": numeric = true; // number l=1:+, l=2:{2}, l=3:{3,}, l=4:{4,}, ... if ( length === 1 ) { // Unicode equivalent to /\d+/. tokenRe = new RegExp( "^(" + digitsRe.source + ")+" ); } else if ( length === 2 ) { // Lenient parsing: there's no year pattern to indicate non-zero-padded 2-digits // year, so parser accepts both zero-padded and non-zero-padded for `yy`. // // Unicode equivalent to /\d\d?/ tokenRe = new RegExp( "^(" + digitsRe.source + "){1,2}" ); } else { // Unicode equivalent to /\d{length,}/ tokenRe = new RegExp( "^(" + digitsRe.source + "){" + length + ",}" ); } break; // Quarter case "Q": case "q": // number l=1:{1}, l=2:{2}. // lookup l=3... oneDigitIfLengthOne() || twoDigitsIfLengthTwo() || lookup([ "gregorian/quarters", chr === "Q" ? "format" : "stand-alone", widths[ length - 3 ] ]); break; // Month case "M": case "L": // number l=1:{1,2}, l=2:{2}. // lookup l=3... // // Lenient parsing: skeleton "yMd" (i.e., one M) may include MM for the pattern, // therefore parser accepts both zero-padded and non-zero-padded for M and MM. // Similar for L. oneOrTwoDigitsIfLengthOneOrTwo() || lookup([ "gregorian/months", chr === "M" ? "format" : "stand-alone", widths[ length - 3 ] ]); break; // Day case "D": // number {l,3}. if ( length <= 3 ) { // Equivalent to /\d{length,3}/ numeric = true; tokenRe = new RegExp( "^(" + digitsRe.source + "){" + length + ",3}" ); } break; case "W": case "F": // number l=1:{1}. oneDigitIfLengthOne(); break; // Week day case "e": case "c": // number l=1:{1}, l=2:{2}. // lookup for length >=3. if ( length <= 2 ) { oneDigitIfLengthOne() || twoDigitsIfLengthTwo(); break; } /* falls through */ case "E": if ( length === 6 ) { // Note: if short day names are not explicitly specified, abbreviated day // names are used instead http://www.unicode.org/reports/tr35/tr35-dates.html#months_days_quarters_eras lookup([ "gregorian/days", [ chr === "c" ? "stand-alone" : "format" ], "short" ]) || lookup([ "gregorian/days", [ chr === "c" ? "stand-alone" : "format" ], "abbreviated" ]); } else { lookup([ "gregorian/days", [ chr === "c" ? "stand-alone" : "format" ], widths[ length < 3 ? 0 : length - 3 ] ]); } break; // Period (AM or PM) case "a": lookup([ "gregorian/dayPeriods/format/wide" ]); break; // Week case "w": // number l1:{1,2}, l2:{2}. oneOrTwoDigitsIfLengthOne() || twoDigitsIfLengthTwo(); break; // Day, Hour, Minute, or Second case "d": case "h": case "H": case "K": case "k": case "j": case "m": case "s": // number l1:{1,2}, l2:{2}. // // Lenient parsing: // - skeleton "hms" (i.e., one m) always includes mm for the pattern, i.e., it's // impossible to use a different skeleton to parse non-zero-padded minutes, // therefore parser accepts both zero-padded and non-zero-padded for m. Similar // for seconds s. // - skeleton "hms" (i.e., one h) may include h or hh for the pattern, i.e., it's // impossible to use a different skeleton to parser non-zero-padded hours for some // locales, therefore parser accepts both zero-padded and non-zero-padded for h. // Similar for d (in skeleton yMd). oneOrTwoDigitsIfLengthOneOrTwo(); break; case "S": // number {l}. // Unicode equivalent to /\d{length}/ numeric = true; tokenRe = new RegExp( "^(" + digitsRe.source + "){" + length + "}" ); break; case "A": // number {l+5}. // Unicode equivalent to /\d{length+5}/ numeric = true; tokenRe = new RegExp( "^(" + digitsRe.source + "){" + ( length + 5 ) + "}" ); break; // Zone case "v": case "V": case "z": if ( tokenRe && tokenRe.test( value ) ) { break; } if ( chr === "V" && length === 2 ) { break; } /* falls through */ case "O": // O: "{gmtFormat}+H;{gmtFormat}-H" or "{gmtZeroFormat}", eg. "GMT-8" or "GMT". // OOOO: "{gmtFormat}{hourFormat}" or "{gmtZeroFormat}", eg. "GMT-08:00" or "GMT". if ( value === properties[ "timeZoneNames/gmtZeroFormat" ] ) { token.value = 0; tokenRe = properties[ "timeZoneNames/gmtZeroFormatRe" ]; } else { aux = properties[ "timeZoneNames/hourFormat" ].some(function( hourFormatRe ) { if ( hourFormatParse( hourFormatRe, numberParser ) ) { tokenRe = hourFormatRe; return true; } }); if ( !aux ) { return null; } } break; case "X": // Same as x*, except it uses "Z" for zero offset. if ( value === "Z" ) { token.value = 0; tokenRe = /^Z/; break; } /* falls through */ case "x": // x: hourFormat("+HH[mm];-HH[mm]") // xx: hourFormat("+HHmm;-HHmm") // xxx: hourFormat("+HH:mm;-HH:mm") // xxxx: hourFormat("+HHmm[ss];-HHmm[ss]") // xxxxx: hourFormat("+HH:mm[:ss];-HH:mm[:ss]") aux = properties.x.some(function( hourFormatRe ) { if ( hourFormatParse( hourFormatRe ) ) { tokenRe = hourFormatRe; return true; } }); if ( !aux ) { return null; } break; case "'": token.type = "literal"; tokenRe = new RegExp( "^" + regexpEscape( removeLiteralQuotes( current ) ) ); break; default: token.type = "literal"; tokenRe = new RegExp( "^" + regexpEscape( current ) ); } if ( !tokenRe ) { return false; } // Get lexeme and consume it. value = value.replace( tokenRe, function( lexeme ) { token.lexeme = lexeme; if ( numeric ) { token.value = numberParser( lexeme ); } return ""; }); if ( !token.lexeme ) { return false; } if ( numeric && isNaN( token.value ) ) { return false; } tokens.push( token ); return true; }); if ( value !== "" ) { valid = false; } return valid ? tokens : []; }; var dateParserFn = function( numberParser, parseProperties, tokenizerProperties ) { return function dateParser( value ) { var tokens; validateParameterPresence( value, "value" ); validateParameterTypeString( value, "value" ); tokens = dateTokenizer( value, numberParser, tokenizerProperties ); return dateParse( value, tokens, parseProperties ) || null; }; }; var dateToPartsFormatterFn = function( numberFormatters, properties ) { return function dateToPartsFormatter( value ) { validateParameterPresence( value, "value" ); validateParameterTypeDate( value, "value" ); return dateFormat( value, numberFormatters, properties ); }; }; Globalize._dateFormat = dateFormat; Globalize._dateFormatterFn = dateFormatterFn; Globalize._dateParser = dateParse; Globalize._dateParserFn = dateParserFn; Globalize._dateTokenizer = dateTokenizer; Globalize._dateToPartsFormatterFn = dateToPartsFormatterFn; Globalize._validateParameterTypeDate = validateParameterTypeDate; function optionsHasStyle( options ) { return options.skeleton !== undefined || options.date !== undefined || options.time !== undefined || options.datetime !== undefined || options.raw !== undefined; } Globalize.dateFormatter = Globalize.prototype.dateFormatter = function( options ) { options = options || {}; if ( !optionsHasStyle( options ) ) { options.skeleton = "yMd"; } return Globalize[ runtimeKey( "dateFormatter", this._locale, [ options ] ) ]; }; Globalize.dateToPartsFormatter = Globalize.prototype.dateToPartsFormatter = function( options ) { options = options || {}; if ( !optionsHasStyle( options ) ) { options.skeleton = "yMd"; } return Globalize[ runtimeKey( "dateToPartsFormatter", this._locale, [ options ] ) ]; }; Globalize.dateParser = Globalize.prototype.dateParser = function( options ) { options = options || {}; if ( !optionsHasStyle( options ) ) { options.skeleton = "yMd"; } return Globalize[ runtimeKey( "dateParser", this._locale, [ options ] ) ]; }; Globalize.formatDate = Globalize.prototype.formatDate = function( value, options ) { validateParameterPresence( value, "value" ); validateParameterTypeDate( value, "value" ); return this.dateFormatter( options )( value ); }; Globalize.formatDateToParts = Globalize.prototype.formatDateToParts = function( value, options ) { validateParameterPresence( value, "value" ); validateParameterTypeDate( value, "value" ); return this.dateToPartsFormatter( options )( value ); }; Globalize.parseDate = Globalize.prototype.parseDate = function( value, options ) { validateParameterPresence( value, "value" ); validateParameterTypeString( value, "value" ); return this.dateParser( options )( value ); }; return Globalize; }));