@ui5/webcomponents-localization 0.0.0-de4752078 → 0.0.0-e58530409

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.
@@ -3,6 +3,7 @@ import CalendarType from '../CalendarType.js';
3
3
  import Locale from '../Locale.js';
4
4
  import LocaleData from '../LocaleData.js';
5
5
  import UniversalDate from '../date/UniversalDate.js';
6
+ import TimezoneUtil from './TimezoneUtil.js';
6
7
  import deepEqual from '../../../base/util/deepEqual.js';
7
8
  import formatMessage from '../../../base/strings/formatMessage.js';
8
9
  import Log from '../../../base/Log.js';
@@ -10,8 +11,20 @@ import extend from '../../../base/util/extend.js';
10
11
  var DateFormat = function () {
11
12
  throw new Error();
12
13
  };
14
+ var mDateFormatTypes = {
15
+ DATE: 'date',
16
+ TIME: 'time',
17
+ DATETIME: 'datetime',
18
+ DATETIME_WITH_TIMEZONE: 'datetimeWithTimezone'
19
+ };
13
20
  var mCldrDatePattern = {};
21
+ var checkTimezoneParameterType = function (sTimezone) {
22
+ if (typeof sTimezone !== 'string' && !(sTimezone instanceof String) && sTimezone != null) {
23
+ throw new TypeError('The given timezone must be a string.');
24
+ }
25
+ };
14
26
  DateFormat.oDateInfo = {
27
+ type: mDateFormatTypes.DATE,
15
28
  oDefaultFormatOptions: {
16
29
  style: 'medium',
17
30
  relativeScale: 'day',
@@ -64,6 +77,7 @@ DateFormat.oDateInfo = {
64
77
  ]
65
78
  };
66
79
  DateFormat.oDateTimeInfo = {
80
+ type: mDateFormatTypes.DATETIME,
67
81
  oDefaultFormatOptions: {
68
82
  style: 'medium',
69
83
  relativeScale: 'auto',
@@ -126,7 +140,41 @@ DateFormat.oDateTimeInfo = {
126
140
  'Seconds'
127
141
  ]
128
142
  };
143
+ DateFormat._getDateTimeWithTimezoneInfo = function (oFormatOptions) {
144
+ var bShowDate = oFormatOptions.showDate === undefined || oFormatOptions.showDate;
145
+ var bShowTime = oFormatOptions.showTime === undefined || oFormatOptions.showTime;
146
+ var bShowTimezone = oFormatOptions.showTimezone === undefined || oFormatOptions.showTimezone;
147
+ var oBaselineType = DateFormat.oDateTimeInfo;
148
+ if (bShowDate && !bShowTime) {
149
+ oBaselineType = DateFormat.oDateInfo;
150
+ } else if (!bShowDate && bShowTime) {
151
+ oBaselineType = DateFormat.oTimeInfo;
152
+ }
153
+ return Object.assign({}, oBaselineType, {
154
+ type: mDateFormatTypes.DATETIME_WITH_TIMEZONE,
155
+ getTimezonePattern: function (sPattern) {
156
+ if (!bShowDate && !bShowTime && bShowTimezone) {
157
+ return 'VV';
158
+ } else if (!bShowTimezone) {
159
+ return sPattern;
160
+ } else {
161
+ return sPattern + ' VV';
162
+ }
163
+ },
164
+ getPattern: function (oLocaleData, sStyle, sCalendarType) {
165
+ if (!bShowDate && !bShowTime && bShowTimezone) {
166
+ return 'VV';
167
+ }
168
+ if (!bShowTimezone) {
169
+ return oBaselineType.getPattern(oLocaleData, sStyle, sCalendarType);
170
+ }
171
+ var sPattern = oBaselineType.getPattern(oLocaleData, sStyle, sCalendarType);
172
+ return oLocaleData.applyTimezonePattern(sPattern);
173
+ }
174
+ });
175
+ };
129
176
  DateFormat.oTimeInfo = {
177
+ type: mDateFormatTypes.TIME,
130
178
  oDefaultFormatOptions: {
131
179
  style: 'medium',
132
180
  relativeScale: 'auto',
@@ -179,6 +227,27 @@ DateFormat.getDateInstance = function (oFormatOptions, oLocale) {
179
227
  DateFormat.getDateTimeInstance = function (oFormatOptions, oLocale) {
180
228
  return this.createInstance(oFormatOptions, oLocale, this.oDateTimeInfo);
181
229
  };
230
+ DateFormat.getDateTimeWithTimezoneInstance = function (oFormatOptions, oLocale) {
231
+ if (oFormatOptions && !(oFormatOptions instanceof Locale)) {
232
+ oFormatOptions = Object.assign({}, oFormatOptions);
233
+ if (typeof oFormatOptions.showTimezone === 'string') {
234
+ var sShowTimezone = oFormatOptions.showTimezone;
235
+ if (oFormatOptions.showDate === undefined && oFormatOptions.showTime === undefined) {
236
+ if (sShowTimezone === 'Hide') {
237
+ oFormatOptions.showTimezone = false;
238
+ } else if (sShowTimezone === 'Only') {
239
+ oFormatOptions.showDate = false;
240
+ oFormatOptions.showTime = false;
241
+ }
242
+ }
243
+ oFormatOptions.showTimezone = sShowTimezone !== 'Hide';
244
+ }
245
+ if (oFormatOptions.showDate === false && oFormatOptions.showTime === false && oFormatOptions.showTimezone === false) {
246
+ throw new TypeError('Invalid Configuration. One of the following format options must be true: showDate, showTime or showTimezone.');
247
+ }
248
+ }
249
+ return this.createInstance(oFormatOptions, oLocale, DateFormat._getDateTimeWithTimezoneInfo(oFormatOptions || {}));
250
+ };
182
251
  DateFormat.getTimeInstance = function (oFormatOptions, oLocale) {
183
252
  return this.createInstance(oFormatOptions, oLocale, this.oTimeInfo);
184
253
  };
@@ -199,6 +268,16 @@ DateFormat.createInstance = function (oFormatOptions, oLocale, oInfo) {
199
268
  oFormat.oLocale = oLocale;
200
269
  oFormat.oLocaleData = LocaleData.getInstance(oLocale);
201
270
  oFormat.oFormatOptions = extend({}, oInfo.oDefaultFormatOptions, oFormatOptions);
271
+ if (oInfo.type === mDateFormatTypes.DATETIME_WITH_TIMEZONE) {
272
+ oFormat.oFormatOptions.interval = false;
273
+ oFormat.oFormatOptions.singleIntervalValue = false;
274
+ oFormat.oFormatOptions.UTC = false;
275
+ } else {
276
+ oFormat.oFormatOptions.showTimezone = undefined;
277
+ oFormat.oFormatOptions.showDate = undefined;
278
+ oFormat.oFormatOptions.showTime = undefined;
279
+ }
280
+ oFormat.type = oInfo.type;
202
281
  if (!oFormat.oFormatOptions.calendarType) {
203
282
  oFormat.oFormatOptions.calendarType = Core.getConfiguration().getCalendarType();
204
283
  }
@@ -246,7 +325,7 @@ DateFormat.createInstance = function (oFormatOptions, oLocale, oInfo) {
246
325
  if (oFormat.oFormatOptions.pattern && oInfo.bPatternFallbackWithoutDelimiter) {
247
326
  aFallbackFormatOptions = DateFormat._createFallbackOptionsWithoutDelimiter(oFormat.oFormatOptions.pattern).concat(aFallbackFormatOptions);
248
327
  }
249
- oFallbackFormats = DateFormat._createFallbackFormat(aFallbackFormatOptions, sCalendarType, oLocale, oInfo, oFormat.oFormatOptions.interval);
328
+ oFallbackFormats = DateFormat._createFallbackFormat(aFallbackFormatOptions, sCalendarType, oLocale, oInfo, oFormat.oFormatOptions);
250
329
  }
251
330
  oFormat.aFallbackFormats = oFallbackFormats;
252
331
  }
@@ -286,10 +365,16 @@ DateFormat.prototype.init = function () {
286
365
  this.aFormatArray = this.parseCldrDatePattern(this.oFormatOptions.pattern);
287
366
  this.sAllowedCharacters = this.getAllowedCharacters(this.aFormatArray);
288
367
  };
289
- DateFormat._createFallbackFormat = function (aFallbackFormatOptions, sCalendarType, oLocale, oInfo, bInterval) {
368
+ DateFormat._createFallbackFormat = function (aFallbackFormatOptions, sCalendarType, oLocale, oInfo, oParentFormatOptions) {
290
369
  return aFallbackFormatOptions.map(function (oOptions) {
291
370
  var oFormatOptions = Object.assign({}, oOptions);
292
- if (bInterval) {
371
+ oFormatOptions.showDate = oParentFormatOptions.showDate;
372
+ oFormatOptions.showTime = oParentFormatOptions.showTime;
373
+ oFormatOptions.showTimezone = oParentFormatOptions.showTimezone;
374
+ if (typeof oInfo.getTimezonePattern === 'function' && oFormatOptions.pattern) {
375
+ oFormatOptions.pattern = oInfo.getTimezonePattern(oFormatOptions.pattern);
376
+ }
377
+ if (oParentFormatOptions.interval) {
293
378
  oFormatOptions.interval = true;
294
379
  }
295
380
  oFormatOptions.calendarType = sCalendarType;
@@ -337,17 +422,32 @@ var oParseHelper = {
337
422
  }
338
423
  return sValue.substr(0, iLength);
339
424
  },
340
- findEntry: function (sValue, aList) {
425
+ startsWithIgnoreCase: function (sValue, sSubstring, sLocale) {
426
+ if (sValue.startsWith(sSubstring)) {
427
+ return true;
428
+ }
429
+ try {
430
+ var sSubToLocaleUpperCase = sSubstring.toLocaleUpperCase(sLocale);
431
+ var sValueUpperCase = sValue.toLocaleUpperCase(sLocale);
432
+ if (sSubToLocaleUpperCase.length !== sSubstring.length || sValueUpperCase.length !== sValue.length) {
433
+ return false;
434
+ }
435
+ return sValueUpperCase.startsWith(sSubToLocaleUpperCase);
436
+ } catch (e) {
437
+ return false;
438
+ }
439
+ },
440
+ findEntry: function (sValue, aList, sLocale) {
341
441
  var iFoundIndex = -1, iMatchedLength = 0;
342
442
  for (var j = 0; j < aList.length; j++) {
343
- if (aList[j] && aList[j].length > iMatchedLength && sValue.indexOf(aList[j]) === 0) {
443
+ if (aList[j] && aList[j].length > iMatchedLength && this.startsWithIgnoreCase(sValue, aList[j], sLocale)) {
344
444
  iFoundIndex = j;
345
445
  iMatchedLength = aList[j].length;
346
446
  }
347
447
  }
348
448
  return {
349
449
  index: iFoundIndex,
350
- value: iFoundIndex === -1 ? null : aList[iFoundIndex]
450
+ length: iMatchedLength
351
451
  };
352
452
  },
353
453
  parseTZ: function (sValue, bColonSeparated) {
@@ -369,7 +469,7 @@ var oParseHelper = {
369
469
  }
370
470
  return {
371
471
  length: iLength,
372
- tzDiff: (iTZDiff + 60 * iTZDiffHour) * iTZFactor
472
+ tzDiff: (iTZDiff + 60 * iTZDiffHour) * 60 * iTZFactor
373
473
  };
374
474
  },
375
475
  checkValid: function (sType, bPartInvalid, oFormat) {
@@ -381,7 +481,7 @@ var oParseHelper = {
381
481
  DateFormat.prototype.oSymbols = {
382
482
  '': {
383
483
  name: 'text',
384
- format: function (oField, oDate, bUTC, oFormat) {
484
+ format: function (oField, oDate) {
385
485
  return oField.value;
386
486
  },
387
487
  parse: function (sValue, oPart, oFormat, oConfig) {
@@ -425,7 +525,7 @@ DateFormat.prototype.oSymbols = {
425
525
  'G': {
426
526
  name: 'era',
427
527
  format: function (oField, oDate, bUTC, oFormat) {
428
- var iEra = bUTC ? oDate.getUTCEra() : oDate.getEra();
528
+ var iEra = oDate.getUTCEra();
429
529
  if (oField.digits <= 3) {
430
530
  return oFormat.aErasAbbrev[iEra];
431
531
  } else if (oField.digits === 4) {
@@ -442,11 +542,11 @@ DateFormat.prototype.oSymbols = {
442
542
  ];
443
543
  for (var i = 0; i < aErasVariants.length; i++) {
444
544
  var aVariants = aErasVariants[i];
445
- var oFound = oParseHelper.findEntry(sValue, aVariants);
545
+ var oFound = oParseHelper.findEntry(sValue, aVariants, oFormat.oLocaleData.sCLDRLocaleId);
446
546
  if (oFound.index !== -1) {
447
547
  return {
448
548
  era: oFound.index,
449
- length: oFound.value.length
549
+ length: oFound.length
450
550
  };
451
551
  }
452
552
  }
@@ -459,7 +559,7 @@ DateFormat.prototype.oSymbols = {
459
559
  'y': {
460
560
  name: 'year',
461
561
  format: function (oField, oDate, bUTC, oFormat) {
462
- var iYear = bUTC ? oDate.getUTCFullYear() : oDate.getFullYear();
562
+ var iYear = oDate.getUTCFullYear();
463
563
  var sYear = String(iYear);
464
564
  var sCalendarType = oFormat.oFormatOptions.calendarType;
465
565
  if (oField.digits == 2 && sYear.length > 2) {
@@ -482,7 +582,7 @@ DateFormat.prototype.oSymbols = {
482
582
  }
483
583
  var iYear = parseInt(sPart);
484
584
  if (sCalendarType != CalendarType.Japanese && sPart.length <= 2) {
485
- var oCurrentDate = UniversalDate.getInstance(new Date(), sCalendarType), iCurrentYear = oCurrentDate.getFullYear(), iCurrentCentury = Math.floor(iCurrentYear / 100), iYearDiff = iCurrentCentury * 100 + iYear - iCurrentYear;
585
+ var oCurrentDate = UniversalDate.getInstance(new Date(), sCalendarType), iCurrentYear = oCurrentDate.getUTCFullYear(), iCurrentCentury = Math.floor(iCurrentYear / 100), iYearDiff = iCurrentCentury * 100 + iYear - iCurrentYear;
486
586
  if (iYearDiff < -70) {
487
587
  iYear += (iCurrentCentury + 1) * 100;
488
588
  } else if (iYearDiff < 30) {
@@ -501,7 +601,7 @@ DateFormat.prototype.oSymbols = {
501
601
  'Y': {
502
602
  name: 'weekYear',
503
603
  format: function (oField, oDate, bUTC, oFormat) {
504
- var oWeek = bUTC ? oDate.getUTCWeek() : oDate.getWeek();
604
+ var oWeek = oDate.getUTCWeek();
505
605
  var iWeekYear = oWeek.year;
506
606
  var sWeekYear = String(iWeekYear);
507
607
  var sCalendarType = oFormat.oFormatOptions.calendarType;
@@ -524,9 +624,9 @@ DateFormat.prototype.oSymbols = {
524
624
  sPart = oParseHelper.findNumbers(sValue, oPart.digits);
525
625
  }
526
626
  var iYear = parseInt(sPart);
527
- var iWeekYear;
627
+ var iWeekYear = iYear;
528
628
  if (sCalendarType != CalendarType.Japanese && sPart.length <= 2) {
529
- var oCurrentDate = UniversalDate.getInstance(new Date(), sCalendarType), iCurrentYear = oCurrentDate.getFullYear(), iCurrentCentury = Math.floor(iCurrentYear / 100), iYearDiff = iCurrentCentury * 100 + iWeekYear - iCurrentYear;
629
+ var oCurrentDate = UniversalDate.getInstance(new Date(), sCalendarType), iCurrentYear = oCurrentDate.getUTCFullYear(), iCurrentCentury = Math.floor(iCurrentYear / 100), iYearDiff = iCurrentCentury * 100 + iWeekYear - iCurrentYear;
530
630
  if (iYearDiff < -70) {
531
631
  iWeekYear += (iCurrentCentury + 1) * 100;
532
632
  } else if (iYearDiff < 30) {
@@ -546,7 +646,7 @@ DateFormat.prototype.oSymbols = {
546
646
  'M': {
547
647
  name: 'month',
548
648
  format: function (oField, oDate, bUTC, oFormat) {
549
- var iMonth = bUTC ? oDate.getUTCMonth() : oDate.getMonth();
649
+ var iMonth = oDate.getUTCMonth();
550
650
  if (oField.digits == 3) {
551
651
  return oFormat.aMonthsAbbrev[iMonth];
552
652
  } else if (oField.digits == 4) {
@@ -579,11 +679,11 @@ DateFormat.prototype.oSymbols = {
579
679
  } else {
580
680
  for (var i = 0; i < aMonthsVariants.length; i++) {
581
681
  var aVariants = aMonthsVariants[i];
582
- var oFound = oParseHelper.findEntry(sValue, aVariants);
682
+ var oFound = oParseHelper.findEntry(sValue, aVariants, oFormat.oLocaleData.sCLDRLocaleId);
583
683
  if (oFound.index !== -1) {
584
684
  return {
585
685
  month: oFound.index,
586
- length: oFound.value.length
686
+ length: oFound.length
587
687
  };
588
688
  }
589
689
  }
@@ -599,7 +699,7 @@ DateFormat.prototype.oSymbols = {
599
699
  'L': {
600
700
  name: 'monthStandalone',
601
701
  format: function (oField, oDate, bUTC, oFormat) {
602
- var iMonth = bUTC ? oDate.getUTCMonth() : oDate.getMonth();
702
+ var iMonth = oDate.getUTCMonth();
603
703
  if (oField.digits == 3) {
604
704
  return oFormat.aMonthsAbbrevSt[iMonth];
605
705
  } else if (oField.digits == 4) {
@@ -632,11 +732,11 @@ DateFormat.prototype.oSymbols = {
632
732
  } else {
633
733
  for (var i = 0; i < aMonthsVariants.length; i++) {
634
734
  var aVariants = aMonthsVariants[i];
635
- var oFound = oParseHelper.findEntry(sValue, aVariants);
735
+ var oFound = oParseHelper.findEntry(sValue, aVariants, oFormat.oLocaleData.sCLDRLocaleId);
636
736
  if (oFound.index !== -1) {
637
737
  return {
638
738
  month: oFound.index,
639
- length: oFound.value.length
739
+ length: oFound.length
640
740
  };
641
741
  }
642
742
  }
@@ -652,7 +752,7 @@ DateFormat.prototype.oSymbols = {
652
752
  'w': {
653
753
  name: 'weekInYear',
654
754
  format: function (oField, oDate, bUTC, oFormat) {
655
- var oWeek = bUTC ? oDate.getUTCWeek() : oDate.getWeek();
755
+ var oWeek = oDate.getUTCWeek();
656
756
  var iWeek = oWeek.week;
657
757
  var sWeek = String(iWeek + 1);
658
758
  if (oField.digits < 3) {
@@ -674,11 +774,11 @@ DateFormat.prototype.oSymbols = {
674
774
  bValid = oParseHelper.checkValid(oPart.type, !sPart, oFormat);
675
775
  } else {
676
776
  sPart = oFormat.oLocaleData.getCalendarWeek(oPart.digits === 3 ? 'narrow' : 'wide');
677
- sPart = sPart.replace('{0}', '[0-9]+');
777
+ sPart = sPart.replace('{0}', '([0-9]+)');
678
778
  var rWeekNumber = new RegExp(sPart), oResult = rWeekNumber.exec(sValue);
679
779
  if (oResult) {
680
780
  iLength = oResult[0].length;
681
- iWeek = parseInt(oResult[0]) - 1;
781
+ iWeek = parseInt(oResult[oResult.length - 1]) - 1;
682
782
  } else {
683
783
  bValid = oParseHelper.checkValid(oPart.type, true, oFormat);
684
784
  }
@@ -692,7 +792,7 @@ DateFormat.prototype.oSymbols = {
692
792
  },
693
793
  'W': {
694
794
  name: 'weekInMonth',
695
- format: function (oField, oDate, bUTC, oFormat) {
795
+ format: function (oField, oDate) {
696
796
  return '';
697
797
  },
698
798
  parse: function () {
@@ -701,7 +801,7 @@ DateFormat.prototype.oSymbols = {
701
801
  },
702
802
  'D': {
703
803
  name: 'dayInYear',
704
- format: function (oField, oDate, bUTC, oFormat) {
804
+ format: function (oField, oDate) {
705
805
  },
706
806
  parse: function () {
707
807
  return {};
@@ -709,8 +809,8 @@ DateFormat.prototype.oSymbols = {
709
809
  },
710
810
  'd': {
711
811
  name: 'day',
712
- format: function (oField, oDate, bUTC, oFormat) {
713
- var iDate = bUTC ? oDate.getUTCDate() : oDate.getDate();
812
+ format: function (oField, oDate) {
813
+ var iDate = oDate.getUTCDate();
714
814
  return String(iDate).padStart(oField.digits, '0');
715
815
  },
716
816
  parse: function (sValue, oPart, oFormat, oConfig) {
@@ -730,7 +830,7 @@ DateFormat.prototype.oSymbols = {
730
830
  'Q': {
731
831
  name: 'quarter',
732
832
  format: function (oField, oDate, bUTC, oFormat) {
733
- var iQuarter = bUTC ? oDate.getUTCQuarter() : oDate.getQuarter();
833
+ var iQuarter = oDate.getUTCQuarter();
734
834
  if (oField.digits == 3) {
735
835
  return oFormat.aQuartersAbbrev[iQuarter];
736
836
  } else if (oField.digits == 4) {
@@ -763,11 +863,11 @@ DateFormat.prototype.oSymbols = {
763
863
  } else {
764
864
  for (var i = 0; i < aQuartersVariants.length; i++) {
765
865
  var aVariants = aQuartersVariants[i];
766
- var oFound = oParseHelper.findEntry(sValue, aVariants);
866
+ var oFound = oParseHelper.findEntry(sValue, aVariants, oFormat.oLocaleData.sCLDRLocaleId);
767
867
  if (oFound.index !== -1) {
768
868
  return {
769
869
  quarter: oFound.index,
770
- length: oFound.value.length
870
+ length: oFound.length
771
871
  };
772
872
  }
773
873
  }
@@ -783,7 +883,7 @@ DateFormat.prototype.oSymbols = {
783
883
  'q': {
784
884
  name: 'quarterStandalone',
785
885
  format: function (oField, oDate, bUTC, oFormat) {
786
- var iQuarter = bUTC ? oDate.getUTCQuarter() : oDate.getQuarter();
886
+ var iQuarter = oDate.getUTCQuarter();
787
887
  if (oField.digits == 3) {
788
888
  return oFormat.aQuartersAbbrevSt[iQuarter];
789
889
  } else if (oField.digits == 4) {
@@ -816,11 +916,11 @@ DateFormat.prototype.oSymbols = {
816
916
  } else {
817
917
  for (var i = 0; i < aQuartersVariants.length; i++) {
818
918
  var aVariants = aQuartersVariants[i];
819
- var oFound = oParseHelper.findEntry(sValue, aVariants);
919
+ var oFound = oParseHelper.findEntry(sValue, aVariants, oFormat.oLocaleData.sCLDRLocaleId);
820
920
  if (oFound.index !== -1) {
821
921
  return {
822
922
  quarter: oFound.index,
823
- length: oFound.value.length
923
+ length: oFound.length
824
924
  };
825
925
  }
826
926
  }
@@ -835,7 +935,7 @@ DateFormat.prototype.oSymbols = {
835
935
  },
836
936
  'F': {
837
937
  name: 'dayOfWeekInMonth',
838
- format: function (oField, oDate, bUTC, oFormat) {
938
+ format: function (oField, oDate, oFormat) {
839
939
  return '';
840
940
  },
841
941
  parse: function () {
@@ -845,7 +945,7 @@ DateFormat.prototype.oSymbols = {
845
945
  'E': {
846
946
  name: 'dayNameInWeek',
847
947
  format: function (oField, oDate, bUTC, oFormat) {
848
- var iDay = bUTC ? oDate.getUTCDay() : oDate.getDay();
948
+ var iDay = oDate.getUTCDay();
849
949
  if (oField.digits < 4) {
850
950
  return oFormat.aDaysAbbrev[iDay];
851
951
  } else if (oField.digits == 4) {
@@ -869,11 +969,11 @@ DateFormat.prototype.oSymbols = {
869
969
  ];
870
970
  for (var i = 0; i < aDaysVariants.length; i++) {
871
971
  var aVariants = aDaysVariants[i];
872
- var oFound = oParseHelper.findEntry(sValue, aVariants);
972
+ var oFound = oParseHelper.findEntry(sValue, aVariants, oFormat.oLocaleData.sCLDRLocaleId);
873
973
  if (oFound.index !== -1) {
874
974
  return {
875
975
  dayOfWeek: oFound.index,
876
- length: oFound.value.length
976
+ length: oFound.length
877
977
  };
878
978
  }
879
979
  }
@@ -882,7 +982,7 @@ DateFormat.prototype.oSymbols = {
882
982
  'c': {
883
983
  name: 'dayNameInWeekStandalone',
884
984
  format: function (oField, oDate, bUTC, oFormat) {
885
- var iDay = bUTC ? oDate.getUTCDay() : oDate.getDay();
985
+ var iDay = oDate.getUTCDay();
886
986
  if (oField.digits < 4) {
887
987
  return oFormat.aDaysAbbrevSt[iDay];
888
988
  } else if (oField.digits == 4) {
@@ -906,11 +1006,11 @@ DateFormat.prototype.oSymbols = {
906
1006
  ];
907
1007
  for (var i = 0; i < aDaysVariants.length; i++) {
908
1008
  var aVariants = aDaysVariants[i];
909
- var oFound = oParseHelper.findEntry(sValue, aVariants);
1009
+ var oFound = oParseHelper.findEntry(sValue, aVariants, oFormat.oLocaleData.sCLDRLocaleId);
910
1010
  if (oFound.index !== -1) {
911
1011
  return {
912
1012
  day: oFound.index,
913
- length: oFound.value.length
1013
+ length: oFound.length
914
1014
  };
915
1015
  }
916
1016
  }
@@ -919,7 +1019,7 @@ DateFormat.prototype.oSymbols = {
919
1019
  'u': {
920
1020
  name: 'dayNumberOfWeek',
921
1021
  format: function (oField, oDate, bUTC, oFormat) {
922
- var iDay = bUTC ? oDate.getUTCDay() : oDate.getDay();
1022
+ var iDay = oDate.getUTCDay();
923
1023
  return oFormat._adaptDayOfWeek(iDay);
924
1024
  },
925
1025
  parse: function (sValue, oPart, oFormat, oConfig) {
@@ -933,20 +1033,24 @@ DateFormat.prototype.oSymbols = {
933
1033
  'a': {
934
1034
  name: 'amPmMarker',
935
1035
  format: function (oField, oDate, bUTC, oFormat) {
936
- var iDayPeriod = bUTC ? oDate.getUTCDayPeriod() : oDate.getDayPeriod();
1036
+ var iDayPeriod = oDate.getUTCDayPeriod();
937
1037
  return oFormat.aDayPeriods[iDayPeriod];
938
1038
  },
939
1039
  parse: function (sValue, oPart, oFormat, oConfig) {
940
1040
  var bPM;
941
1041
  var iLength;
942
1042
  var sAM = oFormat.aDayPeriods[0], sPM = oFormat.aDayPeriods[1];
943
- var rAMPM = /[aApP](?:\.)?[mM](?:\.)?/;
1043
+ var rAMPM = /[aApP](?:\.)?[\x20\xA0]?[mM](?:\.)?/;
944
1044
  var aMatch = sValue.match(rAMPM);
945
1045
  var bVariant = aMatch && aMatch.index === 0;
946
1046
  if (bVariant) {
947
- sValue = aMatch[0].replace(/\./g, '').toLowerCase() + sValue.substring(aMatch[0].length);
1047
+ sValue = aMatch[0];
1048
+ sAM = sAM.replace(/[\x20\xA0]/g, '');
1049
+ sPM = sPM.replace(/[\x20\xA0]/g, '');
1050
+ sValue = sValue.replace(/[\x20\xA0]/g, '');
948
1051
  sAM = sAM.replace(/\./g, '').toLowerCase();
949
1052
  sPM = sPM.replace(/\./g, '').toLowerCase();
1053
+ sValue = sValue.replace(/\./g, '').toLowerCase();
950
1054
  }
951
1055
  if (sValue.indexOf(sAM) === 0) {
952
1056
  bPM = false;
@@ -963,8 +1067,8 @@ DateFormat.prototype.oSymbols = {
963
1067
  },
964
1068
  'H': {
965
1069
  name: 'hour0_23',
966
- format: function (oField, oDate, bUTC, oFormat) {
967
- var iHours = bUTC ? oDate.getUTCHours() : oDate.getHours();
1070
+ format: function (oField, oDate) {
1071
+ var iHours = oDate.getUTCHours();
968
1072
  return String(iHours).padStart(oField.digits, '0');
969
1073
  },
970
1074
  parse: function (sValue, oPart, oFormat, oConfig) {
@@ -984,8 +1088,8 @@ DateFormat.prototype.oSymbols = {
984
1088
  },
985
1089
  'k': {
986
1090
  name: 'hour1_24',
987
- format: function (oField, oDate, bUTC, oFormat) {
988
- var iHours = bUTC ? oDate.getUTCHours() : oDate.getHours();
1091
+ format: function (oField, oDate) {
1092
+ var iHours = oDate.getUTCHours();
989
1093
  var sHours = iHours === 0 ? '24' : String(iHours);
990
1094
  return sHours.padStart(oField.digits, '0');
991
1095
  },
@@ -1009,8 +1113,8 @@ DateFormat.prototype.oSymbols = {
1009
1113
  },
1010
1114
  'K': {
1011
1115
  name: 'hour0_11',
1012
- format: function (oField, oDate, bUTC, oFormat) {
1013
- var iHours = bUTC ? oDate.getUTCHours() : oDate.getHours();
1116
+ format: function (oField, oDate) {
1117
+ var iHours = oDate.getUTCHours();
1014
1118
  var sHours = String(iHours > 11 ? iHours - 12 : iHours);
1015
1119
  return sHours.padStart(oField.digits, '0');
1016
1120
  },
@@ -1031,8 +1135,8 @@ DateFormat.prototype.oSymbols = {
1031
1135
  },
1032
1136
  'h': {
1033
1137
  name: 'hour1_12',
1034
- format: function (oField, oDate, bUTC, oFormat) {
1035
- var iHours = bUTC ? oDate.getUTCHours() : oDate.getHours();
1138
+ format: function (oField, oDate) {
1139
+ var iHours = oDate.getUTCHours();
1036
1140
  var sHours;
1037
1141
  if (iHours > 12) {
1038
1142
  sHours = String(iHours - 12);
@@ -1065,8 +1169,8 @@ DateFormat.prototype.oSymbols = {
1065
1169
  },
1066
1170
  'm': {
1067
1171
  name: 'minute',
1068
- format: function (oField, oDate, bUTC, oFormat) {
1069
- var iMinutes = bUTC ? oDate.getUTCMinutes() : oDate.getMinutes();
1172
+ format: function (oField, oDate) {
1173
+ var iMinutes = oDate.getUTCMinutes();
1070
1174
  return String(iMinutes).padStart(oField.digits, '0');
1071
1175
  },
1072
1176
  parse: function (sValue, oPart, oFormat, oConfig) {
@@ -1086,8 +1190,8 @@ DateFormat.prototype.oSymbols = {
1086
1190
  },
1087
1191
  's': {
1088
1192
  name: 'second',
1089
- format: function (oField, oDate, bUTC, oFormat) {
1090
- var iSeconds = bUTC ? oDate.getUTCSeconds() : oDate.getSeconds();
1193
+ format: function (oField, oDate) {
1194
+ var iSeconds = oDate.getUTCSeconds();
1091
1195
  return String(iSeconds).padStart(oField.digits, '0');
1092
1196
  },
1093
1197
  parse: function (sValue, oPart, oFormat, oConfig) {
@@ -1107,8 +1211,8 @@ DateFormat.prototype.oSymbols = {
1107
1211
  },
1108
1212
  'S': {
1109
1213
  name: 'fractionalsecond',
1110
- format: function (oField, oDate, bUTC, oFormat) {
1111
- var iMilliseconds = bUTC ? oDate.getUTCMilliseconds() : oDate.getMilliseconds();
1214
+ format: function (oField, oDate) {
1215
+ var iMilliseconds = oDate.getUTCMilliseconds();
1112
1216
  var sMilliseconds = String(iMilliseconds);
1113
1217
  var sFractionalseconds = sMilliseconds.padStart(3, '0');
1114
1218
  sFractionalseconds = sFractionalseconds.substr(0, oField.digits);
@@ -1129,17 +1233,18 @@ DateFormat.prototype.oSymbols = {
1129
1233
  },
1130
1234
  'z': {
1131
1235
  name: 'timezoneGeneral',
1132
- format: function (oField, oDate, bUTC, oFormat) {
1236
+ format: function (oField, oDate, bUTC, oFormat, sTimezone) {
1133
1237
  if (oField.digits > 3 && oDate.getTimezoneLong && oDate.getTimezoneLong()) {
1134
1238
  return oDate.getTimezoneLong();
1135
1239
  } else if (oDate.getTimezoneShort && oDate.getTimezoneShort()) {
1136
1240
  return oDate.getTimezoneShort();
1137
1241
  }
1242
+ var iTimezoneOffset = TimezoneUtil.calculateOffset(oDate, sTimezone);
1138
1243
  var sTimeZone = 'GMT';
1139
- var iTZOffset = Math.abs(oDate.getTimezoneOffset());
1140
- var bPositiveOffset = oDate.getTimezoneOffset() > 0;
1244
+ var iTZOffset = Math.abs(iTimezoneOffset / 60);
1245
+ var bPositiveOffset = iTimezoneOffset > 0;
1141
1246
  var iHourOffset = Math.floor(iTZOffset / 60);
1142
- var iMinuteOffset = iTZOffset % 60;
1247
+ var iMinuteOffset = Math.floor(iTZOffset % 60);
1143
1248
  if (!bUTC && iTZOffset != 0) {
1144
1249
  sTimeZone += bPositiveOffset ? '-' : '+';
1145
1250
  sTimeZone += String(iHourOffset).padStart(2, '0');
@@ -1162,7 +1267,7 @@ DateFormat.prototype.oSymbols = {
1162
1267
  iLength = 1;
1163
1268
  iTZDiff = 0;
1164
1269
  } else {
1165
- return { error: 'cannot be parsed correcly by sap.ui.core.format.DateFormat: The given timezone is not supported!' };
1270
+ return { error: 'cannot be parsed correctly by sap.ui.core.format.DateFormat: The given timezone is not supported!' };
1166
1271
  }
1167
1272
  if (sValue.charAt(0) !== 'Z') {
1168
1273
  var oParsedTZ = oParseHelper.parseTZ(sValue.substr(iLength), true);
@@ -1177,11 +1282,12 @@ DateFormat.prototype.oSymbols = {
1177
1282
  },
1178
1283
  'Z': {
1179
1284
  name: 'timezoneRFC822',
1180
- format: function (oField, oDate, bUTC, oFormat) {
1181
- var iTZOffset = Math.abs(oDate.getTimezoneOffset());
1182
- var bPositiveOffset = oDate.getTimezoneOffset() > 0;
1285
+ format: function (oField, oDate, bUTC, oFormat, sTimezone) {
1286
+ var iTimezoneOffset = TimezoneUtil.calculateOffset(oDate, sTimezone);
1287
+ var iTZOffset = Math.abs(iTimezoneOffset / 60);
1288
+ var bPositiveOffset = iTimezoneOffset > 0;
1183
1289
  var iHourOffset = Math.floor(iTZOffset / 60);
1184
- var iMinuteOffset = iTZOffset % 60;
1290
+ var iMinuteOffset = Math.floor(iTZOffset % 60);
1185
1291
  var sTimeZone = '';
1186
1292
  if (!bUTC) {
1187
1293
  sTimeZone += bPositiveOffset ? '-' : '+';
@@ -1196,11 +1302,12 @@ DateFormat.prototype.oSymbols = {
1196
1302
  },
1197
1303
  'X': {
1198
1304
  name: 'timezoneISO8601',
1199
- format: function (oField, oDate, bUTC, oFormat) {
1200
- var iTZOffset = Math.abs(oDate.getTimezoneOffset());
1201
- var bPositiveOffset = oDate.getTimezoneOffset() > 0;
1305
+ format: function (oField, oDate, bUTC, oFormat, sTimezone) {
1306
+ var iTimezoneOffset = TimezoneUtil.calculateOffset(oDate, sTimezone);
1307
+ var iTZOffset = Math.abs(iTimezoneOffset / 60);
1308
+ var bPositiveOffset = iTimezoneOffset > 0;
1202
1309
  var iHourOffset = Math.floor(iTZOffset / 60);
1203
- var iMinuteOffset = iTZOffset % 60;
1310
+ var iMinuteOffset = Math.floor(iTZOffset % 60);
1204
1311
  var sTimeZone = '';
1205
1312
  if (!bUTC && iTZOffset != 0) {
1206
1313
  sTimeZone += bPositiveOffset ? '-' : '+';
@@ -1226,11 +1333,43 @@ DateFormat.prototype.oSymbols = {
1226
1333
  return oParseHelper.parseTZ(sValue, oPart.digits === 3 || oPart.digits === 5);
1227
1334
  }
1228
1335
  }
1336
+ },
1337
+ 'V': {
1338
+ name: 'timezoneID',
1339
+ format: function (oField, oDate, bUTC, oFormat, sTimezone) {
1340
+ if (!bUTC && oField.digits === 2) {
1341
+ return sTimezone;
1342
+ }
1343
+ return '';
1344
+ },
1345
+ parse: function (sValue, oPart, oFormat, oConfig, sTimezone) {
1346
+ var oTimezoneParsed = {
1347
+ timezone: '',
1348
+ length: 0
1349
+ };
1350
+ if (oPart.digits === 2) {
1351
+ if (sValue === sTimezone) {
1352
+ oTimezoneParsed.timezone = sTimezone;
1353
+ oTimezoneParsed.length = sTimezone.length;
1354
+ return oTimezoneParsed;
1355
+ }
1356
+ if (sValue) {
1357
+ var rIanaTimezone = new RegExp('([A-Za-z_])+([/][A-Za-z_]+)+');
1358
+ var aResult = rIanaTimezone.exec(sValue);
1359
+ if (aResult && aResult[0] && TimezoneUtil.isValidTimezone(aResult[0])) {
1360
+ oTimezoneParsed.timezone = aResult[0];
1361
+ oTimezoneParsed.length = aResult[0].length;
1362
+ return oTimezoneParsed;
1363
+ }
1364
+ }
1365
+ }
1366
+ return oTimezoneParsed;
1367
+ }
1229
1368
  }
1230
1369
  };
1231
- DateFormat.prototype._format = function (oJSDate, bUTC) {
1370
+ DateFormat.prototype._format = function (oJSDate, bUTC, sTimezone) {
1232
1371
  if (this.oFormatOptions.relative) {
1233
- var sRes = this.formatRelative(oJSDate, bUTC, this.oFormatOptions.relativeRange);
1372
+ var sRes = this.formatRelative(oJSDate, bUTC, this.oFormatOptions.relativeRange, sTimezone);
1234
1373
  if (sRes) {
1235
1374
  return sRes;
1236
1375
  }
@@ -1241,7 +1380,7 @@ DateFormat.prototype._format = function (oJSDate, bUTC) {
1241
1380
  for (var i = 0; i < this.aFormatArray.length; i++) {
1242
1381
  oPart = this.aFormatArray[i];
1243
1382
  sSymbol = oPart.symbol || '';
1244
- aBuffer.push(this.oSymbols[sSymbol].format(oPart, oDate, bUTC, this));
1383
+ aBuffer.push(this.oSymbols[sSymbol].format(oPart, oDate, bUTC, this, sTimezone));
1245
1384
  }
1246
1385
  sResult = aBuffer.join('');
1247
1386
  if (Core.getConfiguration().getOriginInfo()) {
@@ -1256,10 +1395,21 @@ DateFormat.prototype._format = function (oJSDate, bUTC) {
1256
1395
  return sResult;
1257
1396
  };
1258
1397
  DateFormat.prototype.format = function (vJSDate, bUTC) {
1398
+ var sTimezone;
1399
+ if (this.type === mDateFormatTypes.DATETIME_WITH_TIMEZONE) {
1400
+ sTimezone = bUTC;
1401
+ bUTC = false;
1402
+ checkTimezoneParameterType(sTimezone);
1403
+ if (sTimezone && !TimezoneUtil.isValidTimezone(sTimezone)) {
1404
+ Log.error('The given timezone isn\'t valid.');
1405
+ return '';
1406
+ }
1407
+ }
1259
1408
  var sCalendarType = this.oFormatOptions.calendarType, sResult;
1260
1409
  if (bUTC === undefined) {
1261
1410
  bUTC = this.oFormatOptions.UTC;
1262
1411
  }
1412
+ sTimezone = sTimezone || Core.getConfiguration().getTimezone();
1263
1413
  if (Array.isArray(vJSDate)) {
1264
1414
  if (!this.oFormatOptions.interval) {
1265
1415
  Log.error('Non-interval DateFormat can\'t format more than one date instance.');
@@ -1269,27 +1419,30 @@ DateFormat.prototype.format = function (vJSDate, bUTC) {
1269
1419
  Log.error('Interval DateFormat can only format with 2 date instances but ' + vJSDate.length + ' is given.');
1270
1420
  return '';
1271
1421
  }
1422
+ vJSDate = vJSDate.map(function (oJSDate) {
1423
+ return convertToTimezone(oJSDate, sTimezone, bUTC);
1424
+ });
1272
1425
  if (this.oFormatOptions.singleIntervalValue) {
1273
1426
  if (vJSDate[0] === null) {
1274
1427
  Log.error('First date instance which is passed to the interval DateFormat shouldn\'t be null.');
1275
1428
  return '';
1276
1429
  }
1277
1430
  if (vJSDate[1] === null) {
1278
- sResult = this._format(vJSDate[0], bUTC);
1431
+ sResult = this._format(vJSDate[0], bUTC, sTimezone);
1279
1432
  }
1280
1433
  }
1281
1434
  if (sResult === undefined) {
1282
- var bValid = vJSDate.every(function (oJSDate) {
1283
- return oJSDate && !isNaN(oJSDate.getTime());
1284
- });
1285
- if (!bValid) {
1435
+ if (!vJSDate.every(isValidDateObject)) {
1286
1436
  Log.error('At least one date instance which is passed to the interval DateFormat isn\'t valid.');
1287
1437
  return '';
1288
1438
  }
1289
1439
  sResult = this._formatInterval(vJSDate, bUTC);
1290
1440
  }
1291
1441
  } else {
1292
- if (!vJSDate || isNaN(vJSDate.getTime())) {
1442
+ if (!isValidDateObject(vJSDate)) {
1443
+ if (this.type === mDateFormatTypes.DATETIME_WITH_TIMEZONE && this.oFormatOptions.pattern.includes('VV')) {
1444
+ return sTimezone;
1445
+ }
1293
1446
  Log.error('The given date instance isn\'t valid.');
1294
1447
  return '';
1295
1448
  }
@@ -1297,7 +1450,8 @@ DateFormat.prototype.format = function (vJSDate, bUTC) {
1297
1450
  Log.error('Interval DateFormat expects an array with two dates for the first argument but only one date is given.');
1298
1451
  return '';
1299
1452
  }
1300
- sResult = this._format(vJSDate, bUTC);
1453
+ vJSDate = convertToTimezone(vJSDate, sTimezone, bUTC);
1454
+ sResult = this._format(vJSDate, bUTC, sTimezone);
1301
1455
  }
1302
1456
  if (sCalendarType == CalendarType.Japanese && this.oLocale.getLanguage() === 'ja') {
1303
1457
  sResult = sResult.replace(/(^|[^\d])1年/g, '$1元年');
@@ -1317,7 +1471,7 @@ DateFormat.prototype._formatInterval = function (aJSDates, bUTC) {
1317
1471
  var oDiffField = this._getGreatestDiffField([
1318
1472
  oFromDate,
1319
1473
  oToDate
1320
- ], bUTC);
1474
+ ]);
1321
1475
  if (!oDiffField) {
1322
1476
  return this._format(aJSDates[0], bUTC);
1323
1477
  }
@@ -1350,10 +1504,10 @@ var mFieldToGroup = {
1350
1504
  Minutes: 'Minute',
1351
1505
  Seconds: 'Second'
1352
1506
  };
1353
- DateFormat.prototype._getGreatestDiffField = function (aDates, bUTC) {
1507
+ DateFormat.prototype._getGreatestDiffField = function (aDates) {
1354
1508
  var bDiffFound = false, mDiff = {};
1355
1509
  this.aIntervalCompareFields.forEach(function (sField) {
1356
- var sGetterPrefix = 'get' + (bUTC ? 'UTC' : ''), sMethodName = sGetterPrefix + sField, sFieldGroup = mFieldToGroup[sField], vFromValue = aDates[0][sMethodName].apply(aDates[0]), vToValue = aDates[1][sMethodName].apply(aDates[1]);
1510
+ var sGetterPrefix = 'getUTC', sMethodName = sGetterPrefix + sField, sFieldGroup = mFieldToGroup[sField], vFromValue = aDates[0][sMethodName].apply(aDates[0]), vToValue = aDates[1][sMethodName].apply(aDates[1]);
1357
1511
  if (!deepEqual(vFromValue, vToValue)) {
1358
1512
  bDiffFound = true;
1359
1513
  mDiff[sFieldGroup] = true;
@@ -1364,9 +1518,12 @@ DateFormat.prototype._getGreatestDiffField = function (aDates, bUTC) {
1364
1518
  }
1365
1519
  return null;
1366
1520
  };
1367
- DateFormat.prototype._parse = function (sValue, aFormatArray, bUTC, bStrict) {
1521
+ DateFormat.prototype._parse = function (sValue, aFormatArray, bUTC, bStrict, sTimezone) {
1368
1522
  var iIndex = 0, oPart, sSubValue, oResult;
1369
- var oDateValue = { valid: true };
1523
+ var oDateValue = {
1524
+ valid: true,
1525
+ lastTimezonePatternSymbol: ''
1526
+ };
1370
1527
  var oParseConf = {
1371
1528
  formatArray: aFormatArray,
1372
1529
  dateValue: oDateValue,
@@ -1376,7 +1533,10 @@ DateFormat.prototype._parse = function (sValue, aFormatArray, bUTC, bStrict) {
1376
1533
  sSubValue = sValue.substr(iIndex);
1377
1534
  oPart = aFormatArray[i];
1378
1535
  oParseConf.index = i;
1379
- oResult = this.oSymbols[oPart.symbol || ''].parse(sSubValue, oPart, this, oParseConf) || {};
1536
+ oResult = this.oSymbols[oPart.symbol || ''].parse(sSubValue, oPart, this, oParseConf, sTimezone) || {};
1537
+ if (oResult.tzDiff !== undefined || oResult.timezone) {
1538
+ oResult.lastTimezonePatternSymbol = oPart.symbol;
1539
+ }
1380
1540
  oDateValue = extend(oDateValue, oResult);
1381
1541
  if (oResult.valid === false) {
1382
1542
  break;
@@ -1396,7 +1556,7 @@ DateFormat.prototype._parse = function (sValue, aFormatArray, bUTC, bStrict) {
1396
1556
  }
1397
1557
  return oDateValue;
1398
1558
  };
1399
- DateFormat.prototype._parseInterval = function (sValue, sCalendarType, bUTC, bStrict) {
1559
+ DateFormat.prototype._parseInterval = function (sValue, sCalendarType, bUTC, bStrict, sTimezone) {
1400
1560
  var aDateValues, iRepeat, oDateValue;
1401
1561
  this.intervalPatterns.some(function (sPattern) {
1402
1562
  var aFormatArray = this.parseCldrDatePattern(sPattern);
@@ -1408,7 +1568,7 @@ DateFormat.prototype._parseInterval = function (sValue, sCalendarType, bUTC, bSt
1408
1568
  }
1409
1569
  }
1410
1570
  if (iRepeat === undefined) {
1411
- oDateValue = this._parse(sValue, aFormatArray, bUTC, bStrict);
1571
+ oDateValue = this._parse(sValue, aFormatArray, bUTC, bStrict, sTimezone);
1412
1572
  if (oDateValue.index === 0 || oDateValue.index < sValue.length) {
1413
1573
  oDateValue.valid = false;
1414
1574
  }
@@ -1422,13 +1582,13 @@ DateFormat.prototype._parseInterval = function (sValue, sCalendarType, bUTC, bSt
1422
1582
  return true;
1423
1583
  } else {
1424
1584
  aDateValues = [];
1425
- oDateValue = this._parse(sValue, aFormatArray.slice(0, iRepeat), bUTC, bStrict);
1585
+ oDateValue = this._parse(sValue, aFormatArray.slice(0, iRepeat), bUTC, bStrict, sTimezone);
1426
1586
  if (oDateValue.valid === false) {
1427
1587
  return;
1428
1588
  }
1429
1589
  aDateValues.push(oDateValue);
1430
1590
  var iLength = oDateValue.index;
1431
- oDateValue = this._parse(sValue.substring(iLength), aFormatArray.slice(iRepeat), bUTC, bStrict);
1591
+ oDateValue = this._parse(sValue.substring(iLength), aFormatArray.slice(iRepeat), bUTC, bStrict, sTimezone);
1432
1592
  if (oDateValue.index === 0 || oDateValue.index + iLength < sValue.length) {
1433
1593
  oDateValue.valid = false;
1434
1594
  }
@@ -1441,65 +1601,51 @@ DateFormat.prototype._parseInterval = function (sValue, sCalendarType, bUTC, bSt
1441
1601
  }.bind(this));
1442
1602
  return aDateValues;
1443
1603
  };
1444
- var fnCreateDate = function (oDateValue, sCalendarType, bUTC, bStrict) {
1604
+ var convertToTimezone = function (oJSDate, sTimezone, bUTC) {
1605
+ if (!bUTC && isValidDateObject(oJSDate)) {
1606
+ return TimezoneUtil.convertToTimezone(oJSDate, sTimezone);
1607
+ }
1608
+ return oJSDate;
1609
+ };
1610
+ var fnCreateDate = function (oDateValue, sCalendarType, bUTC, bStrict, sTimezone) {
1611
+ if (!oDateValue.valid) {
1612
+ return null;
1613
+ }
1445
1614
  var oDate, iYear = typeof oDateValue.year === 'number' ? oDateValue.year : 1970;
1446
- if (oDateValue.valid) {
1447
- if (bUTC || oDateValue.tzDiff !== undefined) {
1448
- oDate = UniversalDate.getInstance(new Date(0), sCalendarType);
1449
- oDate.setUTCEra(oDateValue.era || UniversalDate.getCurrentEra(sCalendarType));
1450
- oDate.setUTCFullYear(iYear);
1451
- oDate.setUTCMonth(oDateValue.month || 0);
1452
- oDate.setUTCDate(oDateValue.day || 1);
1453
- oDate.setUTCHours(oDateValue.hour || 0);
1454
- oDate.setUTCMinutes(oDateValue.minute || 0);
1455
- oDate.setUTCSeconds(oDateValue.second || 0);
1456
- oDate.setUTCMilliseconds(oDateValue.millisecond || 0);
1457
- if (bStrict && (oDateValue.day || 1) !== oDate.getUTCDate()) {
1458
- oDateValue.valid = false;
1459
- oDate = undefined;
1460
- } else {
1461
- if (oDateValue.tzDiff) {
1462
- oDate.setUTCMinutes((oDateValue.minute || 0) + oDateValue.tzDiff);
1463
- }
1464
- if (oDateValue.week !== undefined && (oDateValue.month === undefined || oDateValue.day === undefined)) {
1465
- oDate.setUTCWeek({
1466
- year: oDateValue.weekYear || oDateValue.year,
1467
- week: oDateValue.week
1468
- });
1469
- if (oDateValue.dayNumberOfWeek !== undefined) {
1470
- oDate.setUTCDate(oDate.getUTCDate() + oDateValue.dayNumberOfWeek - 1);
1471
- }
1472
- }
1473
- }
1474
- } else {
1475
- oDate = UniversalDate.getInstance(new Date(1970, 0, 1, 0, 0, 0), sCalendarType);
1476
- oDate.setEra(oDateValue.era || UniversalDate.getCurrentEra(sCalendarType));
1477
- oDate.setFullYear(iYear);
1478
- oDate.setMonth(oDateValue.month || 0);
1479
- oDate.setDate(oDateValue.day || 1);
1480
- oDate.setHours(oDateValue.hour || 0);
1481
- oDate.setMinutes(oDateValue.minute || 0);
1482
- oDate.setSeconds(oDateValue.second || 0);
1483
- oDate.setMilliseconds(oDateValue.millisecond || 0);
1484
- if (bStrict && (oDateValue.day || 1) !== oDate.getDate()) {
1485
- oDateValue.valid = false;
1486
- oDate = undefined;
1487
- } else if (oDateValue.week !== undefined && (oDateValue.month === undefined || oDateValue.day === undefined)) {
1488
- oDate.setWeek({
1489
- year: oDateValue.weekYear || oDateValue.year,
1490
- week: oDateValue.week
1491
- });
1492
- if (oDateValue.dayNumberOfWeek !== undefined) {
1493
- oDate.setDate(oDate.getDate() + oDateValue.dayNumberOfWeek - 1);
1494
- }
1495
- }
1615
+ oDate = UniversalDate.getInstance(new Date(0), sCalendarType);
1616
+ oDate.setUTCEra(oDateValue.era || UniversalDate.getCurrentEra(sCalendarType));
1617
+ oDate.setUTCFullYear(iYear);
1618
+ oDate.setUTCMonth(oDateValue.month || 0);
1619
+ oDate.setUTCDate(oDateValue.day || 1);
1620
+ oDate.setUTCHours(oDateValue.hour || 0);
1621
+ oDate.setUTCMinutes(oDateValue.minute || 0);
1622
+ oDate.setUTCSeconds(oDateValue.second || 0);
1623
+ oDate.setUTCMilliseconds(oDateValue.millisecond || 0);
1624
+ if (bStrict && (oDateValue.day || 1) !== oDate.getUTCDate()) {
1625
+ return null;
1626
+ }
1627
+ if (oDateValue.week !== undefined && (oDateValue.month === undefined || oDateValue.day === undefined)) {
1628
+ oDate.setUTCWeek({
1629
+ year: oDateValue.weekYear || oDateValue.year,
1630
+ week: oDateValue.week
1631
+ });
1632
+ if (oDateValue.dayNumberOfWeek !== undefined) {
1633
+ oDate.setUTCDate(oDate.getUTCDate() + oDateValue.dayNumberOfWeek - 1);
1496
1634
  }
1497
- if (oDateValue.valid) {
1498
- oDate = oDate.getJSDate();
1499
- return oDate;
1635
+ }
1636
+ oDate = oDate.getJSDate();
1637
+ if (!bUTC && (oDateValue.lastTimezonePatternSymbol === 'V' && oDateValue.timezone || oDateValue.tzDiff === undefined)) {
1638
+ if (oDateValue.timezone) {
1639
+ sTimezone = oDateValue.timezone;
1640
+ }
1641
+ if (sTimezone) {
1642
+ oDateValue.tzDiff = TimezoneUtil.calculateOffset(oDate, sTimezone);
1500
1643
  }
1501
1644
  }
1502
- return null;
1645
+ if (oDateValue.tzDiff) {
1646
+ oDate.setUTCSeconds((oDateValue.second || 0) + oDateValue.tzDiff);
1647
+ }
1648
+ return oDate;
1503
1649
  };
1504
1650
  function mergeWithoutOverwrite(object1, object2) {
1505
1651
  if (object1 === object2) {
@@ -1522,13 +1668,33 @@ function isValidDateRange(oStartDate, oEndDate) {
1522
1668
  }
1523
1669
  return true;
1524
1670
  }
1671
+ function isValidDateObject(oDate) {
1672
+ return oDate && typeof oDate.getTime === 'function' && !isNaN(oDate.getTime());
1673
+ }
1525
1674
  DateFormat.prototype.parse = function (sValue, bUTC, bStrict) {
1675
+ var bShowDate = this.oFormatOptions.showDate === undefined || this.oFormatOptions.showDate;
1676
+ var bShowTime = this.oFormatOptions.showTime === undefined || this.oFormatOptions.showTime;
1677
+ if (this.type === mDateFormatTypes.DATETIME_WITH_TIMEZONE && (bShowDate && !bShowTime || !bShowDate && bShowTime)) {
1678
+ throw new TypeError('The input can only be parsed back to date if both date and time are supplied.');
1679
+ }
1680
+ var sTimezone;
1681
+ if (bUTC === undefined && this.type !== mDateFormatTypes.DATETIME_WITH_TIMEZONE) {
1682
+ bUTC = this.oFormatOptions.UTC;
1683
+ }
1684
+ var bUTCInputParameter = bUTC;
1685
+ if (this.type === mDateFormatTypes.DATETIME_WITH_TIMEZONE) {
1686
+ sTimezone = bUTC;
1687
+ bUTC = false;
1688
+ checkTimezoneParameterType(sTimezone);
1689
+ if (sTimezone && !TimezoneUtil.isValidTimezone(sTimezone)) {
1690
+ Log.error('The given timezone isn\'t valid.');
1691
+ return null;
1692
+ }
1693
+ }
1526
1694
  sValue = sValue == null ? '' : String(sValue).trim();
1527
1695
  var oDateValue;
1528
1696
  var sCalendarType = this.oFormatOptions.calendarType;
1529
- if (bUTC === undefined) {
1530
- bUTC = this.oFormatOptions.UTC;
1531
- }
1697
+ sTimezone = sTimezone || Core.getConfiguration().getTimezone();
1532
1698
  if (bStrict === undefined) {
1533
1699
  bStrict = this.oFormatOptions.strictParsing;
1534
1700
  }
@@ -1540,22 +1706,40 @@ DateFormat.prototype.parse = function (sValue, bUTC, bStrict) {
1540
1706
  if (oJSDate) {
1541
1707
  return oJSDate;
1542
1708
  }
1543
- oDateValue = this._parse(sValue, this.aFormatArray, bUTC, bStrict);
1709
+ oDateValue = this._parse(sValue, this.aFormatArray, bUTC, bStrict, sTimezone);
1544
1710
  if (oDateValue.index === 0 || oDateValue.index < sValue.length) {
1545
1711
  oDateValue.valid = false;
1546
1712
  }
1547
- oJSDate = fnCreateDate(oDateValue, sCalendarType, bUTC, bStrict);
1713
+ oJSDate = fnCreateDate(oDateValue, sCalendarType, bUTC, bStrict, sTimezone);
1548
1714
  if (oJSDate) {
1715
+ if (this.type === mDateFormatTypes.DATETIME_WITH_TIMEZONE) {
1716
+ var bShowTimezone = this.oFormatOptions.showTimezone === undefined || this.oFormatOptions.showTimezone;
1717
+ if (!bShowTimezone && bShowDate && bShowTime) {
1718
+ return [
1719
+ oJSDate,
1720
+ undefined
1721
+ ];
1722
+ } else if (bShowTimezone && !bShowDate && !bShowTime) {
1723
+ return [
1724
+ undefined,
1725
+ oDateValue.timezone
1726
+ ];
1727
+ }
1728
+ return [
1729
+ oJSDate,
1730
+ oDateValue.timezone || undefined
1731
+ ];
1732
+ }
1549
1733
  return oJSDate;
1550
1734
  }
1551
1735
  } else {
1552
- var aDateValues = this._parseInterval(sValue, sCalendarType, bUTC, bStrict);
1736
+ var aDateValues = this._parseInterval(sValue, sCalendarType, bUTC, bStrict, sTimezone);
1553
1737
  var oJSDate1, oJSDate2;
1554
1738
  if (aDateValues && aDateValues.length == 2) {
1555
1739
  var oDateValue1 = mergeWithoutOverwrite(aDateValues[0], aDateValues[1]);
1556
1740
  var oDateValue2 = mergeWithoutOverwrite(aDateValues[1], aDateValues[0]);
1557
- oJSDate1 = fnCreateDate(oDateValue1, sCalendarType, bUTC, bStrict);
1558
- oJSDate2 = fnCreateDate(oDateValue2, sCalendarType, bUTC, bStrict);
1741
+ oJSDate1 = fnCreateDate(oDateValue1, sCalendarType, bUTC, bStrict, sTimezone);
1742
+ oJSDate2 = fnCreateDate(oDateValue2, sCalendarType, bUTC, bStrict, sTimezone);
1559
1743
  if (oJSDate1 && oJSDate2) {
1560
1744
  if (this.oFormatOptions.singleIntervalValue && oJSDate1.getTime() === oJSDate2.getTime()) {
1561
1745
  return [
@@ -1581,8 +1765,11 @@ DateFormat.prototype.parse = function (sValue, bUTC, bStrict) {
1581
1765
  if (!this.bIsFallback) {
1582
1766
  var vDate;
1583
1767
  this.aFallbackFormats.every(function (oFallbackFormat) {
1584
- vDate = oFallbackFormat.parse(sValue, bUTC, bStrict);
1768
+ vDate = oFallbackFormat.parse(sValue, bUTCInputParameter, bStrict);
1585
1769
  if (Array.isArray(vDate)) {
1770
+ if (oFallbackFormat.type === mDateFormatTypes.DATETIME_WITH_TIMEZONE) {
1771
+ return false;
1772
+ }
1586
1773
  return !(vDate[0] && vDate[1]);
1587
1774
  } else {
1588
1775
  return !vDate;
@@ -1731,23 +1918,20 @@ DateFormat.prototype.parseRelative = function (sValue, bUTC) {
1731
1918
  }
1732
1919
  }
1733
1920
  };
1734
- DateFormat.prototype.formatRelative = function (oJSDate, bUTC, aRange) {
1735
- var oToday = new Date(), oDateUTC, sScale = this.oFormatOptions.relativeScale || 'day', iDiff, sPattern, iDiffSeconds;
1921
+ DateFormat.prototype.formatRelative = function (oJSDate, bUTC, aRange, sTimezone) {
1922
+ var oToday = convertToTimezone(new Date(), sTimezone), oDateUTC, sScale = this.oFormatOptions.relativeScale || 'day', iDiff, sPattern, iDiffSeconds;
1736
1923
  iDiffSeconds = (oJSDate.getTime() - oToday.getTime()) / 1000;
1737
1924
  if (this.oFormatOptions.relativeScale == 'auto') {
1738
1925
  sScale = this._getScale(iDiffSeconds, this.aRelativeScales);
1926
+ sScale = fixScaleForMonths(oJSDate, oToday, sScale, iDiffSeconds);
1739
1927
  }
1740
1928
  if (!aRange) {
1741
1929
  aRange = this._mRanges[sScale];
1742
1930
  }
1743
1931
  if (sScale == 'year' || sScale == 'month' || sScale == 'day') {
1744
- oToday = new Date(Date.UTC(oToday.getFullYear(), oToday.getMonth(), oToday.getDate()));
1932
+ oToday = new Date(Date.UTC(oToday.getUTCFullYear(), oToday.getUTCMonth(), oToday.getUTCDate()));
1745
1933
  oDateUTC = new Date(0);
1746
- if (bUTC) {
1747
- oDateUTC.setUTCFullYear(oJSDate.getUTCFullYear(), oJSDate.getUTCMonth(), oJSDate.getUTCDate());
1748
- } else {
1749
- oDateUTC.setUTCFullYear(oJSDate.getFullYear(), oJSDate.getMonth(), oJSDate.getDate());
1750
- }
1934
+ oDateUTC.setUTCFullYear(oJSDate.getUTCFullYear(), oJSDate.getUTCMonth(), oJSDate.getUTCDate());
1751
1935
  oJSDate = oDateUTC;
1752
1936
  }
1753
1937
  iDiff = this._getDifference(sScale, [
@@ -1815,6 +1999,21 @@ DateFormat.prototype._getScale = function (iDiffSeconds, aScales) {
1815
1999
  }
1816
2000
  return sScale;
1817
2001
  };
2002
+ function fixScaleForMonths(oJSDate, oToday, sScale, iDiffSeconds) {
2003
+ var iMonthDiff = Math.abs(oJSDate.getUTCMonth() - oToday.getUTCMonth());
2004
+ if (sScale === 'week' && iMonthDiff === 2) {
2005
+ return 'month';
2006
+ } else if (sScale === 'week' && iMonthDiff === 1) {
2007
+ if (oJSDate.getUTCDate() === oToday.getUTCDate() || iDiffSeconds < 0 && oJSDate.getUTCDate() < oToday.getUTCDate() || iDiffSeconds > 0 && oJSDate.getUTCDate() > oToday.getUTCDate()) {
2008
+ return 'month';
2009
+ }
2010
+ } else if (sScale === 'month' && iMonthDiff === 1) {
2011
+ if (iDiffSeconds > 0 && oJSDate.getUTCDate() < oToday.getUTCDate() || iDiffSeconds < 0 && oJSDate.getUTCDate() > oToday.getUTCDate()) {
2012
+ return 'week';
2013
+ }
2014
+ }
2015
+ return sScale;
2016
+ }
1818
2017
  function cutDateFields(oDate, iStartIndex) {
1819
2018
  var aFields = [
1820
2019
  'FullYear',
@@ -1827,21 +2026,21 @@ function cutDateFields(oDate, iStartIndex) {
1827
2026
  ], sMethodName;
1828
2027
  var oDateCopy = new Date(oDate.getTime());
1829
2028
  for (var i = iStartIndex; i < aFields.length; i++) {
1830
- sMethodName = 'set' + aFields[iStartIndex];
2029
+ sMethodName = 'setUTC' + aFields[iStartIndex];
1831
2030
  oDateCopy[sMethodName].apply(oDateCopy, [0]);
1832
2031
  }
1833
2032
  return oDateCopy;
1834
2033
  }
1835
2034
  var mRelativeDiffs = {
1836
2035
  year: function (oFromDate, oToDate) {
1837
- return oToDate.getFullYear() - oFromDate.getFullYear();
2036
+ return oToDate.getUTCFullYear() - oFromDate.getUTCFullYear();
1838
2037
  },
1839
2038
  month: function (oFromDate, oToDate) {
1840
- return oToDate.getMonth() - oFromDate.getMonth() + this.year(oFromDate, oToDate) * 12;
2039
+ return oToDate.getUTCMonth() - oFromDate.getUTCMonth() + this.year(oFromDate, oToDate) * 12;
1841
2040
  },
1842
2041
  week: function (oFromDate, oToDate, oFormat) {
1843
- var iFromDay = oFormat._adaptDayOfWeek(oFromDate.getDay());
1844
- var iToDay = oFormat._adaptDayOfWeek(oToDate.getDay());
2042
+ var iFromDay = oFormat._adaptDayOfWeek(oFromDate.getUTCDay());
2043
+ var iToDay = oFormat._adaptDayOfWeek(oToDate.getUTCDay());
1845
2044
  oFromDate = cutDateFields(oFromDate, 3);
1846
2045
  oToDate = cutDateFields(oToDate, 3);
1847
2046
  return (oToDate.getTime() - oFromDate.getTime() - (iToDay - iFromDay) * oFormat._mScales.day * 1000) / (oFormat._mScales.week * 1000);