]>
Commit | Line | Data |
---|---|---|
f0cfe83e AD |
1 | define("dojo/date/locale", [ |
2 | "../_base/lang", | |
3 | "../_base/array", | |
4 | "../date", | |
5 | /*===== "../_base/declare", =====*/ | |
6 | "../cldr/supplemental", | |
7 | "../i18n", | |
8 | "../regexp", | |
9 | "../string", | |
10 | "../i18n!../cldr/nls/gregorian", | |
11 | "module" | |
12 | ], function(lang, array, date, /*===== declare, =====*/ supplemental, i18n, regexp, string, gregorian, module){ | |
13 | ||
14 | // module: | |
15 | // dojo/date/locale | |
16 | ||
17 | var exports = { | |
18 | // summary: | |
19 | // This modules defines dojo/date/locale, localization methods for Date. | |
20 | }; | |
21 | lang.setObject(module.id.replace(/\//g, "."), exports); | |
22 | ||
23 | // Localization methods for Date. Honor local customs using locale-dependent dojo.cldr data. | |
24 | ||
25 | // Load the bundles containing localization information for | |
26 | // names and formats | |
27 | ||
28 | //NOTE: Everything in this module assumes Gregorian calendars. | |
29 | // Other calendars will be implemented in separate modules. | |
30 | ||
31 | // Format a pattern without literals | |
32 | function formatPattern(dateObject, bundle, options, pattern){ | |
33 | return pattern.replace(/([a-z])\1*/ig, function(match){ | |
34 | var s, pad, | |
35 | c = match.charAt(0), | |
36 | l = match.length, | |
37 | widthList = ["abbr", "wide", "narrow"]; | |
38 | switch(c){ | |
39 | case 'G': | |
40 | s = bundle[(l < 4) ? "eraAbbr" : "eraNames"][dateObject.getFullYear() < 0 ? 0 : 1]; | |
41 | break; | |
42 | case 'y': | |
43 | s = dateObject.getFullYear(); | |
44 | switch(l){ | |
45 | case 1: | |
46 | break; | |
47 | case 2: | |
48 | if(!options.fullYear){ | |
49 | s = String(s); s = s.substr(s.length - 2); | |
50 | break; | |
51 | } | |
52 | // fallthrough | |
53 | default: | |
54 | pad = true; | |
55 | } | |
56 | break; | |
57 | case 'Q': | |
58 | case 'q': | |
59 | s = Math.ceil((dateObject.getMonth()+1)/3); | |
60 | // switch(l){ | |
61 | // case 1: case 2: | |
62 | pad = true; | |
63 | // break; | |
64 | // case 3: case 4: // unimplemented | |
65 | // } | |
66 | break; | |
67 | case 'M': | |
68 | case 'L': | |
69 | var m = dateObject.getMonth(); | |
70 | if(l<3){ | |
71 | s = m+1; pad = true; | |
72 | }else{ | |
73 | var propM = [ | |
74 | "months", | |
75 | c == 'L' ? "standAlone" : "format", | |
76 | widthList[l-3] | |
77 | ].join("-"); | |
78 | s = bundle[propM][m]; | |
79 | } | |
80 | break; | |
81 | case 'w': | |
82 | var firstDay = 0; | |
83 | s = exports._getWeekOfYear(dateObject, firstDay); pad = true; | |
84 | break; | |
85 | case 'd': | |
86 | s = dateObject.getDate(); pad = true; | |
87 | break; | |
88 | case 'D': | |
89 | s = exports._getDayOfYear(dateObject); pad = true; | |
90 | break; | |
91 | case 'e': | |
92 | case 'c': | |
93 | var d = dateObject.getDay(); | |
94 | if(l<2){ | |
95 | s = (d - supplemental.getFirstDayOfWeek(options.locale) + 8) % 7 | |
96 | break; | |
97 | } | |
98 | // fallthrough | |
99 | case 'E': | |
100 | d = dateObject.getDay(); | |
101 | if(l<3){ | |
102 | s = d+1; pad = true; | |
103 | }else{ | |
104 | var propD = [ | |
105 | "days", | |
106 | c == 'c' ? "standAlone" : "format", | |
107 | widthList[l-3] | |
108 | ].join("-"); | |
109 | s = bundle[propD][d]; | |
110 | } | |
111 | break; | |
112 | case 'a': | |
113 | var timePeriod = dateObject.getHours() < 12 ? 'am' : 'pm'; | |
114 | s = options[timePeriod] || bundle['dayPeriods-format-wide-' + timePeriod]; | |
115 | break; | |
116 | case 'h': | |
117 | case 'H': | |
118 | case 'K': | |
119 | case 'k': | |
120 | var h = dateObject.getHours(); | |
121 | // strange choices in the date format make it impossible to write this succinctly | |
122 | switch (c){ | |
123 | case 'h': // 1-12 | |
124 | s = (h % 12) || 12; | |
125 | break; | |
126 | case 'H': // 0-23 | |
127 | s = h; | |
128 | break; | |
129 | case 'K': // 0-11 | |
130 | s = (h % 12); | |
131 | break; | |
132 | case 'k': // 1-24 | |
133 | s = h || 24; | |
134 | break; | |
135 | } | |
136 | pad = true; | |
137 | break; | |
138 | case 'm': | |
139 | s = dateObject.getMinutes(); pad = true; | |
140 | break; | |
141 | case 's': | |
142 | s = dateObject.getSeconds(); pad = true; | |
143 | break; | |
144 | case 'S': | |
145 | s = Math.round(dateObject.getMilliseconds() * Math.pow(10, l-3)); pad = true; | |
146 | break; | |
147 | case 'v': // FIXME: don't know what this is. seems to be same as z? | |
148 | case 'z': | |
149 | // We only have one timezone to offer; the one from the browser | |
150 | s = exports._getZone(dateObject, true, options); | |
151 | if(s){break;} | |
152 | l=4; | |
153 | // fallthrough... use GMT if tz not available | |
154 | case 'Z': | |
155 | var offset = exports._getZone(dateObject, false, options); | |
156 | var tz = [ | |
157 | (offset<=0 ? "+" : "-"), | |
158 | string.pad(Math.floor(Math.abs(offset)/60), 2), | |
159 | string.pad(Math.abs(offset)% 60, 2) | |
160 | ]; | |
161 | if(l==4){ | |
162 | tz.splice(0, 0, "GMT"); | |
163 | tz.splice(3, 0, ":"); | |
164 | } | |
165 | s = tz.join(""); | |
166 | break; | |
167 | // case 'Y': case 'u': case 'W': case 'F': case 'g': case 'A': | |
168 | // console.log(match+" modifier unimplemented"); | |
169 | default: | |
170 | throw new Error("dojo.date.locale.format: invalid pattern char: "+pattern); | |
171 | } | |
172 | if(pad){ s = string.pad(s, l); } | |
173 | return s; | |
174 | }); | |
175 | } | |
176 | ||
177 | /*===== | |
178 | var __FormatOptions = exports.__FormatOptions = declare(null, { | |
179 | // selector: String | |
180 | // choice of 'time','date' (default: date and time) | |
181 | // formatLength: String | |
182 | // choice of long, short, medium or full (plus any custom additions). Defaults to 'short' | |
183 | // datePattern:String | |
184 | // override pattern with this string | |
185 | // timePattern:String | |
186 | // override pattern with this string | |
187 | // am: String | |
188 | // override strings for am in times | |
189 | // pm: String | |
190 | // override strings for pm in times | |
191 | // locale: String | |
192 | // override the locale used to determine formatting rules | |
193 | // fullYear: Boolean | |
194 | // (format only) use 4 digit years whenever 2 digit years are called for | |
195 | // strict: Boolean | |
196 | // (parse only) strict parsing, off by default | |
197 | }); | |
198 | =====*/ | |
199 | ||
200 | exports._getZone = function(/*Date*/ dateObject, /*boolean*/ getName, /*__FormatOptions?*/ options){ | |
201 | // summary: | |
202 | // Returns the zone (or offset) for the given date and options. This | |
203 | // is broken out into a separate function so that it can be overridden | |
204 | // by timezone-aware code. | |
205 | // | |
206 | // dateObject: | |
207 | // the date and/or time being formatted. | |
208 | // | |
209 | // getName: | |
210 | // Whether to return the timezone string (if true), or the offset (if false) | |
211 | // | |
212 | // options: | |
213 | // The options being used for formatting | |
214 | if(getName){ | |
215 | return date.getTimezoneName(dateObject); | |
216 | }else{ | |
217 | return dateObject.getTimezoneOffset(); | |
218 | } | |
219 | }; | |
220 | ||
221 | ||
222 | exports.format = function(/*Date*/ dateObject, /*__FormatOptions?*/ options){ | |
223 | // summary: | |
224 | // Format a Date object as a String, using locale-specific settings. | |
225 | // | |
226 | // description: | |
227 | // Create a string from a Date object using a known localized pattern. | |
228 | // By default, this method formats both date and time from dateObject. | |
229 | // Formatting patterns are chosen appropriate to the locale. Different | |
230 | // formatting lengths may be chosen, with "full" used by default. | |
231 | // Custom patterns may be used or registered with translations using | |
232 | // the dojo/date/locale.addCustomFormats() method. | |
233 | // Formatting patterns are implemented using [the syntax described at | |
234 | // unicode.org](http://www.unicode.org/reports/tr35/tr35-4.html#Date_Format_Patterns) | |
235 | // | |
236 | // dateObject: | |
237 | // the date and/or time to be formatted. If a time only is formatted, | |
238 | // the values in the year, month, and day fields are irrelevant. The | |
239 | // opposite is true when formatting only dates. | |
240 | ||
241 | options = options || {}; | |
242 | ||
243 | var locale = i18n.normalizeLocale(options.locale), | |
244 | formatLength = options.formatLength || 'short', | |
245 | bundle = exports._getGregorianBundle(locale), | |
246 | str = [], | |
247 | sauce = lang.hitch(this, formatPattern, dateObject, bundle, options); | |
248 | if(options.selector == "year"){ | |
249 | return _processPattern(bundle["dateFormatItem-yyyy"] || "yyyy", sauce); | |
250 | } | |
251 | var pattern; | |
252 | if(options.selector != "date"){ | |
253 | pattern = options.timePattern || bundle["timeFormat-"+formatLength]; | |
254 | if(pattern){str.push(_processPattern(pattern, sauce));} | |
255 | } | |
256 | if(options.selector != "time"){ | |
257 | pattern = options.datePattern || bundle["dateFormat-"+formatLength]; | |
258 | if(pattern){str.push(_processPattern(pattern, sauce));} | |
259 | } | |
260 | ||
261 | return str.length == 1 ? str[0] : bundle["dateTimeFormat-"+formatLength].replace(/\'/g,'').replace(/\{(\d+)\}/g, | |
262 | function(match, key){ return str[key]; }); // String | |
263 | }; | |
264 | ||
265 | exports.regexp = function(/*__FormatOptions?*/ options){ | |
266 | // summary: | |
267 | // Builds the regular needed to parse a localized date | |
268 | ||
269 | return exports._parseInfo(options).regexp; // String | |
270 | }; | |
271 | ||
272 | exports._parseInfo = function(/*__FormatOptions?*/ options){ | |
273 | options = options || {}; | |
274 | var locale = i18n.normalizeLocale(options.locale), | |
275 | bundle = exports._getGregorianBundle(locale), | |
276 | formatLength = options.formatLength || 'short', | |
277 | datePattern = options.datePattern || bundle["dateFormat-" + formatLength], | |
278 | timePattern = options.timePattern || bundle["timeFormat-" + formatLength], | |
279 | pattern; | |
280 | if(options.selector == 'date'){ | |
281 | pattern = datePattern; | |
282 | }else if(options.selector == 'time'){ | |
283 | pattern = timePattern; | |
284 | }else{ | |
285 | pattern = bundle["dateTimeFormat-"+formatLength].replace(/\{(\d+)\}/g, | |
286 | function(match, key){ return [timePattern, datePattern][key]; }); | |
287 | } | |
288 | ||
289 | var tokens = [], | |
290 | re = _processPattern(pattern, lang.hitch(this, _buildDateTimeRE, tokens, bundle, options)); | |
291 | return {regexp: re, tokens: tokens, bundle: bundle}; | |
292 | }; | |
293 | ||
294 | exports.parse = function(/*String*/ value, /*__FormatOptions?*/ options){ | |
295 | // summary: | |
296 | // Convert a properly formatted string to a primitive Date object, | |
297 | // using locale-specific settings. | |
298 | // | |
299 | // description: | |
300 | // Create a Date object from a string using a known localized pattern. | |
301 | // By default, this method parses looking for both date and time in the string. | |
302 | // Formatting patterns are chosen appropriate to the locale. Different | |
303 | // formatting lengths may be chosen, with "full" used by default. | |
304 | // Custom patterns may be used or registered with translations using | |
305 | // the dojo/date/locale.addCustomFormats() method. | |
306 | // | |
307 | // Formatting patterns are implemented using [the syntax described at | |
308 | // unicode.org](http://www.unicode.org/reports/tr35/tr35-4.html#Date_Format_Patterns) | |
309 | // When two digit years are used, a century is chosen according to a sliding | |
310 | // window of 80 years before and 20 years after present year, for both `yy` and `yyyy` patterns. | |
311 | // year < 100CE requires strict mode. | |
312 | // | |
313 | // value: | |
314 | // A string representation of a date | |
315 | ||
316 | // remove non-printing bidi control chars from input and pattern | |
317 | var controlChars = /[\u200E\u200F\u202A\u202E]/g, | |
318 | info = exports._parseInfo(options), | |
319 | tokens = info.tokens, bundle = info.bundle, | |
320 | re = new RegExp("^" + info.regexp.replace(controlChars, "") + "$", | |
321 | info.strict ? "" : "i"), | |
322 | match = re.exec(value && value.replace(controlChars, "")); | |
323 | ||
324 | if(!match){ return null; } // null | |
325 | ||
326 | var widthList = ['abbr', 'wide', 'narrow'], | |
327 | result = [1970,0,1,0,0,0,0], // will get converted to a Date at the end | |
328 | amPm = "", | |
329 | valid = array.every(match, function(v, i){ | |
330 | if(!i){return true;} | |
331 | var token = tokens[i-1], | |
332 | l = token.length, | |
333 | c = token.charAt(0); | |
334 | switch(c){ | |
335 | case 'y': | |
336 | if(l != 2 && options.strict){ | |
337 | //interpret year literally, so '5' would be 5 A.D. | |
338 | result[0] = v; | |
339 | }else{ | |
340 | if(v<100){ | |
341 | v = Number(v); | |
342 | //choose century to apply, according to a sliding window | |
343 | //of 80 years before and 20 years after present year | |
344 | var year = '' + new Date().getFullYear(), | |
345 | century = year.substring(0, 2) * 100, | |
346 | cutoff = Math.min(Number(year.substring(2, 4)) + 20, 99); | |
347 | result[0] = (v < cutoff) ? century + v : century - 100 + v; | |
348 | }else{ | |
349 | //we expected 2 digits and got more... | |
350 | if(options.strict){ | |
351 | return false; | |
352 | } | |
353 | //interpret literally, so '150' would be 150 A.D. | |
354 | //also tolerate '1950', if 'yyyy' input passed to 'yy' format | |
355 | result[0] = v; | |
356 | } | |
357 | } | |
358 | break; | |
359 | case 'M': | |
360 | case 'L': | |
361 | if(l>2){ | |
362 | var months = bundle['months-' + | |
363 | (c == 'L' ? 'standAlone' : 'format') + | |
364 | '-' + widthList[l-3]].concat(); | |
365 | if(!options.strict){ | |
366 | //Tolerate abbreviating period in month part | |
367 | //Case-insensitive comparison | |
368 | v = v.replace(".","").toLowerCase(); | |
369 | months = array.map(months, function(s){ return s.replace(".","").toLowerCase(); } ); | |
370 | } | |
371 | v = array.indexOf(months, v); | |
372 | if(v == -1){ | |
373 | // console.log("dojo/date/locale.parse: Could not parse month name: '" + v + "'."); | |
374 | return false; | |
375 | } | |
376 | }else{ | |
377 | v--; | |
378 | } | |
379 | result[1] = v; | |
380 | break; | |
381 | case 'E': | |
382 | case 'e': | |
383 | case 'c': | |
384 | var days = bundle['days-' + | |
385 | (c == 'c' ? 'standAlone' : 'format') + | |
386 | '-' + widthList[l-3]].concat(); | |
387 | if(!options.strict){ | |
388 | //Case-insensitive comparison | |
389 | v = v.toLowerCase(); | |
390 | days = array.map(days, function(d){return d.toLowerCase();}); | |
391 | } | |
392 | v = array.indexOf(days, v); | |
393 | if(v == -1){ | |
394 | // console.log("dojo/date/locale.parse: Could not parse weekday name: '" + v + "'."); | |
395 | return false; | |
396 | } | |
397 | ||
398 | //TODO: not sure what to actually do with this input, | |
399 | //in terms of setting something on the Date obj...? | |
400 | //without more context, can't affect the actual date | |
401 | //TODO: just validate? | |
402 | break; | |
403 | case 'D': | |
404 | result[1] = 0; | |
405 | // fallthrough... | |
406 | case 'd': | |
407 | result[2] = v; | |
408 | break; | |
409 | case 'a': //am/pm | |
410 | var am = options.am || bundle['dayPeriods-format-wide-am'], | |
411 | pm = options.pm || bundle['dayPeriods-format-wide-pm']; | |
412 | if(!options.strict){ | |
413 | var period = /\./g; | |
414 | v = v.replace(period,'').toLowerCase(); | |
415 | am = am.replace(period,'').toLowerCase(); | |
416 | pm = pm.replace(period,'').toLowerCase(); | |
417 | } | |
418 | if(options.strict && v != am && v != pm){ | |
419 | // console.log("dojo/date/locale.parse: Could not parse am/pm part."); | |
420 | return false; | |
421 | } | |
422 | ||
423 | // we might not have seen the hours field yet, so store the state and apply hour change later | |
424 | amPm = (v == pm) ? 'p' : (v == am) ? 'a' : ''; | |
425 | break; | |
426 | case 'K': //hour (1-24) | |
427 | if(v == 24){ v = 0; } | |
428 | // fallthrough... | |
429 | case 'h': //hour (1-12) | |
430 | case 'H': //hour (0-23) | |
431 | case 'k': //hour (0-11) | |
432 | //TODO: strict bounds checking, padding | |
433 | if(v > 23){ | |
434 | // console.log("dojo/date/locale.parse: Illegal hours value"); | |
435 | return false; | |
436 | } | |
437 | ||
438 | //in the 12-hour case, adjusting for am/pm requires the 'a' part | |
439 | //which could come before or after the hour, so we will adjust later | |
440 | result[3] = v; | |
441 | break; | |
442 | case 'm': //minutes | |
443 | result[4] = v; | |
444 | break; | |
445 | case 's': //seconds | |
446 | result[5] = v; | |
447 | break; | |
448 | case 'S': //milliseconds | |
449 | result[6] = v; | |
450 | // break; | |
451 | // case 'w': | |
452 | //TODO var firstDay = 0; | |
453 | // default: | |
454 | //TODO: throw? | |
455 | // console.log("dojo/date/locale.parse: unsupported pattern char=" + token.charAt(0)); | |
456 | } | |
457 | return true; | |
458 | }); | |
459 | ||
460 | var hours = +result[3]; | |
461 | if(amPm === 'p' && hours < 12){ | |
462 | result[3] = hours + 12; //e.g., 3pm -> 15 | |
463 | }else if(amPm === 'a' && hours == 12){ | |
464 | result[3] = 0; //12am -> 0 | |
465 | } | |
466 | ||
467 | //TODO: implement a getWeekday() method in order to test | |
468 | //validity of input strings containing 'EEE' or 'EEEE'... | |
469 | ||
470 | var dateObject = new Date(result[0], result[1], result[2], result[3], result[4], result[5], result[6]); // Date | |
471 | if(options.strict){ | |
472 | dateObject.setFullYear(result[0]); | |
473 | } | |
474 | ||
475 | // Check for overflow. The Date() constructor normalizes things like April 32nd... | |
476 | //TODO: why isn't this done for times as well? | |
477 | var allTokens = tokens.join(""), | |
478 | dateToken = allTokens.indexOf('d') != -1, | |
479 | monthToken = allTokens.indexOf('M') != -1; | |
480 | ||
481 | if(!valid || | |
482 | (monthToken && dateObject.getMonth() > result[1]) || | |
483 | (dateToken && dateObject.getDate() > result[2])){ | |
484 | return null; | |
485 | } | |
486 | ||
487 | // Check for underflow, due to DST shifts. See #9366 | |
488 | // This assumes a 1 hour dst shift correction at midnight | |
489 | // We could compare the timezone offset after the shift and add the difference instead. | |
490 | if((monthToken && dateObject.getMonth() < result[1]) || | |
491 | (dateToken && dateObject.getDate() < result[2])){ | |
492 | dateObject = date.add(dateObject, "hour", 1); | |
493 | } | |
494 | ||
495 | return dateObject; // Date | |
496 | }; | |
497 | ||
498 | function _processPattern(pattern, applyPattern, applyLiteral, applyAll){ | |
499 | //summary: Process a pattern with literals in it | |
500 | ||
501 | // Break up on single quotes, treat every other one as a literal, except '' which becomes ' | |
502 | var identity = function(x){return x;}; | |
503 | applyPattern = applyPattern || identity; | |
504 | applyLiteral = applyLiteral || identity; | |
505 | applyAll = applyAll || identity; | |
506 | ||
507 | //split on single quotes (which escape literals in date format strings) | |
508 | //but preserve escaped single quotes (e.g., o''clock) | |
509 | var chunks = pattern.match(/(''|[^'])+/g), | |
510 | literal = pattern.charAt(0) == "'"; | |
511 | ||
512 | array.forEach(chunks, function(chunk, i){ | |
513 | if(!chunk){ | |
514 | chunks[i]=''; | |
515 | }else{ | |
516 | chunks[i]=(literal ? applyLiteral : applyPattern)(chunk.replace(/''/g, "'")); | |
517 | literal = !literal; | |
518 | } | |
519 | }); | |
520 | return applyAll(chunks.join('')); | |
521 | } | |
522 | ||
523 | function _buildDateTimeRE(tokens, bundle, options, pattern){ | |
524 | pattern = regexp.escapeString(pattern); | |
525 | if(!options.strict){ pattern = pattern.replace(" a", " ?a"); } // kludge to tolerate no space before am/pm | |
526 | return pattern.replace(/([a-z])\1*/ig, function(match){ | |
527 | // Build a simple regexp. Avoid captures, which would ruin the tokens list | |
528 | var s, | |
529 | c = match.charAt(0), | |
530 | l = match.length, | |
531 | p2 = '', p3 = ''; | |
532 | if(options.strict){ | |
533 | if(l > 1){ p2 = '0' + '{'+(l-1)+'}'; } | |
534 | if(l > 2){ p3 = '0' + '{'+(l-2)+'}'; } | |
535 | }else{ | |
536 | p2 = '0?'; p3 = '0{0,2}'; | |
537 | } | |
538 | switch(c){ | |
539 | case 'y': | |
540 | s = '\\d{2,4}'; | |
541 | break; | |
542 | case 'M': | |
543 | case 'L': | |
544 | s = (l>2) ? '\\S+?' : '1[0-2]|'+p2+'[1-9]'; | |
545 | break; | |
546 | case 'D': | |
547 | s = '[12][0-9][0-9]|3[0-5][0-9]|36[0-6]|'+p2+'[1-9][0-9]|'+p3+'[1-9]'; | |
548 | break; | |
549 | case 'd': | |
550 | s = '3[01]|[12]\\d|'+p2+'[1-9]'; | |
551 | break; | |
552 | case 'w': | |
553 | s = '[1-4][0-9]|5[0-3]|'+p2+'[1-9]'; | |
554 | break; | |
555 | case 'E': | |
556 | case 'e': | |
557 | case 'c': | |
558 | s = '\\S+'; | |
559 | break; | |
560 | case 'h': //hour (1-12) | |
561 | s = '1[0-2]|'+p2+'[1-9]'; | |
562 | break; | |
563 | case 'k': //hour (0-11) | |
564 | s = '1[01]|'+p2+'\\d'; | |
565 | break; | |
566 | case 'H': //hour (0-23) | |
567 | s = '1\\d|2[0-3]|'+p2+'\\d'; | |
568 | break; | |
569 | case 'K': //hour (1-24) | |
570 | s = '1\\d|2[0-4]|'+p2+'[1-9]'; | |
571 | break; | |
572 | case 'm': | |
573 | case 's': | |
574 | s = '[0-5]\\d'; | |
575 | break; | |
576 | case 'S': | |
577 | s = '\\d{'+l+'}'; | |
578 | break; | |
579 | case 'a': | |
580 | var am = options.am || bundle['dayPeriods-format-wide-am'], | |
581 | pm = options.pm || bundle['dayPeriods-format-wide-pm']; | |
582 | s = am + '|' + pm; | |
583 | if(!options.strict){ | |
584 | if(am != am.toLowerCase()){ s += '|' + am.toLowerCase(); } | |
585 | if(pm != pm.toLowerCase()){ s += '|' + pm.toLowerCase(); } | |
586 | if(s.indexOf('.') != -1){ s += '|' + s.replace(/\./g, ""); } | |
587 | } | |
588 | s = s.replace(/\./g, "\\."); | |
589 | break; | |
590 | default: | |
591 | // case 'v': | |
592 | // case 'z': | |
593 | // case 'Z': | |
594 | s = ".*"; | |
595 | // console.log("parse of date format, pattern=" + pattern); | |
596 | } | |
597 | ||
598 | if(tokens){ tokens.push(match); } | |
599 | ||
600 | return "(" + s + ")"; // add capture | |
601 | }).replace(/[\xa0 ]/g, "[\\s\\xa0]"); // normalize whitespace. Need explicit handling of \xa0 for IE. | |
602 | } | |
603 | ||
604 | var _customFormats = []; | |
605 | exports.addCustomFormats = function(/*String*/ packageName, /*String*/ bundleName){ | |
606 | // summary: | |
607 | // Add a reference to a bundle containing localized custom formats to be | |
608 | // used by date/time formatting and parsing routines. | |
609 | // | |
610 | // description: | |
611 | // The user may add custom localized formats where the bundle has properties following the | |
612 | // same naming convention used by dojo.cldr: `dateFormat-xxxx` / `timeFormat-xxxx` | |
613 | // The pattern string should match the format used by the CLDR. | |
614 | // See dojo/date/locale.format() for details. | |
615 | // The resources must be loaded by dojo.requireLocalization() prior to use | |
616 | ||
617 | _customFormats.push({pkg:packageName,name:bundleName}); | |
618 | }; | |
619 | ||
620 | exports._getGregorianBundle = function(/*String*/ locale){ | |
621 | var gregorian = {}; | |
622 | array.forEach(_customFormats, function(desc){ | |
623 | var bundle = i18n.getLocalization(desc.pkg, desc.name, locale); | |
624 | gregorian = lang.mixin(gregorian, bundle); | |
625 | }, this); | |
626 | return gregorian; /*Object*/ | |
627 | }; | |
628 | ||
629 | exports.addCustomFormats(module.id.replace(/\/date\/locale$/, ".cldr"),"gregorian"); | |
630 | ||
631 | exports.getNames = function(/*String*/ item, /*String*/ type, /*String?*/ context, /*String?*/ locale){ | |
632 | // summary: | |
633 | // Used to get localized strings from dojo.cldr for day or month names. | |
634 | // | |
635 | // item: | |
636 | // 'months' || 'days' | |
637 | // type: | |
638 | // 'wide' || 'abbr' || 'narrow' (e.g. "Monday", "Mon", or "M" respectively, in English) | |
639 | // context: | |
640 | // 'standAlone' || 'format' (default) | |
641 | // locale: | |
642 | // override locale used to find the names | |
643 | ||
644 | var label, | |
645 | lookup = exports._getGregorianBundle(locale), | |
646 | props = [item, context, type]; | |
647 | if(context == 'standAlone'){ | |
648 | var key = props.join('-'); | |
649 | label = lookup[key]; | |
650 | // Fall back to 'format' flavor of name | |
651 | if(label[0] == 1){ label = undefined; } // kludge, in the absence of real aliasing support in dojo.cldr | |
652 | } | |
653 | props[1] = 'format'; | |
654 | ||
655 | // return by copy so changes won't be made accidentally to the in-memory model | |
656 | return (label || lookup[props.join('-')]).concat(); /*Array*/ | |
657 | }; | |
658 | ||
659 | exports.isWeekend = function(/*Date?*/ dateObject, /*String?*/ locale){ | |
660 | // summary: | |
661 | // Determines if the date falls on a weekend, according to local custom. | |
662 | ||
663 | var weekend = supplemental.getWeekend(locale), | |
664 | day = (dateObject || new Date()).getDay(); | |
665 | if(weekend.end < weekend.start){ | |
666 | weekend.end += 7; | |
667 | if(day < weekend.start){ day += 7; } | |
668 | } | |
669 | return day >= weekend.start && day <= weekend.end; // Boolean | |
670 | }; | |
671 | ||
672 | // These are used only by format and strftime. Do they need to be public? Which module should they go in? | |
673 | ||
674 | exports._getDayOfYear = function(/*Date*/ dateObject){ | |
675 | // summary: | |
676 | // gets the day of the year as represented by dateObject | |
677 | return date.difference(new Date(dateObject.getFullYear(), 0, 1, dateObject.getHours()), dateObject) + 1; // Number | |
678 | }; | |
679 | ||
680 | exports._getWeekOfYear = function(/*Date*/ dateObject, /*Number*/ firstDayOfWeek){ | |
681 | if(arguments.length == 1){ firstDayOfWeek = 0; } // Sunday | |
682 | ||
683 | var firstDayOfYear = new Date(dateObject.getFullYear(), 0, 1).getDay(), | |
684 | adj = (firstDayOfYear - firstDayOfWeek + 7) % 7, | |
685 | week = Math.floor((exports._getDayOfYear(dateObject) + adj - 1) / 7); | |
686 | ||
687 | // if year starts on the specified day, start counting weeks at 1 | |
688 | if(firstDayOfYear == firstDayOfWeek){ week++; } | |
689 | ||
690 | return week; // Number | |
691 | }; | |
692 | ||
693 | return exports; | |
694 | }); |