Permalink
Browse files

Date: Dynamically augment skeleton (3/3)

Ref #271
Closes #604
Closes #462
  • Loading branch information...
rxaviers committed Mar 13, 2017
1 parent c91e911 commit 687207b396e52fb1be6678674fa48bb6860f9048
View
@@ -7,4 +7,3 @@ before_install:
install:
- npm install
- bower install
-sudo: false
@@ -126,7 +126,7 @@ For comparison, follow the same formatter `{ datetime: "short" }` on different l
| *pt* | `"01/11/10 17:55"` |
| *ar* | `"١‏/١١‏/٢٠١٠ ٥،٥٥ م"` |
-Use skeletons for more flexibility (see its description [above](#parameters)).
+Use open-ended skeletons for more flexibility (see its description [above](#parameters)). See some examples below.
| `skeleton` | `Globalize( "en" ).dateFormatter( skeleton )( new Date( 2010, 10, 1, 17, 55 ) )` |
| --- | --- |
View
@@ -45,6 +45,7 @@ var createError = Globalize._createError,
removeLiteralQuotes = Globalize._removeLiteralQuotes,
runtimeBind = Globalize._runtimeBind,
stringPad = Globalize._stringPad,
+ validate = Globalize._validate,
validateCldr = Globalize._validateCldr,
validateDefaultLocale = Globalize._validateDefaultLocale,
validateParameterPresence = Globalize._validateParameterPresence,
View
@@ -1,6 +1,7 @@
define([
"cldr",
"./common/runtime-bind",
+ "./common/validate",
"./common/validate/cldr",
"./common/validate/default-locale",
"./common/validate/parameter-presence",
@@ -18,10 +19,10 @@ define([
"cldr/event",
"cldr/supplemental",
"./number"
-], function( Cldr, runtimeBind, validateCldr, validateDefaultLocale, validateParameterPresence,
- validateParameterTypeDate, validateParameterTypePlainObject, validateParameterTypeString,
- Globalize, dateExpandPattern, dateFormatterFn, dateFormatProperties, dateParserFn,
- dateParseProperties, dateTokenizerProperties ) {
+], function( Cldr, runtimeBind, validate, validateCldr, validateDefaultLocale,
+ validateParameterPresence, validateParameterTypeDate, validateParameterTypePlainObject,
+ validateParameterTypeString, Globalize, dateExpandPattern, dateFormatterFn,
+ dateFormatProperties, dateParserFn, dateParseProperties, dateTokenizerProperties ) {
function validateRequiredCldr( path, value ) {
validateCldr( path, value, {
@@ -34,6 +35,31 @@ function validateRequiredCldr( path, value ) {
});
}
+function validateOptionsPreset( options ) {
+ validateOptionsPresetEach( "date", options );
+ validateOptionsPresetEach( "time", options );
+ validateOptionsPresetEach( "datetime", options );
+}
+
+function validateOptionsPresetEach( type, options ) {
+ var value = options[ type ];
+ validate(
+ "E_INVALID_OPTIONS",
+ "Invalid `{{type}: \"{value}\"}`.",
+ value === undefined || [ "short", "medium", "long", "full" ].indexOf( value ) !== -1,
+ { type: type, value: value }
+ );
+}
+
+function validateOptionsSkeleton( pattern, skeleton ) {
+ validate(
+ "E_INVALID_OPTIONS",
+ "Invalid `{skeleton: \"{value}\"}` based on provided CLDR.",
+ skeleton === undefined || ( typeof pattern === "string" && pattern ),
+ { type: "skeleton", value: skeleton }
+ );
+}
+
/**
* .dateFormatter( options )
*
@@ -58,12 +84,14 @@ Globalize.prototype.dateFormatter = function( options ) {
cldr = this.cldr;
options = options || { skeleton: "yMd" };
- args = [ options ];
-
+ validateOptionsPreset( options );
validateDefaultLocale( cldr );
+ args = [ options ];
+
cldr.on( "get", validateRequiredCldr );
pattern = dateExpandPattern( options, cldr );
+ validateOptionsSkeleton( pattern, options.skeleton );
properties = dateFormatProperties( pattern, cldr );
cldr.off( "get", validateRequiredCldr );
@@ -100,12 +128,14 @@ Globalize.prototype.dateParser = function( options ) {
cldr = this.cldr;
options = options || { skeleton: "yMd" };
- args = [ options ];
-
+ validateOptionsPreset( options );
validateDefaultLocale( cldr );
+ args = [ options ];
+
cldr.on( "get", validateRequiredCldr );
pattern = dateExpandPattern( options, cldr );
+ validateOptionsSkeleton( pattern, options.skeleton );
tokenizerProperties = dateTokenizerProperties( pattern, cldr );
parseProperties = dateParseProperties( cldr );
cldr.off( "get", validateRequiredCldr );
View
@@ -1,8 +1,9 @@
define([
"../common/format-message",
"../common/create-error/invalid-parameter-value",
- "./pattern-re"
-], function( formatMessage, createErrorInvalidParameterValue, datePatternRe ) {
+ "./expand-pattern/get-best-match-pattern"
+], function( formatMessage, createErrorInvalidParameterValue,
+ dateExpandPatternGetBestMatchPattern ) {
/**
* expandPattern( options, cldr )
@@ -25,9 +26,11 @@ define([
* - { datetime: "full" } returns "EEEE, MMMM d, y 'at' h:mm:ss a zzzz";
* - { raw: "dd/mm" } returns "dd/mm";
*/
-
return function( options, cldr ) {
- var dateSkeleton, result, skeleton, timeSkeleton, type, dateTimeSkeleton;
+ var dateSkeleton, result, skeleton, timeSkeleton, type,
+
+ // Using easier to read variables.
+ getBestMatchPattern = dateExpandPatternGetBestMatchPattern;
function combineDateTime( type, datePattern, timePattern ) {
return formatMessage(
@@ -39,171 +42,53 @@ return function( options, cldr ) {
);
}
- function getBestMatchPattern( path, skeleton ) {
- var availableFormats, ratedFormats, format, pattern;
-
- pattern = cldr.main([ path, skeleton ]);
-
- if ( skeleton && !pattern ) {
- availableFormats = cldr.main([ path ]);
- ratedFormats = [];
-
- for ( format in availableFormats ) {
- ratedFormats.push({
- format: format,
- pattern: availableFormats[format],
- rate: compareFormats( skeleton, format )
- });
- }
+ switch ( true ) {
+ case "skeleton" in options:
+ skeleton = options.skeleton;
- ratedFormats = ratedFormats
- .filter( function( format ) {
- return format.rate > -1;
- } )
- .sort( function( formatA, formatB ) {
- return formatA.rate - formatB.rate;
- });
+ // Preferred hour (j).
+ skeleton = skeleton.replace( /j/g, function() {
+ return cldr.supplemental.timeData.preferred();
+ });
- if ( ratedFormats.length ) {
- pattern = augmentFormat( skeleton, ratedFormats[0].pattern );
+ // Try direct map (note that getBestMatchPattern handles it).
+ // ... or, try to "best match" the whole skeleton.
+ result = getBestMatchPattern(
+ cldr,
+ skeleton
+ );
+ if ( result ) {
+ break;
}
- }
-
- return pattern;
- }
-
- function repeatStr( str, count ) {
- var i, result = "";
- for ( i = 0; i < count; i++ ) {
- result = result + str;
- }
- return result;
- }
- function normalizePatternType( char ) {
- switch ( char ) {
- case "e":
- case "E":
- case "c":
- return "e";
-
- case "M":
- case "L":
- return "L";
-
- default:
- return char;
- }
- }
+ // ... or, try to "best match" the date and time parts individually.
+ timeSkeleton = skeleton.split( /[^hHKkmsSAzZOvVXx]/ ).slice( -1 )[ 0 ];
+ dateSkeleton = skeleton.split( /[^GyYuUrQqMLlwWdDFgEec]/ )[ 0 ];
+ dateSkeleton = getBestMatchPattern(
+ cldr,
+ dateSkeleton
+ );
+ timeSkeleton = getBestMatchPattern(
+ cldr,
+ timeSkeleton
+ );
- function compareFormats( formatA, formatB ) {
- var distance,
- typeA,
- typeB,
- matchFound,
- i,
- j;
-
- if ( formatA === formatB ) {
- return 0;
- }
-
- formatA = formatA.match( datePatternRe );
- formatB = formatB.match( datePatternRe );
- if ( formatA.length === formatB.length ) {
- distance = 1;
- for ( i = 0; i < formatA.length; i++ ) {
- typeA = normalizePatternType( formatA[i].charAt( 0 ) );
- typeB = null;
- matchFound = false;
- for ( j = 0; j < formatB.length; j++ ) {
- typeB = normalizePatternType( formatB[j].charAt( 0 ) );
- if ( typeA === typeB ) {
- break;
- } else {
- typeB = null;
- }
- }
- if ( null === typeB ) {
- return -1;
- }
- distance = distance + Math.abs( formatA[i].length - formatB[j].length );
- if ( formatA[i].charAt( 0 ) !== formatB[j].charAt( 0 ) ) {
- distance = distance + 1;
- }
+ if ( /(MMMM|LLLL).*[Ec]/.test( dateSkeleton ) ) {
+ type = "full";
+ } else if ( /MMMM|LLLL/.test( dateSkeleton ) ) {
+ type = "long";
+ } else if ( /MMM|LLL/.test( dateSkeleton ) ) {
+ type = "medium";
+ } else {
+ type = "short";
}
- return distance;
- }
- return -1;
- }
- function augmentFormat( requestedSkeleton, bestMatchFormat ) {
- var i, j, matchedType, matchedLength, requestedType, requestedLength;
-
- requestedSkeleton = requestedSkeleton.match( datePatternRe );
- bestMatchFormat = bestMatchFormat.match( datePatternRe );
-
- for ( i = 0; i < bestMatchFormat.length; i++ ) {
- matchedType = bestMatchFormat[i].charAt( 0 );
- matchedLength = bestMatchFormat[i].length;
- for ( j = 0; j < requestedSkeleton.length; j++ ) {
- requestedType = requestedSkeleton[j].charAt( 0 );
- requestedLength = requestedSkeleton[j].length;
- if (
- normalizePatternType( matchedType ) === normalizePatternType( requestedType ) &&
- matchedLength < requestedLength
- ) {
- bestMatchFormat[i] = repeatStr( matchedType, requestedLength );
- }
+ if ( dateSkeleton && timeSkeleton ) {
+ result = combineDateTime( type, dateSkeleton, timeSkeleton );
+ } else {
+ result = dateSkeleton || timeSkeleton;
}
- }
-
- return bestMatchFormat.join( "" );
- }
- switch ( true ) {
- case "skeleton" in options:
- skeleton = options.skeleton;
- result = cldr.main([
- "dates/calendars/gregorian/dateTimeFormats/availableFormats",
- skeleton
- ]);
- if ( !result ) {
- timeSkeleton = skeleton.split( /[^hHKkmsSAzZOvVXx]/ ).slice( -1 )[ 0 ];
- dateSkeleton = skeleton.split( /[^GyYuUrQqMLlwWdDFgEec]/ )[ 0 ];
- dateTimeSkeleton = getBestMatchPattern(
- "dates/calendars/gregorian/dateTimeFormats/availableFormats",
- skeleton
- );
- if ( dateTimeSkeleton ) {
- result = dateTimeSkeleton;
- } else {
- dateSkeleton = getBestMatchPattern(
- "dates/calendars/gregorian/dateTimeFormats/availableFormats",
- dateSkeleton
- );
- timeSkeleton = getBestMatchPattern(
- "dates/calendars/gregorian/dateTimeFormats/availableFormats",
- timeSkeleton
- );
-
- if ( /(MMMM|LLLL).*[Ec]/.test( dateSkeleton ) ) {
- type = "full";
- } else if ( /MMMM/g.test( dateSkeleton ) ) {
- type = "long";
- } else if ( /MMM/g.test( dateSkeleton ) || /LLL/g.test( dateSkeleton ) ) {
- type = "medium";
- } else {
- type = "short";
- }
-
- if ( dateSkeleton && timeSkeleton ) {
- result = combineDateTime( type, dateSkeleton, timeSkeleton );
- } else {
- result = dateSkeleton || timeSkeleton;
- }
- }
- }
break;
case "date" in options:
@@ -0,0 +1,33 @@
+define([
+ "./normalize-pattern-type",
+ "../pattern-re",
+ "../../util/string/repeat"
+], function( dateExpandPatternNormalizePatternType, datePatternRe, stringRepeat ) {
+
+return function( requestedSkeleton, bestMatchFormat ) {
+ var i, j, matchedType, matchedLength, requestedType, requestedLength,
+
+ // Using an easier to read variable.
+ normalizePatternType = dateExpandPatternNormalizePatternType;
+
+ requestedSkeleton = requestedSkeleton.match( datePatternRe );
+ bestMatchFormat = bestMatchFormat.match( datePatternRe );
+
+ for ( i = 0; i < bestMatchFormat.length; i++ ) {
+ matchedType = bestMatchFormat[i].charAt( 0 );
+ matchedLength = bestMatchFormat[i].length;
+ for ( j = 0; j < requestedSkeleton.length; j++ ) {
+ requestedType = requestedSkeleton[j].charAt( 0 );
+ requestedLength = requestedSkeleton[j].length;
+ if ( normalizePatternType( matchedType ) === normalizePatternType( requestedType ) &&
+ matchedLength < requestedLength
+ ) {
+ bestMatchFormat[i] = stringRepeat( matchedType, requestedLength );
+ }
+ }
+ }
+
+ return bestMatchFormat.join( "" );
+};
+
+});
Oops, something went wrong.

0 comments on commit 687207b

Please sign in to comment.