globalize-rpk 1.7.0 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/globalize/currency.js +590 -0
- package/globalize/date.js +3139 -0
- package/globalize/globalize-runtime.js +326 -0
- package/globalize/globalize.js +507 -0
- package/globalize/message.js +2076 -0
- package/globalize/number.js +1727 -0
- package/globalize/plural.js +376 -0
- package/globalize/relative-time.js +203 -0
- package/globalize/unit.js +301 -0
- package/node-main.js +27 -0
- package/package.json +3 -26
- package/CONTRIBUTING.md +0 -5
- package/README.md +0 -818
- package/doc/api/core/constructor.md +0 -28
- package/doc/api/core/load.md +0 -96
- package/doc/api/core/locale.md +0 -43
- package/doc/api/currency/currency-formatter.md +0 -196
- package/doc/api/currency/currency-to-parts-formatter.md +0 -117
- package/doc/api/date/date-formatter.md +0 -203
- package/doc/api/date/date-parser.md +0 -60
- package/doc/api/date/date-to-parts-formatter.md +0 -176
- package/doc/api/date/load-iana-time-zone.md +0 -29
- package/doc/api/message/load-messages.md +0 -105
- package/doc/api/message/message-formatter.md +0 -208
- package/doc/api/number/number-formatter.md +0 -202
- package/doc/api/number/number-parser.md +0 -130
- package/doc/api/number/number-to-parts-formatter.md +0 -140
- package/doc/api/plural/plural-generator.md +0 -84
- package/doc/api/relative-time/relative-time-formatter.md +0 -60
- package/doc/api/unit/unit-formatter.md +0 -72
- package/doc/blog-post/2017-07-xx-1.3.0-announcement.md +0 -177
- package/doc/cldr.md +0 -114
- package/doc/error/e-default-locale-not-defined.md +0 -9
- package/doc/error/e-invalid-cldr.md +0 -14
- package/doc/error/e-invalid-par-type.md +0 -12
- package/doc/error/e-invalid-par-value.md +0 -11
- package/doc/error/e-missing-cldr.md +0 -11
- package/doc/error/e-missing-parameter.md +0 -10
- package/doc/error/e-missing-plural-module.md +0 -9
- package/doc/error/e-par-missing-key.md +0 -11
- package/doc/error/e-par-out-of-range.md +0 -13
- package/doc/error/e-unsupported.md +0 -10
- package/doc/migrating-from-0.x.md +0 -64
- package/examples/amd-bower/.bowerrc +0 -7
- package/examples/amd-bower/README.md +0 -65
- package/examples/amd-bower/bower.json +0 -13
- package/examples/amd-bower/index.html +0 -46
- package/examples/amd-bower/main.js +0 -141
- package/examples/amd-bower/messages/en.json +0 -12
- package/examples/amd-bower/package.json +0 -14
- package/examples/app-npm-webpack/README.md +0 -74
- package/examples/app-npm-webpack/app/index.js +0 -89
- package/examples/app-npm-webpack/index-template.html +0 -71
- package/examples/app-npm-webpack/messages/ar.json +0 -25
- package/examples/app-npm-webpack/messages/de.json +0 -21
- package/examples/app-npm-webpack/messages/en.json +0 -21
- package/examples/app-npm-webpack/messages/es.json +0 -21
- package/examples/app-npm-webpack/messages/pt.json +0 -21
- package/examples/app-npm-webpack/messages/ru.json +0 -23
- package/examples/app-npm-webpack/messages/zh.json +0 -20
- package/examples/app-npm-webpack/package.json +0 -17
- package/examples/app-npm-webpack/webpack-config.js +0 -63
- package/examples/globalize-compiler/README.md +0 -45
- package/examples/globalize-compiler/app.js +0 -58
- package/examples/globalize-compiler/development.html +0 -121
- package/examples/globalize-compiler/messages.json +0 -12
- package/examples/globalize-compiler/package.json +0 -15
- package/examples/globalize-compiler/production.html +0 -75
- package/examples/node-npm/README.md +0 -57
- package/examples/node-npm/main.js +0 -65
- package/examples/node-npm/messages/en.json +0 -12
- package/examples/node-npm/package.json +0 -10
- package/examples/plain-javascript/README.md +0 -81
- package/examples/plain-javascript/index.html +0 -445
@@ -0,0 +1,3139 @@
|
|
1
|
+
/**
|
2
|
+
* Globalize v1.7.0
|
3
|
+
*
|
4
|
+
* https://github.com/globalizejs/globalize
|
5
|
+
*
|
6
|
+
* Copyright OpenJS Foundation and other contributors
|
7
|
+
* Released under the MIT license
|
8
|
+
* https://jquery.org/license
|
9
|
+
*
|
10
|
+
* Date: 2021-08-02T11:53Z
|
11
|
+
*/
|
12
|
+
/*!
|
13
|
+
* Globalize v1.7.0 2021-08-02T11:53Z Released under the MIT license
|
14
|
+
* http://git.io/TrdQbw
|
15
|
+
*/
|
16
|
+
(function( root, factory ) {
|
17
|
+
|
18
|
+
// UMD returnExports
|
19
|
+
if ( typeof define === "function" && define.amd ) {
|
20
|
+
|
21
|
+
// AMD
|
22
|
+
define([
|
23
|
+
"cldr",
|
24
|
+
"../globalize",
|
25
|
+
"./number",
|
26
|
+
"cldr/event",
|
27
|
+
"cldr/supplemental"
|
28
|
+
], factory );
|
29
|
+
} else if ( typeof exports === "object" ) {
|
30
|
+
|
31
|
+
// Node, CommonJS
|
32
|
+
module.exports = factory( require( "cldrjs" ), require( "../globalize" ) );
|
33
|
+
} else {
|
34
|
+
|
35
|
+
// Extend global
|
36
|
+
factory( root.Cldr, root.Globalize );
|
37
|
+
}
|
38
|
+
}(this, function( Cldr, Globalize ) {
|
39
|
+
|
40
|
+
var createError = Globalize._createError,
|
41
|
+
createErrorUnsupportedFeature = Globalize._createErrorUnsupportedFeature,
|
42
|
+
formatMessage = Globalize._formatMessage,
|
43
|
+
isPlainObject = Globalize._isPlainObject,
|
44
|
+
looseMatching = Globalize._looseMatching,
|
45
|
+
numberNumberingSystemDigitsMap = Globalize._numberNumberingSystemDigitsMap,
|
46
|
+
numberSymbol = Globalize._numberSymbol,
|
47
|
+
partsJoin = Globalize._partsJoin,
|
48
|
+
partsPush = Globalize._partsPush,
|
49
|
+
regexpEscape = Globalize._regexpEscape,
|
50
|
+
removeLiteralQuotes = Globalize._removeLiteralQuotes,
|
51
|
+
runtimeBind = Globalize._runtimeBind,
|
52
|
+
stringPad = Globalize._stringPad,
|
53
|
+
validate = Globalize._validate,
|
54
|
+
validateCldr = Globalize._validateCldr,
|
55
|
+
validateDefaultLocale = Globalize._validateDefaultLocale,
|
56
|
+
validateParameterPresence = Globalize._validateParameterPresence,
|
57
|
+
validateParameterType = Globalize._validateParameterType,
|
58
|
+
validateParameterTypePlainObject = Globalize._validateParameterTypePlainObject,
|
59
|
+
validateParameterTypeString = Globalize._validateParameterTypeString;
|
60
|
+
|
61
|
+
|
62
|
+
var validateParameterTypeDate = function( value, name ) {
|
63
|
+
validateParameterType( value, name, value === undefined || value instanceof Date, "Date" );
|
64
|
+
};
|
65
|
+
|
66
|
+
|
67
|
+
|
68
|
+
|
69
|
+
var createErrorInvalidParameterValue = function( name, value ) {
|
70
|
+
return createError( "E_INVALID_PAR_VALUE", "Invalid `{name}` value ({value}).", {
|
71
|
+
name: name,
|
72
|
+
value: value
|
73
|
+
});
|
74
|
+
};
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
/**
|
80
|
+
* Create a map between the skeleton fields and their positions, e.g.,
|
81
|
+
* {
|
82
|
+
* G: 0
|
83
|
+
* y: 1
|
84
|
+
* ...
|
85
|
+
* }
|
86
|
+
*/
|
87
|
+
var validateSkeletonFieldsPosMap = "GyYuUrQqMLlwWEecdDFghHKkmsSAzZOvVXx".split( "" ).reduce(function( memo, item, i ) {
|
88
|
+
memo[ item ] = i;
|
89
|
+
return memo;
|
90
|
+
}, {});
|
91
|
+
|
92
|
+
|
93
|
+
|
94
|
+
|
95
|
+
/**
|
96
|
+
* validateSkeleton( skeleton )
|
97
|
+
*
|
98
|
+
* skeleton: Assume `j` has already been converted into a localized hour field.
|
99
|
+
*/
|
100
|
+
var validateSkeleton = function validateSkeleton( skeleton ) {
|
101
|
+
var last,
|
102
|
+
|
103
|
+
// Using easier to read variable.
|
104
|
+
fieldsPosMap = validateSkeletonFieldsPosMap;
|
105
|
+
|
106
|
+
// "The fields are from the Date Field Symbol Table in Date Format Patterns"
|
107
|
+
// Ref: http://www.unicode.org/reports/tr35/tr35-dates.html#availableFormats_appendItems
|
108
|
+
// I.e., check for invalid characters.
|
109
|
+
skeleton.replace( /[^GyYuUrQqMLlwWEecdDFghHKkmsSAzZOvVXx]/, function( field ) {
|
110
|
+
throw createError(
|
111
|
+
"E_INVALID_OPTIONS", "Invalid field `{invalidField}` of skeleton `{value}`",
|
112
|
+
{
|
113
|
+
invalidField: field,
|
114
|
+
type: "skeleton",
|
115
|
+
value: skeleton
|
116
|
+
}
|
117
|
+
);
|
118
|
+
});
|
119
|
+
|
120
|
+
// "The canonical order is from top to bottom in that table; that is, yM not My".
|
121
|
+
// http://www.unicode.org/reports/tr35/tr35-dates.html#availableFormats_appendItems
|
122
|
+
// I.e., check for invalid order.
|
123
|
+
skeleton.split( "" ).every(function( field ) {
|
124
|
+
if ( fieldsPosMap[ field ] < last ) {
|
125
|
+
throw createError(
|
126
|
+
"E_INVALID_OPTIONS", "Invalid order `{invalidField}` of skeleton `{value}`",
|
127
|
+
{
|
128
|
+
invalidField: field,
|
129
|
+
type: "skeleton",
|
130
|
+
value: skeleton
|
131
|
+
}
|
132
|
+
);
|
133
|
+
}
|
134
|
+
last = fieldsPosMap[ field ];
|
135
|
+
return true;
|
136
|
+
});
|
137
|
+
};
|
138
|
+
|
139
|
+
|
140
|
+
|
141
|
+
|
142
|
+
/**
|
143
|
+
* Returns a new object created by using `object`'s values as keys, and the keys as values.
|
144
|
+
*/
|
145
|
+
var objectInvert = function( object, fn ) {
|
146
|
+
fn = fn || function( object, key, value ) {
|
147
|
+
object[ value ] = key;
|
148
|
+
return object;
|
149
|
+
};
|
150
|
+
return Object.keys( object ).reduce(function( newObject, key ) {
|
151
|
+
return fn( newObject, key, object[ key ] );
|
152
|
+
}, {});
|
153
|
+
};
|
154
|
+
|
155
|
+
|
156
|
+
|
157
|
+
|
158
|
+
// Invert key and values, e.g., {"e": "eEc"} ==> {"e": "e", "E": "e", "c": "e"}.
|
159
|
+
var dateExpandPatternSimilarFieldsMap = objectInvert({
|
160
|
+
"e": "eEc",
|
161
|
+
"L": "ML"
|
162
|
+
}, function( object, key, value ) {
|
163
|
+
value.split( "" ).forEach(function( field ) {
|
164
|
+
object[ field ] = key;
|
165
|
+
});
|
166
|
+
return object;
|
167
|
+
});
|
168
|
+
|
169
|
+
|
170
|
+
|
171
|
+
|
172
|
+
var dateExpandPatternNormalizePatternType = function( character ) {
|
173
|
+
return dateExpandPatternSimilarFieldsMap[ character ] || character;
|
174
|
+
};
|
175
|
+
|
176
|
+
|
177
|
+
|
178
|
+
|
179
|
+
var datePatternRe = ( /([a-z])\1*|'([^']|'')+'|''|./ig );
|
180
|
+
|
181
|
+
|
182
|
+
|
183
|
+
|
184
|
+
var stringRepeat = function( str, count ) {
|
185
|
+
var i, result = "";
|
186
|
+
for ( i = 0; i < count; i++ ) {
|
187
|
+
result = result + str;
|
188
|
+
}
|
189
|
+
return result;
|
190
|
+
};
|
191
|
+
|
192
|
+
|
193
|
+
|
194
|
+
|
195
|
+
function expandBestMatchFormat( skeletonWithoutFractionalSeconds, bestMatchFormat ) {
|
196
|
+
var i, j, bestMatchFormatParts, matchedType, matchedLength, requestedType,
|
197
|
+
requestedLength, requestedSkeletonParts,
|
198
|
+
|
199
|
+
// Using an easier to read variable.
|
200
|
+
normalizePatternType = dateExpandPatternNormalizePatternType;
|
201
|
+
|
202
|
+
requestedSkeletonParts = skeletonWithoutFractionalSeconds.match( datePatternRe );
|
203
|
+
bestMatchFormatParts = bestMatchFormat.match( datePatternRe );
|
204
|
+
|
205
|
+
for ( i = 0; i < bestMatchFormatParts.length; i++ ) {
|
206
|
+
matchedType = bestMatchFormatParts[ i ].charAt( 0 );
|
207
|
+
matchedLength = bestMatchFormatParts[ i ].length;
|
208
|
+
for ( j = 0; j < requestedSkeletonParts.length; j++ ) {
|
209
|
+
requestedType = requestedSkeletonParts[ j ].charAt( 0 );
|
210
|
+
requestedLength = requestedSkeletonParts[ j ].length;
|
211
|
+
if ( normalizePatternType( matchedType ) === normalizePatternType( requestedType ) &&
|
212
|
+
matchedLength < requestedLength
|
213
|
+
) {
|
214
|
+
bestMatchFormatParts[ i ] = stringRepeat( matchedType, requestedLength );
|
215
|
+
}
|
216
|
+
}
|
217
|
+
}
|
218
|
+
|
219
|
+
return bestMatchFormatParts.join( "" );
|
220
|
+
}
|
221
|
+
|
222
|
+
// See: http://www.unicode.org/reports/tr35/tr35-dates.html#Matching_Skeletons
|
223
|
+
var dateExpandPatternAugmentFormat = function( requestedSkeleton, bestMatchFormat, decimalSeparator ) {
|
224
|
+
var countOfFractionalSeconds, fractionalSecondMatch, lastSecondIdx,
|
225
|
+
skeletonWithoutFractionalSeconds;
|
226
|
+
|
227
|
+
fractionalSecondMatch = requestedSkeleton.match( /S/g );
|
228
|
+
countOfFractionalSeconds = fractionalSecondMatch ? fractionalSecondMatch.length : 0;
|
229
|
+
skeletonWithoutFractionalSeconds = requestedSkeleton.replace( /S/g, "" );
|
230
|
+
|
231
|
+
bestMatchFormat = expandBestMatchFormat( skeletonWithoutFractionalSeconds, bestMatchFormat );
|
232
|
+
|
233
|
+
lastSecondIdx = bestMatchFormat.lastIndexOf( "s" );
|
234
|
+
if ( lastSecondIdx !== -1 && countOfFractionalSeconds !== 0 ) {
|
235
|
+
bestMatchFormat =
|
236
|
+
bestMatchFormat.slice( 0, lastSecondIdx + 1 ) +
|
237
|
+
decimalSeparator +
|
238
|
+
stringRepeat( "S", countOfFractionalSeconds ) +
|
239
|
+
bestMatchFormat.slice( lastSecondIdx + 1 );
|
240
|
+
}
|
241
|
+
return bestMatchFormat;
|
242
|
+
};
|
243
|
+
|
244
|
+
|
245
|
+
|
246
|
+
|
247
|
+
var dateExpandPatternCompareFormats = function( formatA, formatB ) {
|
248
|
+
var a, b, distance, lenA, lenB, typeA, typeB, i, j,
|
249
|
+
|
250
|
+
// Using easier to read variables.
|
251
|
+
normalizePatternType = dateExpandPatternNormalizePatternType;
|
252
|
+
|
253
|
+
if ( formatA === formatB ) {
|
254
|
+
return 0;
|
255
|
+
}
|
256
|
+
|
257
|
+
formatA = formatA.match( datePatternRe );
|
258
|
+
formatB = formatB.match( datePatternRe );
|
259
|
+
|
260
|
+
if ( formatA.length !== formatB.length ) {
|
261
|
+
return -1;
|
262
|
+
}
|
263
|
+
|
264
|
+
distance = 1;
|
265
|
+
for ( i = 0; i < formatA.length; i++ ) {
|
266
|
+
a = formatA[ i ].charAt( 0 );
|
267
|
+
typeA = normalizePatternType( a );
|
268
|
+
typeB = null;
|
269
|
+
for ( j = 0; j < formatB.length; j++ ) {
|
270
|
+
b = formatB[ j ].charAt( 0 );
|
271
|
+
typeB = normalizePatternType( b );
|
272
|
+
if ( typeA === typeB ) {
|
273
|
+
break;
|
274
|
+
} else {
|
275
|
+
typeB = null;
|
276
|
+
}
|
277
|
+
}
|
278
|
+
if ( typeB === null ) {
|
279
|
+
return -1;
|
280
|
+
}
|
281
|
+
lenA = formatA[ i ].length;
|
282
|
+
lenB = formatB[ j ].length;
|
283
|
+
distance = distance + Math.abs( lenA - lenB );
|
284
|
+
|
285
|
+
// Most symbols have a small distance from each other, e.g., M ≅ L; E ≅ c; a ≅ b ≅ B;
|
286
|
+
// H ≅ k ≅ h ≅ K; ...
|
287
|
+
if ( a !== b ) {
|
288
|
+
distance += 1;
|
289
|
+
}
|
290
|
+
|
291
|
+
// Numeric (l<3) and text fields (l>=3) are given a larger distance from each other.
|
292
|
+
if ( ( lenA < 3 && lenB >= 3 ) || ( lenA >= 3 && lenB < 3 ) ) {
|
293
|
+
distance += 20;
|
294
|
+
}
|
295
|
+
}
|
296
|
+
return distance;
|
297
|
+
};
|
298
|
+
|
299
|
+
|
300
|
+
|
301
|
+
|
302
|
+
var dateExpandPatternGetBestMatchPattern = function( cldr, askedSkeleton ) {
|
303
|
+
var availableFormats, decimalSeparator, pattern, ratedFormats, skeleton,
|
304
|
+
path = "dates/calendars/gregorian/dateTimeFormats/availableFormats",
|
305
|
+
|
306
|
+
// Using easier to read variables.
|
307
|
+
augmentFormat = dateExpandPatternAugmentFormat,
|
308
|
+
compareFormats = dateExpandPatternCompareFormats;
|
309
|
+
|
310
|
+
pattern = cldr.main([ path, askedSkeleton ]);
|
311
|
+
|
312
|
+
if ( askedSkeleton && !pattern ) {
|
313
|
+
availableFormats = cldr.main([ path ]);
|
314
|
+
ratedFormats = [];
|
315
|
+
|
316
|
+
for ( skeleton in availableFormats ) {
|
317
|
+
ratedFormats.push({
|
318
|
+
skeleton: skeleton,
|
319
|
+
pattern: availableFormats[ skeleton ],
|
320
|
+
rate: compareFormats( askedSkeleton, skeleton )
|
321
|
+
});
|
322
|
+
}
|
323
|
+
|
324
|
+
ratedFormats = ratedFormats
|
325
|
+
.filter( function( format ) {
|
326
|
+
return format.rate > -1;
|
327
|
+
} )
|
328
|
+
.sort( function( formatA, formatB ) {
|
329
|
+
return formatA.rate - formatB.rate;
|
330
|
+
});
|
331
|
+
|
332
|
+
if ( ratedFormats.length ) {
|
333
|
+
decimalSeparator = numberSymbol( "decimal", cldr );
|
334
|
+
pattern = augmentFormat( askedSkeleton, ratedFormats[ 0 ].pattern, decimalSeparator );
|
335
|
+
}
|
336
|
+
}
|
337
|
+
|
338
|
+
return pattern;
|
339
|
+
};
|
340
|
+
|
341
|
+
|
342
|
+
|
343
|
+
|
344
|
+
/**
|
345
|
+
* expandPattern( options, cldr )
|
346
|
+
*
|
347
|
+
* @options [Object] if String, it's considered a skeleton. Object accepts:
|
348
|
+
* - skeleton: [String] lookup availableFormat;
|
349
|
+
* - date: [String] ( "full" | "long" | "medium" | "short" );
|
350
|
+
* - time: [String] ( "full" | "long" | "medium" | "short" );
|
351
|
+
* - datetime: [String] ( "full" | "long" | "medium" | "short" );
|
352
|
+
* - raw: [String] For more info see datetime/format.js.
|
353
|
+
*
|
354
|
+
* @cldr [Cldr instance].
|
355
|
+
*
|
356
|
+
* Return the corresponding pattern.
|
357
|
+
* Eg for "en":
|
358
|
+
* - "GyMMMd" returns "MMM d, y G";
|
359
|
+
* - { skeleton: "GyMMMd" } returns "MMM d, y G";
|
360
|
+
* - { date: "full" } returns "EEEE, MMMM d, y";
|
361
|
+
* - { time: "full" } returns "h:mm:ss a zzzz";
|
362
|
+
* - { datetime: "full" } returns "EEEE, MMMM d, y 'at' h:mm:ss a zzzz";
|
363
|
+
* - { raw: "dd/mm" } returns "dd/mm";
|
364
|
+
*/
|
365
|
+
var dateExpandPattern = function( options, cldr ) {
|
366
|
+
var dateSkeleton, result, skeleton, timeSkeleton, type,
|
367
|
+
|
368
|
+
// Using easier to read variables.
|
369
|
+
getBestMatchPattern = dateExpandPatternGetBestMatchPattern;
|
370
|
+
|
371
|
+
function combineDateTime( type, datePattern, timePattern ) {
|
372
|
+
return formatMessage(
|
373
|
+
cldr.main([
|
374
|
+
"dates/calendars/gregorian/dateTimeFormats",
|
375
|
+
type
|
376
|
+
]),
|
377
|
+
[ timePattern, datePattern ]
|
378
|
+
);
|
379
|
+
}
|
380
|
+
|
381
|
+
switch ( true ) {
|
382
|
+
case "skeleton" in options:
|
383
|
+
skeleton = options.skeleton;
|
384
|
+
|
385
|
+
// Preferred hour (j).
|
386
|
+
skeleton = skeleton.replace( /j/g, function() {
|
387
|
+
return cldr.supplemental.timeData.preferred();
|
388
|
+
});
|
389
|
+
|
390
|
+
validateSkeleton( skeleton );
|
391
|
+
|
392
|
+
// Try direct map (note that getBestMatchPattern handles it).
|
393
|
+
// ... or, try to "best match" the whole skeleton.
|
394
|
+
result = getBestMatchPattern(
|
395
|
+
cldr,
|
396
|
+
skeleton
|
397
|
+
);
|
398
|
+
if ( result ) {
|
399
|
+
break;
|
400
|
+
}
|
401
|
+
|
402
|
+
// ... or, try to "best match" the date and time parts individually.
|
403
|
+
timeSkeleton = skeleton.split( /[^hHKkmsSAzZOvVXx]/ ).slice( -1 )[ 0 ];
|
404
|
+
dateSkeleton = skeleton.split( /[^GyYuUrQqMLlwWdDFgEec]/ )[ 0 ];
|
405
|
+
dateSkeleton = getBestMatchPattern(
|
406
|
+
cldr,
|
407
|
+
dateSkeleton
|
408
|
+
);
|
409
|
+
timeSkeleton = getBestMatchPattern(
|
410
|
+
cldr,
|
411
|
+
timeSkeleton
|
412
|
+
);
|
413
|
+
|
414
|
+
if ( /(MMMM|LLLL).*[Ec]/.test( dateSkeleton ) ) {
|
415
|
+
type = "full";
|
416
|
+
} else if ( /MMMM|LLLL/.test( dateSkeleton ) ) {
|
417
|
+
type = "long";
|
418
|
+
} else if ( /MMM|LLL/.test( dateSkeleton ) ) {
|
419
|
+
type = "medium";
|
420
|
+
} else {
|
421
|
+
type = "short";
|
422
|
+
}
|
423
|
+
|
424
|
+
if ( dateSkeleton && timeSkeleton ) {
|
425
|
+
result = combineDateTime( type, dateSkeleton, timeSkeleton );
|
426
|
+
} else {
|
427
|
+
result = dateSkeleton || timeSkeleton;
|
428
|
+
}
|
429
|
+
|
430
|
+
break;
|
431
|
+
|
432
|
+
case "date" in options:
|
433
|
+
case "time" in options:
|
434
|
+
result = cldr.main([
|
435
|
+
"dates/calendars/gregorian",
|
436
|
+
"date" in options ? "dateFormats" : "timeFormats",
|
437
|
+
( options.date || options.time )
|
438
|
+
]);
|
439
|
+
break;
|
440
|
+
|
441
|
+
case "datetime" in options:
|
442
|
+
result = combineDateTime( options.datetime,
|
443
|
+
cldr.main([ "dates/calendars/gregorian/dateFormats", options.datetime ]),
|
444
|
+
cldr.main([ "dates/calendars/gregorian/timeFormats", options.datetime ])
|
445
|
+
);
|
446
|
+
break;
|
447
|
+
|
448
|
+
case "raw" in options:
|
449
|
+
result = options.raw;
|
450
|
+
break;
|
451
|
+
|
452
|
+
default:
|
453
|
+
throw createErrorInvalidParameterValue({
|
454
|
+
name: "options",
|
455
|
+
value: options
|
456
|
+
});
|
457
|
+
}
|
458
|
+
|
459
|
+
return result;
|
460
|
+
};
|
461
|
+
|
462
|
+
|
463
|
+
|
464
|
+
|
465
|
+
var dateWeekDays = [ "sun", "mon", "tue", "wed", "thu", "fri", "sat" ];
|
466
|
+
|
467
|
+
|
468
|
+
|
469
|
+
|
470
|
+
/**
|
471
|
+
* firstDayOfWeek
|
472
|
+
*/
|
473
|
+
var dateFirstDayOfWeek = function( cldr ) {
|
474
|
+
return dateWeekDays.indexOf( cldr.supplemental.weekData.firstDay() );
|
475
|
+
};
|
476
|
+
|
477
|
+
|
478
|
+
|
479
|
+
|
480
|
+
/**
|
481
|
+
* getTimeZoneName( length, type )
|
482
|
+
*/
|
483
|
+
var dateGetTimeZoneName = function( length, type, timeZone, cldr ) {
|
484
|
+
var metaZone, result;
|
485
|
+
|
486
|
+
if ( !timeZone ) {
|
487
|
+
return;
|
488
|
+
}
|
489
|
+
|
490
|
+
result = cldr.main([
|
491
|
+
"dates/timeZoneNames/zone",
|
492
|
+
timeZone,
|
493
|
+
length < 4 ? "short" : "long",
|
494
|
+
type
|
495
|
+
]);
|
496
|
+
|
497
|
+
if ( result ) {
|
498
|
+
return result;
|
499
|
+
}
|
500
|
+
|
501
|
+
// The latest metazone data of the metazone array.
|
502
|
+
// TODO expand to support the historic metazones based on the given date.
|
503
|
+
metaZone = cldr.supplemental([
|
504
|
+
"metaZones/metazoneInfo/timezone", timeZone, 0,
|
505
|
+
"usesMetazone/_mzone"
|
506
|
+
]);
|
507
|
+
|
508
|
+
return cldr.main([
|
509
|
+
"dates/timeZoneNames/metazone",
|
510
|
+
metaZone,
|
511
|
+
length < 4 ? "short" : "long",
|
512
|
+
type
|
513
|
+
]);
|
514
|
+
};
|
515
|
+
|
516
|
+
|
517
|
+
|
518
|
+
|
519
|
+
/**
|
520
|
+
* timezoneHourFormatShortH( hourFormat )
|
521
|
+
*
|
522
|
+
* @hourFormat [String]
|
523
|
+
*
|
524
|
+
* Unofficial deduction of the short hourFormat given time zone `hourFormat` element.
|
525
|
+
* Official spec is pending resolution: http://unicode.org/cldr/trac/ticket/8293
|
526
|
+
*
|
527
|
+
* Example:
|
528
|
+
* - "+HH.mm;-HH.mm" => "+H;-H"
|
529
|
+
* - "+HH:mm;-HH:mm" => "+H;-H"
|
530
|
+
* - "+HH:mm;−HH:mm" => "+H;−H" (Note MINUS SIGN \u2212)
|
531
|
+
* - "+HHmm;-HHmm" => "+H:-H"
|
532
|
+
*/
|
533
|
+
var dateTimezoneHourFormatH = function( hourFormat ) {
|
534
|
+
return hourFormat
|
535
|
+
.split( ";" )
|
536
|
+
.map(function( format ) {
|
537
|
+
return format.slice( 0, format.indexOf( "H" ) + 1 );
|
538
|
+
})
|
539
|
+
.join( ";" );
|
540
|
+
};
|
541
|
+
|
542
|
+
|
543
|
+
|
544
|
+
|
545
|
+
/**
|
546
|
+
* timezoneHourFormatLongHm( hourFormat )
|
547
|
+
*
|
548
|
+
* @hourFormat [String]
|
549
|
+
*
|
550
|
+
* Unofficial deduction of the short hourFormat given time zone `hourFormat` element.
|
551
|
+
* Official spec is pending resolution: http://unicode.org/cldr/trac/ticket/8293
|
552
|
+
*
|
553
|
+
* Example (hFormat === "H"): (used for short Hm)
|
554
|
+
* - "+HH.mm;-HH.mm" => "+H.mm;-H.mm"
|
555
|
+
* - "+HH:mm;-HH:mm" => "+H:mm;-H:mm"
|
556
|
+
* - "+HH:mm;−HH:mm" => "+H:mm;−H:mm" (Note MINUS SIGN \u2212)
|
557
|
+
* - "+HHmm;-HHmm" => "+Hmm:-Hmm"
|
558
|
+
*
|
559
|
+
* Example (hFormat === "HH": (used for long Hm)
|
560
|
+
* - "+HH.mm;-HH.mm" => "+HH.mm;-HH.mm"
|
561
|
+
* - "+HH:mm;-HH:mm" => "+HH:mm;-HH:mm"
|
562
|
+
* - "+H:mm;-H:mm" => "+HH:mm;-HH:mm"
|
563
|
+
* - "+HH:mm;−HH:mm" => "+HH:mm;−HH:mm" (Note MINUS SIGN \u2212)
|
564
|
+
* - "+HHmm;-HHmm" => "+HHmm:-HHmm"
|
565
|
+
*/
|
566
|
+
var dateTimezoneHourFormatHm = function( hourFormat, hFormat ) {
|
567
|
+
return hourFormat
|
568
|
+
.split( ";" )
|
569
|
+
.map(function( format ) {
|
570
|
+
var parts = format.split( /H+/ );
|
571
|
+
parts.splice( 1, 0, hFormat );
|
572
|
+
return parts.join( "" );
|
573
|
+
})
|
574
|
+
.join( ";" );
|
575
|
+
};
|
576
|
+
|
577
|
+
|
578
|
+
|
579
|
+
|
580
|
+
var runtimeCacheDataBind = function( key, data ) {
|
581
|
+
var fn = function() {
|
582
|
+
return data;
|
583
|
+
};
|
584
|
+
fn.dataCacheKey = key;
|
585
|
+
return fn;
|
586
|
+
};
|
587
|
+
|
588
|
+
|
589
|
+
|
590
|
+
|
591
|
+
/**
|
592
|
+
* properties( pattern, cldr )
|
593
|
+
*
|
594
|
+
* @pattern [String] raw pattern.
|
595
|
+
* ref: http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns
|
596
|
+
*
|
597
|
+
* @cldr [Cldr instance].
|
598
|
+
*
|
599
|
+
* Return the properties given the pattern and cldr.
|
600
|
+
*
|
601
|
+
* TODO Support other calendar types.
|
602
|
+
*/
|
603
|
+
var dateFormatProperties = function( pattern, cldr, timeZone ) {
|
604
|
+
var properties = {
|
605
|
+
numberFormatters: {},
|
606
|
+
pattern: pattern,
|
607
|
+
timeSeparator: numberSymbol( "timeSeparator", cldr )
|
608
|
+
},
|
609
|
+
widths = [ "abbreviated", "wide", "narrow" ];
|
610
|
+
|
611
|
+
function setNumberFormatterPattern( pad ) {
|
612
|
+
properties.numberFormatters[ pad ] = stringPad( "", pad );
|
613
|
+
}
|
614
|
+
|
615
|
+
if ( timeZone ) {
|
616
|
+
properties.timeZoneData = runtimeCacheDataBind( "iana/" + timeZone, {
|
617
|
+
offsets: cldr.get([ "globalize-iana/zoneData", timeZone, "offsets" ]),
|
618
|
+
untils: cldr.get([ "globalize-iana/zoneData", timeZone, "untils" ]),
|
619
|
+
isdsts: cldr.get([ "globalize-iana/zoneData", timeZone, "isdsts" ])
|
620
|
+
});
|
621
|
+
}
|
622
|
+
|
623
|
+
pattern.replace( datePatternRe, function( current ) {
|
624
|
+
var aux, chr, daylightTzName, formatNumber, genericTzName, length, standardTzName;
|
625
|
+
|
626
|
+
chr = current.charAt( 0 );
|
627
|
+
length = current.length;
|
628
|
+
|
629
|
+
if ( chr === "j" ) {
|
630
|
+
|
631
|
+
// Locale preferred hHKk.
|
632
|
+
// http://www.unicode.org/reports/tr35/tr35-dates.html#Time_Data
|
633
|
+
properties.preferredTime = chr = cldr.supplemental.timeData.preferred();
|
634
|
+
}
|
635
|
+
|
636
|
+
// ZZZZ: same as "OOOO".
|
637
|
+
if ( chr === "Z" && length === 4 ) {
|
638
|
+
chr = "O";
|
639
|
+
length = 4;
|
640
|
+
}
|
641
|
+
|
642
|
+
// z...zzz: "{shortRegion}", eg. "PST" or "PDT".
|
643
|
+
// zzzz: "{regionName} {Standard Time}" or "{regionName} {Daylight Time}",
|
644
|
+
// e.g., "Pacific Standard Time" or "Pacific Daylight Time".
|
645
|
+
// http://unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns
|
646
|
+
if ( chr === "z" ) {
|
647
|
+
standardTzName = dateGetTimeZoneName( length, "standard", timeZone, cldr );
|
648
|
+
daylightTzName = dateGetTimeZoneName( length, "daylight", timeZone, cldr );
|
649
|
+
if ( standardTzName ) {
|
650
|
+
properties.standardTzName = standardTzName;
|
651
|
+
}
|
652
|
+
if ( daylightTzName ) {
|
653
|
+
properties.daylightTzName = daylightTzName;
|
654
|
+
}
|
655
|
+
|
656
|
+
// Fall through the "O" format in case one name is missing.
|
657
|
+
if ( !standardTzName || !daylightTzName ) {
|
658
|
+
chr = "O";
|
659
|
+
if ( length < 4 ) {
|
660
|
+
length = 1;
|
661
|
+
}
|
662
|
+
}
|
663
|
+
}
|
664
|
+
|
665
|
+
// v...vvv: "{shortRegion}", eg. "PT".
|
666
|
+
// vvvv: "{regionName} {Time}" or "{regionName} {Time}",
|
667
|
+
// e.g., "Pacific Time"
|
668
|
+
// http://unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns
|
669
|
+
if ( chr === "v" ) {
|
670
|
+
genericTzName = dateGetTimeZoneName( length, "generic", timeZone, cldr );
|
671
|
+
|
672
|
+
// Fall back to "V" format.
|
673
|
+
if ( !genericTzName ) {
|
674
|
+
chr = "V";
|
675
|
+
length = 4;
|
676
|
+
}
|
677
|
+
}
|
678
|
+
|
679
|
+
switch ( chr ) {
|
680
|
+
|
681
|
+
// Era
|
682
|
+
case "G":
|
683
|
+
properties.eras = cldr.main([
|
684
|
+
"dates/calendars/gregorian/eras",
|
685
|
+
length <= 3 ? "eraAbbr" : ( length === 4 ? "eraNames" : "eraNarrow" )
|
686
|
+
]);
|
687
|
+
break;
|
688
|
+
|
689
|
+
// Year
|
690
|
+
case "y":
|
691
|
+
|
692
|
+
// Plain year.
|
693
|
+
formatNumber = true;
|
694
|
+
break;
|
695
|
+
|
696
|
+
case "Y":
|
697
|
+
|
698
|
+
// Year in "Week of Year"
|
699
|
+
properties.firstDay = dateFirstDayOfWeek( cldr );
|
700
|
+
properties.minDays = cldr.supplemental.weekData.minDays();
|
701
|
+
formatNumber = true;
|
702
|
+
break;
|
703
|
+
|
704
|
+
case "u": // Extended year. Need to be implemented.
|
705
|
+
case "U": // Cyclic year name. Need to be implemented.
|
706
|
+
throw createErrorUnsupportedFeature({
|
707
|
+
feature: "year pattern `" + chr + "`"
|
708
|
+
});
|
709
|
+
|
710
|
+
// Quarter
|
711
|
+
case "Q":
|
712
|
+
case "q":
|
713
|
+
if ( length > 2 ) {
|
714
|
+
if ( !properties.quarters ) {
|
715
|
+
properties.quarters = {};
|
716
|
+
}
|
717
|
+
if ( !properties.quarters[ chr ] ) {
|
718
|
+
properties.quarters[ chr ] = {};
|
719
|
+
}
|
720
|
+
properties.quarters[ chr ][ length ] = cldr.main([
|
721
|
+
"dates/calendars/gregorian/quarters",
|
722
|
+
chr === "Q" ? "format" : "stand-alone",
|
723
|
+
widths[ length - 3 ]
|
724
|
+
]);
|
725
|
+
} else {
|
726
|
+
formatNumber = true;
|
727
|
+
}
|
728
|
+
break;
|
729
|
+
|
730
|
+
// Month
|
731
|
+
case "M":
|
732
|
+
case "L":
|
733
|
+
if ( length > 2 ) {
|
734
|
+
if ( !properties.months ) {
|
735
|
+
properties.months = {};
|
736
|
+
}
|
737
|
+
if ( !properties.months[ chr ] ) {
|
738
|
+
properties.months[ chr ] = {};
|
739
|
+
}
|
740
|
+
properties.months[ chr ][ length ] = cldr.main([
|
741
|
+
"dates/calendars/gregorian/months",
|
742
|
+
chr === "M" ? "format" : "stand-alone",
|
743
|
+
widths[ length - 3 ]
|
744
|
+
]);
|
745
|
+
} else {
|
746
|
+
formatNumber = true;
|
747
|
+
}
|
748
|
+
break;
|
749
|
+
|
750
|
+
// Week - Week of Year (w) or Week of Month (W).
|
751
|
+
case "w":
|
752
|
+
case "W":
|
753
|
+
properties.firstDay = dateFirstDayOfWeek( cldr );
|
754
|
+
properties.minDays = cldr.supplemental.weekData.minDays();
|
755
|
+
formatNumber = true;
|
756
|
+
break;
|
757
|
+
|
758
|
+
// Day
|
759
|
+
case "d":
|
760
|
+
case "D":
|
761
|
+
case "F":
|
762
|
+
formatNumber = true;
|
763
|
+
break;
|
764
|
+
|
765
|
+
case "g":
|
766
|
+
|
767
|
+
// Modified Julian day. Need to be implemented.
|
768
|
+
throw createErrorUnsupportedFeature({
|
769
|
+
feature: "Julian day pattern `g`"
|
770
|
+
});
|
771
|
+
|
772
|
+
// Week day
|
773
|
+
case "e":
|
774
|
+
case "c":
|
775
|
+
if ( length <= 2 ) {
|
776
|
+
properties.firstDay = dateFirstDayOfWeek( cldr );
|
777
|
+
formatNumber = true;
|
778
|
+
break;
|
779
|
+
}
|
780
|
+
|
781
|
+
/* falls through */
|
782
|
+
case "E":
|
783
|
+
if ( !properties.days ) {
|
784
|
+
properties.days = {};
|
785
|
+
}
|
786
|
+
if ( !properties.days[ chr ] ) {
|
787
|
+
properties.days[ chr ] = {};
|
788
|
+
}
|
789
|
+
if ( length === 6 ) {
|
790
|
+
|
791
|
+
// If short day names are not explicitly specified, abbreviated day names are
|
792
|
+
// used instead.
|
793
|
+
// http://www.unicode.org/reports/tr35/tr35-dates.html#months_days_quarters_eras
|
794
|
+
// http://unicode.org/cldr/trac/ticket/6790
|
795
|
+
properties.days[ chr ][ length ] = cldr.main([
|
796
|
+
"dates/calendars/gregorian/days",
|
797
|
+
chr === "c" ? "stand-alone" : "format",
|
798
|
+
"short"
|
799
|
+
]) || cldr.main([
|
800
|
+
"dates/calendars/gregorian/days",
|
801
|
+
chr === "c" ? "stand-alone" : "format",
|
802
|
+
"abbreviated"
|
803
|
+
]);
|
804
|
+
} else {
|
805
|
+
properties.days[ chr ][ length ] = cldr.main([
|
806
|
+
"dates/calendars/gregorian/days",
|
807
|
+
chr === "c" ? "stand-alone" : "format",
|
808
|
+
widths[ length < 3 ? 0 : length - 3 ]
|
809
|
+
]);
|
810
|
+
}
|
811
|
+
break;
|
812
|
+
|
813
|
+
// Period (AM or PM)
|
814
|
+
case "a":
|
815
|
+
properties.dayPeriods = {
|
816
|
+
am: cldr.main(
|
817
|
+
"dates/calendars/gregorian/dayPeriods/format/wide/am"
|
818
|
+
),
|
819
|
+
pm: cldr.main(
|
820
|
+
"dates/calendars/gregorian/dayPeriods/format/wide/pm"
|
821
|
+
)
|
822
|
+
};
|
823
|
+
break;
|
824
|
+
|
825
|
+
// Hour
|
826
|
+
case "h": // 1-12
|
827
|
+
case "H": // 0-23
|
828
|
+
case "K": // 0-11
|
829
|
+
case "k": // 1-24
|
830
|
+
|
831
|
+
// Minute
|
832
|
+
case "m":
|
833
|
+
|
834
|
+
// Second
|
835
|
+
case "s":
|
836
|
+
case "S":
|
837
|
+
case "A":
|
838
|
+
formatNumber = true;
|
839
|
+
break;
|
840
|
+
|
841
|
+
// Zone
|
842
|
+
case "v":
|
843
|
+
if ( length !== 1 && length !== 4 ) {
|
844
|
+
throw createErrorUnsupportedFeature({
|
845
|
+
feature: "timezone pattern `" + pattern + "`"
|
846
|
+
});
|
847
|
+
}
|
848
|
+
properties.genericTzName = genericTzName;
|
849
|
+
break;
|
850
|
+
|
851
|
+
case "V":
|
852
|
+
|
853
|
+
if ( length === 1 ) {
|
854
|
+
throw createErrorUnsupportedFeature({
|
855
|
+
feature: "timezone pattern `" + pattern + "`"
|
856
|
+
});
|
857
|
+
}
|
858
|
+
|
859
|
+
if ( timeZone ) {
|
860
|
+
if ( length === 2 ) {
|
861
|
+
properties.timeZoneName = timeZone;
|
862
|
+
break;
|
863
|
+
}
|
864
|
+
|
865
|
+
var timeZoneName,
|
866
|
+
exemplarCity = cldr.main([
|
867
|
+
"dates/timeZoneNames/zone", timeZone, "exemplarCity"
|
868
|
+
]);
|
869
|
+
|
870
|
+
if ( length === 3 ) {
|
871
|
+
if ( !exemplarCity ) {
|
872
|
+
exemplarCity = cldr.main([
|
873
|
+
"dates/timeZoneNames/zone/Etc/Unknown/exemplarCity"
|
874
|
+
]);
|
875
|
+
}
|
876
|
+
timeZoneName = exemplarCity;
|
877
|
+
}
|
878
|
+
|
879
|
+
if ( exemplarCity && length === 4 ) {
|
880
|
+
timeZoneName = formatMessage(
|
881
|
+
cldr.main(
|
882
|
+
"dates/timeZoneNames/regionFormat"
|
883
|
+
),
|
884
|
+
[ exemplarCity ]
|
885
|
+
);
|
886
|
+
}
|
887
|
+
|
888
|
+
if ( timeZoneName ) {
|
889
|
+
properties.timeZoneName = timeZoneName;
|
890
|
+
break;
|
891
|
+
}
|
892
|
+
}
|
893
|
+
|
894
|
+
if ( current === "v" ) {
|
895
|
+
length = 1;
|
896
|
+
}
|
897
|
+
|
898
|
+
/* falls through */
|
899
|
+
case "O":
|
900
|
+
|
901
|
+
// O: "{gmtFormat}+H;{gmtFormat}-H" or "{gmtZeroFormat}", eg. "GMT-8" or "GMT".
|
902
|
+
// OOOO: "{gmtFormat}{hourFormat}" or "{gmtZeroFormat}", eg. "GMT-08:00" or "GMT".
|
903
|
+
properties.gmtFormat = cldr.main( "dates/timeZoneNames/gmtFormat" );
|
904
|
+
properties.gmtZeroFormat = cldr.main( "dates/timeZoneNames/gmtZeroFormat" );
|
905
|
+
|
906
|
+
// Unofficial deduction of the hourFormat variations.
|
907
|
+
// Official spec is pending resolution: http://unicode.org/cldr/trac/ticket/8293
|
908
|
+
aux = cldr.main( "dates/timeZoneNames/hourFormat" );
|
909
|
+
properties.hourFormat = length < 4 ?
|
910
|
+
[ dateTimezoneHourFormatH( aux ), dateTimezoneHourFormatHm( aux, "H" ) ] :
|
911
|
+
dateTimezoneHourFormatHm( aux, "HH" );
|
912
|
+
|
913
|
+
/* falls through */
|
914
|
+
case "Z":
|
915
|
+
case "X":
|
916
|
+
case "x":
|
917
|
+
setNumberFormatterPattern( 1 );
|
918
|
+
setNumberFormatterPattern( 2 );
|
919
|
+
break;
|
920
|
+
}
|
921
|
+
|
922
|
+
if ( formatNumber ) {
|
923
|
+
setNumberFormatterPattern( length );
|
924
|
+
}
|
925
|
+
});
|
926
|
+
|
927
|
+
return properties;
|
928
|
+
};
|
929
|
+
|
930
|
+
|
931
|
+
|
932
|
+
|
933
|
+
var dateFormatterFn = function( dateToPartsFormatter ) {
|
934
|
+
return function dateFormatter( value ) {
|
935
|
+
return partsJoin( dateToPartsFormatter( value ));
|
936
|
+
};
|
937
|
+
};
|
938
|
+
|
939
|
+
|
940
|
+
|
941
|
+
|
942
|
+
/**
|
943
|
+
* parseProperties( cldr )
|
944
|
+
*
|
945
|
+
* @cldr [Cldr instance].
|
946
|
+
*
|
947
|
+
* @timeZone [String] FIXME.
|
948
|
+
*
|
949
|
+
* Return parser properties.
|
950
|
+
*/
|
951
|
+
var dateParseProperties = function( cldr, timeZone ) {
|
952
|
+
var properties = {
|
953
|
+
preferredTimeData: cldr.supplemental.timeData.preferred()
|
954
|
+
};
|
955
|
+
|
956
|
+
if ( timeZone ) {
|
957
|
+
properties.timeZoneData = runtimeCacheDataBind( "iana/" + timeZone, {
|
958
|
+
offsets: cldr.get([ "globalize-iana/zoneData", timeZone, "offsets" ]),
|
959
|
+
untils: cldr.get([ "globalize-iana/zoneData", timeZone, "untils" ]),
|
960
|
+
isdsts: cldr.get([ "globalize-iana/zoneData", timeZone, "isdsts" ])
|
961
|
+
});
|
962
|
+
}
|
963
|
+
|
964
|
+
return properties;
|
965
|
+
};
|
966
|
+
|
967
|
+
|
968
|
+
var ZonedDateTime = (function() {
|
969
|
+
function definePrivateProperty(object, property, value) {
|
970
|
+
Object.defineProperty(object, property, {
|
971
|
+
value: value
|
972
|
+
});
|
973
|
+
}
|
974
|
+
|
975
|
+
function getUntilsIndex(original, untils) {
|
976
|
+
var index = 0;
|
977
|
+
var originalTime = original.getTime();
|
978
|
+
|
979
|
+
// TODO Should we do binary search for improved performance?
|
980
|
+
while (index < untils.length - 1 && originalTime >= untils[index]) {
|
981
|
+
index++;
|
982
|
+
}
|
983
|
+
return index;
|
984
|
+
}
|
985
|
+
|
986
|
+
function setWrap(fn) {
|
987
|
+
var offset1 = this.getTimezoneOffset();
|
988
|
+
var ret = fn();
|
989
|
+
this.original.setTime(new Date(this.getTime()));
|
990
|
+
var offset2 = this.getTimezoneOffset();
|
991
|
+
if (offset2 - offset1) {
|
992
|
+
this.original.setMinutes(this.original.getMinutes() + offset2 - offset1);
|
993
|
+
}
|
994
|
+
return ret;
|
995
|
+
}
|
996
|
+
|
997
|
+
var ZonedDateTime = function(date, timeZoneData) {
|
998
|
+
definePrivateProperty(this, "original", new Date(date.getTime()));
|
999
|
+
definePrivateProperty(this, "local", new Date(date.getTime()));
|
1000
|
+
definePrivateProperty(this, "timeZoneData", timeZoneData);
|
1001
|
+
definePrivateProperty(this, "setWrap", setWrap);
|
1002
|
+
if (!(timeZoneData.untils && timeZoneData.offsets && timeZoneData.isdsts)) {
|
1003
|
+
throw new Error("Invalid IANA data");
|
1004
|
+
}
|
1005
|
+
this.setTime(this.local.getTime() - this.getTimezoneOffset() * 60 * 1000);
|
1006
|
+
};
|
1007
|
+
|
1008
|
+
ZonedDateTime.prototype.clone = function() {
|
1009
|
+
return new ZonedDateTime(this.original, this.timeZoneData);
|
1010
|
+
};
|
1011
|
+
|
1012
|
+
// Date field getters.
|
1013
|
+
["getFullYear", "getMonth", "getDate", "getDay", "getHours", "getMinutes",
|
1014
|
+
"getSeconds", "getMilliseconds"].forEach(function(method) {
|
1015
|
+
// Corresponding UTC method, e.g., "getUTCFullYear" if method === "getFullYear".
|
1016
|
+
var utcMethod = "getUTC" + method.substr(3);
|
1017
|
+
ZonedDateTime.prototype[method] = function() {
|
1018
|
+
return this.local[utcMethod]();
|
1019
|
+
};
|
1020
|
+
});
|
1021
|
+
|
1022
|
+
// Note: Define .valueOf = .getTime for arithmetic operations like date1 - date2.
|
1023
|
+
ZonedDateTime.prototype.valueOf =
|
1024
|
+
ZonedDateTime.prototype.getTime = function() {
|
1025
|
+
return this.local.getTime() + this.getTimezoneOffset() * 60 * 1000;
|
1026
|
+
};
|
1027
|
+
|
1028
|
+
ZonedDateTime.prototype.getTimezoneOffset = function() {
|
1029
|
+
var index = getUntilsIndex(this.original, this.timeZoneData.untils);
|
1030
|
+
return this.timeZoneData.offsets[index];
|
1031
|
+
};
|
1032
|
+
|
1033
|
+
// Date field setters.
|
1034
|
+
["setFullYear", "setMonth", "setDate", "setHours", "setMinutes", "setSeconds", "setMilliseconds"].forEach(function(method) {
|
1035
|
+
// Corresponding UTC method, e.g., "setUTCFullYear" if method === "setFullYear".
|
1036
|
+
var utcMethod = "setUTC" + method.substr(3);
|
1037
|
+
ZonedDateTime.prototype[method] = function(value) {
|
1038
|
+
var local = this.local;
|
1039
|
+
// Note setWrap is needed for seconds and milliseconds just because
|
1040
|
+
// abs(value) could be >= a minute.
|
1041
|
+
return this.setWrap(function() {
|
1042
|
+
return local[utcMethod](value);
|
1043
|
+
});
|
1044
|
+
};
|
1045
|
+
});
|
1046
|
+
|
1047
|
+
ZonedDateTime.prototype.setTime = function(time) {
|
1048
|
+
return this.local.setTime(time);
|
1049
|
+
};
|
1050
|
+
|
1051
|
+
ZonedDateTime.prototype.isDST = function() {
|
1052
|
+
var index = getUntilsIndex(this.original, this.timeZoneData.untils);
|
1053
|
+
return Boolean(this.timeZoneData.isdsts[index]);
|
1054
|
+
};
|
1055
|
+
|
1056
|
+
ZonedDateTime.prototype.inspect = function() {
|
1057
|
+
var index = getUntilsIndex(this.original, this.timeZoneData.untils);
|
1058
|
+
var abbrs = this.timeZoneData.abbrs;
|
1059
|
+
return this.local.toISOString().replace(/Z$/, "") + " " +
|
1060
|
+
(abbrs && abbrs[index] + " " || (this.getTimezoneOffset() * -1) + " ") +
|
1061
|
+
(this.isDST() ? "(daylight savings)" : "");
|
1062
|
+
};
|
1063
|
+
|
1064
|
+
ZonedDateTime.prototype.toDate = function() {
|
1065
|
+
return new Date(this.getTime());
|
1066
|
+
};
|
1067
|
+
|
1068
|
+
// Type cast getters.
|
1069
|
+
["toISOString", "toJSON", "toUTCString"].forEach(function(method) {
|
1070
|
+
ZonedDateTime.prototype[method] = function() {
|
1071
|
+
return this.toDate()[method]();
|
1072
|
+
};
|
1073
|
+
});
|
1074
|
+
|
1075
|
+
return ZonedDateTime;
|
1076
|
+
}());
|
1077
|
+
|
1078
|
+
|
1079
|
+
/**
|
1080
|
+
* isLeapYear( year )
|
1081
|
+
*
|
1082
|
+
* @year [Number]
|
1083
|
+
*
|
1084
|
+
* Returns an indication whether the specified year is a leap year.
|
1085
|
+
*/
|
1086
|
+
var dateIsLeapYear = function( year ) {
|
1087
|
+
return new Date( year, 1, 29 ).getMonth() === 1;
|
1088
|
+
};
|
1089
|
+
|
1090
|
+
|
1091
|
+
|
1092
|
+
|
1093
|
+
/**
|
1094
|
+
* lastDayOfMonth( date )
|
1095
|
+
*
|
1096
|
+
* @date [Date]
|
1097
|
+
*
|
1098
|
+
* Return the last day of the given date's month
|
1099
|
+
*/
|
1100
|
+
var dateLastDayOfMonth = function( date ) {
|
1101
|
+
return new Date( date.getFullYear(), date.getMonth() + 1, 0 ).getDate();
|
1102
|
+
};
|
1103
|
+
|
1104
|
+
|
1105
|
+
|
1106
|
+
|
1107
|
+
/**
|
1108
|
+
* startOf changes the input to the beginning of the given unit.
|
1109
|
+
*
|
1110
|
+
* For example, starting at the start of a day, resets hours, minutes
|
1111
|
+
* seconds and milliseconds to 0. Starting at the month does the same, but
|
1112
|
+
* also sets the date to 1.
|
1113
|
+
*
|
1114
|
+
* Returns the modified date
|
1115
|
+
*/
|
1116
|
+
var dateStartOf = function( date, unit ) {
|
1117
|
+
date = date instanceof ZonedDateTime ? date.clone() : new Date( date.getTime() );
|
1118
|
+
switch ( unit ) {
|
1119
|
+
case "year":
|
1120
|
+
date.setMonth( 0 );
|
1121
|
+
/* falls through */
|
1122
|
+
case "month":
|
1123
|
+
date.setDate( 1 );
|
1124
|
+
/* falls through */
|
1125
|
+
case "day":
|
1126
|
+
date.setHours( 0 );
|
1127
|
+
/* falls through */
|
1128
|
+
case "hour":
|
1129
|
+
date.setMinutes( 0 );
|
1130
|
+
/* falls through */
|
1131
|
+
case "minute":
|
1132
|
+
date.setSeconds( 0 );
|
1133
|
+
/* falls through */
|
1134
|
+
case "second":
|
1135
|
+
date.setMilliseconds( 0 );
|
1136
|
+
}
|
1137
|
+
return date;
|
1138
|
+
};
|
1139
|
+
|
1140
|
+
|
1141
|
+
|
1142
|
+
|
1143
|
+
/**
|
1144
|
+
* Differently from native date.setDate(), this function returns a date whose
|
1145
|
+
* day remains inside the month boundaries. For example:
|
1146
|
+
*
|
1147
|
+
* setDate( FebDate, 31 ): a "Feb 28" date.
|
1148
|
+
* setDate( SepDate, 31 ): a "Sep 30" date.
|
1149
|
+
*/
|
1150
|
+
var dateSetDate = function( date, day ) {
|
1151
|
+
var lastDay = new Date( date.getFullYear(), date.getMonth() + 1, 0 ).getDate();
|
1152
|
+
|
1153
|
+
date.setDate( day < 1 ? 1 : day < lastDay ? day : lastDay );
|
1154
|
+
};
|
1155
|
+
|
1156
|
+
|
1157
|
+
|
1158
|
+
|
1159
|
+
/**
|
1160
|
+
* Differently from native date.setMonth(), this function adjusts date if
|
1161
|
+
* needed, so final month is always the one set.
|
1162
|
+
*
|
1163
|
+
* setMonth( Jan31Date, 1 ): a "Feb 28" date.
|
1164
|
+
* setDate( Jan31Date, 8 ): a "Sep 30" date.
|
1165
|
+
*/
|
1166
|
+
var dateSetMonth = function( date, month ) {
|
1167
|
+
var originalDate = date.getDate();
|
1168
|
+
|
1169
|
+
date.setDate( 1 );
|
1170
|
+
date.setMonth( month );
|
1171
|
+
dateSetDate( date, originalDate );
|
1172
|
+
};
|
1173
|
+
|
1174
|
+
|
1175
|
+
|
1176
|
+
|
1177
|
+
var outOfRange = function( value, low, high ) {
|
1178
|
+
return value < low || value > high;
|
1179
|
+
};
|
1180
|
+
|
1181
|
+
|
1182
|
+
|
1183
|
+
|
1184
|
+
/**
|
1185
|
+
* parse( value, tokens, properties )
|
1186
|
+
*
|
1187
|
+
* @value [String] string date.
|
1188
|
+
*
|
1189
|
+
* @tokens [Object] tokens returned by date/tokenizer.
|
1190
|
+
*
|
1191
|
+
* @properties [Object] output returned by date/tokenizer-properties.
|
1192
|
+
*
|
1193
|
+
* ref: http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns
|
1194
|
+
*/
|
1195
|
+
var dateParse = function( _value, tokens, properties ) {
|
1196
|
+
var amPm, day, daysOfYear, month, era, hour, hour12, timezoneOffset, valid,
|
1197
|
+
YEAR = 0,
|
1198
|
+
MONTH = 1,
|
1199
|
+
DAY = 2,
|
1200
|
+
HOUR = 3,
|
1201
|
+
MINUTE = 4,
|
1202
|
+
SECOND = 5,
|
1203
|
+
MILLISECONDS = 6,
|
1204
|
+
date = new Date(),
|
1205
|
+
truncateAt = [],
|
1206
|
+
units = [ "year", "month", "day", "hour", "minute", "second", "milliseconds" ];
|
1207
|
+
|
1208
|
+
// Create globalize date with given timezone data.
|
1209
|
+
if ( properties.timeZoneData ) {
|
1210
|
+
date = new ZonedDateTime( date, properties.timeZoneData() );
|
1211
|
+
}
|
1212
|
+
|
1213
|
+
if ( !tokens.length ) {
|
1214
|
+
return null;
|
1215
|
+
}
|
1216
|
+
|
1217
|
+
valid = tokens.every(function( token ) {
|
1218
|
+
var century, chr, value, length;
|
1219
|
+
|
1220
|
+
if ( token.type === "literal" ) {
|
1221
|
+
|
1222
|
+
// continue
|
1223
|
+
return true;
|
1224
|
+
}
|
1225
|
+
|
1226
|
+
chr = token.type.charAt( 0 );
|
1227
|
+
length = token.type.length;
|
1228
|
+
|
1229
|
+
if ( chr === "j" ) {
|
1230
|
+
|
1231
|
+
// Locale preferred hHKk.
|
1232
|
+
// http://www.unicode.org/reports/tr35/tr35-dates.html#Time_Data
|
1233
|
+
chr = properties.preferredTimeData;
|
1234
|
+
}
|
1235
|
+
|
1236
|
+
switch ( chr ) {
|
1237
|
+
|
1238
|
+
// Era
|
1239
|
+
case "G":
|
1240
|
+
truncateAt.push( YEAR );
|
1241
|
+
era = +token.value;
|
1242
|
+
break;
|
1243
|
+
|
1244
|
+
// Year
|
1245
|
+
case "y":
|
1246
|
+
value = token.value;
|
1247
|
+
if ( length === 2 ) {
|
1248
|
+
if ( outOfRange( value, 0, 99 ) ) {
|
1249
|
+
return false;
|
1250
|
+
}
|
1251
|
+
|
1252
|
+
// mimic dojo/date/locale: choose century to apply, according to a sliding
|
1253
|
+
// window of 80 years before and 20 years after present year.
|
1254
|
+
century = Math.floor( date.getFullYear() / 100 ) * 100;
|
1255
|
+
value += century;
|
1256
|
+
if ( value > date.getFullYear() + 20 ) {
|
1257
|
+
value -= 100;
|
1258
|
+
}
|
1259
|
+
}
|
1260
|
+
date.setFullYear( value );
|
1261
|
+
truncateAt.push( YEAR );
|
1262
|
+
break;
|
1263
|
+
|
1264
|
+
case "Y": // Year in "Week of Year"
|
1265
|
+
throw createErrorUnsupportedFeature({
|
1266
|
+
feature: "year pattern `" + chr + "`"
|
1267
|
+
});
|
1268
|
+
|
1269
|
+
// Quarter (skip)
|
1270
|
+
case "Q":
|
1271
|
+
case "q":
|
1272
|
+
break;
|
1273
|
+
|
1274
|
+
// Month
|
1275
|
+
case "M":
|
1276
|
+
case "L":
|
1277
|
+
if ( length <= 2 ) {
|
1278
|
+
value = token.value;
|
1279
|
+
} else {
|
1280
|
+
value = +token.value;
|
1281
|
+
}
|
1282
|
+
if ( outOfRange( value, 1, 12 ) ) {
|
1283
|
+
return false;
|
1284
|
+
}
|
1285
|
+
|
1286
|
+
// Setting the month later so that we have the correct year and can determine
|
1287
|
+
// the correct last day of February in case of leap year.
|
1288
|
+
month = value;
|
1289
|
+
truncateAt.push( MONTH );
|
1290
|
+
break;
|
1291
|
+
|
1292
|
+
// Week (skip)
|
1293
|
+
case "w": // Week of Year.
|
1294
|
+
case "W": // Week of Month.
|
1295
|
+
break;
|
1296
|
+
|
1297
|
+
// Day
|
1298
|
+
case "d":
|
1299
|
+
day = token.value;
|
1300
|
+
truncateAt.push( DAY );
|
1301
|
+
break;
|
1302
|
+
|
1303
|
+
case "D":
|
1304
|
+
daysOfYear = token.value;
|
1305
|
+
truncateAt.push( DAY );
|
1306
|
+
break;
|
1307
|
+
|
1308
|
+
case "F":
|
1309
|
+
|
1310
|
+
// Day of Week in month. eg. 2nd Wed in July.
|
1311
|
+
// Skip
|
1312
|
+
break;
|
1313
|
+
|
1314
|
+
// Week day
|
1315
|
+
case "e":
|
1316
|
+
case "c":
|
1317
|
+
case "E":
|
1318
|
+
|
1319
|
+
// Skip.
|
1320
|
+
// value = arrayIndexOf( dateWeekDays, token.value );
|
1321
|
+
break;
|
1322
|
+
|
1323
|
+
// Period (AM or PM)
|
1324
|
+
case "a":
|
1325
|
+
amPm = token.value;
|
1326
|
+
break;
|
1327
|
+
|
1328
|
+
// Hour
|
1329
|
+
case "h": // 1-12
|
1330
|
+
value = token.value;
|
1331
|
+
if ( outOfRange( value, 1, 12 ) ) {
|
1332
|
+
return false;
|
1333
|
+
}
|
1334
|
+
hour = hour12 = true;
|
1335
|
+
date.setHours( value === 12 ? 0 : value );
|
1336
|
+
truncateAt.push( HOUR );
|
1337
|
+
break;
|
1338
|
+
|
1339
|
+
case "K": // 0-11
|
1340
|
+
value = token.value;
|
1341
|
+
if ( outOfRange( value, 0, 11 ) ) {
|
1342
|
+
return false;
|
1343
|
+
}
|
1344
|
+
hour = hour12 = true;
|
1345
|
+
date.setHours( value );
|
1346
|
+
truncateAt.push( HOUR );
|
1347
|
+
break;
|
1348
|
+
|
1349
|
+
case "k": // 1-24
|
1350
|
+
value = token.value;
|
1351
|
+
if ( outOfRange( value, 1, 24 ) ) {
|
1352
|
+
return false;
|
1353
|
+
}
|
1354
|
+
hour = true;
|
1355
|
+
date.setHours( value === 24 ? 0 : value );
|
1356
|
+
truncateAt.push( HOUR );
|
1357
|
+
break;
|
1358
|
+
|
1359
|
+
case "H": // 0-23
|
1360
|
+
value = token.value;
|
1361
|
+
if ( outOfRange( value, 0, 23 ) ) {
|
1362
|
+
return false;
|
1363
|
+
}
|
1364
|
+
hour = true;
|
1365
|
+
date.setHours( value );
|
1366
|
+
truncateAt.push( HOUR );
|
1367
|
+
break;
|
1368
|
+
|
1369
|
+
// Minute
|
1370
|
+
case "m":
|
1371
|
+
value = token.value;
|
1372
|
+
if ( outOfRange( value, 0, 59 ) ) {
|
1373
|
+
return false;
|
1374
|
+
}
|
1375
|
+
date.setMinutes( value );
|
1376
|
+
truncateAt.push( MINUTE );
|
1377
|
+
break;
|
1378
|
+
|
1379
|
+
// Second
|
1380
|
+
case "s":
|
1381
|
+
value = token.value;
|
1382
|
+
if ( outOfRange( value, 0, 59 ) ) {
|
1383
|
+
return false;
|
1384
|
+
}
|
1385
|
+
date.setSeconds( value );
|
1386
|
+
truncateAt.push( SECOND );
|
1387
|
+
break;
|
1388
|
+
|
1389
|
+
case "A":
|
1390
|
+
date.setHours( 0 );
|
1391
|
+
date.setMinutes( 0 );
|
1392
|
+
date.setSeconds( 0 );
|
1393
|
+
|
1394
|
+
/* falls through */
|
1395
|
+
case "S":
|
1396
|
+
value = Math.round( token.value * Math.pow( 10, 3 - length ) );
|
1397
|
+
date.setMilliseconds( value );
|
1398
|
+
truncateAt.push( MILLISECONDS );
|
1399
|
+
break;
|
1400
|
+
|
1401
|
+
// Zone
|
1402
|
+
case "z":
|
1403
|
+
case "Z":
|
1404
|
+
case "O":
|
1405
|
+
case "v":
|
1406
|
+
case "V":
|
1407
|
+
case "X":
|
1408
|
+
case "x":
|
1409
|
+
if ( typeof token.value === "number" ) {
|
1410
|
+
timezoneOffset = token.value;
|
1411
|
+
}
|
1412
|
+
break;
|
1413
|
+
}
|
1414
|
+
|
1415
|
+
return true;
|
1416
|
+
});
|
1417
|
+
|
1418
|
+
if ( !valid ) {
|
1419
|
+
return null;
|
1420
|
+
}
|
1421
|
+
|
1422
|
+
// 12-hour format needs AM or PM, 24-hour format doesn't, ie. return null
|
1423
|
+
// if amPm && !hour12 || !amPm && hour12.
|
1424
|
+
if ( hour && !( !amPm ^ hour12 ) ) {
|
1425
|
+
return null;
|
1426
|
+
}
|
1427
|
+
|
1428
|
+
if ( era === 0 ) {
|
1429
|
+
|
1430
|
+
// 1 BC = year 0
|
1431
|
+
date.setFullYear( date.getFullYear() * -1 + 1 );
|
1432
|
+
}
|
1433
|
+
|
1434
|
+
if ( month !== undefined ) {
|
1435
|
+
dateSetMonth( date, month - 1 );
|
1436
|
+
}
|
1437
|
+
|
1438
|
+
if ( day !== undefined ) {
|
1439
|
+
if ( outOfRange( day, 1, dateLastDayOfMonth( date ) ) ) {
|
1440
|
+
return null;
|
1441
|
+
}
|
1442
|
+
date.setDate( day );
|
1443
|
+
} else if ( daysOfYear !== undefined ) {
|
1444
|
+
if ( outOfRange( daysOfYear, 1, dateIsLeapYear( date.getFullYear() ) ? 366 : 365 ) ) {
|
1445
|
+
return null;
|
1446
|
+
}
|
1447
|
+
date.setMonth( 0 );
|
1448
|
+
date.setDate( daysOfYear );
|
1449
|
+
}
|
1450
|
+
|
1451
|
+
if ( hour12 && amPm === "pm" ) {
|
1452
|
+
date.setHours( date.getHours() + 12 );
|
1453
|
+
}
|
1454
|
+
|
1455
|
+
if ( timezoneOffset !== undefined ) {
|
1456
|
+
date.setMinutes( date.getMinutes() + timezoneOffset - date.getTimezoneOffset() );
|
1457
|
+
}
|
1458
|
+
|
1459
|
+
// Truncate date at the most precise unit defined. Eg.
|
1460
|
+
// If value is "12/31", and pattern is "MM/dd":
|
1461
|
+
// => new Date( <current Year>, 12, 31, 0, 0, 0, 0 );
|
1462
|
+
truncateAt = Math.max.apply( null, truncateAt );
|
1463
|
+
date = dateStartOf( date, units[ truncateAt ] );
|
1464
|
+
|
1465
|
+
// Get date back from globalize date.
|
1466
|
+
if ( date instanceof ZonedDateTime ) {
|
1467
|
+
date = date.toDate();
|
1468
|
+
}
|
1469
|
+
|
1470
|
+
return date;
|
1471
|
+
};
|
1472
|
+
|
1473
|
+
|
1474
|
+
/* eslint-disable no-unused-expressions */
|
1475
|
+
|
1476
|
+
|
1477
|
+
|
1478
|
+
/**
|
1479
|
+
* tokenizer( value, numberParser, properties )
|
1480
|
+
*
|
1481
|
+
* @value [String] string date.
|
1482
|
+
*
|
1483
|
+
* @numberParser [Function]
|
1484
|
+
*
|
1485
|
+
* @properties [Object] output returned by date/tokenizer-properties.
|
1486
|
+
*
|
1487
|
+
* Returns an Array of tokens, eg. value "5 o'clock PM", pattern "h 'o''clock' a":
|
1488
|
+
* [{
|
1489
|
+
* type: "h",
|
1490
|
+
* lexeme: "5"
|
1491
|
+
* }, {
|
1492
|
+
* type: "literal",
|
1493
|
+
* lexeme: " "
|
1494
|
+
* }, {
|
1495
|
+
* type: "literal",
|
1496
|
+
* lexeme: "o'clock"
|
1497
|
+
* }, {
|
1498
|
+
* type: "literal",
|
1499
|
+
* lexeme: " "
|
1500
|
+
* }, {
|
1501
|
+
* type: "a",
|
1502
|
+
* lexeme: "PM",
|
1503
|
+
* value: "pm"
|
1504
|
+
* }]
|
1505
|
+
*
|
1506
|
+
* OBS: lexeme's are always String and may return invalid ranges depending of the token type.
|
1507
|
+
* Eg. "99" for month number.
|
1508
|
+
*
|
1509
|
+
* Return an empty Array when not successfully parsed.
|
1510
|
+
*/
|
1511
|
+
var dateTokenizer = function( value, numberParser, properties ) {
|
1512
|
+
var digitsRe, valid,
|
1513
|
+
tokens = [],
|
1514
|
+
widths = [ "abbreviated", "wide", "narrow" ];
|
1515
|
+
|
1516
|
+
digitsRe = properties.digitsRe;
|
1517
|
+
value = looseMatching( value );
|
1518
|
+
|
1519
|
+
valid = properties.pattern.match( datePatternRe ).every(function( current ) {
|
1520
|
+
var aux, chr, length, numeric, tokenRe,
|
1521
|
+
token = {};
|
1522
|
+
|
1523
|
+
function hourFormatParse( tokenRe, numberParser ) {
|
1524
|
+
var aux, isPositive,
|
1525
|
+
match = value.match( tokenRe );
|
1526
|
+
numberParser = numberParser || function( value ) {
|
1527
|
+
return +value;
|
1528
|
+
};
|
1529
|
+
|
1530
|
+
if ( !match ) {
|
1531
|
+
return false;
|
1532
|
+
}
|
1533
|
+
|
1534
|
+
isPositive = match[ 1 ];
|
1535
|
+
|
1536
|
+
// hourFormat containing H only, e.g., `+H;-H`
|
1537
|
+
if ( match.length < 6 ) {
|
1538
|
+
aux = isPositive ? 1 : 3;
|
1539
|
+
token.value = numberParser( match[ aux ] ) * 60;
|
1540
|
+
|
1541
|
+
// hourFormat containing H and m, e.g., `+HHmm;-HHmm`
|
1542
|
+
} else if ( match.length < 10 ) {
|
1543
|
+
aux = isPositive ? [ 1, 3 ] : [ 5, 7 ];
|
1544
|
+
token.value = numberParser( match[ aux[ 0 ] ] ) * 60 +
|
1545
|
+
numberParser( match[ aux[ 1 ] ] );
|
1546
|
+
|
1547
|
+
// hourFormat containing H, m, and s e.g., `+HHmmss;-HHmmss`
|
1548
|
+
} else {
|
1549
|
+
aux = isPositive ? [ 1, 3, 5 ] : [ 7, 9, 11 ];
|
1550
|
+
token.value = numberParser( match[ aux[ 0 ] ] ) * 60 +
|
1551
|
+
numberParser( match[ aux[ 1 ] ] ) +
|
1552
|
+
numberParser( match[ aux[ 2 ] ] ) / 60;
|
1553
|
+
}
|
1554
|
+
|
1555
|
+
if ( isPositive ) {
|
1556
|
+
token.value *= -1;
|
1557
|
+
}
|
1558
|
+
|
1559
|
+
return true;
|
1560
|
+
}
|
1561
|
+
|
1562
|
+
function oneDigitIfLengthOne() {
|
1563
|
+
if ( length === 1 ) {
|
1564
|
+
|
1565
|
+
// Unicode equivalent to /\d/
|
1566
|
+
numeric = true;
|
1567
|
+
return tokenRe = digitsRe;
|
1568
|
+
}
|
1569
|
+
}
|
1570
|
+
|
1571
|
+
function oneOrTwoDigitsIfLengthOne() {
|
1572
|
+
if ( length === 1 ) {
|
1573
|
+
|
1574
|
+
// Unicode equivalent to /\d\d?/
|
1575
|
+
numeric = true;
|
1576
|
+
return tokenRe = new RegExp( "^(" + digitsRe.source + "){1,2}" );
|
1577
|
+
}
|
1578
|
+
}
|
1579
|
+
|
1580
|
+
function oneOrTwoDigitsIfLengthOneOrTwo() {
|
1581
|
+
if ( length === 1 || length === 2 ) {
|
1582
|
+
|
1583
|
+
// Unicode equivalent to /\d\d?/
|
1584
|
+
numeric = true;
|
1585
|
+
return tokenRe = new RegExp( "^(" + digitsRe.source + "){1,2}" );
|
1586
|
+
}
|
1587
|
+
}
|
1588
|
+
|
1589
|
+
function twoDigitsIfLengthTwo() {
|
1590
|
+
if ( length === 2 ) {
|
1591
|
+
|
1592
|
+
// Unicode equivalent to /\d\d/
|
1593
|
+
numeric = true;
|
1594
|
+
return tokenRe = new RegExp( "^(" + digitsRe.source + "){2}" );
|
1595
|
+
}
|
1596
|
+
}
|
1597
|
+
|
1598
|
+
// Brute-force test every locale entry in an attempt to match the given value.
|
1599
|
+
// Return the first found one (and set token accordingly), or null.
|
1600
|
+
function lookup( path ) {
|
1601
|
+
var array = properties[ path.join( "/" ) ];
|
1602
|
+
|
1603
|
+
if ( !array ) {
|
1604
|
+
return null;
|
1605
|
+
}
|
1606
|
+
|
1607
|
+
// array of pairs [key, value] sorted by desc value length.
|
1608
|
+
array.some(function( item ) {
|
1609
|
+
var valueRe = item[ 1 ];
|
1610
|
+
if ( valueRe.test( value ) ) {
|
1611
|
+
token.value = item[ 0 ];
|
1612
|
+
tokenRe = item[ 1 ];
|
1613
|
+
return true;
|
1614
|
+
}
|
1615
|
+
});
|
1616
|
+
return null;
|
1617
|
+
}
|
1618
|
+
|
1619
|
+
token.type = current;
|
1620
|
+
chr = current.charAt( 0 );
|
1621
|
+
length = current.length;
|
1622
|
+
|
1623
|
+
if ( chr === "Z" ) {
|
1624
|
+
|
1625
|
+
// Z..ZZZ: same as "xxxx".
|
1626
|
+
if ( length < 4 ) {
|
1627
|
+
chr = "x";
|
1628
|
+
length = 4;
|
1629
|
+
|
1630
|
+
// ZZZZ: same as "OOOO".
|
1631
|
+
} else if ( length < 5 ) {
|
1632
|
+
chr = "O";
|
1633
|
+
length = 4;
|
1634
|
+
|
1635
|
+
// ZZZZZ: same as "XXXXX"
|
1636
|
+
} else {
|
1637
|
+
chr = "X";
|
1638
|
+
length = 5;
|
1639
|
+
}
|
1640
|
+
}
|
1641
|
+
|
1642
|
+
if ( chr === "z" ) {
|
1643
|
+
if ( properties.standardOrDaylightTzName ) {
|
1644
|
+
token.value = null;
|
1645
|
+
tokenRe = properties.standardOrDaylightTzName;
|
1646
|
+
}
|
1647
|
+
}
|
1648
|
+
|
1649
|
+
// v...vvv: "{shortRegion}", eg. "PT".
|
1650
|
+
// vvvv: "{regionName} {Time}" or "{regionName} {Time}",
|
1651
|
+
// e.g., "Pacific Time"
|
1652
|
+
// http://unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns
|
1653
|
+
if ( chr === "v" ) {
|
1654
|
+
if ( properties.genericTzName ) {
|
1655
|
+
token.value = null;
|
1656
|
+
tokenRe = properties.genericTzName;
|
1657
|
+
|
1658
|
+
// Fall back to "V" format.
|
1659
|
+
} else {
|
1660
|
+
chr = "V";
|
1661
|
+
length = 4;
|
1662
|
+
}
|
1663
|
+
}
|
1664
|
+
|
1665
|
+
if ( chr === "V" && properties.timeZoneName ) {
|
1666
|
+
token.value = length === 2 ? properties.timeZoneName : null;
|
1667
|
+
tokenRe = properties.timeZoneNameRe;
|
1668
|
+
}
|
1669
|
+
|
1670
|
+
switch ( chr ) {
|
1671
|
+
|
1672
|
+
// Era
|
1673
|
+
case "G":
|
1674
|
+
lookup([
|
1675
|
+
"gregorian/eras",
|
1676
|
+
length <= 3 ? "eraAbbr" : ( length === 4 ? "eraNames" : "eraNarrow" )
|
1677
|
+
]);
|
1678
|
+
break;
|
1679
|
+
|
1680
|
+
// Year
|
1681
|
+
case "y":
|
1682
|
+
case "Y":
|
1683
|
+
numeric = true;
|
1684
|
+
|
1685
|
+
// number l=1:+, l=2:{2}, l=3:{3,}, l=4:{4,}, ...
|
1686
|
+
if ( length === 1 ) {
|
1687
|
+
|
1688
|
+
// Unicode equivalent to /\d+/.
|
1689
|
+
tokenRe = new RegExp( "^(" + digitsRe.source + ")+" );
|
1690
|
+
} else if ( length === 2 ) {
|
1691
|
+
|
1692
|
+
// Lenient parsing: there's no year pattern to indicate non-zero-padded 2-digits
|
1693
|
+
// year, so parser accepts both zero-padded and non-zero-padded for `yy`.
|
1694
|
+
//
|
1695
|
+
// Unicode equivalent to /\d\d?/
|
1696
|
+
tokenRe = new RegExp( "^(" + digitsRe.source + "){1,2}" );
|
1697
|
+
} else {
|
1698
|
+
|
1699
|
+
// Unicode equivalent to /\d{length,}/
|
1700
|
+
tokenRe = new RegExp( "^(" + digitsRe.source + "){" + length + ",}" );
|
1701
|
+
}
|
1702
|
+
break;
|
1703
|
+
|
1704
|
+
// Quarter
|
1705
|
+
case "Q":
|
1706
|
+
case "q":
|
1707
|
+
|
1708
|
+
// number l=1:{1}, l=2:{2}.
|
1709
|
+
// lookup l=3...
|
1710
|
+
oneDigitIfLengthOne() || twoDigitsIfLengthTwo() ||
|
1711
|
+
lookup([
|
1712
|
+
"gregorian/quarters",
|
1713
|
+
chr === "Q" ? "format" : "stand-alone",
|
1714
|
+
widths[ length - 3 ]
|
1715
|
+
]);
|
1716
|
+
break;
|
1717
|
+
|
1718
|
+
// Month
|
1719
|
+
case "M":
|
1720
|
+
case "L":
|
1721
|
+
|
1722
|
+
// number l=1:{1,2}, l=2:{2}.
|
1723
|
+
// lookup l=3...
|
1724
|
+
//
|
1725
|
+
// Lenient parsing: skeleton "yMd" (i.e., one M) may include MM for the pattern,
|
1726
|
+
// therefore parser accepts both zero-padded and non-zero-padded for M and MM.
|
1727
|
+
// Similar for L.
|
1728
|
+
oneOrTwoDigitsIfLengthOneOrTwo() || lookup([
|
1729
|
+
"gregorian/months",
|
1730
|
+
chr === "M" ? "format" : "stand-alone",
|
1731
|
+
widths[ length - 3 ]
|
1732
|
+
]);
|
1733
|
+
break;
|
1734
|
+
|
1735
|
+
// Day
|
1736
|
+
case "D":
|
1737
|
+
|
1738
|
+
// number {l,3}.
|
1739
|
+
if ( length <= 3 ) {
|
1740
|
+
|
1741
|
+
// Equivalent to /\d{length,3}/
|
1742
|
+
numeric = true;
|
1743
|
+
tokenRe = new RegExp( "^(" + digitsRe.source + "){" + length + ",3}" );
|
1744
|
+
}
|
1745
|
+
break;
|
1746
|
+
|
1747
|
+
case "W":
|
1748
|
+
case "F":
|
1749
|
+
|
1750
|
+
// number l=1:{1}.
|
1751
|
+
oneDigitIfLengthOne();
|
1752
|
+
break;
|
1753
|
+
|
1754
|
+
// Week day
|
1755
|
+
case "e":
|
1756
|
+
case "c":
|
1757
|
+
|
1758
|
+
// number l=1:{1}, l=2:{2}.
|
1759
|
+
// lookup for length >=3.
|
1760
|
+
if ( length <= 2 ) {
|
1761
|
+
oneDigitIfLengthOne() || twoDigitsIfLengthTwo();
|
1762
|
+
break;
|
1763
|
+
}
|
1764
|
+
|
1765
|
+
/* falls through */
|
1766
|
+
case "E":
|
1767
|
+
if ( length === 6 ) {
|
1768
|
+
|
1769
|
+
// Note: if short day names are not explicitly specified, abbreviated day
|
1770
|
+
// names are used instead http://www.unicode.org/reports/tr35/tr35-dates.html#months_days_quarters_eras
|
1771
|
+
lookup([
|
1772
|
+
"gregorian/days",
|
1773
|
+
[ chr === "c" ? "stand-alone" : "format" ],
|
1774
|
+
"short"
|
1775
|
+
]) || lookup([
|
1776
|
+
"gregorian/days",
|
1777
|
+
[ chr === "c" ? "stand-alone" : "format" ],
|
1778
|
+
"abbreviated"
|
1779
|
+
]);
|
1780
|
+
} else {
|
1781
|
+
lookup([
|
1782
|
+
"gregorian/days",
|
1783
|
+
[ chr === "c" ? "stand-alone" : "format" ],
|
1784
|
+
widths[ length < 3 ? 0 : length - 3 ]
|
1785
|
+
]);
|
1786
|
+
}
|
1787
|
+
break;
|
1788
|
+
|
1789
|
+
// Period (AM or PM)
|
1790
|
+
case "a":
|
1791
|
+
lookup([
|
1792
|
+
"gregorian/dayPeriods/format/wide"
|
1793
|
+
]);
|
1794
|
+
break;
|
1795
|
+
|
1796
|
+
// Week
|
1797
|
+
case "w":
|
1798
|
+
|
1799
|
+
// number l1:{1,2}, l2:{2}.
|
1800
|
+
oneOrTwoDigitsIfLengthOne() || twoDigitsIfLengthTwo();
|
1801
|
+
break;
|
1802
|
+
|
1803
|
+
// Day, Hour, Minute, or Second
|
1804
|
+
case "d":
|
1805
|
+
case "h":
|
1806
|
+
case "H":
|
1807
|
+
case "K":
|
1808
|
+
case "k":
|
1809
|
+
case "j":
|
1810
|
+
case "m":
|
1811
|
+
case "s":
|
1812
|
+
|
1813
|
+
// number l1:{1,2}, l2:{2}.
|
1814
|
+
//
|
1815
|
+
// Lenient parsing:
|
1816
|
+
// - skeleton "hms" (i.e., one m) always includes mm for the pattern, i.e., it's
|
1817
|
+
// impossible to use a different skeleton to parse non-zero-padded minutes,
|
1818
|
+
// therefore parser accepts both zero-padded and non-zero-padded for m. Similar
|
1819
|
+
// for seconds s.
|
1820
|
+
// - skeleton "hms" (i.e., one h) may include h or hh for the pattern, i.e., it's
|
1821
|
+
// impossible to use a different skeleton to parser non-zero-padded hours for some
|
1822
|
+
// locales, therefore parser accepts both zero-padded and non-zero-padded for h.
|
1823
|
+
// Similar for d (in skeleton yMd).
|
1824
|
+
oneOrTwoDigitsIfLengthOneOrTwo();
|
1825
|
+
break;
|
1826
|
+
|
1827
|
+
case "S":
|
1828
|
+
|
1829
|
+
// number {l}.
|
1830
|
+
|
1831
|
+
// Unicode equivalent to /\d{length}/
|
1832
|
+
numeric = true;
|
1833
|
+
tokenRe = new RegExp( "^(" + digitsRe.source + "){" + length + "}" );
|
1834
|
+
break;
|
1835
|
+
|
1836
|
+
case "A":
|
1837
|
+
|
1838
|
+
// number {l+5}.
|
1839
|
+
|
1840
|
+
// Unicode equivalent to /\d{length+5}/
|
1841
|
+
numeric = true;
|
1842
|
+
tokenRe = new RegExp( "^(" + digitsRe.source + "){" + ( length + 5 ) + "}" );
|
1843
|
+
break;
|
1844
|
+
|
1845
|
+
// Zone
|
1846
|
+
case "v":
|
1847
|
+
case "V":
|
1848
|
+
case "z":
|
1849
|
+
if ( tokenRe && tokenRe.test( value ) ) {
|
1850
|
+
break;
|
1851
|
+
}
|
1852
|
+
if ( chr === "V" && length === 2 ) {
|
1853
|
+
break;
|
1854
|
+
}
|
1855
|
+
|
1856
|
+
/* falls through */
|
1857
|
+
case "O":
|
1858
|
+
|
1859
|
+
// O: "{gmtFormat}+H;{gmtFormat}-H" or "{gmtZeroFormat}", eg. "GMT-8" or "GMT".
|
1860
|
+
// OOOO: "{gmtFormat}{hourFormat}" or "{gmtZeroFormat}", eg. "GMT-08:00" or "GMT".
|
1861
|
+
if ( value === properties[ "timeZoneNames/gmtZeroFormat" ] ) {
|
1862
|
+
token.value = 0;
|
1863
|
+
tokenRe = properties[ "timeZoneNames/gmtZeroFormatRe" ];
|
1864
|
+
} else {
|
1865
|
+
aux = properties[ "timeZoneNames/hourFormat" ].some(function( hourFormatRe ) {
|
1866
|
+
if ( hourFormatParse( hourFormatRe, numberParser ) ) {
|
1867
|
+
tokenRe = hourFormatRe;
|
1868
|
+
return true;
|
1869
|
+
}
|
1870
|
+
});
|
1871
|
+
if ( !aux ) {
|
1872
|
+
return null;
|
1873
|
+
}
|
1874
|
+
}
|
1875
|
+
break;
|
1876
|
+
|
1877
|
+
case "X":
|
1878
|
+
|
1879
|
+
// Same as x*, except it uses "Z" for zero offset.
|
1880
|
+
if ( value === "Z" ) {
|
1881
|
+
token.value = 0;
|
1882
|
+
tokenRe = /^Z/;
|
1883
|
+
break;
|
1884
|
+
}
|
1885
|
+
|
1886
|
+
/* falls through */
|
1887
|
+
case "x":
|
1888
|
+
|
1889
|
+
// x: hourFormat("+HH[mm];-HH[mm]")
|
1890
|
+
// xx: hourFormat("+HHmm;-HHmm")
|
1891
|
+
// xxx: hourFormat("+HH:mm;-HH:mm")
|
1892
|
+
// xxxx: hourFormat("+HHmm[ss];-HHmm[ss]")
|
1893
|
+
// xxxxx: hourFormat("+HH:mm[:ss];-HH:mm[:ss]")
|
1894
|
+
aux = properties.x.some(function( hourFormatRe ) {
|
1895
|
+
if ( hourFormatParse( hourFormatRe ) ) {
|
1896
|
+
tokenRe = hourFormatRe;
|
1897
|
+
return true;
|
1898
|
+
}
|
1899
|
+
});
|
1900
|
+
if ( !aux ) {
|
1901
|
+
return null;
|
1902
|
+
}
|
1903
|
+
break;
|
1904
|
+
|
1905
|
+
case "'":
|
1906
|
+
token.type = "literal";
|
1907
|
+
tokenRe = new RegExp( "^" + regexpEscape( removeLiteralQuotes( current ) ) );
|
1908
|
+
break;
|
1909
|
+
|
1910
|
+
default:
|
1911
|
+
token.type = "literal";
|
1912
|
+
tokenRe = new RegExp( "^" + regexpEscape( current ) );
|
1913
|
+
}
|
1914
|
+
|
1915
|
+
if ( !tokenRe ) {
|
1916
|
+
return false;
|
1917
|
+
}
|
1918
|
+
|
1919
|
+
// Get lexeme and consume it.
|
1920
|
+
value = value.replace( tokenRe, function( lexeme ) {
|
1921
|
+
token.lexeme = lexeme;
|
1922
|
+
if ( numeric ) {
|
1923
|
+
token.value = numberParser( lexeme );
|
1924
|
+
}
|
1925
|
+
return "";
|
1926
|
+
});
|
1927
|
+
|
1928
|
+
if ( !token.lexeme ) {
|
1929
|
+
return false;
|
1930
|
+
}
|
1931
|
+
|
1932
|
+
if ( numeric && isNaN( token.value ) ) {
|
1933
|
+
return false;
|
1934
|
+
}
|
1935
|
+
|
1936
|
+
tokens.push( token );
|
1937
|
+
return true;
|
1938
|
+
});
|
1939
|
+
|
1940
|
+
if ( value !== "" ) {
|
1941
|
+
valid = false;
|
1942
|
+
}
|
1943
|
+
|
1944
|
+
return valid ? tokens : [];
|
1945
|
+
};
|
1946
|
+
|
1947
|
+
|
1948
|
+
|
1949
|
+
|
1950
|
+
var dateParserFn = function( numberParser, parseProperties, tokenizerProperties ) {
|
1951
|
+
return function dateParser( value ) {
|
1952
|
+
var tokens;
|
1953
|
+
|
1954
|
+
validateParameterPresence( value, "value" );
|
1955
|
+
validateParameterTypeString( value, "value" );
|
1956
|
+
|
1957
|
+
tokens = dateTokenizer( value, numberParser, tokenizerProperties );
|
1958
|
+
return dateParse( value, tokens, parseProperties ) || null;
|
1959
|
+
};
|
1960
|
+
};
|
1961
|
+
|
1962
|
+
|
1963
|
+
|
1964
|
+
|
1965
|
+
var objectFilter = function( object, testRe ) {
|
1966
|
+
var key,
|
1967
|
+
copy = {};
|
1968
|
+
|
1969
|
+
for ( key in object ) {
|
1970
|
+
if ( testRe.test( key ) ) {
|
1971
|
+
copy[ key ] = object[ key ];
|
1972
|
+
}
|
1973
|
+
}
|
1974
|
+
|
1975
|
+
return copy;
|
1976
|
+
};
|
1977
|
+
|
1978
|
+
|
1979
|
+
|
1980
|
+
|
1981
|
+
/**
|
1982
|
+
* tokenizerProperties( pattern, cldr )
|
1983
|
+
*
|
1984
|
+
* @pattern [String] raw pattern.
|
1985
|
+
*
|
1986
|
+
* @cldr [Cldr instance].
|
1987
|
+
*
|
1988
|
+
* Return Object with data that will be used by tokenizer.
|
1989
|
+
*/
|
1990
|
+
var dateTokenizerProperties = function( pattern, cldr, timeZone ) {
|
1991
|
+
var digitsReSource,
|
1992
|
+
properties = {
|
1993
|
+
pattern: looseMatching( pattern )
|
1994
|
+
},
|
1995
|
+
timeSeparator = numberSymbol( "timeSeparator", cldr ),
|
1996
|
+
widths = [ "abbreviated", "wide", "narrow" ];
|
1997
|
+
|
1998
|
+
digitsReSource = numberNumberingSystemDigitsMap( cldr );
|
1999
|
+
digitsReSource = digitsReSource ? "[" + digitsReSource + "]" : "\\d";
|
2000
|
+
properties.digitsRe = new RegExp( digitsReSource );
|
2001
|
+
|
2002
|
+
// Transform:
|
2003
|
+
// - "+H;-H" -> /\+(\d\d?)|-(\d\d?)/
|
2004
|
+
// - "+HH;-HH" -> /\+(\d\d)|-(\d\d)/
|
2005
|
+
// - "+HHmm;-HHmm" -> /\+(\d\d)(\d\d)|-(\d\d)(\d\d)/
|
2006
|
+
// - "+HH:mm;-HH:mm" -> /\+(\d\d):(\d\d)|-(\d\d):(\d\d)/
|
2007
|
+
//
|
2008
|
+
// If gmtFormat is GMT{0}, the regexp must fill {0} in each side, e.g.:
|
2009
|
+
// - "+H;-H" -> /GMT\+(\d\d?)|GMT-(\d\d?)/
|
2010
|
+
function hourFormatRe( hourFormat, gmtFormat, digitsReSource, timeSeparator ) {
|
2011
|
+
var re;
|
2012
|
+
|
2013
|
+
if ( !digitsReSource ) {
|
2014
|
+
digitsReSource = "\\d";
|
2015
|
+
}
|
2016
|
+
if ( !gmtFormat ) {
|
2017
|
+
gmtFormat = "{0}";
|
2018
|
+
}
|
2019
|
+
|
2020
|
+
re = hourFormat
|
2021
|
+
.replace( "+", "\\+" )
|
2022
|
+
|
2023
|
+
// Unicode equivalent to (\\d\\d)
|
2024
|
+
.replace( /HH|mm|ss/g, "((" + digitsReSource + "){2})" )
|
2025
|
+
|
2026
|
+
// Unicode equivalent to (\\d\\d?)
|
2027
|
+
.replace( /H|m/g, "((" + digitsReSource + "){1,2})" );
|
2028
|
+
|
2029
|
+
if ( timeSeparator ) {
|
2030
|
+
re = re.replace( /:/g, timeSeparator );
|
2031
|
+
}
|
2032
|
+
|
2033
|
+
re = re.split( ";" ).map(function( part ) {
|
2034
|
+
return gmtFormat.replace( "{0}", part );
|
2035
|
+
}).join( "|" );
|
2036
|
+
|
2037
|
+
return new RegExp( "^" + re );
|
2038
|
+
}
|
2039
|
+
|
2040
|
+
function populateProperties( path, value ) {
|
2041
|
+
|
2042
|
+
// Skip
|
2043
|
+
var skipRe = /(timeZoneNames\/zone|supplemental\/metaZones|timeZoneNames\/metazone|timeZoneNames\/regionFormat|timeZoneNames\/gmtFormat)/;
|
2044
|
+
if ( skipRe.test( path ) ) {
|
2045
|
+
return;
|
2046
|
+
}
|
2047
|
+
|
2048
|
+
if ( !value ) {
|
2049
|
+
return;
|
2050
|
+
}
|
2051
|
+
|
2052
|
+
// The `dates` and `calendars` trim's purpose is to reduce properties' key size only.
|
2053
|
+
path = path.replace( /^.*\/dates\//, "" ).replace( /calendars\//, "" );
|
2054
|
+
|
2055
|
+
// Specific filter for "gregorian/dayPeriods/format/wide".
|
2056
|
+
if ( path === "gregorian/dayPeriods/format/wide" ) {
|
2057
|
+
value = objectFilter( value, /^am|^pm/ );
|
2058
|
+
}
|
2059
|
+
|
2060
|
+
// Transform object into array of pairs [key, /value/], sort by desc value length.
|
2061
|
+
if ( isPlainObject( value ) ) {
|
2062
|
+
value = Object.keys( value ).map(function( key ) {
|
2063
|
+
return [ key, new RegExp( "^" + regexpEscape( looseMatching( value[ key ] ) ) ) ];
|
2064
|
+
}).sort(function( a, b ) {
|
2065
|
+
return b[ 1 ].source.length - a[ 1 ].source.length;
|
2066
|
+
});
|
2067
|
+
|
2068
|
+
// If typeof value === "string".
|
2069
|
+
} else {
|
2070
|
+
value = looseMatching( value );
|
2071
|
+
}
|
2072
|
+
properties[ path ] = value;
|
2073
|
+
}
|
2074
|
+
|
2075
|
+
function regexpSourceSomeTerm( terms ) {
|
2076
|
+
return "(" + terms.filter(function( item ) {
|
2077
|
+
return item;
|
2078
|
+
}).reduce(function( memo, item ) {
|
2079
|
+
return memo + "|" + item;
|
2080
|
+
}) + ")";
|
2081
|
+
}
|
2082
|
+
|
2083
|
+
cldr.on( "get", populateProperties );
|
2084
|
+
|
2085
|
+
pattern.match( datePatternRe ).forEach(function( current ) {
|
2086
|
+
var aux, chr, daylightTzName, gmtFormat, length, standardTzName;
|
2087
|
+
|
2088
|
+
chr = current.charAt( 0 );
|
2089
|
+
length = current.length;
|
2090
|
+
|
2091
|
+
if ( chr === "Z" ) {
|
2092
|
+
if ( length < 5 ) {
|
2093
|
+
chr = "O";
|
2094
|
+
length = 4;
|
2095
|
+
} else {
|
2096
|
+
chr = "X";
|
2097
|
+
length = 5;
|
2098
|
+
}
|
2099
|
+
}
|
2100
|
+
|
2101
|
+
// z...zzz: "{shortRegion}", eg. "PST" or "PDT".
|
2102
|
+
// zzzz: "{regionName} {Standard Time}" or "{regionName} {Daylight Time}",
|
2103
|
+
// e.g., "Pacific Standard Time" or "Pacific Daylight Time".
|
2104
|
+
// http://unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns
|
2105
|
+
if ( chr === "z" ) {
|
2106
|
+
standardTzName = dateGetTimeZoneName( length, "standard", timeZone, cldr );
|
2107
|
+
daylightTzName = dateGetTimeZoneName( length, "daylight", timeZone, cldr );
|
2108
|
+
if ( standardTzName ) {
|
2109
|
+
standardTzName = regexpEscape( looseMatching( standardTzName ) );
|
2110
|
+
}
|
2111
|
+
if ( daylightTzName ) {
|
2112
|
+
daylightTzName = regexpEscape( looseMatching( daylightTzName ) );
|
2113
|
+
}
|
2114
|
+
if ( standardTzName || daylightTzName ) {
|
2115
|
+
properties.standardOrDaylightTzName = new RegExp(
|
2116
|
+
"^" + regexpSourceSomeTerm([ standardTzName, daylightTzName ])
|
2117
|
+
);
|
2118
|
+
}
|
2119
|
+
|
2120
|
+
// Fall through the "O" format in case one name is missing.
|
2121
|
+
if ( !standardTzName || !daylightTzName ) {
|
2122
|
+
chr = "O";
|
2123
|
+
if ( length < 4 ) {
|
2124
|
+
length = 1;
|
2125
|
+
}
|
2126
|
+
}
|
2127
|
+
}
|
2128
|
+
|
2129
|
+
// v...vvv: "{shortRegion}", eg. "PT".
|
2130
|
+
// vvvv: "{regionName} {Time}" or "{regionName} {Time}",
|
2131
|
+
// e.g., "Pacific Time"
|
2132
|
+
// http://unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns
|
2133
|
+
if ( chr === "v" ) {
|
2134
|
+
if ( length !== 1 && length !== 4 ) {
|
2135
|
+
throw createErrorUnsupportedFeature({
|
2136
|
+
feature: "timezone pattern `" + pattern + "`"
|
2137
|
+
});
|
2138
|
+
}
|
2139
|
+
var genericTzName = dateGetTimeZoneName( length, "generic", timeZone, cldr );
|
2140
|
+
if ( genericTzName ) {
|
2141
|
+
properties.genericTzName = new RegExp(
|
2142
|
+
"^" + regexpEscape( looseMatching( genericTzName ) )
|
2143
|
+
);
|
2144
|
+
chr = "O";
|
2145
|
+
|
2146
|
+
// Fall back to "V" format.
|
2147
|
+
} else {
|
2148
|
+
chr = "V";
|
2149
|
+
length = 4;
|
2150
|
+
}
|
2151
|
+
}
|
2152
|
+
|
2153
|
+
switch ( chr ) {
|
2154
|
+
|
2155
|
+
// Era
|
2156
|
+
case "G":
|
2157
|
+
cldr.main([
|
2158
|
+
"dates/calendars/gregorian/eras",
|
2159
|
+
length <= 3 ? "eraAbbr" : ( length === 4 ? "eraNames" : "eraNarrow" )
|
2160
|
+
]);
|
2161
|
+
break;
|
2162
|
+
|
2163
|
+
// Year
|
2164
|
+
case "u": // Extended year. Need to be implemented.
|
2165
|
+
case "U": // Cyclic year name. Need to be implemented.
|
2166
|
+
throw createErrorUnsupportedFeature({
|
2167
|
+
feature: "year pattern `" + chr + "`"
|
2168
|
+
});
|
2169
|
+
|
2170
|
+
// Quarter
|
2171
|
+
case "Q":
|
2172
|
+
case "q":
|
2173
|
+
if ( length > 2 ) {
|
2174
|
+
cldr.main([
|
2175
|
+
"dates/calendars/gregorian/quarters",
|
2176
|
+
chr === "Q" ? "format" : "stand-alone",
|
2177
|
+
widths[ length - 3 ]
|
2178
|
+
]);
|
2179
|
+
}
|
2180
|
+
break;
|
2181
|
+
|
2182
|
+
// Month
|
2183
|
+
case "M":
|
2184
|
+
case "L":
|
2185
|
+
|
2186
|
+
// number l=1:{1,2}, l=2:{2}.
|
2187
|
+
// lookup l=3...
|
2188
|
+
if ( length > 2 ) {
|
2189
|
+
cldr.main([
|
2190
|
+
"dates/calendars/gregorian/months",
|
2191
|
+
chr === "M" ? "format" : "stand-alone",
|
2192
|
+
widths[ length - 3 ]
|
2193
|
+
]);
|
2194
|
+
}
|
2195
|
+
break;
|
2196
|
+
|
2197
|
+
// Day
|
2198
|
+
case "g":
|
2199
|
+
|
2200
|
+
// Modified Julian day. Need to be implemented.
|
2201
|
+
throw createErrorUnsupportedFeature({
|
2202
|
+
feature: "Julian day pattern `g`"
|
2203
|
+
});
|
2204
|
+
|
2205
|
+
// Week day
|
2206
|
+
case "e":
|
2207
|
+
case "c":
|
2208
|
+
|
2209
|
+
// lookup for length >=3.
|
2210
|
+
if ( length <= 2 ) {
|
2211
|
+
break;
|
2212
|
+
}
|
2213
|
+
|
2214
|
+
/* falls through */
|
2215
|
+
case "E":
|
2216
|
+
if ( length === 6 ) {
|
2217
|
+
|
2218
|
+
// Note: if short day names are not explicitly specified, abbreviated day
|
2219
|
+
// names are used instead http://www.unicode.org/reports/tr35/tr35-dates.html#months_days_quarters_eras
|
2220
|
+
// eslint-disable-next-line no-unused-expressions
|
2221
|
+
cldr.main([
|
2222
|
+
"dates/calendars/gregorian/days",
|
2223
|
+
[ chr === "c" ? "stand-alone" : "format" ],
|
2224
|
+
"short"
|
2225
|
+
]) || cldr.main([
|
2226
|
+
"dates/calendars/gregorian/days",
|
2227
|
+
[ chr === "c" ? "stand-alone" : "format" ],
|
2228
|
+
"abbreviated"
|
2229
|
+
]);
|
2230
|
+
} else {
|
2231
|
+
cldr.main([
|
2232
|
+
"dates/calendars/gregorian/days",
|
2233
|
+
[ chr === "c" ? "stand-alone" : "format" ],
|
2234
|
+
widths[ length < 3 ? 0 : length - 3 ]
|
2235
|
+
]);
|
2236
|
+
}
|
2237
|
+
break;
|
2238
|
+
|
2239
|
+
// Period (AM or PM)
|
2240
|
+
case "a":
|
2241
|
+
cldr.main(
|
2242
|
+
"dates/calendars/gregorian/dayPeriods/format/wide"
|
2243
|
+
);
|
2244
|
+
break;
|
2245
|
+
|
2246
|
+
// Zone
|
2247
|
+
case "V":
|
2248
|
+
|
2249
|
+
if ( length === 1 ) {
|
2250
|
+
throw createErrorUnsupportedFeature({
|
2251
|
+
feature: "timezone pattern `" + pattern + "`"
|
2252
|
+
});
|
2253
|
+
}
|
2254
|
+
|
2255
|
+
if ( timeZone ) {
|
2256
|
+
if ( length === 2 ) {
|
2257
|
+
|
2258
|
+
// Skip looseMatching processing since timeZone is a canonical posix value.
|
2259
|
+
properties.timeZoneName = timeZone;
|
2260
|
+
properties.timeZoneNameRe = new RegExp( "^" + regexpEscape( timeZone ) );
|
2261
|
+
break;
|
2262
|
+
}
|
2263
|
+
|
2264
|
+
var timeZoneName,
|
2265
|
+
exemplarCity = cldr.main([
|
2266
|
+
"dates/timeZoneNames/zone", timeZone, "exemplarCity"
|
2267
|
+
]);
|
2268
|
+
|
2269
|
+
if ( length === 3 ) {
|
2270
|
+
if ( !exemplarCity ) {
|
2271
|
+
exemplarCity = cldr.main([
|
2272
|
+
"dates/timeZoneNames/zone/Etc/Unknown/exemplarCity"
|
2273
|
+
]);
|
2274
|
+
}
|
2275
|
+
timeZoneName = exemplarCity;
|
2276
|
+
}
|
2277
|
+
|
2278
|
+
if ( exemplarCity && length === 4 ) {
|
2279
|
+
timeZoneName = formatMessage(
|
2280
|
+
cldr.main(
|
2281
|
+
"dates/timeZoneNames/regionFormat"
|
2282
|
+
),
|
2283
|
+
[ exemplarCity ]
|
2284
|
+
);
|
2285
|
+
}
|
2286
|
+
|
2287
|
+
if ( timeZoneName ) {
|
2288
|
+
timeZoneName = looseMatching( timeZoneName );
|
2289
|
+
properties.timeZoneName = timeZoneName;
|
2290
|
+
properties.timeZoneNameRe = new RegExp(
|
2291
|
+
"^" + regexpEscape( timeZoneName )
|
2292
|
+
);
|
2293
|
+
}
|
2294
|
+
}
|
2295
|
+
|
2296
|
+
if ( current === "v" ) {
|
2297
|
+
length = 1;
|
2298
|
+
}
|
2299
|
+
|
2300
|
+
/* falls through */
|
2301
|
+
case "z":
|
2302
|
+
case "O":
|
2303
|
+
gmtFormat = cldr.main( "dates/timeZoneNames/gmtFormat" );
|
2304
|
+
cldr.main( "dates/timeZoneNames/gmtZeroFormat" );
|
2305
|
+
cldr.main( "dates/timeZoneNames/hourFormat" );
|
2306
|
+
properties[ "timeZoneNames/gmtZeroFormatRe" ] =
|
2307
|
+
new RegExp( "^" + regexpEscape( properties[ "timeZoneNames/gmtZeroFormat" ] ) );
|
2308
|
+
aux = properties[ "timeZoneNames/hourFormat" ];
|
2309
|
+
properties[ "timeZoneNames/hourFormat" ] = (
|
2310
|
+
length < 4 ?
|
2311
|
+
[ dateTimezoneHourFormatHm( aux, "H" ), dateTimezoneHourFormatH( aux ) ] :
|
2312
|
+
[ dateTimezoneHourFormatHm( aux, "HH" ) ]
|
2313
|
+
).map(function( hourFormat ) {
|
2314
|
+
return hourFormatRe(
|
2315
|
+
hourFormat,
|
2316
|
+
gmtFormat,
|
2317
|
+
digitsReSource,
|
2318
|
+
timeSeparator
|
2319
|
+
);
|
2320
|
+
});
|
2321
|
+
|
2322
|
+
/* falls through */
|
2323
|
+
case "X":
|
2324
|
+
case "x":
|
2325
|
+
|
2326
|
+
// x: hourFormat("+HH[mm];-HH[mm]")
|
2327
|
+
// xx: hourFormat("+HHmm;-HHmm")
|
2328
|
+
// xxx: hourFormat("+HH:mm;-HH:mm")
|
2329
|
+
// xxxx: hourFormat("+HHmm[ss];-HHmm[ss]")
|
2330
|
+
// xxxxx: hourFormat("+HH:mm[:ss];-HH:mm[:ss]")
|
2331
|
+
properties.x = [
|
2332
|
+
[ "+HHmm;-HHmm", "+HH;-HH" ],
|
2333
|
+
[ "+HHmm;-HHmm" ],
|
2334
|
+
[ "+HH:mm;-HH:mm" ],
|
2335
|
+
[ "+HHmmss;-HHmmss", "+HHmm;-HHmm" ],
|
2336
|
+
[ "+HH:mm:ss;-HH:mm:ss", "+HH:mm;-HH:mm" ]
|
2337
|
+
][ length - 1 ].map(function( hourFormat ) {
|
2338
|
+
return hourFormatRe( hourFormat );
|
2339
|
+
});
|
2340
|
+
}
|
2341
|
+
});
|
2342
|
+
|
2343
|
+
cldr.off( "get", populateProperties );
|
2344
|
+
|
2345
|
+
return properties;
|
2346
|
+
};
|
2347
|
+
|
2348
|
+
|
2349
|
+
|
2350
|
+
|
2351
|
+
/**
|
2352
|
+
* dayOfWeek( date, firstDay )
|
2353
|
+
*
|
2354
|
+
* @date
|
2355
|
+
*
|
2356
|
+
* @firstDay the result of `dateFirstDayOfWeek( cldr )`
|
2357
|
+
*
|
2358
|
+
* Return the day of the week normalized by the territory's firstDay [0-6].
|
2359
|
+
* Eg for "mon":
|
2360
|
+
* - return 0 if territory is GB, or BR, or DE, or FR (week starts on "mon");
|
2361
|
+
* - return 1 if territory is US (week starts on "sun");
|
2362
|
+
* - return 2 if territory is EG (week starts on "sat");
|
2363
|
+
*/
|
2364
|
+
var dateDayOfWeek = function( date, firstDay ) {
|
2365
|
+
return ( date.getDay() - firstDay + 7 ) % 7;
|
2366
|
+
};
|
2367
|
+
|
2368
|
+
|
2369
|
+
|
2370
|
+
|
2371
|
+
/**
|
2372
|
+
* distanceInDays( from, to )
|
2373
|
+
*
|
2374
|
+
* Return the distance in days between from and to Dates.
|
2375
|
+
*/
|
2376
|
+
var dateDistanceInDays = function( from, to ) {
|
2377
|
+
var inDays = 864e5;
|
2378
|
+
return ( to.getTime() - from.getTime() ) / inDays;
|
2379
|
+
};
|
2380
|
+
|
2381
|
+
|
2382
|
+
|
2383
|
+
|
2384
|
+
/**
|
2385
|
+
* dayOfYear
|
2386
|
+
*
|
2387
|
+
* Return the distance in days of the date to the begin of the year [0-d].
|
2388
|
+
*/
|
2389
|
+
var dateDayOfYear = function( date ) {
|
2390
|
+
return Math.floor( dateDistanceInDays( dateStartOf( date, "year" ), date ) );
|
2391
|
+
};
|
2392
|
+
|
2393
|
+
|
2394
|
+
|
2395
|
+
|
2396
|
+
// Invert key and values, e.g., {"year": "yY"} ==> {"y": "year", "Y": "year"}
|
2397
|
+
var dateFieldsMap = objectInvert({
|
2398
|
+
"era": "G",
|
2399
|
+
"year": "yY",
|
2400
|
+
"quarter": "qQ",
|
2401
|
+
"month": "ML",
|
2402
|
+
"week": "wW",
|
2403
|
+
"day": "dDF",
|
2404
|
+
"weekday": "ecE",
|
2405
|
+
"dayperiod": "a",
|
2406
|
+
"hour": "hHkK",
|
2407
|
+
"minute": "m",
|
2408
|
+
"second": "sSA",
|
2409
|
+
"zone": "zvVOxX"
|
2410
|
+
}, function( object, key, value ) {
|
2411
|
+
value.split( "" ).forEach(function( symbol ) {
|
2412
|
+
object[ symbol ] = key;
|
2413
|
+
});
|
2414
|
+
return object;
|
2415
|
+
});
|
2416
|
+
|
2417
|
+
|
2418
|
+
|
2419
|
+
|
2420
|
+
/**
|
2421
|
+
* millisecondsInDay
|
2422
|
+
*/
|
2423
|
+
var dateMillisecondsInDay = function( date ) {
|
2424
|
+
|
2425
|
+
// TODO Handle daylight savings discontinuities
|
2426
|
+
return date - dateStartOf( date, "day" );
|
2427
|
+
};
|
2428
|
+
|
2429
|
+
|
2430
|
+
|
2431
|
+
|
2432
|
+
/**
|
2433
|
+
* hourFormat( date, format, timeSeparator, formatNumber )
|
2434
|
+
*
|
2435
|
+
* Return date's timezone offset according to the format passed.
|
2436
|
+
* Eg for format when timezone offset is 180:
|
2437
|
+
* - "+H;-H": -3
|
2438
|
+
* - "+HHmm;-HHmm": -0300
|
2439
|
+
* - "+HH:mm;-HH:mm": -03:00
|
2440
|
+
* - "+HH:mm:ss;-HH:mm:ss": -03:00:00
|
2441
|
+
*/
|
2442
|
+
var dateTimezoneHourFormat = function( date, format, timeSeparator, formatNumber ) {
|
2443
|
+
var absOffset,
|
2444
|
+
offset = date.getTimezoneOffset();
|
2445
|
+
|
2446
|
+
absOffset = Math.abs( offset );
|
2447
|
+
formatNumber = formatNumber || {
|
2448
|
+
1: function( value ) {
|
2449
|
+
return stringPad( value, 1 );
|
2450
|
+
},
|
2451
|
+
2: function( value ) {
|
2452
|
+
return stringPad( value, 2 );
|
2453
|
+
}
|
2454
|
+
};
|
2455
|
+
|
2456
|
+
return format
|
2457
|
+
|
2458
|
+
// Pick the correct sign side (+ or -).
|
2459
|
+
.split( ";" )[ offset > 0 ? 1 : 0 ]
|
2460
|
+
|
2461
|
+
// Localize time separator
|
2462
|
+
.replace( ":", timeSeparator )
|
2463
|
+
|
2464
|
+
// Update hours offset.
|
2465
|
+
.replace( /HH?/, function( match ) {
|
2466
|
+
return formatNumber[ match.length ]( Math.floor( absOffset / 60 ) );
|
2467
|
+
})
|
2468
|
+
|
2469
|
+
// Update minutes offset and return.
|
2470
|
+
.replace( /mm/, function() {
|
2471
|
+
return formatNumber[ 2 ]( Math.floor( absOffset % 60 ) );
|
2472
|
+
})
|
2473
|
+
|
2474
|
+
// Update minutes offset and return.
|
2475
|
+
.replace( /ss/, function() {
|
2476
|
+
return formatNumber[ 2 ]( Math.floor( absOffset % 1 * 60 ) );
|
2477
|
+
});
|
2478
|
+
};
|
2479
|
+
|
2480
|
+
|
2481
|
+
|
2482
|
+
|
2483
|
+
/**
|
2484
|
+
* format( date, properties )
|
2485
|
+
*
|
2486
|
+
* @date [Date instance].
|
2487
|
+
*
|
2488
|
+
* @properties
|
2489
|
+
*
|
2490
|
+
* TODO Support other calendar types.
|
2491
|
+
*
|
2492
|
+
* Disclosure: this function borrows excerpts of dojo/date/locale.
|
2493
|
+
*/
|
2494
|
+
var dateFormat = function( date, numberFormatters, properties ) {
|
2495
|
+
var parts = [];
|
2496
|
+
|
2497
|
+
var timeSeparator = properties.timeSeparator;
|
2498
|
+
|
2499
|
+
// create globalize date with given timezone data
|
2500
|
+
if ( properties.timeZoneData ) {
|
2501
|
+
date = new ZonedDateTime( date, properties.timeZoneData() );
|
2502
|
+
}
|
2503
|
+
|
2504
|
+
properties.pattern.replace( datePatternRe, function( current ) {
|
2505
|
+
var aux, dateField, type, value,
|
2506
|
+
chr = current.charAt( 0 ),
|
2507
|
+
length = current.length;
|
2508
|
+
|
2509
|
+
if ( chr === "j" ) {
|
2510
|
+
|
2511
|
+
// Locale preferred hHKk.
|
2512
|
+
// http://www.unicode.org/reports/tr35/tr35-dates.html#Time_Data
|
2513
|
+
chr = properties.preferredTime;
|
2514
|
+
}
|
2515
|
+
|
2516
|
+
if ( chr === "Z" ) {
|
2517
|
+
|
2518
|
+
// Z..ZZZ: same as "xxxx".
|
2519
|
+
if ( length < 4 ) {
|
2520
|
+
chr = "x";
|
2521
|
+
length = 4;
|
2522
|
+
|
2523
|
+
// ZZZZ: same as "OOOO".
|
2524
|
+
} else if ( length < 5 ) {
|
2525
|
+
chr = "O";
|
2526
|
+
length = 4;
|
2527
|
+
|
2528
|
+
// ZZZZZ: same as "XXXXX"
|
2529
|
+
} else {
|
2530
|
+
chr = "X";
|
2531
|
+
length = 5;
|
2532
|
+
}
|
2533
|
+
}
|
2534
|
+
|
2535
|
+
// z...zzz: "{shortRegion}", e.g., "PST" or "PDT".
|
2536
|
+
// zzzz: "{regionName} {Standard Time}" or "{regionName} {Daylight Time}",
|
2537
|
+
// e.g., "Pacific Standard Time" or "Pacific Daylight Time".
|
2538
|
+
if ( chr === "z" ) {
|
2539
|
+
if ( date.isDST ) {
|
2540
|
+
value = date.isDST() ? properties.daylightTzName : properties.standardTzName;
|
2541
|
+
}
|
2542
|
+
|
2543
|
+
// Fall back to "O" format.
|
2544
|
+
if ( !value ) {
|
2545
|
+
chr = "O";
|
2546
|
+
if ( length < 4 ) {
|
2547
|
+
length = 1;
|
2548
|
+
}
|
2549
|
+
}
|
2550
|
+
}
|
2551
|
+
|
2552
|
+
switch ( chr ) {
|
2553
|
+
|
2554
|
+
// Era
|
2555
|
+
case "G":
|
2556
|
+
value = properties.eras[ date.getFullYear() < 0 ? 0 : 1 ];
|
2557
|
+
break;
|
2558
|
+
|
2559
|
+
// Year
|
2560
|
+
case "y":
|
2561
|
+
|
2562
|
+
// Plain year.
|
2563
|
+
// The length specifies the padding, but for two letters it also specifies the
|
2564
|
+
// maximum length.
|
2565
|
+
value = date.getFullYear();
|
2566
|
+
if ( length === 2 ) {
|
2567
|
+
value = String( value );
|
2568
|
+
value = +value.substr( value.length - 2 );
|
2569
|
+
}
|
2570
|
+
break;
|
2571
|
+
|
2572
|
+
case "Y":
|
2573
|
+
|
2574
|
+
// Year in "Week of Year"
|
2575
|
+
// The length specifies the padding, but for two letters it also specifies the
|
2576
|
+
// maximum length.
|
2577
|
+
// yearInWeekofYear = date + DaysInAWeek - (dayOfWeek - firstDay) - minDays
|
2578
|
+
value = new Date( date.getTime() );
|
2579
|
+
value.setDate(
|
2580
|
+
value.getDate() + 7 -
|
2581
|
+
dateDayOfWeek( date, properties.firstDay ) -
|
2582
|
+
properties.firstDay -
|
2583
|
+
properties.minDays
|
2584
|
+
);
|
2585
|
+
value = value.getFullYear();
|
2586
|
+
if ( length === 2 ) {
|
2587
|
+
value = String( value );
|
2588
|
+
value = +value.substr( value.length - 2 );
|
2589
|
+
}
|
2590
|
+
break;
|
2591
|
+
|
2592
|
+
// Quarter
|
2593
|
+
case "Q":
|
2594
|
+
case "q":
|
2595
|
+
value = Math.ceil( ( date.getMonth() + 1 ) / 3 );
|
2596
|
+
if ( length > 2 ) {
|
2597
|
+
value = properties.quarters[ chr ][ length ][ value ];
|
2598
|
+
}
|
2599
|
+
break;
|
2600
|
+
|
2601
|
+
// Month
|
2602
|
+
case "M":
|
2603
|
+
case "L":
|
2604
|
+
value = date.getMonth() + 1;
|
2605
|
+
if ( length > 2 ) {
|
2606
|
+
value = properties.months[ chr ][ length ][ value ];
|
2607
|
+
}
|
2608
|
+
break;
|
2609
|
+
|
2610
|
+
// Week
|
2611
|
+
case "w":
|
2612
|
+
|
2613
|
+
// Week of Year.
|
2614
|
+
// woy = ceil( ( doy + dow of 1/1 ) / 7 ) - minDaysStuff ? 1 : 0.
|
2615
|
+
// TODO should pad on ww? Not documented, but I guess so.
|
2616
|
+
value = dateDayOfWeek( dateStartOf( date, "year" ), properties.firstDay );
|
2617
|
+
value = Math.ceil( ( dateDayOfYear( date ) + value ) / 7 ) -
|
2618
|
+
( 7 - value >= properties.minDays ? 0 : 1 );
|
2619
|
+
break;
|
2620
|
+
|
2621
|
+
case "W":
|
2622
|
+
|
2623
|
+
// Week of Month.
|
2624
|
+
// wom = ceil( ( dom + dow of `1/month` ) / 7 ) - minDaysStuff ? 1 : 0.
|
2625
|
+
value = dateDayOfWeek( dateStartOf( date, "month" ), properties.firstDay );
|
2626
|
+
value = Math.ceil( ( date.getDate() + value ) / 7 ) -
|
2627
|
+
( 7 - value >= properties.minDays ? 0 : 1 );
|
2628
|
+
break;
|
2629
|
+
|
2630
|
+
// Day
|
2631
|
+
case "d":
|
2632
|
+
value = date.getDate();
|
2633
|
+
break;
|
2634
|
+
|
2635
|
+
case "D":
|
2636
|
+
value = dateDayOfYear( date ) + 1;
|
2637
|
+
break;
|
2638
|
+
|
2639
|
+
case "F":
|
2640
|
+
|
2641
|
+
// Day of Week in month. eg. 2nd Wed in July.
|
2642
|
+
value = Math.floor( date.getDate() / 7 ) + 1;
|
2643
|
+
break;
|
2644
|
+
|
2645
|
+
// Week day
|
2646
|
+
case "e":
|
2647
|
+
case "c":
|
2648
|
+
if ( length <= 2 ) {
|
2649
|
+
|
2650
|
+
// Range is [1-7] (deduced by example provided on documentation)
|
2651
|
+
// TODO Should pad with zeros (not specified in the docs)?
|
2652
|
+
value = dateDayOfWeek( date, properties.firstDay ) + 1;
|
2653
|
+
break;
|
2654
|
+
}
|
2655
|
+
|
2656
|
+
/* falls through */
|
2657
|
+
case "E":
|
2658
|
+
value = dateWeekDays[ date.getDay() ];
|
2659
|
+
value = properties.days[ chr ][ length ][ value ];
|
2660
|
+
break;
|
2661
|
+
|
2662
|
+
// Period (AM or PM)
|
2663
|
+
case "a":
|
2664
|
+
value = properties.dayPeriods[ date.getHours() < 12 ? "am" : "pm" ];
|
2665
|
+
break;
|
2666
|
+
|
2667
|
+
// Hour
|
2668
|
+
case "h": // 1-12
|
2669
|
+
value = ( date.getHours() % 12 ) || 12;
|
2670
|
+
break;
|
2671
|
+
|
2672
|
+
case "H": // 0-23
|
2673
|
+
value = date.getHours();
|
2674
|
+
break;
|
2675
|
+
|
2676
|
+
case "K": // 0-11
|
2677
|
+
value = date.getHours() % 12;
|
2678
|
+
break;
|
2679
|
+
|
2680
|
+
case "k": // 1-24
|
2681
|
+
value = date.getHours() || 24;
|
2682
|
+
break;
|
2683
|
+
|
2684
|
+
// Minute
|
2685
|
+
case "m":
|
2686
|
+
value = date.getMinutes();
|
2687
|
+
break;
|
2688
|
+
|
2689
|
+
// Second
|
2690
|
+
case "s":
|
2691
|
+
value = date.getSeconds();
|
2692
|
+
break;
|
2693
|
+
|
2694
|
+
case "S":
|
2695
|
+
value = Math.round( date.getMilliseconds() * Math.pow( 10, length - 3 ) );
|
2696
|
+
break;
|
2697
|
+
|
2698
|
+
case "A":
|
2699
|
+
value = Math.round( dateMillisecondsInDay( date ) * Math.pow( 10, length - 3 ) );
|
2700
|
+
break;
|
2701
|
+
|
2702
|
+
// Zone
|
2703
|
+
case "z":
|
2704
|
+
break;
|
2705
|
+
|
2706
|
+
case "v":
|
2707
|
+
|
2708
|
+
// v...vvv: "{shortRegion}", eg. "PT".
|
2709
|
+
// vvvv: "{regionName} {Time}",
|
2710
|
+
// e.g., "Pacific Time".
|
2711
|
+
if ( properties.genericTzName ) {
|
2712
|
+
value = properties.genericTzName;
|
2713
|
+
break;
|
2714
|
+
}
|
2715
|
+
|
2716
|
+
/* falls through */
|
2717
|
+
case "V":
|
2718
|
+
|
2719
|
+
//VVVV: "{explarCity} {Time}", e.g., "Los Angeles Time"
|
2720
|
+
if ( properties.timeZoneName ) {
|
2721
|
+
value = properties.timeZoneName;
|
2722
|
+
break;
|
2723
|
+
}
|
2724
|
+
|
2725
|
+
if ( current === "v" ) {
|
2726
|
+
length = 1;
|
2727
|
+
}
|
2728
|
+
|
2729
|
+
/* falls through */
|
2730
|
+
case "O":
|
2731
|
+
|
2732
|
+
// O: "{gmtFormat}+H;{gmtFormat}-H" or "{gmtZeroFormat}", eg. "GMT-8" or "GMT".
|
2733
|
+
// OOOO: "{gmtFormat}{hourFormat}" or "{gmtZeroFormat}", eg. "GMT-08:00" or "GMT".
|
2734
|
+
if ( date.getTimezoneOffset() === 0 ) {
|
2735
|
+
value = properties.gmtZeroFormat;
|
2736
|
+
} else {
|
2737
|
+
|
2738
|
+
// If O..OOO and timezone offset has non-zero minutes, show minutes.
|
2739
|
+
if ( length < 4 ) {
|
2740
|
+
aux = date.getTimezoneOffset();
|
2741
|
+
aux = properties.hourFormat[ aux % 60 - aux % 1 === 0 ? 0 : 1 ];
|
2742
|
+
} else {
|
2743
|
+
aux = properties.hourFormat;
|
2744
|
+
}
|
2745
|
+
|
2746
|
+
value = dateTimezoneHourFormat(
|
2747
|
+
date,
|
2748
|
+
aux,
|
2749
|
+
timeSeparator,
|
2750
|
+
numberFormatters
|
2751
|
+
);
|
2752
|
+
value = properties.gmtFormat.replace( /\{0\}/, value );
|
2753
|
+
}
|
2754
|
+
break;
|
2755
|
+
|
2756
|
+
case "X":
|
2757
|
+
|
2758
|
+
// Same as x*, except it uses "Z" for zero offset.
|
2759
|
+
if ( date.getTimezoneOffset() === 0 ) {
|
2760
|
+
value = "Z";
|
2761
|
+
break;
|
2762
|
+
}
|
2763
|
+
|
2764
|
+
/* falls through */
|
2765
|
+
case "x":
|
2766
|
+
|
2767
|
+
// x: hourFormat("+HH[mm];-HH[mm]")
|
2768
|
+
// xx: hourFormat("+HHmm;-HHmm")
|
2769
|
+
// xxx: hourFormat("+HH:mm;-HH:mm")
|
2770
|
+
// xxxx: hourFormat("+HHmm[ss];-HHmm[ss]")
|
2771
|
+
// xxxxx: hourFormat("+HH:mm[:ss];-HH:mm[:ss]")
|
2772
|
+
aux = date.getTimezoneOffset();
|
2773
|
+
|
2774
|
+
// If x and timezone offset has non-zero minutes, use xx (i.e., show minutes).
|
2775
|
+
if ( length === 1 && aux % 60 - aux % 1 !== 0 ) {
|
2776
|
+
length += 1;
|
2777
|
+
}
|
2778
|
+
|
2779
|
+
// If (xxxx or xxxxx) and timezone offset has zero seconds, use xx or xxx
|
2780
|
+
// respectively (i.e., don't show optional seconds).
|
2781
|
+
if ( ( length === 4 || length === 5 ) && aux % 1 === 0 ) {
|
2782
|
+
length -= 2;
|
2783
|
+
}
|
2784
|
+
|
2785
|
+
value = [
|
2786
|
+
"+HH;-HH",
|
2787
|
+
"+HHmm;-HHmm",
|
2788
|
+
"+HH:mm;-HH:mm",
|
2789
|
+
"+HHmmss;-HHmmss",
|
2790
|
+
"+HH:mm:ss;-HH:mm:ss"
|
2791
|
+
][ length - 1 ];
|
2792
|
+
|
2793
|
+
value = dateTimezoneHourFormat( date, value, ":" );
|
2794
|
+
break;
|
2795
|
+
|
2796
|
+
// timeSeparator
|
2797
|
+
case ":":
|
2798
|
+
value = timeSeparator;
|
2799
|
+
break;
|
2800
|
+
|
2801
|
+
// ' literals.
|
2802
|
+
case "'":
|
2803
|
+
value = removeLiteralQuotes( current );
|
2804
|
+
break;
|
2805
|
+
|
2806
|
+
// Anything else is considered a literal, including [ ,:/.@#], chinese, japonese, and
|
2807
|
+
// arabic characters.
|
2808
|
+
default:
|
2809
|
+
value = current;
|
2810
|
+
|
2811
|
+
}
|
2812
|
+
if ( typeof value === "number" ) {
|
2813
|
+
value = numberFormatters[ length ]( value );
|
2814
|
+
}
|
2815
|
+
|
2816
|
+
dateField = dateFieldsMap[ chr ];
|
2817
|
+
type = dateField ? dateField : "literal";
|
2818
|
+
|
2819
|
+
partsPush( parts, type, value );
|
2820
|
+
});
|
2821
|
+
|
2822
|
+
return parts;
|
2823
|
+
|
2824
|
+
};
|
2825
|
+
|
2826
|
+
|
2827
|
+
|
2828
|
+
|
2829
|
+
var dateToPartsFormatterFn = function( numberFormatters, properties ) {
|
2830
|
+
return function dateToPartsFormatter( value ) {
|
2831
|
+
validateParameterPresence( value, "value" );
|
2832
|
+
validateParameterTypeDate( value, "value" );
|
2833
|
+
|
2834
|
+
return dateFormat( value, numberFormatters, properties );
|
2835
|
+
};
|
2836
|
+
|
2837
|
+
};
|
2838
|
+
|
2839
|
+
|
2840
|
+
|
2841
|
+
|
2842
|
+
function optionsHasStyle( options ) {
|
2843
|
+
return options.skeleton !== undefined ||
|
2844
|
+
options.date !== undefined ||
|
2845
|
+
options.time !== undefined ||
|
2846
|
+
options.datetime !== undefined ||
|
2847
|
+
options.raw !== undefined;
|
2848
|
+
}
|
2849
|
+
|
2850
|
+
function validateRequiredCldr( path, value ) {
|
2851
|
+
validateCldr( path, value, {
|
2852
|
+
skip: [
|
2853
|
+
/dates\/calendars\/gregorian\/dateTimeFormats\/availableFormats/,
|
2854
|
+
/dates\/calendars\/gregorian\/days\/.*\/short/,
|
2855
|
+
/dates\/timeZoneNames\/zone/,
|
2856
|
+
/dates\/timeZoneNames\/metazone/,
|
2857
|
+
/globalize-iana/,
|
2858
|
+
/supplemental\/metaZones/,
|
2859
|
+
/supplemental\/timeData\/(?!001)/,
|
2860
|
+
/supplemental\/weekData\/(?!001)/
|
2861
|
+
]
|
2862
|
+
});
|
2863
|
+
}
|
2864
|
+
|
2865
|
+
function validateOptionsPreset( options ) {
|
2866
|
+
validateOptionsPresetEach( "date", options );
|
2867
|
+
validateOptionsPresetEach( "time", options );
|
2868
|
+
validateOptionsPresetEach( "datetime", options );
|
2869
|
+
}
|
2870
|
+
|
2871
|
+
function validateOptionsPresetEach( type, options ) {
|
2872
|
+
var value = options[ type ];
|
2873
|
+
validate(
|
2874
|
+
"E_INVALID_OPTIONS",
|
2875
|
+
"Invalid `{{type}: \"{value}\"}`.",
|
2876
|
+
value === undefined || [ "short", "medium", "long", "full" ].indexOf( value ) !== -1,
|
2877
|
+
{ type: type, value: value }
|
2878
|
+
);
|
2879
|
+
}
|
2880
|
+
|
2881
|
+
function validateOptionsSkeleton( pattern, skeleton ) {
|
2882
|
+
validate(
|
2883
|
+
"E_INVALID_OPTIONS",
|
2884
|
+
"Invalid `{skeleton: \"{value}\"}` based on provided CLDR.",
|
2885
|
+
skeleton === undefined || ( typeof pattern === "string" && pattern ),
|
2886
|
+
{ type: "skeleton", value: skeleton }
|
2887
|
+
);
|
2888
|
+
}
|
2889
|
+
|
2890
|
+
function validateRequiredIana( timeZone ) {
|
2891
|
+
return function( path, value ) {
|
2892
|
+
|
2893
|
+
if ( !/globalize-iana/.test( path ) ) {
|
2894
|
+
return;
|
2895
|
+
}
|
2896
|
+
|
2897
|
+
validate(
|
2898
|
+
"E_MISSING_IANA_TZ",
|
2899
|
+
"Missing required IANA timezone content for `{timeZone}`: `{path}`.",
|
2900
|
+
value,
|
2901
|
+
{
|
2902
|
+
path: path.replace( /globalize-iana\//, "" ),
|
2903
|
+
timeZone: timeZone
|
2904
|
+
}
|
2905
|
+
);
|
2906
|
+
};
|
2907
|
+
}
|
2908
|
+
|
2909
|
+
/**
|
2910
|
+
* .loadTimeZone( json )
|
2911
|
+
*
|
2912
|
+
* @json [JSON]
|
2913
|
+
*
|
2914
|
+
* Load IANA timezone data.
|
2915
|
+
*/
|
2916
|
+
Globalize.loadTimeZone = function( json ) {
|
2917
|
+
var customData = {
|
2918
|
+
"globalize-iana": json
|
2919
|
+
};
|
2920
|
+
|
2921
|
+
validateParameterPresence( json, "json" );
|
2922
|
+
validateParameterTypePlainObject( json, "json" );
|
2923
|
+
|
2924
|
+
Cldr.load( customData );
|
2925
|
+
};
|
2926
|
+
|
2927
|
+
/**
|
2928
|
+
* .dateFormatter( options )
|
2929
|
+
*
|
2930
|
+
* @options [Object] see date/expand_pattern for more info.
|
2931
|
+
*
|
2932
|
+
* Return a date formatter function (of the form below) according to the given options and the
|
2933
|
+
* default/instance locale.
|
2934
|
+
*
|
2935
|
+
* fn( value )
|
2936
|
+
*
|
2937
|
+
* @value [Date]
|
2938
|
+
*
|
2939
|
+
* Return a function that formats a date according to the given `format` and the default/instance
|
2940
|
+
* locale.
|
2941
|
+
*/
|
2942
|
+
Globalize.dateFormatter =
|
2943
|
+
Globalize.prototype.dateFormatter = function( options ) {
|
2944
|
+
var args, dateToPartsFormatter, returnFn;
|
2945
|
+
|
2946
|
+
validateParameterTypePlainObject( options, "options" );
|
2947
|
+
|
2948
|
+
options = options || {};
|
2949
|
+
if ( !optionsHasStyle( options ) ) {
|
2950
|
+
options.skeleton = "yMd";
|
2951
|
+
}
|
2952
|
+
args = [ options ];
|
2953
|
+
|
2954
|
+
dateToPartsFormatter = this.dateToPartsFormatter( options );
|
2955
|
+
returnFn = dateFormatterFn( dateToPartsFormatter );
|
2956
|
+
runtimeBind( args, this.cldr, returnFn, [ dateToPartsFormatter ] );
|
2957
|
+
|
2958
|
+
return returnFn;
|
2959
|
+
};
|
2960
|
+
|
2961
|
+
/**
|
2962
|
+
* .dateToPartsFormatter( options )
|
2963
|
+
*
|
2964
|
+
* @options [Object] see date/expand_pattern for more info.
|
2965
|
+
*
|
2966
|
+
* Return a date formatter function (of the form below) according to the given options and the
|
2967
|
+
* default/instance locale.
|
2968
|
+
*
|
2969
|
+
* fn( value )
|
2970
|
+
*
|
2971
|
+
* @value [Date]
|
2972
|
+
*
|
2973
|
+
* Return a function that formats a date to parts according to the given `format`
|
2974
|
+
* and the default/instance
|
2975
|
+
* locale.
|
2976
|
+
*/
|
2977
|
+
Globalize.dateToPartsFormatter =
|
2978
|
+
Globalize.prototype.dateToPartsFormatter = function( options ) {
|
2979
|
+
var args, cldr, numberFormatters, pad, pattern, properties, returnFn,
|
2980
|
+
timeZone, ianaListener;
|
2981
|
+
|
2982
|
+
validateParameterTypePlainObject( options, "options" );
|
2983
|
+
|
2984
|
+
cldr = this.cldr;
|
2985
|
+
options = options || {};
|
2986
|
+
if ( !optionsHasStyle( options ) ) {
|
2987
|
+
options.skeleton = "yMd";
|
2988
|
+
}
|
2989
|
+
|
2990
|
+
validateOptionsPreset( options );
|
2991
|
+
validateDefaultLocale( cldr );
|
2992
|
+
|
2993
|
+
timeZone = options.timeZone;
|
2994
|
+
validateParameterTypeString( timeZone, "options.timeZone" );
|
2995
|
+
|
2996
|
+
args = [ options ];
|
2997
|
+
|
2998
|
+
cldr.on( "get", validateRequiredCldr );
|
2999
|
+
if ( timeZone ) {
|
3000
|
+
ianaListener = validateRequiredIana( timeZone );
|
3001
|
+
cldr.on( "get", ianaListener );
|
3002
|
+
}
|
3003
|
+
try {
|
3004
|
+
pattern = dateExpandPattern( options, cldr );
|
3005
|
+
validateOptionsSkeleton( pattern, options.skeleton );
|
3006
|
+
properties = dateFormatProperties( pattern, cldr, timeZone );
|
3007
|
+
} finally {
|
3008
|
+
cldr.off( "get", validateRequiredCldr );
|
3009
|
+
if ( ianaListener ) {
|
3010
|
+
cldr.off( "get", ianaListener );
|
3011
|
+
}
|
3012
|
+
}
|
3013
|
+
|
3014
|
+
// Create needed number formatters.
|
3015
|
+
numberFormatters = properties.numberFormatters;
|
3016
|
+
delete properties.numberFormatters;
|
3017
|
+
for ( pad in numberFormatters ) {
|
3018
|
+
numberFormatters[ pad ] = this.numberFormatter({
|
3019
|
+
raw: numberFormatters[ pad ]
|
3020
|
+
});
|
3021
|
+
}
|
3022
|
+
|
3023
|
+
returnFn = dateToPartsFormatterFn( numberFormatters, properties );
|
3024
|
+
|
3025
|
+
runtimeBind( args, cldr, returnFn, [ numberFormatters, properties ] );
|
3026
|
+
|
3027
|
+
return returnFn;
|
3028
|
+
};
|
3029
|
+
|
3030
|
+
/**
|
3031
|
+
* .dateParser( options )
|
3032
|
+
*
|
3033
|
+
* @options [Object] see date/expand_pattern for more info.
|
3034
|
+
*
|
3035
|
+
* Return a function that parses a string date according to the given `formats` and the
|
3036
|
+
* default/instance locale.
|
3037
|
+
*/
|
3038
|
+
Globalize.dateParser =
|
3039
|
+
Globalize.prototype.dateParser = function( options ) {
|
3040
|
+
var args, cldr, numberParser, parseProperties, pattern, returnFn, timeZone,
|
3041
|
+
tokenizerProperties;
|
3042
|
+
|
3043
|
+
validateParameterTypePlainObject( options, "options" );
|
3044
|
+
|
3045
|
+
cldr = this.cldr;
|
3046
|
+
options = options || {};
|
3047
|
+
if ( !optionsHasStyle( options ) ) {
|
3048
|
+
options.skeleton = "yMd";
|
3049
|
+
}
|
3050
|
+
|
3051
|
+
validateOptionsPreset( options );
|
3052
|
+
validateDefaultLocale( cldr );
|
3053
|
+
|
3054
|
+
timeZone = options.timeZone;
|
3055
|
+
validateParameterTypeString( timeZone, "options.timeZone" );
|
3056
|
+
|
3057
|
+
args = [ options ];
|
3058
|
+
|
3059
|
+
try {
|
3060
|
+
cldr.on( "get", validateRequiredCldr );
|
3061
|
+
if ( timeZone ) {
|
3062
|
+
cldr.on( "get", validateRequiredIana( timeZone ) );
|
3063
|
+
}
|
3064
|
+
pattern = dateExpandPattern( options, cldr );
|
3065
|
+
validateOptionsSkeleton( pattern, options.skeleton );
|
3066
|
+
tokenizerProperties = dateTokenizerProperties( pattern, cldr, timeZone );
|
3067
|
+
parseProperties = dateParseProperties( cldr, timeZone );
|
3068
|
+
} finally {
|
3069
|
+
cldr.off( "get", validateRequiredCldr );
|
3070
|
+
if ( timeZone ) {
|
3071
|
+
cldr.off( "get", validateRequiredIana( timeZone ) );
|
3072
|
+
}
|
3073
|
+
}
|
3074
|
+
numberParser = this.numberParser({ raw: "0" });
|
3075
|
+
|
3076
|
+
returnFn = dateParserFn( numberParser, parseProperties, tokenizerProperties );
|
3077
|
+
|
3078
|
+
runtimeBind( args, cldr, returnFn, [ numberParser, parseProperties, tokenizerProperties ] );
|
3079
|
+
|
3080
|
+
return returnFn;
|
3081
|
+
};
|
3082
|
+
|
3083
|
+
/**
|
3084
|
+
* .formatDate( value, options )
|
3085
|
+
*
|
3086
|
+
* @value [Date]
|
3087
|
+
*
|
3088
|
+
* @options [Object] see date/expand_pattern for more info.
|
3089
|
+
*
|
3090
|
+
* Formats a date or number according to the given options string and the default/instance locale.
|
3091
|
+
*/
|
3092
|
+
Globalize.formatDate =
|
3093
|
+
Globalize.prototype.formatDate = function( value, options ) {
|
3094
|
+
validateParameterPresence( value, "value" );
|
3095
|
+
validateParameterTypeDate( value, "value" );
|
3096
|
+
|
3097
|
+
return this.dateFormatter( options )( value );
|
3098
|
+
};
|
3099
|
+
|
3100
|
+
/**
|
3101
|
+
* .formatDateToParts( value, options )
|
3102
|
+
*
|
3103
|
+
* @value [Date]
|
3104
|
+
*
|
3105
|
+
* @options [Object] see date/expand_pattern for more info.
|
3106
|
+
*
|
3107
|
+
* Formats a date or number to parts according to the given options and the default/instance locale.
|
3108
|
+
*/
|
3109
|
+
Globalize.formatDateToParts =
|
3110
|
+
Globalize.prototype.formatDateToParts = function( value, options ) {
|
3111
|
+
validateParameterPresence( value, "value" );
|
3112
|
+
validateParameterTypeDate( value, "value" );
|
3113
|
+
|
3114
|
+
return this.dateToPartsFormatter( options )( value );
|
3115
|
+
};
|
3116
|
+
|
3117
|
+
/**
|
3118
|
+
* .parseDate( value, options )
|
3119
|
+
*
|
3120
|
+
* @value [String]
|
3121
|
+
*
|
3122
|
+
* @options [Object] see date/expand_pattern for more info.
|
3123
|
+
*
|
3124
|
+
* Return a Date instance or null.
|
3125
|
+
*/
|
3126
|
+
Globalize.parseDate =
|
3127
|
+
Globalize.prototype.parseDate = function( value, options ) {
|
3128
|
+
validateParameterPresence( value, "value" );
|
3129
|
+
validateParameterTypeString( value, "value" );
|
3130
|
+
|
3131
|
+
return this.dateParser( options )( value );
|
3132
|
+
};
|
3133
|
+
|
3134
|
+
return Globalize;
|
3135
|
+
|
3136
|
+
|
3137
|
+
|
3138
|
+
|
3139
|
+
}));
|