Skip to content

Commit

Permalink
Date: Loose Matching
Browse files Browse the repository at this point in the history
Fixes #292
  • Loading branch information
rxaviers committed May 23, 2017
1 parent 1ad3eda commit 7aea387
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 63 deletions.
1 change: 1 addition & 0 deletions src/build/intro-date-runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"use strict";

var createErrorUnsupportedFeature = Globalize._createErrorUnsupportedFeature,
looseMatching = Globalize._looseMatching,
regexpEscape = Globalize._regexpEscape,
removeLiteralQuotes = Globalize._removeLiteralQuotes,
runtimeKey = Globalize._runtimeKey,
Expand Down
2 changes: 2 additions & 0 deletions src/build/intro-date.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
var createError = Globalize._createError,
createErrorUnsupportedFeature = Globalize._createErrorUnsupportedFeature,
formatMessage = Globalize._formatMessage,
isPlainObject = Globalize._isPlainObject,
looseMatching = Globalize._looseMatching,
numberSymbol = Globalize._numberSymbol,
regexpEscape = Globalize._regexpEscape,
removeLiteralQuotes = Globalize._removeLiteralQuotes,
Expand Down
47 changes: 35 additions & 12 deletions src/date/tokenizer-properties.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ define([
"../common/create-error/unsupported-feature",
"../common/format-message",
"../number/symbol",
"../util/is-plain-object",
"../util/loose-matching",
"../util/object/filter"
], function( dateGetTimeZoneName, datePatternRe, dateTimezoneHourFormatH, dateTimezoneHourFormatHm,
createErrorUnsupportedFeature, formatMessage, numberSymbol, objectFilter ) {
createErrorUnsupportedFeature, formatMessage, numberSymbol, isPlainObject, looseMatching,
objectFilter ) {

/**
* tokenizerProperties( pattern, cldr )
Expand All @@ -21,7 +24,7 @@ define([
*/
return function( pattern, cldr, timeZone ) {
var properties = {
pattern: pattern,
pattern: looseMatching( pattern ),
timeSeparator: numberSymbol( "timeSeparator", cldr )
},
widths = [ "abbreviated", "wide", "narrow" ];
Expand All @@ -34,8 +37,29 @@ return function( pattern, cldr, timeZone ) {
return;
}

if ( !value ) {
return;
}

// The `dates` and `calendars` trim's purpose is to reduce properties' key size only.
properties[ path.replace( /^.*\/dates\//, "" ).replace( /calendars\//, "" ) ] = value;
path = path.replace( /^.*\/dates\//, "" ).replace( /calendars\//, "" );

// Specific filter for "gregorian/dayPeriods/format/wide".
if ( path === "gregorian/dayPeriods/format/wide" ) {
value = objectFilter( value, /^am|^pm/ );
}

// Transform object into array of pairs [key, /value/].
if ( isPlainObject( value ) ) {
value = Object.keys( value ).map(function( key ) {
return [ key, new RegExp( "^" + looseMatching( value[ key ] ) ) ];
});

// If typeof value === "string".
} else {
value = looseMatching( value );
}
properties[ path ] = value;
}

cldr.on( "get", populateProperties );
Expand All @@ -59,10 +83,10 @@ return function( pattern, cldr, timeZone ) {
standardTzName = dateGetTimeZoneName( length, "standard", timeZone, cldr );
daylightTzName = dateGetTimeZoneName( length, "daylight", timeZone, cldr );
if ( standardTzName ) {
properties.standardTzName = standardTzName;
properties.standardTzName = looseMatching( standardTzName );
}
if ( daylightTzName ) {
properties.daylightTzName = daylightTzName;
properties.daylightTzName = looseMatching( daylightTzName );
}

// Fall through the "O" format in case one name is missing.
Expand All @@ -86,7 +110,7 @@ return function( pattern, cldr, timeZone ) {
}
var genericTzName = dateGetTimeZoneName( length, "generic", timeZone, cldr );
if ( genericTzName ) {
properties.genericTzName = genericTzName;
properties.genericTzName = looseMatching( genericTzName );
chr = "O";

// Fall back to "V" format.
Expand Down Expand Up @@ -186,10 +210,6 @@ return function( pattern, cldr, timeZone ) {
cldr.main(
"dates/calendars/gregorian/dayPeriods/format/wide"
);
properties[ "gregorian/dayPeriods/format/wide" ] = objectFilter(
properties[ "gregorian/dayPeriods/format/wide" ],
/^am|^pm/
);
break;

// Zone
Expand All @@ -203,6 +223,8 @@ return function( pattern, cldr, timeZone ) {

if ( timeZone ) {
if ( length === 2 ) {

// Skip looseMatching processing since timeZone is a canonical posix value.
properties.timeZoneName = timeZone;
break;
}
Expand Down Expand Up @@ -231,7 +253,7 @@ return function( pattern, cldr, timeZone ) {
}

if ( timeZoneName ) {
properties.timeZoneName = timeZoneName;
properties.timeZoneName = looseMatching( timeZoneName );
}
}

Expand All @@ -244,7 +266,8 @@ return function( pattern, cldr, timeZone ) {
case "O":
cldr.main( "dates/timeZoneNames/gmtFormat" );
cldr.main( "dates/timeZoneNames/gmtZeroFormat" );
aux = cldr.main( "dates/timeZoneNames/hourFormat" );
cldr.main( "dates/timeZoneNames/hourFormat" );
aux = properties[ "timeZoneNames/hourFormat" ];
properties[ "timeZoneNames/hourFormat" ] = length < 4 ?
[ dateTimezoneHourFormatH( aux ), dateTimezoneHourFormatHm( aux, "H" ) ] :
[ dateTimezoneHourFormatHm( aux, "HH" ) ];
Expand Down
32 changes: 21 additions & 11 deletions src/date/tokenizer.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
define([
"./pattern-re",
"../util/loose-matching",
"../util/regexp/escape",
"../util/regexp/n",
"../util/remove-literal-quotes"
], function( datePatternRe, regexpEscape, regexpN, removeLiteralQuotes ) {
], function( datePatternRe, looseMatching, regexpEscape, regexpN, removeLiteralQuotes ) {

/**
* tokenizer( value, pattern, properties )
* 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":
Expand Down Expand Up @@ -50,6 +53,8 @@ return function( value, numberParser, properties ) {
}) + ")";
}

value = looseMatching( value );

valid = properties.pattern.match( datePatternRe ).every(function( current ) {
var aux, chr, length, numeric, tokenRe,
token = {};
Expand Down Expand Up @@ -153,16 +158,21 @@ return function( value, numberParser, properties ) {
// 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 i, re,
data = properties[ path.join( "/" ) ];

for ( i in data ) {
re = new RegExp( "^" + data[ i ] );
if ( re.test( value ) ) {
token.value = i;
return tokenRe = new RegExp( data[ i ] );
}
var array = properties[ path.join( "/" ) ];

if ( !array ) {
return null;
}

// array of pairs [key, value].
array.some(function( item ) {
var valueRe = item[ 1 ];
if ( valueRe.test( value ) ) {
token.value = item[ 0 ];
tokenRe = item[ 1 ];
return true;
}
});
return null;
}

Expand Down
5 changes: 4 additions & 1 deletion src/number-runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@ define([
"./number/formatter-fn",
"./number/parse",
"./number/parser-fn",
"./util/loose-matching",
"./util/number/round",
"./util/remove-literal-quotes"
], function( runtimeKey, createErrorUnsupportedFeature, validateParameterPresence,
validateParameterTypeNumber, validateParameterTypeString, Globalize, numberFormat,
numberFormatterFn, numberParse, numberParserFn, numberRound, removeLiteralQuotes ) {
numberFormatterFn, numberParse, numberParserFn, looseMatching, numberRound,
removeLiteralQuotes ) {

Globalize._createErrorUnsupportedFeature = createErrorUnsupportedFeature;
Globalize._looseMatching = looseMatching;
Globalize._numberFormat = numberFormat;
Globalize._numberFormatterFn = numberFormatterFn;
Globalize._numberParse = numberParse;
Expand Down
5 changes: 4 additions & 1 deletion src/number.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ define([
"./number/parse-properties",
"./number/pattern",
"./number/symbol",
"./util/loose-matching",
"./util/remove-literal-quotes",
"./util/string/pad",

Expand All @@ -25,7 +26,8 @@ define([
validateDefaultLocale, validateParameterPresence, validateParameterRange,
validateParameterTypeNumber, validateParameterTypePlainObject, validateParameterTypeString,
numberFormatterFn, numberFormatProperties, numberNumberingSystem, numberParserFn,
numberParseProperties, numberPattern, numberSymbol, removeLiteralQuotes, stringPad ) {
numberParseProperties, numberPattern, numberSymbol, looseMatching, removeLiteralQuotes,
stringPad ) {

function validateDigits( properties ) {
var minimumIntegerDigits = properties[ 2 ],
Expand Down Expand Up @@ -178,6 +180,7 @@ Globalize._createErrorUnsupportedFeature = createErrorUnsupportedFeature;
Globalize._numberNumberingSystem = numberNumberingSystem;
Globalize._numberPattern = numberPattern;
Globalize._numberSymbol = numberSymbol;
Globalize._looseMatching = looseMatching;
Globalize._removeLiteralQuotes = removeLiteralQuotes;
Globalize._stringPad = stringPad;
Globalize._validateParameterTypeNumber = validateParameterTypeNumber;
Expand Down
20 changes: 3 additions & 17 deletions src/number/parse-properties.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@ define([
"./format-properties",
"./symbol/inverted-map",
"../util/always-array",
"../util/loose-matching",
"../util/object/map",
"../util/regexp/cf-g",
"../util/regexp/dash-g",
"../util/regexp/escape",
"../util/regexp/zs-g",
"../util/remove-literal-quotes"
], function( numberFormatProperties, numberSymbolInvertedMap, alwaysArray, objectMap, regexpCfG,
regexpDashG, regexpEscape, regexpZsG, removeLiteralQuotes ) {
], function( numberFormatProperties, numberSymbolInvertedMap, alwaysArray, looseMatching, objectMap,
regexpEscape, removeLiteralQuotes ) {

/**
* parseProperties( pattern, cldr )
Expand All @@ -31,18 +29,6 @@ return function( pattern, cldr, options ) {
numberTokenizer, prefix, primaryGroupingSize, secondaryGroupingSize, suffix, symbolMap,
formatProperties = numberFormatProperties( pattern, cldr, options );

// Loose Matching:
// - Ignore all format characters, which includes RLM, LRM or ALM used to control BIDI
// formatting.
// - Map all characters in [:Zs:] to U+0020 SPACE;
// - Map all characters in [:Dash:] to U+002D HYPHEN-MINUS;
function looseMatching( value ) {
return value
.replace( regexpCfG, "" )
.replace( regexpDashG, "-" )
.replace( regexpZsG, " " );
}

prefix = looseMatching( formatProperties[ 0 ] );
maximumFractionDigits = formatProperties[ 4 ];
minimumSignificantDigits = formatProperties[ 5 ];
Expand Down
16 changes: 3 additions & 13 deletions src/number/parse.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
define([
"../util/regexp/cf-g",
"../util/regexp/dash-g",
"../util/regexp/zs-g"
], function( regexpCfG, regexpDashG, regexpZsG ) {
"../util/loose-matching"
], function( looseMatching ) {

/**
* parse( value, properties )
Expand Down Expand Up @@ -36,15 +34,7 @@ return function( value, properties ) {
invertedNuDigitsMap = properties[ 1 ] || {};
tokenizer = properties[ 2 ];

// Loose Matching:
// - Ignore all format characters, which includes RLM, LRM or ALM used to control BIDI
// formatting.
// - Map all characters in [:Zs:] to U+0020 SPACE;
// - Map all characters in [:Dash:] to U+002D HYPHEN-MINUS;
value = value
.replace( regexpCfG, "" )
.replace( regexpDashG, "-" )
.replace( regexpZsG, " " );
value = looseMatching( value );

function parse( type ) {
return function( lexeme ) {
Expand Down
21 changes: 21 additions & 0 deletions src/util/loose-matching.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
define([
"./regexp/cf-g",
"./regexp/dash-g",
"./regexp/zs-g"
], function( regexpCfG, regexpDashG, regexpZsG ) {

/**
* Loose Matching:
* - Ignore all format characters, which includes RLM, LRM or ALM used to control BIDI
* formatting.
* - Map all characters in [:Zs:] to U+0020 SPACE;
* - Map all characters in [:Dash:] to U+002D HYPHEN-MINUS;
*/
return function( value ) {
return value
.replace( regexpCfG, "" )
.replace( regexpDashG, "-" )
.replace( regexpZsG, " " );
};

});
3 changes: 3 additions & 0 deletions test/functional/date/parse-date.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ QUnit.test( "should parse skeleton", function( assert ) {
assertParseDate( assert, "9/15/2010", { skeleton: "yMd" }, date );
assertParseDate( assert, "الأربعاء، ١٥ سبتمبر، ٢٠١٠ م", { skeleton: "GyMMMEd" }, date, ar );

// Loose matching: ignore control characters.
assertParseDate( assert, "١٥/٩/٢٠١٠", { skeleton: "yMd" }, date, ar );

date = new Date( 2010, 0 );
date = startOf( date, "year" );
assertParseDate( assert, "Q3 2010", { skeleton: "yQQQ" }, date );
Expand Down
Loading

0 comments on commit 7aea387

Please sign in to comment.