Repository navigation
Expand file tree
/
Copy pathDate.Globalization.js
More file actions
697 lines (665 loc) · 26 KB
/
Copy pathDate.Globalization.js
File metadata and controls
697 lines (665 loc) · 26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
#if COPYRIGHT
//------------------------------------------------------------------------------
// <copyright file="Date.Globalization.js" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
#endif
Date._appendPreOrPostMatch = function(preMatch, strBuilder) {
// appends pre- and post- token match strings while removing escaped characters.
// Returns a single quote count which is used to determine if the token occurs
// in a string literal.
var quoteCount = 0;
var escaped = false;
for (var i = 0, il = preMatch.length; i < il; i++) {
var c = preMatch.charAt(i);
switch (c) {
case '\'':
if (escaped) strBuilder.append("'");
else quoteCount++;
escaped = false;
break;
case '\\':
if (escaped) strBuilder.append("\\");
escaped = !escaped;
break;
default:
strBuilder.append(c);
escaped = false;
break;
}
}
return quoteCount;
}
Date._expandFormat = function(dtf, format) {
// expands unspecified or single character date formats into the full pattern.
if (!format) {
format = "F";
}
var len = format.length;
if (len === 1) {
switch (format) {
case "d":
return dtf.ShortDatePattern;
case "D":
return dtf.LongDatePattern;
case "t":
return dtf.ShortTimePattern;
case "T":
return dtf.LongTimePattern;
case "f":
return dtf.LongDatePattern + " " + dtf.ShortTimePattern;
case "F":
return dtf.FullDateTimePattern;
case "M": case "m":
return dtf.MonthDayPattern;
case "s":
return dtf.SortableDateTimePattern;
case "Y": case "y":
return dtf.YearMonthPattern;
default:
throw Error.format(Sys.Res.formatInvalidString);
}
}
else if ((len === 2) && (format.charAt(0) === "%")) {
// Dev10 580024: %X escape format -- intended as a custom format string that
// is only one character, not a built-in format.
format = format.charAt(1);
}
return format;
}
Date._expandYear = function(dtf, year) {
// expands 2-digit year into 4 digits.
var now = new Date(),
era = Date._getEra(now);
if (year < 100) {
var curr = Date._getEraYear(now, dtf, era);
year += curr - (curr % 100);
if (year > dtf.Calendar.TwoDigitYearMax) {
year -= 100;
}
}
return year;
}
Date._getEra = function(date, eras) {
if (!eras) return 0;
var start, ticks = date.getTime();
for (var i = 0, l = eras.length; i < l; i += 4) {
start = eras[i+2];
if ((start === null) || (ticks >= start)) {
return i;
}
}
return 0;
}
Date._getEraYear = function(date, dtf, era, sortable) {
var year = date.getFullYear();
if (!sortable && dtf.eras) {
// convert normal gregorian year to era-shifted gregorian
// year by subtracting the era offset
year -= dtf.eras[era + 3];
}
return year;
}
Date._getParseRegExp = function(dtf, format) {
// converts a format string into a regular expression with groups that
// can be used to extract date fields from a date string.
// check for a cached parse regex.
if (!dtf._parseRegExp) {
dtf._parseRegExp = {};
}
else if (dtf._parseRegExp[format]) {
return dtf._parseRegExp[format];
}
// expand single digit formats, then escape regular expression characters.
var expFormat = Date._expandFormat(dtf, format);
expFormat = expFormat.replace(/([\^\$\.\*\+\?\|\[\]\(\)\{\}])/g, "\\\\$1");
var regexp = new Sys.StringBuilder("^");
var groups = [];
var index = 0;
var quoteCount = 0;
var tokenRegExp = Date._getTokenRegExp();
var match;
// iterate through each date token found.
while ((match = tokenRegExp.exec(expFormat)) !== null) {
var preMatch = expFormat.slice(index, match.index);
index = tokenRegExp.lastIndex;
// don't replace any matches that occur inside a string literal.
quoteCount += Date._appendPreOrPostMatch(preMatch, regexp);
if ((quoteCount%2) === 1) {
regexp.append(match[0]);
continue;
}
// add a regex group for the token.
switch (match[0]) {
case 'dddd': case 'ddd':
case 'MMMM': case 'MMM':
case 'gg': case 'g':
regexp.append("(\\D+)");
break;
case 'tt': case 't':
regexp.append("(\\D*)");
break;
case 'yyyy':
regexp.append("(\\d{4})");
break;
case 'fff':
regexp.append("(\\d{3})");
break;
case 'ff':
regexp.append("(\\d{2})");
break;
case 'f':
regexp.append("(\\d)");
break;
case 'dd': case 'd':
case 'MM': case 'M':
case 'yy': case 'y':
case 'HH': case 'H':
case 'hh': case 'h':
case 'mm': case 'm':
case 'ss': case 's':
regexp.append("(\\d\\d?)");
break;
case 'zzz':
regexp.append("([+-]?\\d\\d?:\\d{2})");
break;
case 'zz': case 'z':
regexp.append("([+-]?\\d\\d?)");
break;
case '/':
regexp.append("(\\" + dtf.DateSeparator + ")");
break;
#if DEBUGINTERNAL
default:
Sys.Debug.fail("Invalid date format pattern");
#endif
}
Array.add(groups, match[0]);
}
Date._appendPreOrPostMatch(expFormat.slice(index), regexp);
regexp.append("$");
// allow whitespace to differ when matching formats.
var regexpStr = regexp.toString().replace(/\s+/g, "\\s+");
var parseRegExp = {'regExp': regexpStr, 'groups': groups};
// cache the regex for this format.
dtf._parseRegExp[format] = parseRegExp;
return parseRegExp;
}
Date._getTokenRegExp = function() {
// regular expression for matching dateTime tokens in format strings.
return /\/|dddd|ddd|dd|d|MMMM|MMM|MM|M|yyyy|yy|y|hh|h|HH|H|mm|m|ss|s|tt|t|fff|ff|f|zzz|zz|z|gg|g/g;
}
Date.parseLocale = function(value, formats) {
/// <summary>Creates a date from a locale-specific string representation.</summary>
/// <param name="value" type="String">A locale-specific string that can parse to a date.</param>
/// <param name="formats" parameterArray="true" optional="true" mayBeNull="true">Custom formats to match.</param>
/// <returns type="Date"/>
return Date._parse(value, Sys.CultureInfo.CurrentCulture, arguments);
}
Date.parseInvariant = function(value, formats) {
/// <summary>Creates a date from its string representation.</summary>
/// <param name="value" type="String">A string that can parse to a date.</param>
/// <param name="formats" parameterArray="true" optional="true" mayBeNull="true">Custom formats to match.</param>
/// <returns type="Date"/>
return Date._parse(value, Sys.CultureInfo.InvariantCulture, arguments);
}
Date._parse = function(value, cultureInfo, args) {
// args is a params array with value as the first item, followed by custom formats.
// try parse with custom formats.
var i, l, date, format, formats, custom = false;
for (i = 1, l = args.length; i < l; i++) {
format = args[i];
if (format) {
custom = true;
date = Date._parseExact(value, format, cultureInfo);
if (date) return date;
}
}
// try parse with culture formats.
if (! custom) {
formats = cultureInfo._getDateTimeFormats();
for (i = 0, l = formats.length; i < l; i++) {
date = Date._parseExact(value, formats[i], cultureInfo);
if (date) return date;
}
}
return null;
}
Date._parseExact = function(value, format, cultureInfo) {
// try to parse the date string value by matching against the format string
// while using the specified culture for date field names.
value = value.trim();
var dtf = cultureInfo.dateTimeFormat,
// convert date formats into regular expressions with groupings.
// use the regexp to determine the input format and extract the date fields.
parseInfo = Date._getParseRegExp(dtf, format),
match = new RegExp(parseInfo.regExp).exec(value);
// DevDiv 124696: Return null to avoid Firefox warning "does not always return a value"
if (match === null) return null;
// found a date format that matches the input.
var groups = parseInfo.groups,
era = null, year = null, month = null, date = null, weekDay = null,
hour = 0, hourOffset, min = 0, sec = 0, msec = 0, tzMinOffset = null,
pmHour = false;
// iterate the format groups to extract and set the date fields.
for (var j = 0, jl = groups.length; j < jl; j++) {
var matchGroup = match[j+1];
if (matchGroup) {
switch (groups[j]) {
case 'dd': case 'd':
// Day of month.
date = parseInt(matchGroup, 10);
// check that date is generally in valid range, also checking overflow below.
if ((date < 1) || (date > 31)) return null;
break;
case 'MMMM':
// Month, long name.
month = cultureInfo._getMonthIndex(matchGroup);
if ((month < 0) || (month > 11)) return null;
break;
case 'MMM':
// Month, short name.
month = cultureInfo._getAbbrMonthIndex(matchGroup);
if ((month < 0) || (month > 11)) return null;
break;
case 'M': case 'MM':
// Month.
month = parseInt(matchGroup, 10) - 1;
if ((month < 0) || (month > 11)) return null;
break;
case 'y': case 'yy':
// 2-Digit Year.
year = Date._expandYear(dtf,parseInt(matchGroup, 10));
if ((year < 0) || (year > 9999)) return null;
break;
case 'yyyy':
// 4-Digit Year.
year = parseInt(matchGroup, 10);
if ((year < 0) || (year > 9999)) return null;
break;
case 'h': case 'hh':
// Hours (12-hour clock).
hour = parseInt(matchGroup, 10);
if (hour === 12) hour = 0;
if ((hour < 0) || (hour > 11)) return null;
break;
case 'H': case 'HH':
// Hours (24-hour clock).
hour = parseInt(matchGroup, 10);
if ((hour < 0) || (hour > 23)) return null;
break;
case 'm': case 'mm':
// Minutes.
min = parseInt(matchGroup, 10);
if ((min < 0) || (min > 59)) return null;
break;
case 's': case 'ss':
// Seconds.
sec = parseInt(matchGroup, 10);
if ((sec < 0) || (sec > 59)) return null;
break;
case 'tt': case 't':
// AM/PM designator.
var upperToken = matchGroup.toUpperCase();
pmHour = (upperToken === dtf.PMDesignator.toUpperCase());
if (!pmHour && (upperToken !== dtf.AMDesignator.toUpperCase())) return null;
break;
case 'f':
// Deciseconds.
msec = parseInt(matchGroup, 10) * 100;
if ((msec < 0) || (msec > 999)) return null;
break;
case 'ff':
// Centiseconds.
msec = parseInt(matchGroup, 10) * 10;
if ((msec < 0) || (msec > 999)) return null;
break;
case 'fff':
// Milliseconds.
msec = parseInt(matchGroup, 10);
if ((msec < 0) || (msec > 999)) return null;
break;
case 'dddd':
// Day of week.
weekDay = cultureInfo._getDayIndex(matchGroup);
if ((weekDay < 0) || (weekDay > 6)) return null;
break;
case 'ddd':
// Day of week.
weekDay = cultureInfo._getAbbrDayIndex(matchGroup);
if ((weekDay < 0) || (weekDay > 6)) return null;
break;
case 'zzz':
// Time zone offset in +/- hours:min.
var offsets = matchGroup.split(/:/);
if (offsets.length !== 2) return null;
hourOffset = parseInt(offsets[0], 10);
if ((hourOffset < -12) || (hourOffset > 13)) return null;
var minOffset = parseInt(offsets[1], 10);
if ((minOffset < 0) || (minOffset > 59)) return null;
tzMinOffset = (hourOffset * 60) + (matchGroup.startsWith('-')? -minOffset : minOffset);
break;
case 'z': case 'zz':
// Time zone offset in +/- hours.
hourOffset = parseInt(matchGroup, 10);
if ((hourOffset < -12) || (hourOffset > 13)) return null;
tzMinOffset = hourOffset * 60;
break;
case 'g': case 'gg':
var eraName = matchGroup;
if (!eraName || !dtf.eras) return null;
eraName = eraName.toLowerCase().trim();
for (var i = 0, l = dtf.eras.length; i < l; i += 4) {
if (eraName === dtf.eras[i + 1].toLowerCase()) {
era = i;
break;
}
}
// could not find an era with that name
if (era === null) return null;
break;
}
}
}
var result = new Date(), defaultYear, convert = dtf.Calendar.convert;
if (convert) {
defaultYear = convert.fromGregorian(result)[0];
}
else {
defaultYear = result.getFullYear();
}
if (year === null) {
year = defaultYear;
}
else if (dtf.eras) {
// year must be shifted to normal gregorian year
// but not if year was not specified, its already normal gregorian
// per the main if clause above.
year += dtf.eras[(era || 0) + 3];
}
// Dev10 747832: set default day and month to 1 and January to be consistent with .Net behavior.
if (month === null) {
month = 0;
}
if (date === null) {
date = 1;
}
// now have year, month, and date, but in the culture's calendar.
// convert to gregorian if necessary
if (convert) {
result = convert.toGregorian(year, month, date);
// conversion failed, must be an invalid match
if (result === null) return null;
}
else {
// have to set year, month and date together to avoid overflow based on current date.
result.setFullYear(year, month, date);
// check to see if date overflowed for specified month (only checked 1-31 above).
if (result.getDate() !== date) return null;
// invalid day of week.
if ((weekDay !== null) && (result.getDay() !== weekDay)) {
return null;
}
}
// if pm designator token was found make sure the hours fit the 24-hour clock.
if (pmHour && (hour < 12)) {
hour += 12;
}
result.setHours(hour, min, sec, msec);
if (tzMinOffset !== null) {
// adjust timezone to utc before applying local offset.
var adjustedMin = result.getMinutes() - (tzMinOffset + result.getTimezoneOffset());
// Safari limits hours and minutes to the range of -127 to 127. We need to use setHours
// to ensure both these fields will not exceed this range. adjustedMin will range
// somewhere between -1440 and 1500, so we only need to split this into hours.
result.setHours(result.getHours() + parseInt(adjustedMin/60, 10), adjustedMin%60);
}
return result;
}
Date.prototype.format = function(format) {
/// <summary>Format a date using the invariant culture.</summary>
/// <param name="format" type="String">Format string.</param>
/// <returns type="String">Formatted date.</returns>
return this._toFormattedString(format, Sys.CultureInfo.InvariantCulture);
}
Date.prototype.localeFormat = function(format) {
/// <summary>Format a date using the current culture.</summary>
/// <param name="format" type="String">Format string.</param>
/// <returns type="String">Formatted date.</returns>
return this._toFormattedString(format, Sys.CultureInfo.CurrentCulture);
}
Date.prototype._toFormattedString = function(format, cultureInfo) {
var dtf = cultureInfo.dateTimeFormat,
convert = dtf.Calendar.convert;
if (!format || !format.length || (format === 'i')) {
if (cultureInfo && cultureInfo.name.length) {
if (convert) {
// non-gregorian calendar, so we cannot use built-in toLocaleString()
return this._toFormattedString(dtf.FullDateTimePattern, cultureInfo);
}
else {
var eraDate = new Date(this.getTime());
var era = Date._getEra(this, dtf.eras);
eraDate.setFullYear(Date._getEraYear(this, dtf, era));
return eraDate.toLocaleString();
}
}
else {
return this.toString();
}
}
var eras = dtf.eras,
sortable = (format === "s");
format = Date._expandFormat(dtf, format);
// Start with an empty string
var ret = new Sys.StringBuilder();
var hour;
function addLeadingZero(num) {
if (num < 10) {
return '0' + num;
}
return num.toString();
}
function addLeadingZeros(num) {
if (num < 10) {
return '00' + num;
}
if (num < 100) {
return '0' + num;
}
return num.toString();
}
function padYear(year) {
if (year < 10) {
return '000' + year;
}
else if (year < 100) {
return '00' + year;
}
else if (year < 1000) {
return '0' + year;
}
return year.toString();
}
var foundDay, checkedDay, dayPartRegExp = /([^d]|^)(d|dd)([^d]|$)/g;
function hasDay() {
if (foundDay || checkedDay) {
return foundDay;
}
foundDay = dayPartRegExp.test(format);
checkedDay = true;
return foundDay;
}
var quoteCount = 0,
tokenRegExp = Date._getTokenRegExp(),
converted;
if (!sortable && convert) {
converted = convert.fromGregorian(this);
}
for (;;) {
// Save the current index
var index = tokenRegExp.lastIndex;
// Look for the next pattern
var ar = tokenRegExp.exec(format);
// Append the text before the pattern (or the end of the string if not found)
var preMatch = format.slice(index, ar ? ar.index : format.length);
quoteCount += Date._appendPreOrPostMatch(preMatch, ret);
if (!ar) break;
// do not replace any matches that occur inside a string literal.
if ((quoteCount%2) === 1) {
ret.append(ar[0]);
continue;
}
function getPart(date, part) {
if (converted) {
return converted[part];
}
switch (part) {
case 0: return date.getFullYear();
case 1: return date.getMonth();
case 2: return date.getDate();
}
}
switch (ar[0]) {
case "dddd":
// Day of the week, using the full name
ret.append(dtf.DayNames[this.getDay()]);
break;
case "ddd":
//Day of the week, as a three-letter abbreviation
ret.append(dtf.AbbreviatedDayNames[this.getDay()]);
break;
case "dd":
foundDay = true;
// Day of month, with leading zero for single-digit days
ret.append(addLeadingZero(getPart(this, 2)));
break;
case "d":
foundDay = true;
// Day of month, without leading zero for single-digit days
ret.append(getPart(this, 2));
break;
case "MMMM":
// Month, using the full name
ret.append((dtf.MonthGenitiveNames && hasDay())
? dtf.MonthGenitiveNames[getPart(this, 1)]
: dtf.MonthNames[getPart(this, 1)]);
break;
case "MMM":
// Month, as a three-letter abbreviation
ret.append((dtf.AbbreviatedMonthGenitiveNames && hasDay())
? dtf.AbbreviatedMonthGenitiveNames[getPart(this, 1)]
: dtf.AbbreviatedMonthNames[getPart(this, 1)]);
break;
case "MM":
// Month, as digits, with leading zero for single-digit months
ret.append(addLeadingZero(getPart(this, 1) + 1));
break;
case "M":
// Month, as digits, with no leading zero for single-digit months
ret.append(getPart(this, 1) + 1);
break;
case "yyyy":
// Year represented by four full digits
ret.append(padYear(converted ? converted[0] : Date._getEraYear(this, dtf, Date._getEra(this, eras), sortable)));
break;
case "yy":
// Year, as two digits, with leading zero for years less than 10
ret.append(addLeadingZero((converted ? converted[0] : Date._getEraYear(this, dtf, Date._getEra(this, eras), sortable)) % 100));
break;
case "y":
// Year, as two digits, but with no leading zero for years less than 10
ret.append((converted ? converted[0] : Date._getEraYear(this, dtf, Date._getEra(this, eras), sortable)) % 100);
break;
case "hh":
// Hours with leading zero for single-digit hours, using 12-hour clock
hour = this.getHours() % 12;
if (hour === 0) hour = 12;
ret.append(addLeadingZero(hour));
break;
case "h":
// Hours with no leading zero for single-digit hours, using 12-hour clock
hour = this.getHours() % 12;
if (hour === 0) hour = 12;
ret.append(hour);
break;
case "HH":
// Hours with leading zero for single-digit hours, using 24-hour clock
ret.append(addLeadingZero(this.getHours()));
break;
case "H":
// Hours with no leading zero for single-digit hours, using 24-hour clock
ret.append(this.getHours());
break;
case "mm":
// Minutes with leading zero for single-digit minutes
ret.append(addLeadingZero(this.getMinutes()));
break;
case "m":
// Minutes with no leading zero for single-digit minutes
ret.append(this.getMinutes());
break;
case "ss":
// Seconds with leading zero for single-digit seconds
ret.append(addLeadingZero(this.getSeconds()));
break;
case "s":
// Seconds with no leading zero for single-digit seconds
ret.append(this.getSeconds());
break;
case "tt":
// Multicharacter am/pm indicator
ret.append((this.getHours() < 12) ? dtf.AMDesignator : dtf.PMDesignator);
break;
case "t":
// One character am/pm indicator ("a" or "p")
ret.append(((this.getHours() < 12) ? dtf.AMDesignator : dtf.PMDesignator).charAt(0));
break;
case "f":
// Deciseconds
ret.append(addLeadingZeros(this.getMilliseconds()).charAt(0));
break;
case "ff":
// Centiseconds
ret.append(addLeadingZeros(this.getMilliseconds()).substr(0, 2));
break;
case "fff":
// Milliseconds
ret.append(addLeadingZeros(this.getMilliseconds()));
break;
case "z":
// Time zone offset, no leading zero
hour = this.getTimezoneOffset() / 60;
ret.append(((hour <= 0) ? '+' : '-') + Math.floor(Math.abs(hour)));
break;
case "zz":
// Time zone offset with leading zero
hour = this.getTimezoneOffset() / 60;
ret.append(((hour <= 0) ? '+' : '-') + addLeadingZero(Math.floor(Math.abs(hour))));
break;
case "zzz":
// Time zone offset with leading zero
hour = this.getTimezoneOffset() / 60;
ret.append(((hour <= 0) ? '+' : '-') + addLeadingZero(Math.floor(Math.abs(hour))) +
// Dev10 580009: Server has hard coded ":" separator, rather than using dtf.TimeSeparator
// Repeated here for consistency, plus ":" was already assumed in date parsing.
":" + addLeadingZero(Math.abs(this.getTimezoneOffset() % 60)));
break;
case "g":
case "gg":
if (dtf.eras) {
ret.append(dtf.eras[Date._getEra(this, eras) + 1]);
}
break;
case "/":
ret.append(dtf.DateSeparator);
break;
#if DEBUGINTERNAL
default:
Sys.Debug.fail("Invalid date format pattern");
#endif
}
}
return ret.toString();
}