Skip to content

Commit 030691a

Browse files
committed
Date: Dynamically augment skeleton (3/3)
Ref globalizejs#271 Closes globalizejs#604 Closes globalizejs#462
1 parent c91e911 commit 030691a

File tree

10 files changed

+251
-219
lines changed

10 files changed

+251
-219
lines changed

.travis.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,3 @@ before_install:
77
install:
88
- npm install
99
- bower install
10-
sudo: false

src/date/expand-pattern.js

Lines changed: 46 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
define([
22
"../common/format-message",
33
"../common/create-error/invalid-parameter-value",
4-
"./pattern-re"
5-
], function( formatMessage, createErrorInvalidParameterValue, datePatternRe ) {
4+
"./expand-pattern/get-best-match-pattern"
5+
], function( formatMessage, createErrorInvalidParameterValue,
6+
dateExpandPatternGetBestMatchPattern ) {
67

78
/**
89
* expandPattern( options, cldr )
@@ -25,9 +26,11 @@ define([
2526
* - { datetime: "full" } returns "EEEE, MMMM d, y 'at' h:mm:ss a zzzz";
2627
* - { raw: "dd/mm" } returns "dd/mm";
2728
*/
28-
2929
return function( options, cldr ) {
30-
var dateSkeleton, result, skeleton, timeSkeleton, type, dateTimeSkeleton;
30+
var dateSkeleton, result, skeleton, timeSkeleton, type,
31+
32+
// Using easier to read variables.
33+
getBestMatchPattern = dateExpandPatternGetBestMatchPattern;
3134

3235
function combineDateTime( type, datePattern, timePattern ) {
3336
return formatMessage(
@@ -39,171 +42,54 @@ return function( options, cldr ) {
3942
);
4043
}
4144

42-
function getBestMatchPattern( path, skeleton ) {
43-
var availableFormats, ratedFormats, format, pattern;
44-
45-
pattern = cldr.main([ path, skeleton ]);
46-
47-
if ( skeleton && !pattern ) {
48-
availableFormats = cldr.main([ path ]);
49-
ratedFormats = [];
50-
51-
for ( format in availableFormats ) {
52-
ratedFormats.push({
53-
format: format,
54-
pattern: availableFormats[format],
55-
rate: compareFormats( skeleton, format )
56-
});
57-
}
45+
switch ( true ) {
46+
case "skeleton" in options:
47+
skeleton = options.skeleton;
5848

59-
ratedFormats = ratedFormats
60-
.filter( function( format ) {
61-
return format.rate > -1;
62-
} )
63-
.sort( function( formatA, formatB ) {
64-
return formatA.rate - formatB.rate;
65-
});
49+
// Preferred hour (j).
50+
skeleton = skeleton.replace( /j/g, cldr.supplemental.timeData.preferred() );
6651

67-
if ( ratedFormats.length ) {
68-
pattern = augmentFormat( skeleton, ratedFormats[0].pattern );
52+
// Try direct map (note that getBestMatchPattern handles it).
53+
// ... or, try to "best match" the whole skeleton.
54+
result = getBestMatchPattern(
55+
cldr,
56+
"dates/calendars/gregorian/dateTimeFormats/availableFormats",
57+
skeleton
58+
);
59+
if ( result ) {
60+
break;
6961
}
70-
}
71-
72-
return pattern;
73-
}
74-
75-
function repeatStr( str, count ) {
76-
var i, result = "";
77-
for ( i = 0; i < count; i++ ) {
78-
result = result + str;
79-
}
80-
return result;
81-
}
82-
83-
function normalizePatternType( char ) {
84-
switch ( char ) {
85-
case "e":
86-
case "E":
87-
case "c":
88-
return "e";
8962

90-
case "M":
91-
case "L":
92-
return "L";
93-
94-
default:
95-
return char;
96-
}
97-
}
63+
// ... or, try to "best match" the date and time parts individually.
64+
timeSkeleton = skeleton.split( /[^hHKkmsSAzZOvVXx]/ ).slice( -1 )[ 0 ];
65+
dateSkeleton = skeleton.split( /[^GyYuUrQqMLlwWdDFgEec]/ )[ 0 ];
66+
dateSkeleton = getBestMatchPattern(
67+
cldr,
68+
"dates/calendars/gregorian/dateTimeFormats/availableFormats",
69+
dateSkeleton
70+
);
71+
timeSkeleton = getBestMatchPattern(
72+
cldr,
73+
"dates/calendars/gregorian/dateTimeFormats/availableFormats",
74+
timeSkeleton
75+
);
9876

99-
function compareFormats( formatA, formatB ) {
100-
var distance,
101-
typeA,
102-
typeB,
103-
matchFound,
104-
i,
105-
j;
106-
107-
if ( formatA === formatB ) {
108-
return 0;
109-
}
110-
111-
formatA = formatA.match( datePatternRe );
112-
formatB = formatB.match( datePatternRe );
113-
if ( formatA.length === formatB.length ) {
114-
distance = 1;
115-
for ( i = 0; i < formatA.length; i++ ) {
116-
typeA = normalizePatternType( formatA[i].charAt( 0 ) );
117-
typeB = null;
118-
matchFound = false;
119-
for ( j = 0; j < formatB.length; j++ ) {
120-
typeB = normalizePatternType( formatB[j].charAt( 0 ) );
121-
if ( typeA === typeB ) {
122-
break;
123-
} else {
124-
typeB = null;
125-
}
126-
}
127-
if ( null === typeB ) {
128-
return -1;
129-
}
130-
distance = distance + Math.abs( formatA[i].length - formatB[j].length );
131-
if ( formatA[i].charAt( 0 ) !== formatB[j].charAt( 0 ) ) {
132-
distance = distance + 1;
133-
}
77+
if ( /(MMMM|LLLL).*[Ec]/.test( dateSkeleton ) ) {
78+
type = "full";
79+
} else if ( /MMMM|LLLL/.test( dateSkeleton ) ) {
80+
type = "long";
81+
} else if ( /MMM|LLL/.test( dateSkeleton ) ) {
82+
type = "medium";
83+
} else {
84+
type = "short";
13485
}
135-
return distance;
136-
}
137-
return -1;
138-
}
13986

140-
function augmentFormat( requestedSkeleton, bestMatchFormat ) {
141-
var i, j, matchedType, matchedLength, requestedType, requestedLength;
142-
143-
requestedSkeleton = requestedSkeleton.match( datePatternRe );
144-
bestMatchFormat = bestMatchFormat.match( datePatternRe );
145-
146-
for ( i = 0; i < bestMatchFormat.length; i++ ) {
147-
matchedType = bestMatchFormat[i].charAt( 0 );
148-
matchedLength = bestMatchFormat[i].length;
149-
for ( j = 0; j < requestedSkeleton.length; j++ ) {
150-
requestedType = requestedSkeleton[j].charAt( 0 );
151-
requestedLength = requestedSkeleton[j].length;
152-
if (
153-
normalizePatternType( matchedType ) === normalizePatternType( requestedType ) &&
154-
matchedLength < requestedLength
155-
) {
156-
bestMatchFormat[i] = repeatStr( matchedType, requestedLength );
157-
}
87+
if ( dateSkeleton && timeSkeleton ) {
88+
result = combineDateTime( type, dateSkeleton, timeSkeleton );
89+
} else {
90+
result = dateSkeleton || timeSkeleton;
15891
}
159-
}
160-
161-
return bestMatchFormat.join( "" );
162-
}
16392

164-
switch ( true ) {
165-
case "skeleton" in options:
166-
skeleton = options.skeleton;
167-
result = cldr.main([
168-
"dates/calendars/gregorian/dateTimeFormats/availableFormats",
169-
skeleton
170-
]);
171-
if ( !result ) {
172-
timeSkeleton = skeleton.split( /[^hHKkmsSAzZOvVXx]/ ).slice( -1 )[ 0 ];
173-
dateSkeleton = skeleton.split( /[^GyYuUrQqMLlwWdDFgEec]/ )[ 0 ];
174-
dateTimeSkeleton = getBestMatchPattern(
175-
"dates/calendars/gregorian/dateTimeFormats/availableFormats",
176-
skeleton
177-
);
178-
if ( dateTimeSkeleton ) {
179-
result = dateTimeSkeleton;
180-
} else {
181-
dateSkeleton = getBestMatchPattern(
182-
"dates/calendars/gregorian/dateTimeFormats/availableFormats",
183-
dateSkeleton
184-
);
185-
timeSkeleton = getBestMatchPattern(
186-
"dates/calendars/gregorian/dateTimeFormats/availableFormats",
187-
timeSkeleton
188-
);
189-
190-
if ( /(MMMM|LLLL).*[Ec]/.test( dateSkeleton ) ) {
191-
type = "full";
192-
} else if ( /MMMM/g.test( dateSkeleton ) ) {
193-
type = "long";
194-
} else if ( /MMM/g.test( dateSkeleton ) || /LLL/g.test( dateSkeleton ) ) {
195-
type = "medium";
196-
} else {
197-
type = "short";
198-
}
199-
200-
if ( dateSkeleton && timeSkeleton ) {
201-
result = combineDateTime( type, dateSkeleton, timeSkeleton );
202-
} else {
203-
result = dateSkeleton || timeSkeleton;
204-
}
205-
}
206-
}
20793
break;
20894

20995
case "date" in options:
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
define([
2+
"./normalize-pattern-type",
3+
"../pattern-re",
4+
"../../util/string/repeat"
5+
], function( dateExpandPatternNormalizePatternType, datePatternRe, stringRepeat ) {
6+
7+
return function( requestedSkeleton, bestMatchFormat ) {
8+
var i, j, matchedType, matchedLength, requestedType, requestedLength,
9+
10+
// Using an easier to read variable.
11+
normalizePatternType = dateExpandPatternNormalizePatternType;
12+
13+
requestedSkeleton = requestedSkeleton.match( datePatternRe );
14+
bestMatchFormat = bestMatchFormat.match( datePatternRe );
15+
16+
for ( i = 0; i < bestMatchFormat.length; i++ ) {
17+
matchedType = bestMatchFormat[i].charAt( 0 );
18+
matchedLength = bestMatchFormat[i].length;
19+
for ( j = 0; j < requestedSkeleton.length; j++ ) {
20+
requestedType = requestedSkeleton[j].charAt( 0 );
21+
requestedLength = requestedSkeleton[j].length;
22+
if ( normalizePatternType( matchedType ) === normalizePatternType( requestedType ) &&
23+
matchedLength < requestedLength
24+
) {
25+
bestMatchFormat[i] = stringRepeat( matchedType, requestedLength );
26+
}
27+
}
28+
}
29+
30+
return bestMatchFormat.join( "" );
31+
};
32+
33+
});
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
define([
2+
"./normalize-pattern-type",
3+
"../pattern-re"
4+
], function( dateExpandPatternNormalizePatternType, datePatternRe ) {
5+
6+
return function( formatA, formatB ) {
7+
var distance, typeA, typeB, matchFound, i, j,
8+
9+
// Using easier to read variables.
10+
normalizePatternType = dateExpandPatternNormalizePatternType;
11+
12+
if ( formatA === formatB ) {
13+
return 0;
14+
}
15+
16+
formatA = formatA.match( datePatternRe );
17+
formatB = formatB.match( datePatternRe );
18+
if ( formatA.length === formatB.length ) {
19+
distance = 1;
20+
for ( i = 0; i < formatA.length; i++ ) {
21+
typeA = normalizePatternType( formatA[i].charAt( 0 ) );
22+
typeB = null;
23+
matchFound = false;
24+
for ( j = 0; j < formatB.length; j++ ) {
25+
typeB = normalizePatternType( formatB[j].charAt( 0 ) );
26+
if ( typeA === typeB ) {
27+
break;
28+
} else {
29+
typeB = null;
30+
}
31+
}
32+
if ( typeB === null ) {
33+
return -1;
34+
}
35+
distance = distance + Math.abs( formatA[i].length - formatB[j].length );
36+
if ( formatA[i].charAt( 0 ) !== formatB[j].charAt( 0 ) ) {
37+
distance = distance + 1;
38+
}
39+
}
40+
return distance;
41+
}
42+
return -1;
43+
};
44+
45+
});
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
define([
2+
"./augment-format",
3+
"./compare-formats"
4+
], function( dateExpandPatternAugmentFormat, dateExpandPatternCompareFormats ) {
5+
6+
return function( cldr, path, skeleton ) {
7+
var availableFormats, format, pattern, ratedFormats,
8+
9+
// Using easier to read variables.
10+
augmentFormat = dateExpandPatternAugmentFormat,
11+
compareFormats = dateExpandPatternCompareFormats;
12+
13+
pattern = cldr.main([ path, skeleton ]);
14+
15+
if ( skeleton && !pattern ) {
16+
availableFormats = cldr.main([ path ]);
17+
ratedFormats = [];
18+
19+
for ( format in availableFormats ) {
20+
ratedFormats.push({
21+
format: format,
22+
pattern: availableFormats[format],
23+
rate: compareFormats( skeleton, format )
24+
});
25+
}
26+
27+
ratedFormats = ratedFormats
28+
.filter( function( format ) {
29+
return format.rate > -1;
30+
} )
31+
.sort( function( formatA, formatB ) {
32+
return formatA.rate - formatB.rate;
33+
});
34+
35+
if ( ratedFormats.length ) {
36+
pattern = augmentFormat( skeleton, ratedFormats[0].pattern );
37+
}
38+
}
39+
40+
return pattern;
41+
};
42+
43+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
define([
2+
"./similar-fields-map"
3+
], function( dateExpandPatternSimilarFieldsMap ) {
4+
5+
return function( character ) {
6+
return dateExpandPatternSimilarFieldsMap[ character ] || character;
7+
};
8+
9+
});
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
define([
2+
"../../util/object/invert"
3+
], function( objectInvert ) {
4+
5+
// Invert key and values, e.g., {"e": "eEc"} ==> {"e": "e", "E": "e", "c": "e"}.
6+
return objectInvert({
7+
"e": "eEc",
8+
"L": "ML"
9+
}, function( object, key, value ) {
10+
value.split( "" ).forEach(function( field ) {
11+
object[ field ] = key;
12+
});
13+
return object;
14+
});
15+
16+
});

0 commit comments

Comments
 (0)