@ui5/webcomponents-localization 0.0.0-dc606098a → 0.0.0-dcd8e5389

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.
Files changed (191) hide show
  1. package/.eslintignore +1 -0
  2. package/.npsrc.json +3 -0
  3. package/CHANGELOG.md +449 -0
  4. package/README.md +36 -6
  5. package/dist/Assets-static.d.ts +1 -0
  6. package/dist/Assets-static.js +3 -0
  7. package/dist/Assets-static.js.map +1 -0
  8. package/dist/Assets.d.ts +1 -0
  9. package/dist/Assets.js +1 -0
  10. package/dist/Assets.js.map +1 -0
  11. package/dist/DateFormat.d.ts +6 -0
  12. package/dist/DateFormat.js +6 -2
  13. package/dist/DateFormat.js.map +1 -0
  14. package/dist/LocaleData.d.ts +6 -0
  15. package/dist/LocaleData.js +6 -2
  16. package/dist/LocaleData.js.map +1 -0
  17. package/dist/dates/CalendarDate.d.ts +43 -0
  18. package/dist/dates/CalendarDate.js +171 -181
  19. package/dist/dates/CalendarDate.js.map +1 -0
  20. package/dist/dates/ExtremeDates.d.ts +5 -0
  21. package/dist/dates/ExtremeDates.js +29 -0
  22. package/dist/dates/ExtremeDates.js.map +1 -0
  23. package/dist/dates/UI5Date.d.ts +6 -0
  24. package/dist/dates/UI5Date.js +7 -0
  25. package/dist/dates/UI5Date.js.map +1 -0
  26. package/dist/dates/UniversalDate.d.ts +47 -0
  27. package/dist/dates/UniversalDate.js +5 -0
  28. package/dist/dates/UniversalDate.js.map +1 -0
  29. package/dist/dates/calculateWeekNumber.d.ts +5 -0
  30. package/dist/dates/calculateWeekNumber.js +42 -48
  31. package/dist/dates/calculateWeekNumber.js.map +1 -0
  32. package/dist/dates/convertMonthNumbersToMonthNames.d.ts +16 -0
  33. package/dist/dates/convertMonthNumbersToMonthNames.js +30 -0
  34. package/dist/dates/convertMonthNumbersToMonthNames.js.map +1 -0
  35. package/dist/dates/getDaysInMonth.d.ts +3 -0
  36. package/dist/dates/getDaysInMonth.js +10 -0
  37. package/dist/dates/getDaysInMonth.js.map +1 -0
  38. package/dist/dates/getRoundedTimestamp.d.ts +7 -0
  39. package/dist/dates/getRoundedTimestamp.js +15 -0
  40. package/dist/dates/getRoundedTimestamp.js.map +1 -0
  41. package/dist/dates/getTodayUTCTimestamp.d.ts +7 -0
  42. package/dist/dates/getTodayUTCTimestamp.js +9 -0
  43. package/dist/dates/getTodayUTCTimestamp.js.map +1 -0
  44. package/dist/dates/modifyDateBy.d.ts +13 -0
  45. package/dist/dates/modifyDateBy.js +40 -0
  46. package/dist/dates/modifyDateBy.js.map +1 -0
  47. package/dist/dates/transformDateToSecondaryType.d.ts +7 -0
  48. package/dist/dates/transformDateToSecondaryType.js +18 -0
  49. package/dist/dates/transformDateToSecondaryType.js.map +1 -0
  50. package/dist/features/calendar/Buddhist.js +1 -0
  51. package/dist/features/calendar/Buddhist.js.map +1 -0
  52. package/dist/features/calendar/Gregorian.js +1 -0
  53. package/dist/features/calendar/Gregorian.js.map +1 -0
  54. package/dist/features/calendar/Islamic.js +1 -0
  55. package/dist/features/calendar/Islamic.js.map +1 -0
  56. package/dist/features/calendar/Japanese.js +1 -0
  57. package/dist/features/calendar/Japanese.js.map +1 -0
  58. package/dist/features/calendar/Persian.js +1 -0
  59. package/dist/features/calendar/Persian.js.map +1 -0
  60. package/dist/generated/assets/cldr/ar.json +2816 -5904
  61. package/dist/generated/assets/cldr/ar_EG.json +2816 -5904
  62. package/dist/generated/assets/cldr/ar_SA.json +2815 -5904
  63. package/dist/generated/assets/cldr/bg.json +2733 -4977
  64. package/dist/generated/assets/cldr/ca.json +2680 -4994
  65. package/dist/generated/assets/cldr/cs.json +2716 -5496
  66. package/dist/generated/assets/cldr/cy.json +2799 -0
  67. package/dist/generated/assets/cldr/da.json +2597 -4886
  68. package/dist/generated/assets/cldr/de.json +2614 -4914
  69. package/dist/generated/assets/cldr/de_AT.json +2614 -4915
  70. package/dist/generated/assets/cldr/de_CH.json +2614 -4913
  71. package/dist/generated/assets/cldr/el.json +2595 -4881
  72. package/dist/generated/assets/cldr/el_CY.json +2595 -4881
  73. package/dist/generated/assets/cldr/en.json +2583 -4968
  74. package/dist/generated/assets/cldr/en_AU.json +2592 -4960
  75. package/dist/generated/assets/cldr/en_GB.json +2584 -4969
  76. package/dist/generated/assets/cldr/en_HK.json +2593 -4975
  77. package/dist/generated/assets/cldr/en_IE.json +2584 -4969
  78. package/dist/generated/assets/cldr/en_IN.json +2588 -4970
  79. package/dist/generated/assets/cldr/en_NZ.json +2584 -4969
  80. package/dist/generated/assets/cldr/en_PG.json +2584 -4970
  81. package/dist/generated/assets/cldr/en_SG.json +2588 -4971
  82. package/dist/generated/assets/cldr/en_ZA.json +2584 -4970
  83. package/dist/generated/assets/cldr/es.json +2640 -4910
  84. package/dist/generated/assets/cldr/es_AR.json +2645 -4912
  85. package/dist/generated/assets/cldr/es_BO.json +2645 -4911
  86. package/dist/generated/assets/cldr/es_CL.json +2645 -4912
  87. package/dist/generated/assets/cldr/es_CO.json +2644 -4911
  88. package/dist/generated/assets/cldr/es_MX.json +2645 -4913
  89. package/dist/generated/assets/cldr/es_PE.json +2645 -4911
  90. package/dist/generated/assets/cldr/es_UY.json +2645 -4913
  91. package/dist/generated/assets/cldr/es_VE.json +2645 -4912
  92. package/dist/generated/assets/cldr/et.json +2599 -4965
  93. package/dist/generated/assets/cldr/fa.json +2618 -4881
  94. package/dist/generated/assets/cldr/fi.json +2726 -5006
  95. package/dist/generated/assets/cldr/fr.json +2590 -4977
  96. package/dist/generated/assets/cldr/fr_BE.json +2590 -4977
  97. package/dist/generated/assets/cldr/fr_CA.json +2605 -4971
  98. package/dist/generated/assets/cldr/fr_CH.json +2607 -4995
  99. package/dist/generated/assets/cldr/fr_LU.json +2590 -4977
  100. package/dist/generated/assets/cldr/he.json +2711 -5376
  101. package/dist/generated/assets/cldr/hi.json +2602 -4827
  102. package/dist/generated/assets/cldr/hr.json +2652 -4917
  103. package/dist/generated/assets/cldr/hu.json +2598 -4854
  104. package/dist/generated/assets/cldr/id.json +2544 -4656
  105. package/dist/generated/assets/cldr/it.json +2590 -4948
  106. package/dist/generated/assets/cldr/it_CH.json +2590 -4948
  107. package/dist/generated/assets/cldr/ja.json +2663 -4828
  108. package/dist/generated/assets/cldr/kk.json +2589 -4723
  109. package/dist/generated/assets/cldr/ko.json +2641 -4736
  110. package/dist/generated/assets/cldr/lt.json +2720 -5479
  111. package/dist/generated/assets/cldr/lv.json +2644 -5110
  112. package/dist/generated/assets/cldr/ms.json +2538 -4513
  113. package/dist/generated/assets/cldr/nb.json +2621 -4975
  114. package/dist/generated/assets/cldr/nl.json +2601 -4882
  115. package/dist/generated/assets/cldr/nl_BE.json +2601 -4882
  116. package/dist/generated/assets/cldr/pl.json +2723 -5174
  117. package/dist/generated/assets/cldr/pt.json +2607 -4803
  118. package/dist/generated/assets/cldr/pt_PT.json +2658 -4938
  119. package/dist/generated/assets/cldr/ro.json +2645 -5088
  120. package/dist/generated/assets/cldr/ru.json +2704 -5405
  121. package/dist/generated/assets/cldr/ru_UA.json +2704 -5405
  122. package/dist/generated/assets/cldr/sk.json +2726 -5368
  123. package/dist/generated/assets/cldr/sl.json +2688 -5338
  124. package/dist/generated/assets/cldr/sr.json +2663 -5124
  125. package/dist/generated/assets/cldr/sr_Latn.json +2668 -0
  126. package/dist/generated/assets/cldr/sv.json +2631 -5009
  127. package/dist/generated/assets/cldr/th.json +2635 -4795
  128. package/dist/generated/assets/cldr/tr.json +2612 -4977
  129. package/dist/generated/assets/cldr/uk.json +2684 -5351
  130. package/dist/generated/assets/cldr/vi.json +2555 -4671
  131. package/dist/generated/assets/cldr/zh_CN.json +2555 -4630
  132. package/dist/generated/assets/cldr/zh_HK.json +2563 -4638
  133. package/dist/generated/assets/cldr/zh_SG.json +2562 -4638
  134. package/dist/generated/assets/cldr/zh_TW.json +2587 -4726
  135. package/dist/generated/json-imports/LocaleData-static.js +90 -0
  136. package/dist/generated/json-imports/LocaleData.js +88 -91
  137. package/dist/getCachedLocaleDataInstance.d.ts +4 -0
  138. package/dist/getCachedLocaleDataInstance.js +10 -0
  139. package/dist/getCachedLocaleDataInstance.js.map +1 -0
  140. package/dist/locale/getLocaleData.d.ts +11 -0
  141. package/dist/locale/getLocaleData.js +13 -17
  142. package/dist/locale/getLocaleData.js.map +1 -0
  143. package/dist/sap/base/Log.js +28 -37
  144. package/dist/sap/base/assert.js +5 -10
  145. package/dist/sap/base/util/LoaderExtensions.d.ts +4 -0
  146. package/dist/sap/base/util/LoaderExtensions.js +11 -4
  147. package/dist/sap/base/util/LoaderExtensions.js.map +1 -0
  148. package/dist/sap/base/util/array/uniqueSort.js +7 -7
  149. package/dist/sap/base/util/deepEqual.js +1 -5
  150. package/dist/sap/ui/base/Interface.js +3 -5
  151. package/dist/sap/ui/base/Metadata.js +1 -3
  152. package/dist/sap/ui/base/Object.js +26 -2
  153. package/dist/sap/ui/core/Configuration.d.ts +17 -0
  154. package/dist/sap/ui/core/Configuration.js +21 -0
  155. package/dist/sap/ui/core/Configuration.js.map +1 -0
  156. package/dist/sap/ui/core/Core.d.ts +26 -0
  157. package/dist/sap/ui/core/Core.js +7 -32
  158. package/dist/sap/ui/core/Core.js.map +1 -0
  159. package/dist/sap/ui/core/FormatSettings.d.ts +10 -0
  160. package/dist/sap/ui/core/FormatSettings.js +14 -0
  161. package/dist/sap/ui/core/FormatSettings.js.map +1 -0
  162. package/dist/sap/ui/core/Locale.js +70 -31
  163. package/dist/sap/ui/core/LocaleData.js +206 -1525
  164. package/dist/sap/ui/core/date/Buddhist.js +0 -6
  165. package/dist/sap/ui/core/date/CalendarUtils.js +36 -0
  166. package/dist/sap/ui/core/date/CalendarWeekNumbering.js +7 -0
  167. package/dist/sap/ui/core/date/Islamic.js +3 -3
  168. package/dist/sap/ui/core/date/Japanese.js +4 -4
  169. package/dist/sap/ui/core/date/UI5Date.js +305 -0
  170. package/dist/sap/ui/core/date/UniversalDate.js +85 -25
  171. package/dist/sap/ui/core/format/DateFormat.js +819 -497
  172. package/dist/sap/ui/core/format/TimezoneUtil.js +89 -0
  173. package/package-scripts.cjs +37 -0
  174. package/package.json +14 -13
  175. package/tsconfig.json +14 -0
  176. package/used-modules.txt +5 -1
  177. package/package-scripts.js +0 -31
  178. package/src/Assets.js +0 -2
  179. package/src/DateFormat.js +0 -3
  180. package/src/LocaleData.js +0 -3
  181. package/src/dates/CalendarDate.js +0 -203
  182. package/src/dates/calculateWeekNumber.js +0 -51
  183. package/src/locale/getLocaleData.js +0 -27
  184. package/src/sap/base/util/LoaderExtensions.js +0 -7
  185. package/src/sap/ui/core/Core.js +0 -38
  186. /package/{config/.eslintrc.js → .eslintrc.cjs} +0 -0
  187. /package/{src/features/calendar/Buddhist.js → dist/features/calendar/Buddhist.d.ts} +0 -0
  188. /package/{src/features/calendar/Gregorian.js → dist/features/calendar/Gregorian.d.ts} +0 -0
  189. /package/{src/features/calendar/Islamic.js → dist/features/calendar/Islamic.d.ts} +0 -0
  190. /package/{src/features/calendar/Japanese.js → dist/features/calendar/Japanese.d.ts} +0 -0
  191. /package/{src/features/calendar/Persian.js → dist/features/calendar/Persian.d.ts} +0 -0
@@ -1,17 +1,33 @@
1
- import Core from '../Core.js';
2
1
  import CalendarType from '../CalendarType.js';
3
2
  import Locale from '../Locale.js';
4
3
  import LocaleData from '../LocaleData.js';
4
+ import UI5Date from '../date/UI5Date.js';
5
5
  import UniversalDate from '../date/UniversalDate.js';
6
+ import CalendarUtils from '../date/CalendarUtils.js';
7
+ import CalendarWeekNumbering from '../date/CalendarWeekNumbering.js';
8
+ import TimezoneUtil from './TimezoneUtil.js';
6
9
  import deepEqual from '../../../base/util/deepEqual.js';
7
10
  import formatMessage from '../../../base/strings/formatMessage.js';
8
11
  import Log from '../../../base/Log.js';
9
12
  import extend from '../../../base/util/extend.js';
13
+ import Configuration from '../Configuration.js';
10
14
  var DateFormat = function () {
11
15
  throw new Error();
12
16
  };
17
+ var mDateFormatTypes = {
18
+ DATE: 'date',
19
+ TIME: 'time',
20
+ DATETIME: 'datetime',
21
+ DATETIME_WITH_TIMEZONE: 'datetimeWithTimezone'
22
+ };
13
23
  var mCldrDatePattern = {};
24
+ var checkTimezoneParameterType = function (sTimezone) {
25
+ if (typeof sTimezone !== 'string' && !(sTimezone instanceof String) && sTimezone != null) {
26
+ throw new TypeError('The given timezone must be a string.');
27
+ }
28
+ };
14
29
  DateFormat.oDateInfo = {
30
+ type: mDateFormatTypes.DATE,
15
31
  oDefaultFormatOptions: {
16
32
  style: 'medium',
17
33
  relativeScale: 'day',
@@ -64,6 +80,7 @@ DateFormat.oDateInfo = {
64
80
  ]
65
81
  };
66
82
  DateFormat.oDateTimeInfo = {
83
+ type: mDateFormatTypes.DATETIME,
67
84
  oDefaultFormatOptions: {
68
85
  style: 'medium',
69
86
  relativeScale: 'auto',
@@ -126,7 +143,41 @@ DateFormat.oDateTimeInfo = {
126
143
  'Seconds'
127
144
  ]
128
145
  };
146
+ DateFormat._getDateTimeWithTimezoneInfo = function (oFormatOptions) {
147
+ var bShowDate = oFormatOptions.showDate === undefined || oFormatOptions.showDate;
148
+ var bShowTime = oFormatOptions.showTime === undefined || oFormatOptions.showTime;
149
+ var bShowTimezone = oFormatOptions.showTimezone === undefined || oFormatOptions.showTimezone;
150
+ var oBaselineType = DateFormat.oDateTimeInfo;
151
+ if (bShowDate && !bShowTime) {
152
+ oBaselineType = DateFormat.oDateInfo;
153
+ } else if (!bShowDate && bShowTime) {
154
+ oBaselineType = DateFormat.oTimeInfo;
155
+ }
156
+ return Object.assign({}, oBaselineType, {
157
+ type: mDateFormatTypes.DATETIME_WITH_TIMEZONE,
158
+ getTimezonePattern: function (sPattern) {
159
+ if (!bShowDate && !bShowTime && bShowTimezone) {
160
+ return 'VV';
161
+ } else if (!bShowTimezone) {
162
+ return sPattern;
163
+ } else {
164
+ return sPattern + ' VV';
165
+ }
166
+ },
167
+ getPattern: function (oLocaleData, sStyle, sCalendarType) {
168
+ if (!bShowDate && !bShowTime && bShowTimezone) {
169
+ return 'VV';
170
+ }
171
+ if (!bShowTimezone) {
172
+ return oBaselineType.getPattern(oLocaleData, sStyle, sCalendarType);
173
+ }
174
+ var sPattern = oBaselineType.getPattern(oLocaleData, sStyle, sCalendarType);
175
+ return oLocaleData.applyTimezonePattern(sPattern);
176
+ }
177
+ });
178
+ };
129
179
  DateFormat.oTimeInfo = {
180
+ type: mDateFormatTypes.TIME,
130
181
  oDefaultFormatOptions: {
131
182
  style: 'medium',
132
183
  relativeScale: 'auto',
@@ -179,6 +230,27 @@ DateFormat.getDateInstance = function (oFormatOptions, oLocale) {
179
230
  DateFormat.getDateTimeInstance = function (oFormatOptions, oLocale) {
180
231
  return this.createInstance(oFormatOptions, oLocale, this.oDateTimeInfo);
181
232
  };
233
+ DateFormat.getDateTimeWithTimezoneInstance = function (oFormatOptions, oLocale) {
234
+ if (oFormatOptions && !(oFormatOptions instanceof Locale)) {
235
+ oFormatOptions = Object.assign({}, oFormatOptions);
236
+ if (typeof oFormatOptions.showTimezone === 'string') {
237
+ var sShowTimezone = oFormatOptions.showTimezone;
238
+ if (oFormatOptions.showDate === undefined && oFormatOptions.showTime === undefined) {
239
+ if (sShowTimezone === 'Hide') {
240
+ oFormatOptions.showTimezone = false;
241
+ } else if (sShowTimezone === 'Only') {
242
+ oFormatOptions.showDate = false;
243
+ oFormatOptions.showTime = false;
244
+ }
245
+ }
246
+ oFormatOptions.showTimezone = sShowTimezone !== 'Hide';
247
+ }
248
+ if (oFormatOptions.showDate === false && oFormatOptions.showTime === false && oFormatOptions.showTimezone === false) {
249
+ throw new TypeError('Invalid Configuration. One of the following format options must be true: showDate, showTime or showTimezone.');
250
+ }
251
+ }
252
+ return this.createInstance(oFormatOptions, oLocale, DateFormat._getDateTimeWithTimezoneInfo(oFormatOptions || {}));
253
+ };
182
254
  DateFormat.getTimeInstance = function (oFormatOptions, oLocale) {
183
255
  return this.createInstance(oFormatOptions, oLocale, this.oTimeInfo);
184
256
  };
@@ -187,20 +259,37 @@ function createIntervalPatternWithNormalConnector(oFormat) {
187
259
  sPattern = sPattern.replace(/[^\{\}01 ]/, '-');
188
260
  return sPattern.replace(/\{(0|1)\}/g, oFormat.oFormatOptions.pattern);
189
261
  }
190
- DateFormat.createInstance = function (oFormatOptions, oLocale, oInfo) {
191
- var oFormat = Object.create(this.prototype);
262
+ DateFormat.createInstance = function (oFormatOptions, oLocale, oInfo, bIsFallback) {
263
+ var aFallbackFormatOptions, oFormat, sPattern;
264
+ oFormat = Object.create(this.prototype);
192
265
  if (oFormatOptions instanceof Locale) {
193
266
  oLocale = oFormatOptions;
194
267
  oFormatOptions = undefined;
195
268
  }
196
269
  if (!oLocale) {
197
- oLocale = Core.getConfiguration().getFormatSettings().getFormatLocale();
270
+ oLocale = Configuration.getFormatSettings().getFormatLocale();
198
271
  }
199
272
  oFormat.oLocale = oLocale;
200
273
  oFormat.oLocaleData = LocaleData.getInstance(oLocale);
201
274
  oFormat.oFormatOptions = extend({}, oInfo.oDefaultFormatOptions, oFormatOptions);
275
+ if (oInfo.type === mDateFormatTypes.DATETIME_WITH_TIMEZONE) {
276
+ oFormat.oFormatOptions.interval = false;
277
+ oFormat.oFormatOptions.singleIntervalValue = false;
278
+ oFormat.oFormatOptions.UTC = false;
279
+ } else {
280
+ oFormat.oFormatOptions.showTimezone = undefined;
281
+ oFormat.oFormatOptions.showDate = undefined;
282
+ oFormat.oFormatOptions.showTime = undefined;
283
+ }
284
+ oFormat.type = oInfo.type;
202
285
  if (!oFormat.oFormatOptions.calendarType) {
203
- oFormat.oFormatOptions.calendarType = Core.getConfiguration().getCalendarType();
286
+ oFormat.oFormatOptions.calendarType = Configuration.getCalendarType();
287
+ }
288
+ if (oFormat.oFormatOptions.firstDayOfWeek === undefined && oFormat.oFormatOptions.minimalDaysInFirstWeek !== undefined || oFormat.oFormatOptions.firstDayOfWeek !== undefined && oFormat.oFormatOptions.minimalDaysInFirstWeek === undefined) {
289
+ throw new TypeError('Format options firstDayOfWeek and minimalDaysInFirstWeek need both to be set, but only one was provided.');
290
+ }
291
+ if (oFormat.oFormatOptions.calendarWeekNumbering && !Object.values(CalendarWeekNumbering).includes(oFormat.oFormatOptions.calendarWeekNumbering)) {
292
+ throw new TypeError('Illegal format option calendarWeekNumbering: \'' + oFormat.oFormatOptions.calendarWeekNumbering + '\'');
204
293
  }
205
294
  if (!oFormat.oFormatOptions.pattern) {
206
295
  if (oFormat.oFormatOptions.format) {
@@ -225,30 +314,27 @@ DateFormat.createInstance = function (oFormatOptions, oLocale, oInfo) {
225
314
  var sCommonConnectorPattern = createIntervalPatternWithNormalConnector(oFormat);
226
315
  oFormat.intervalPatterns.push(sCommonConnectorPattern);
227
316
  }
228
- if (!oFormat.oFormatOptions.fallback) {
229
- if (!oInfo.oFallbackFormats) {
230
- oInfo.oFallbackFormats = {};
317
+ if (!bIsFallback) {
318
+ aFallbackFormatOptions = oInfo.aFallbackFormatOptions;
319
+ if (oInfo.bShortFallbackFormatOptions) {
320
+ sPattern = oInfo.getPattern(oFormat.oLocaleData, 'short');
321
+ aFallbackFormatOptions = aFallbackFormatOptions.concat(DateFormat._createFallbackOptionsWithoutDelimiter(sPattern));
231
322
  }
232
- var sLocale = oLocale.toString(), sCalendarType = oFormat.oFormatOptions.calendarType, sKey = sLocale + '-' + sCalendarType, sPattern, aFallbackFormatOptions;
233
323
  if (oFormat.oFormatOptions.pattern && oInfo.bPatternFallbackWithoutDelimiter) {
234
- sKey = sKey + '-' + oFormat.oFormatOptions.pattern;
235
- }
236
- if (oFormat.oFormatOptions.interval) {
237
- sKey = sKey + '-' + 'interval';
324
+ aFallbackFormatOptions = DateFormat._createFallbackOptionsWithoutDelimiter(oFormat.oFormatOptions.pattern).concat(aFallbackFormatOptions);
238
325
  }
239
- var oFallbackFormats = oInfo.oFallbackFormats[sKey] ? Object.assign({}, oInfo.oFallbackFormats[sKey]) : undefined;
240
- if (!oFallbackFormats) {
241
- aFallbackFormatOptions = oInfo.aFallbackFormatOptions;
242
- if (oInfo.bShortFallbackFormatOptions) {
243
- sPattern = oInfo.getPattern(oFormat.oLocaleData, 'short');
244
- aFallbackFormatOptions = aFallbackFormatOptions.concat(DateFormat._createFallbackOptionsWithoutDelimiter(sPattern));
245
- }
246
- if (oFormat.oFormatOptions.pattern && oInfo.bPatternFallbackWithoutDelimiter) {
247
- aFallbackFormatOptions = DateFormat._createFallbackOptionsWithoutDelimiter(oFormat.oFormatOptions.pattern).concat(aFallbackFormatOptions);
326
+ aFallbackFormatOptions = aFallbackFormatOptions.reduce(function (aFallbacks, oOptions) {
327
+ var aKeys = Object.keys(oOptions), bDuplicate = aFallbacks.some(function (oOptions0) {
328
+ return Object.keys(oOptions0).length === aKeys.length && aKeys.every(function (sKey) {
329
+ return oOptions0[sKey] === oOptions[sKey];
330
+ });
331
+ });
332
+ if (!bDuplicate) {
333
+ aFallbacks.push(oOptions);
248
334
  }
249
- oFallbackFormats = DateFormat._createFallbackFormat(aFallbackFormatOptions, sCalendarType, oLocale, oInfo, oFormat.oFormatOptions.interval);
250
- }
251
- oFormat.aFallbackFormats = oFallbackFormats;
335
+ return aFallbacks;
336
+ }, []);
337
+ oFormat.aFallbackFormats = DateFormat._createFallbackFormat(aFallbackFormatOptions, oFormat.oFormatOptions.calendarType, oLocale, oInfo, oFormat.oFormatOptions);
252
338
  }
253
339
  oFormat.oRequiredParts = oInfo.oRequiredParts;
254
340
  oFormat.aRelativeScales = oInfo.aRelativeScales;
@@ -282,21 +368,32 @@ DateFormat.prototype.init = function () {
282
368
  this.aErasNarrow = this.oLocaleData.getEras('narrow', sCalendarType);
283
369
  this.aErasAbbrev = this.oLocaleData.getEras('abbreviated', sCalendarType);
284
370
  this.aErasWide = this.oLocaleData.getEras('wide', sCalendarType);
285
- this.aDayPeriods = this.oLocaleData.getDayPeriods('abbreviated', sCalendarType);
371
+ this.aDayPeriodsAbbrev = this.oLocaleData.getDayPeriods('abbreviated', sCalendarType);
372
+ this.aDayPeriodsNarrow = this.oLocaleData.getDayPeriods('narrow', sCalendarType);
373
+ this.aDayPeriodsWide = this.oLocaleData.getDayPeriods('wide', sCalendarType);
374
+ this.oFlexibleDayPeriodsAbbrev = this.oLocaleData.getFlexibleDayPeriods('abbreviated', sCalendarType);
375
+ this.oFlexibleDayPeriodsNarrow = this.oLocaleData.getFlexibleDayPeriods('narrow', sCalendarType);
376
+ this.oFlexibleDayPeriodsWide = this.oLocaleData.getFlexibleDayPeriods('wide', sCalendarType);
377
+ this.oFlexibleDayPeriodsAbbrevSt = this.oLocaleData.getFlexibleDayPeriodsStandAlone('abbreviated', sCalendarType);
378
+ this.oFlexibleDayPeriodsNarrowSt = this.oLocaleData.getFlexibleDayPeriodsStandAlone('narrow', sCalendarType);
379
+ this.oFlexibleDayPeriodsWideSt = this.oLocaleData.getFlexibleDayPeriodsStandAlone('wide', sCalendarType);
286
380
  this.aFormatArray = this.parseCldrDatePattern(this.oFormatOptions.pattern);
287
381
  this.sAllowedCharacters = this.getAllowedCharacters(this.aFormatArray);
288
382
  };
289
- DateFormat._createFallbackFormat = function (aFallbackFormatOptions, sCalendarType, oLocale, oInfo, bInterval) {
383
+ DateFormat._createFallbackFormat = function (aFallbackFormatOptions, sCalendarType, oLocale, oInfo, oParentFormatOptions) {
290
384
  return aFallbackFormatOptions.map(function (oOptions) {
291
385
  var oFormatOptions = Object.assign({}, oOptions);
292
- if (bInterval) {
386
+ oFormatOptions.showDate = oParentFormatOptions.showDate;
387
+ oFormatOptions.showTime = oParentFormatOptions.showTime;
388
+ oFormatOptions.showTimezone = oParentFormatOptions.showTimezone;
389
+ if (typeof oInfo.getTimezonePattern === 'function' && oFormatOptions.pattern) {
390
+ oFormatOptions.pattern = oInfo.getTimezonePattern(oFormatOptions.pattern);
391
+ }
392
+ if (oParentFormatOptions.interval) {
293
393
  oFormatOptions.interval = true;
294
394
  }
295
395
  oFormatOptions.calendarType = sCalendarType;
296
- oFormatOptions.fallback = true;
297
- var oFallbackFormat = DateFormat.createInstance(oFormatOptions, oLocale, oInfo);
298
- oFallbackFormat.bIsFallback = true;
299
- return oFallbackFormat;
396
+ return DateFormat.createInstance(oFormatOptions, oLocale, oInfo, true);
300
397
  });
301
398
  };
302
399
  DateFormat._createFallbackOptionsWithoutDelimiter = function (sBasePattern) {
@@ -332,53 +429,84 @@ var oParseHelper = {
332
429
  while (iLength < iMaxLength && this.isNumber(sValue.charCodeAt(iLength))) {
333
430
  iLength++;
334
431
  }
335
- if (typeof sValue !== 'string') {
336
- sValue = sValue.toString();
337
- }
338
432
  return sValue.substr(0, iLength);
339
433
  },
340
- findEntry: function (sValue, aList) {
434
+ startsWithIgnoreCase: function (sValue, sSubstring, sLocale) {
435
+ if (sValue.startsWith(sSubstring)) {
436
+ return true;
437
+ }
438
+ try {
439
+ var sSubToLocaleUpperCase = sSubstring.toLocaleUpperCase(sLocale);
440
+ var sValueUpperCase = sValue.toLocaleUpperCase(sLocale);
441
+ if (sSubToLocaleUpperCase.length !== sSubstring.length || sValueUpperCase.length !== sValue.length) {
442
+ return false;
443
+ }
444
+ return sValueUpperCase.startsWith(sSubToLocaleUpperCase);
445
+ } catch (e) {
446
+ return false;
447
+ }
448
+ },
449
+ findEntry: function (sValue, aList, sLocale) {
341
450
  var iFoundIndex = -1, iMatchedLength = 0;
342
451
  for (var j = 0; j < aList.length; j++) {
343
- if (aList[j] && aList[j].length > iMatchedLength && sValue.indexOf(aList[j]) === 0) {
452
+ if (aList[j] && aList[j].length > iMatchedLength && this.startsWithIgnoreCase(sValue, aList[j], sLocale)) {
344
453
  iFoundIndex = j;
345
454
  iMatchedLength = aList[j].length;
346
455
  }
347
456
  }
348
457
  return {
349
458
  index: iFoundIndex,
350
- value: iFoundIndex === -1 ? null : aList[iFoundIndex]
459
+ length: iMatchedLength
351
460
  };
352
461
  },
353
- parseTZ: function (sValue, bISO) {
462
+ parseTZ: function (sValue, bColonSeparated) {
354
463
  var iLength = 0;
355
- var iTZFactor = sValue.charAt(0) == '+' ? -1 : 1;
464
+ var iTZFactor = sValue.charAt(0) === '+' ? -1 : 1;
356
465
  var sPart;
357
466
  iLength++;
358
467
  sPart = this.findNumbers(sValue.substr(iLength), 2);
359
468
  var iTZDiffHour = parseInt(sPart);
360
469
  iLength += 2;
361
- if (bISO) {
470
+ if (bColonSeparated) {
362
471
  iLength++;
363
472
  }
364
473
  sPart = this.findNumbers(sValue.substr(iLength), 2);
365
- iLength += 2;
366
- var iTZDiff = parseInt(sPart);
474
+ var iTZDiff = 0;
475
+ if (sPart) {
476
+ iLength += 2;
477
+ iTZDiff = parseInt(sPart);
478
+ }
367
479
  return {
368
480
  length: iLength,
369
- tzDiff: (iTZDiff + 60 * iTZDiffHour) * iTZFactor
481
+ tzDiff: (iTZDiff + 60 * iTZDiffHour) * 60 * iTZFactor
370
482
  };
371
483
  },
372
- checkValid: function (sType, bPartInvalid, oFormat) {
373
- if (sType in oFormat.oRequiredParts && bPartInvalid) {
484
+ checkValid: function (sSymbolName, bPartInvalid, oFormat) {
485
+ if (sSymbolName in oFormat.oRequiredParts && bPartInvalid) {
374
486
  return false;
375
487
  }
488
+ return true;
376
489
  }
377
490
  };
491
+ DateFormat._createPatternSymbol = function (mParameters) {
492
+ var fnIsNumeric = typeof mParameters.isNumeric === 'function' && mParameters.isNumeric || function () {
493
+ return mParameters.isNumeric || false;
494
+ };
495
+ return {
496
+ name: mParameters.name,
497
+ format: mParameters.format || function () {
498
+ return '';
499
+ },
500
+ parse: mParameters.parse || function () {
501
+ return {};
502
+ },
503
+ isNumeric: fnIsNumeric
504
+ };
505
+ };
378
506
  DateFormat.prototype.oSymbols = {
379
- '': {
507
+ '': DateFormat._createPatternSymbol({
380
508
  name: 'text',
381
- format: function (oField, oDate, bUTC, oFormat) {
509
+ format: function (oField, oDate) {
382
510
  return oField.value;
383
511
  },
384
512
  parse: function (sValue, oPart, oFormat, oConfig) {
@@ -418,11 +546,11 @@ DateFormat.prototype.oSymbols = {
418
546
  return { valid: oParseHelper.checkValid(oPart.type, bPartInvalid, oFormat) };
419
547
  }
420
548
  }
421
- },
422
- 'G': {
549
+ }),
550
+ 'G': DateFormat._createPatternSymbol({
423
551
  name: 'era',
424
552
  format: function (oField, oDate, bUTC, oFormat) {
425
- var iEra = bUTC ? oDate.getUTCEra() : oDate.getEra();
553
+ var iEra = oDate.getUTCEra();
426
554
  if (oField.digits <= 3) {
427
555
  return oFormat.aErasAbbrev[iEra];
428
556
  } else if (oField.digits === 4) {
@@ -439,11 +567,11 @@ DateFormat.prototype.oSymbols = {
439
567
  ];
440
568
  for (var i = 0; i < aErasVariants.length; i++) {
441
569
  var aVariants = aErasVariants[i];
442
- var oFound = oParseHelper.findEntry(sValue, aVariants);
570
+ var oFound = oParseHelper.findEntry(sValue, aVariants, oFormat.oLocaleData.sCLDRLocaleId);
443
571
  if (oFound.index !== -1) {
444
572
  return {
445
573
  era: oFound.index,
446
- length: oFound.value.length
574
+ length: oFound.length
447
575
  };
448
576
  }
449
577
  }
@@ -452,34 +580,35 @@ DateFormat.prototype.oSymbols = {
452
580
  valid: oParseHelper.checkValid(oPart.type, true, oFormat)
453
581
  };
454
582
  }
455
- },
456
- 'y': {
583
+ }),
584
+ 'y': DateFormat._createPatternSymbol({
457
585
  name: 'year',
458
586
  format: function (oField, oDate, bUTC, oFormat) {
459
- var iYear = bUTC ? oDate.getUTCFullYear() : oDate.getFullYear();
587
+ var iYear = oDate.getUTCFullYear();
460
588
  var sYear = String(iYear);
461
589
  var sCalendarType = oFormat.oFormatOptions.calendarType;
462
- if (oField.digits == 2 && sYear.length > 2) {
590
+ if (oField.digits === 2 && sYear.length > 2) {
463
591
  sYear = sYear.substr(sYear.length - 2);
464
592
  }
465
- if (sCalendarType != CalendarType.Japanese && oField.digits == 1 && iYear < 100) {
593
+ if (sCalendarType !== CalendarType.Japanese && oField.digits === 1 && iYear < 100) {
466
594
  sYear = sYear.padStart(4, '0');
467
595
  }
468
596
  return sYear.padStart(oField.digits, '0');
469
597
  },
470
598
  parse: function (sValue, oPart, oFormat, oConfig) {
471
- var sCalendarType = oFormat.oFormatOptions.calendarType;
472
- var sPart;
473
- if (oPart.digits == 1) {
474
- sPart = oParseHelper.findNumbers(sValue, 4);
475
- } else if (oPart.digits == 2) {
476
- sPart = oParseHelper.findNumbers(sValue, 2);
599
+ var iExpectedDigits, sPart, bPartInvalid, sCalendarType = oFormat.oFormatOptions.calendarType;
600
+ if (oPart.digits === 1) {
601
+ iExpectedDigits = 4;
602
+ } else if (oPart.digits === 2) {
603
+ iExpectedDigits = 2;
477
604
  } else {
478
- sPart = oParseHelper.findNumbers(sValue, oPart.digits);
605
+ iExpectedDigits = oPart.digits;
479
606
  }
607
+ sPart = oParseHelper.findNumbers(sValue, iExpectedDigits);
608
+ bPartInvalid = sPart === '' || oConfig.exactLength && sPart.length !== iExpectedDigits;
480
609
  var iYear = parseInt(sPart);
481
- if (sCalendarType != CalendarType.Japanese && sPart.length <= 2) {
482
- var oCurrentDate = UniversalDate.getInstance(new Date(), sCalendarType), iCurrentYear = oCurrentDate.getFullYear(), iCurrentCentury = Math.floor(iCurrentYear / 100), iYearDiff = iCurrentCentury * 100 + iYear - iCurrentYear;
610
+ if (sCalendarType !== CalendarType.Japanese && sPart.length <= 2) {
611
+ var oCurrentDate = UniversalDate.getInstance(UI5Date.getInstance(), sCalendarType), iCurrentYear = oCurrentDate.getUTCFullYear(), iCurrentCentury = Math.floor(iCurrentYear / 100), iYearDiff = iCurrentCentury * 100 + iYear - iCurrentYear;
483
612
  if (iYearDiff < -70) {
484
613
  iYear += (iCurrentCentury + 1) * 100;
485
614
  } else if (iYearDiff < 30) {
@@ -490,40 +619,42 @@ DateFormat.prototype.oSymbols = {
490
619
  }
491
620
  return {
492
621
  length: sPart.length,
493
- valid: oParseHelper.checkValid(oPart.type, sPart === '', oFormat),
622
+ valid: oParseHelper.checkValid(oPart.type, bPartInvalid, oFormat),
494
623
  year: iYear
495
624
  };
496
- }
497
- },
498
- 'Y': {
625
+ },
626
+ isNumeric: true
627
+ }),
628
+ 'Y': DateFormat._createPatternSymbol({
499
629
  name: 'weekYear',
500
630
  format: function (oField, oDate, bUTC, oFormat) {
501
- var oWeek = bUTC ? oDate.getUTCWeek() : oDate.getWeek();
631
+ var oWeek = oDate.getUTCWeek(oFormat.oLocale, getCalendarWeekParameter(oFormat.oFormatOptions));
502
632
  var iWeekYear = oWeek.year;
503
633
  var sWeekYear = String(iWeekYear);
504
634
  var sCalendarType = oFormat.oFormatOptions.calendarType;
505
- if (oField.digits == 2 && sWeekYear.length > 2) {
635
+ if (oField.digits === 2 && sWeekYear.length > 2) {
506
636
  sWeekYear = sWeekYear.substr(sWeekYear.length - 2);
507
637
  }
508
- if (sCalendarType != CalendarType.Japanese && oField.digits == 1 && iWeekYear < 100) {
638
+ if (sCalendarType !== CalendarType.Japanese && oField.digits === 1 && iWeekYear < 100) {
509
639
  sWeekYear = sWeekYear.padStart(4, '0');
510
640
  }
511
641
  return sWeekYear.padStart(oField.digits, '0');
512
642
  },
513
643
  parse: function (sValue, oPart, oFormat, oConfig) {
514
- var sCalendarType = oFormat.oFormatOptions.calendarType;
515
- var sPart;
516
- if (oPart.digits == 1) {
517
- sPart = oParseHelper.findNumbers(sValue, 4);
518
- } else if (oPart.digits == 2) {
519
- sPart = oParseHelper.findNumbers(sValue, 2);
644
+ var iExpectedDigits, sPart, bPartInvalid, sCalendarType = oFormat.oFormatOptions.calendarType;
645
+ if (oPart.digits === 1) {
646
+ iExpectedDigits = 4;
647
+ } else if (oPart.digits === 2) {
648
+ iExpectedDigits = 2;
520
649
  } else {
521
- sPart = oParseHelper.findNumbers(sValue, oPart.digits);
650
+ iExpectedDigits = oPart.digits;
522
651
  }
652
+ sPart = oParseHelper.findNumbers(sValue, iExpectedDigits);
653
+ bPartInvalid = sPart === '' || oConfig.exactLength && sPart.length !== iExpectedDigits;
523
654
  var iYear = parseInt(sPart);
524
- var iWeekYear;
525
- if (sCalendarType != CalendarType.Japanese && sPart.length <= 2) {
526
- var oCurrentDate = UniversalDate.getInstance(new Date(), sCalendarType), iCurrentYear = oCurrentDate.getFullYear(), iCurrentCentury = Math.floor(iCurrentYear / 100), iYearDiff = iCurrentCentury * 100 + iWeekYear - iCurrentYear;
655
+ var iWeekYear = iYear;
656
+ if (sCalendarType !== CalendarType.Japanese && sPart.length <= 2) {
657
+ var oCurrentDate = UniversalDate.getInstance(UI5Date.getInstance(), sCalendarType), iCurrentYear = oCurrentDate.getUTCFullYear(), iCurrentCentury = Math.floor(iCurrentYear / 100), iYearDiff = iCurrentCentury * 100 + iWeekYear - iCurrentYear;
527
658
  if (iYearDiff < -70) {
528
659
  iWeekYear += (iCurrentCentury + 1) * 100;
529
660
  } else if (iYearDiff < 30) {
@@ -534,19 +665,20 @@ DateFormat.prototype.oSymbols = {
534
665
  }
535
666
  return {
536
667
  length: sPart.length,
537
- valid: oParseHelper.checkValid(oPart.type, sPart === '', oFormat),
668
+ valid: oParseHelper.checkValid(oPart.type, bPartInvalid, oFormat),
538
669
  year: iYear,
539
670
  weekYear: iWeekYear
540
671
  };
541
- }
542
- },
543
- 'M': {
672
+ },
673
+ isNumeric: true
674
+ }),
675
+ 'M': DateFormat._createPatternSymbol({
544
676
  name: 'month',
545
677
  format: function (oField, oDate, bUTC, oFormat) {
546
- var iMonth = bUTC ? oDate.getUTCMonth() : oDate.getMonth();
547
- if (oField.digits == 3) {
678
+ var iMonth = oDate.getUTCMonth();
679
+ if (oField.digits === 3) {
548
680
  return oFormat.aMonthsAbbrev[iMonth];
549
- } else if (oField.digits == 4) {
681
+ } else if (oField.digits === 4) {
550
682
  return oFormat.aMonthsWide[iMonth];
551
683
  } else if (oField.digits > 4) {
552
684
  return oFormat.aMonthsNarrow[iMonth];
@@ -555,20 +687,18 @@ DateFormat.prototype.oSymbols = {
555
687
  }
556
688
  },
557
689
  parse: function (sValue, oPart, oFormat, oConfig) {
558
- var aMonthsVariants = [
559
- oFormat.aMonthsWide,
560
- oFormat.aMonthsWideSt,
561
- oFormat.aMonthsAbbrev,
562
- oFormat.aMonthsAbbrevSt,
563
- oFormat.aMonthsNarrow,
564
- oFormat.aMonthsNarrowSt
565
- ];
566
- var bValid;
567
- var iMonth;
568
- var sPart;
690
+ var iMonth, sPart, bPartInvalid, bValid, aMonthsVariants = [
691
+ oFormat.aMonthsWide,
692
+ oFormat.aMonthsWideSt,
693
+ oFormat.aMonthsAbbrev,
694
+ oFormat.aMonthsAbbrevSt,
695
+ oFormat.aMonthsNarrow,
696
+ oFormat.aMonthsNarrowSt
697
+ ];
569
698
  if (oPart.digits < 3) {
570
699
  sPart = oParseHelper.findNumbers(sValue, Math.max(oPart.digits, 2));
571
- bValid = oParseHelper.checkValid(oPart.type, sPart === '', oFormat);
700
+ bPartInvalid = sPart === '' || oConfig.exactLength && sPart.length < 2;
701
+ bValid = oParseHelper.checkValid(oPart.type, bPartInvalid, oFormat);
572
702
  iMonth = parseInt(sPart) - 1;
573
703
  if (oConfig.strict && (iMonth > 11 || iMonth < 0)) {
574
704
  bValid = false;
@@ -576,11 +706,11 @@ DateFormat.prototype.oSymbols = {
576
706
  } else {
577
707
  for (var i = 0; i < aMonthsVariants.length; i++) {
578
708
  var aVariants = aMonthsVariants[i];
579
- var oFound = oParseHelper.findEntry(sValue, aVariants);
709
+ var oFound = oParseHelper.findEntry(sValue, aVariants, oFormat.oLocaleData.sCLDRLocaleId);
580
710
  if (oFound.index !== -1) {
581
711
  return {
582
712
  month: oFound.index,
583
- length: oFound.value.length
713
+ length: oFound.length
584
714
  };
585
715
  }
586
716
  }
@@ -591,15 +721,18 @@ DateFormat.prototype.oSymbols = {
591
721
  length: sPart ? sPart.length : 0,
592
722
  valid: bValid
593
723
  };
724
+ },
725
+ isNumeric: function (iDigits) {
726
+ return iDigits < 3;
594
727
  }
595
- },
596
- 'L': {
728
+ }),
729
+ 'L': DateFormat._createPatternSymbol({
597
730
  name: 'monthStandalone',
598
731
  format: function (oField, oDate, bUTC, oFormat) {
599
- var iMonth = bUTC ? oDate.getUTCMonth() : oDate.getMonth();
600
- if (oField.digits == 3) {
732
+ var iMonth = oDate.getUTCMonth();
733
+ if (oField.digits === 3) {
601
734
  return oFormat.aMonthsAbbrevSt[iMonth];
602
- } else if (oField.digits == 4) {
735
+ } else if (oField.digits === 4) {
603
736
  return oFormat.aMonthsWideSt[iMonth];
604
737
  } else if (oField.digits > 4) {
605
738
  return oFormat.aMonthsNarrowSt[iMonth];
@@ -608,20 +741,18 @@ DateFormat.prototype.oSymbols = {
608
741
  }
609
742
  },
610
743
  parse: function (sValue, oPart, oFormat, oConfig) {
611
- var aMonthsVariants = [
612
- oFormat.aMonthsWide,
613
- oFormat.aMonthsWideSt,
614
- oFormat.aMonthsAbbrev,
615
- oFormat.aMonthsAbbrevSt,
616
- oFormat.aMonthsNarrow,
617
- oFormat.aMonthsNarrowSt
618
- ];
619
- var bValid;
620
- var iMonth;
621
- var sPart;
744
+ var iMonth, sPart, bPartInvalid, bValid, aMonthsVariants = [
745
+ oFormat.aMonthsWide,
746
+ oFormat.aMonthsWideSt,
747
+ oFormat.aMonthsAbbrev,
748
+ oFormat.aMonthsAbbrevSt,
749
+ oFormat.aMonthsNarrow,
750
+ oFormat.aMonthsNarrowSt
751
+ ];
622
752
  if (oPart.digits < 3) {
623
753
  sPart = oParseHelper.findNumbers(sValue, Math.max(oPart.digits, 2));
624
- bValid = oParseHelper.checkValid(oPart.type, sPart === '', oFormat);
754
+ bPartInvalid = sPart === '' || oConfig.exactLength && sPart.length < 2;
755
+ bValid = oParseHelper.checkValid(oPart.type, bPartInvalid, oFormat);
625
756
  iMonth = parseInt(sPart) - 1;
626
757
  if (oConfig.strict && (iMonth > 11 || iMonth < 0)) {
627
758
  bValid = false;
@@ -629,11 +760,11 @@ DateFormat.prototype.oSymbols = {
629
760
  } else {
630
761
  for (var i = 0; i < aMonthsVariants.length; i++) {
631
762
  var aVariants = aMonthsVariants[i];
632
- var oFound = oParseHelper.findEntry(sValue, aVariants);
763
+ var oFound = oParseHelper.findEntry(sValue, aVariants, oFormat.oLocaleData.sCLDRLocaleId);
633
764
  if (oFound.index !== -1) {
634
765
  return {
635
766
  month: oFound.index,
636
- length: oFound.value.length
767
+ length: oFound.length
637
768
  };
638
769
  }
639
770
  }
@@ -644,12 +775,15 @@ DateFormat.prototype.oSymbols = {
644
775
  length: sPart ? sPart.length : 0,
645
776
  valid: bValid
646
777
  };
778
+ },
779
+ isNumeric: function (iDigits) {
780
+ return iDigits < 3;
647
781
  }
648
- },
649
- 'w': {
782
+ }),
783
+ 'w': DateFormat._createPatternSymbol({
650
784
  name: 'weekInYear',
651
785
  format: function (oField, oDate, bUTC, oFormat) {
652
- var oWeek = bUTC ? oDate.getUTCWeek() : oDate.getWeek();
786
+ var oWeek = oDate.getUTCWeek(oFormat.oLocale, getCalendarWeekParameter(oFormat.oFormatOptions));
653
787
  var iWeek = oWeek.week;
654
788
  var sWeek = String(iWeek + 1);
655
789
  if (oField.digits < 3) {
@@ -660,22 +794,20 @@ DateFormat.prototype.oSymbols = {
660
794
  return sWeek;
661
795
  },
662
796
  parse: function (sValue, oPart, oFormat, oConfig) {
663
- var bValid;
664
- var sPart;
665
- var iWeek;
666
- var iLength = 0;
797
+ var sPart, bPartInvalid, bValid, iWeek, iLength = 0;
667
798
  if (oPart.digits < 3) {
668
799
  sPart = oParseHelper.findNumbers(sValue, 2);
669
800
  iLength = sPart.length;
670
801
  iWeek = parseInt(sPart) - 1;
671
- bValid = oParseHelper.checkValid(oPart.type, !sPart, oFormat);
802
+ bPartInvalid = !sPart || oConfig.exactLength && iLength < 2;
803
+ bValid = oParseHelper.checkValid(oPart.type, bPartInvalid, oFormat);
672
804
  } else {
673
805
  sPart = oFormat.oLocaleData.getCalendarWeek(oPart.digits === 3 ? 'narrow' : 'wide');
674
- sPart = sPart.replace('{0}', '[0-9]+');
806
+ sPart = sPart.replace('{0}', '([0-9]+)');
675
807
  var rWeekNumber = new RegExp(sPart), oResult = rWeekNumber.exec(sValue);
676
808
  if (oResult) {
677
809
  iLength = oResult[0].length;
678
- iWeek = parseInt(oResult[0]) - 1;
810
+ iWeek = parseInt(oResult[oResult.length - 1]) - 1;
679
811
  } else {
680
812
  bValid = oParseHelper.checkValid(oPart.type, true, oFormat);
681
813
  }
@@ -685,35 +817,21 @@ DateFormat.prototype.oSymbols = {
685
817
  valid: bValid,
686
818
  week: iWeek
687
819
  };
688
- }
689
- },
690
- 'W': {
691
- name: 'weekInMonth',
692
- format: function (oField, oDate, bUTC, oFormat) {
693
- return '';
694
820
  },
695
- parse: function () {
696
- return {};
821
+ isNumeric: function (iDigits) {
822
+ return iDigits < 3;
697
823
  }
698
- },
699
- 'D': {
700
- name: 'dayInYear',
701
- format: function (oField, oDate, bUTC, oFormat) {
702
- },
703
- parse: function () {
704
- return {};
705
- }
706
- },
707
- 'd': {
824
+ }),
825
+ 'W': DateFormat._createPatternSymbol({ name: 'weekInMonth' }),
826
+ 'D': DateFormat._createPatternSymbol({ name: 'dayInYear' }),
827
+ 'd': DateFormat._createPatternSymbol({
708
828
  name: 'day',
709
- format: function (oField, oDate, bUTC, oFormat) {
710
- var iDate = bUTC ? oDate.getUTCDate() : oDate.getDate();
829
+ format: function (oField, oDate) {
830
+ var iDate = oDate.getUTCDate();
711
831
  return String(iDate).padStart(oField.digits, '0');
712
832
  },
713
833
  parse: function (sValue, oPart, oFormat, oConfig) {
714
- var sPart = oParseHelper.findNumbers(sValue, Math.max(oPart.digits, 2));
715
- var bValid = oParseHelper.checkValid(oPart.type, sPart === '', oFormat);
716
- var iDay = parseInt(sPart);
834
+ var sPart = oParseHelper.findNumbers(sValue, Math.max(oPart.digits, 2)), bPartInvalid = sPart === '' || oConfig.exactLength && sPart.length < 2, bValid = oParseHelper.checkValid(oPart.type, bPartInvalid, oFormat), iDay = parseInt(sPart);
717
835
  if (oConfig.strict && (iDay > 31 || iDay < 1)) {
718
836
  bValid = false;
719
837
  }
@@ -722,15 +840,16 @@ DateFormat.prototype.oSymbols = {
722
840
  length: sPart.length,
723
841
  valid: bValid
724
842
  };
725
- }
726
- },
727
- 'Q': {
843
+ },
844
+ isNumeric: true
845
+ }),
846
+ 'Q': DateFormat._createPatternSymbol({
728
847
  name: 'quarter',
729
848
  format: function (oField, oDate, bUTC, oFormat) {
730
- var iQuarter = bUTC ? oDate.getUTCQuarter() : oDate.getQuarter();
731
- if (oField.digits == 3) {
849
+ var iQuarter = oDate.getUTCQuarter();
850
+ if (oField.digits === 3) {
732
851
  return oFormat.aQuartersAbbrev[iQuarter];
733
- } else if (oField.digits == 4) {
852
+ } else if (oField.digits === 4) {
734
853
  return oFormat.aQuartersWide[iQuarter];
735
854
  } else if (oField.digits > 4) {
736
855
  return oFormat.aQuartersNarrow[iQuarter];
@@ -739,9 +858,7 @@ DateFormat.prototype.oSymbols = {
739
858
  }
740
859
  },
741
860
  parse: function (sValue, oPart, oFormat, oConfig) {
742
- var bValid;
743
- var iQuarter;
744
- var sPart;
861
+ var sPart, bPartInvalid, iQuarter, bValid;
745
862
  var aQuartersVariants = [
746
863
  oFormat.aQuartersWide,
747
864
  oFormat.aQuartersWideSt,
@@ -752,7 +869,8 @@ DateFormat.prototype.oSymbols = {
752
869
  ];
753
870
  if (oPart.digits < 3) {
754
871
  sPart = oParseHelper.findNumbers(sValue, Math.max(oPart.digits, 2));
755
- bValid = oParseHelper.checkValid(oPart.type, sPart === '', oFormat);
872
+ bPartInvalid = sPart === '' || oConfig.exactLength && sPart.length < 2;
873
+ bValid = oParseHelper.checkValid(oPart.type, bPartInvalid, oFormat);
756
874
  iQuarter = parseInt(sPart) - 1;
757
875
  if (oConfig.strict && iQuarter > 3) {
758
876
  bValid = false;
@@ -760,11 +878,11 @@ DateFormat.prototype.oSymbols = {
760
878
  } else {
761
879
  for (var i = 0; i < aQuartersVariants.length; i++) {
762
880
  var aVariants = aQuartersVariants[i];
763
- var oFound = oParseHelper.findEntry(sValue, aVariants);
881
+ var oFound = oParseHelper.findEntry(sValue, aVariants, oFormat.oLocaleData.sCLDRLocaleId);
764
882
  if (oFound.index !== -1) {
765
883
  return {
766
884
  quarter: oFound.index,
767
- length: oFound.value.length
885
+ length: oFound.length
768
886
  };
769
887
  }
770
888
  }
@@ -775,15 +893,18 @@ DateFormat.prototype.oSymbols = {
775
893
  quarter: iQuarter,
776
894
  valid: bValid
777
895
  };
896
+ },
897
+ isNumeric: function (iDigits) {
898
+ return iDigits < 3;
778
899
  }
779
- },
780
- 'q': {
900
+ }),
901
+ 'q': DateFormat._createPatternSymbol({
781
902
  name: 'quarterStandalone',
782
903
  format: function (oField, oDate, bUTC, oFormat) {
783
- var iQuarter = bUTC ? oDate.getUTCQuarter() : oDate.getQuarter();
784
- if (oField.digits == 3) {
904
+ var iQuarter = oDate.getUTCQuarter();
905
+ if (oField.digits === 3) {
785
906
  return oFormat.aQuartersAbbrevSt[iQuarter];
786
- } else if (oField.digits == 4) {
907
+ } else if (oField.digits === 4) {
787
908
  return oFormat.aQuartersWideSt[iQuarter];
788
909
  } else if (oField.digits > 4) {
789
910
  return oFormat.aQuartersNarrowSt[iQuarter];
@@ -792,9 +913,7 @@ DateFormat.prototype.oSymbols = {
792
913
  }
793
914
  },
794
915
  parse: function (sValue, oPart, oFormat, oConfig) {
795
- var bValid;
796
- var iQuarter;
797
- var sPart;
916
+ var sPart, bPartInvalid, iQuarter, bValid;
798
917
  var aQuartersVariants = [
799
918
  oFormat.aQuartersWide,
800
919
  oFormat.aQuartersWideSt,
@@ -805,7 +924,8 @@ DateFormat.prototype.oSymbols = {
805
924
  ];
806
925
  if (oPart.digits < 3) {
807
926
  sPart = oParseHelper.findNumbers(sValue, Math.max(oPart.digits, 2));
808
- bValid = oParseHelper.checkValid(oPart.type, sPart === '', oFormat);
927
+ bPartInvalid = sPart === '' || oConfig.exactLength && sPart.length < 2;
928
+ bValid = oParseHelper.checkValid(oPart.type, bPartInvalid, oFormat);
809
929
  iQuarter = parseInt(sPart) - 1;
810
930
  if (oConfig.strict && iQuarter > 3) {
811
931
  bValid = false;
@@ -813,11 +933,11 @@ DateFormat.prototype.oSymbols = {
813
933
  } else {
814
934
  for (var i = 0; i < aQuartersVariants.length; i++) {
815
935
  var aVariants = aQuartersVariants[i];
816
- var oFound = oParseHelper.findEntry(sValue, aVariants);
936
+ var oFound = oParseHelper.findEntry(sValue, aVariants, oFormat.oLocaleData.sCLDRLocaleId);
817
937
  if (oFound.index !== -1) {
818
938
  return {
819
939
  quarter: oFound.index,
820
- length: oFound.value.length
940
+ length: oFound.length
821
941
  };
822
942
  }
823
943
  }
@@ -828,26 +948,21 @@ DateFormat.prototype.oSymbols = {
828
948
  quarter: iQuarter,
829
949
  valid: bValid
830
950
  };
831
- }
832
- },
833
- 'F': {
834
- name: 'dayOfWeekInMonth',
835
- format: function (oField, oDate, bUTC, oFormat) {
836
- return '';
837
951
  },
838
- parse: function () {
839
- return {};
952
+ isNumeric: function (iDigits) {
953
+ return iDigits < 3;
840
954
  }
841
- },
842
- 'E': {
955
+ }),
956
+ 'F': DateFormat._createPatternSymbol({ name: 'dayOfWeekInMonth' }),
957
+ 'E': DateFormat._createPatternSymbol({
843
958
  name: 'dayNameInWeek',
844
959
  format: function (oField, oDate, bUTC, oFormat) {
845
- var iDay = bUTC ? oDate.getUTCDay() : oDate.getDay();
960
+ var iDay = oDate.getUTCDay();
846
961
  if (oField.digits < 4) {
847
962
  return oFormat.aDaysAbbrev[iDay];
848
- } else if (oField.digits == 4) {
963
+ } else if (oField.digits === 4) {
849
964
  return oFormat.aDaysWide[iDay];
850
- } else if (oField.digits == 5) {
965
+ } else if (oField.digits === 5) {
851
966
  return oFormat.aDaysNarrow[iDay];
852
967
  } else {
853
968
  return oFormat.aDaysShort[iDay];
@@ -866,25 +981,25 @@ DateFormat.prototype.oSymbols = {
866
981
  ];
867
982
  for (var i = 0; i < aDaysVariants.length; i++) {
868
983
  var aVariants = aDaysVariants[i];
869
- var oFound = oParseHelper.findEntry(sValue, aVariants);
984
+ var oFound = oParseHelper.findEntry(sValue, aVariants, oFormat.oLocaleData.sCLDRLocaleId);
870
985
  if (oFound.index !== -1) {
871
986
  return {
872
987
  dayOfWeek: oFound.index,
873
- length: oFound.value.length
988
+ length: oFound.length
874
989
  };
875
990
  }
876
991
  }
877
992
  }
878
- },
879
- 'c': {
993
+ }),
994
+ 'c': DateFormat._createPatternSymbol({
880
995
  name: 'dayNameInWeekStandalone',
881
996
  format: function (oField, oDate, bUTC, oFormat) {
882
- var iDay = bUTC ? oDate.getUTCDay() : oDate.getDay();
997
+ var iDay = oDate.getUTCDay();
883
998
  if (oField.digits < 4) {
884
999
  return oFormat.aDaysAbbrevSt[iDay];
885
- } else if (oField.digits == 4) {
1000
+ } else if (oField.digits === 4) {
886
1001
  return oFormat.aDaysWideSt[iDay];
887
- } else if (oField.digits == 5) {
1002
+ } else if (oField.digits === 5) {
888
1003
  return oFormat.aDaysNarrowSt[iDay];
889
1004
  } else {
890
1005
  return oFormat.aDaysShortSt[iDay];
@@ -903,72 +1018,129 @@ DateFormat.prototype.oSymbols = {
903
1018
  ];
904
1019
  for (var i = 0; i < aDaysVariants.length; i++) {
905
1020
  var aVariants = aDaysVariants[i];
906
- var oFound = oParseHelper.findEntry(sValue, aVariants);
1021
+ var oFound = oParseHelper.findEntry(sValue, aVariants, oFormat.oLocaleData.sCLDRLocaleId);
907
1022
  if (oFound.index !== -1) {
908
1023
  return {
909
1024
  day: oFound.index,
910
- length: oFound.value.length
1025
+ length: oFound.length
911
1026
  };
912
1027
  }
913
1028
  }
914
1029
  }
915
- },
916
- 'u': {
1030
+ }),
1031
+ 'u': DateFormat._createPatternSymbol({
917
1032
  name: 'dayNumberOfWeek',
918
1033
  format: function (oField, oDate, bUTC, oFormat) {
919
- var iDay = bUTC ? oDate.getUTCDay() : oDate.getDay();
1034
+ var iDay = oDate.getUTCDay();
920
1035
  return oFormat._adaptDayOfWeek(iDay);
921
1036
  },
922
1037
  parse: function (sValue, oPart, oFormat, oConfig) {
923
- var sPart = oParseHelper.findNumbers(sValue, oPart.digits);
1038
+ var sPart = oParseHelper.findNumbers(sValue, oPart.digits), bPartInvalid = oConfig.exactLength && sPart.length !== oPart.digits;
924
1039
  return {
925
1040
  dayNumberOfWeek: parseInt(sPart),
926
- length: sPart.length
1041
+ length: sPart.length,
1042
+ valid: oParseHelper.checkValid(oPart.type, bPartInvalid, oFormat)
927
1043
  };
928
- }
929
- },
930
- 'a': {
1044
+ },
1045
+ isNumeric: true
1046
+ }),
1047
+ 'a': DateFormat._createPatternSymbol({
931
1048
  name: 'amPmMarker',
932
1049
  format: function (oField, oDate, bUTC, oFormat) {
933
- var iDayPeriod = bUTC ? oDate.getUTCDayPeriod() : oDate.getDayPeriod();
934
- return oFormat.aDayPeriods[iDayPeriod];
1050
+ var iDayPeriod = oDate.getUTCDayPeriod();
1051
+ if (oField.digits <= 3) {
1052
+ return oFormat.aDayPeriodsAbbrev[iDayPeriod];
1053
+ } else if (oField.digits === 4) {
1054
+ return oFormat.aDayPeriodsWide[iDayPeriod];
1055
+ } else {
1056
+ return oFormat.aDayPeriodsNarrow[iDayPeriod];
1057
+ }
1058
+ },
1059
+ parse: function (sValue, oPart, oFormat, oConfig, sTimezone) {
1060
+ var rAMPM, bAMPMAlternativeCase, oEntry, i, aMatch, normalize, aVariants, aDayPeriodsVariants = [
1061
+ oFormat.aDayPeriodsWide,
1062
+ oFormat.aDayPeriodsAbbrev,
1063
+ oFormat.aDayPeriodsNarrow
1064
+ ];
1065
+ rAMPM = /[aApP](?:\.)?[\x20\xA0]?[mM](?:\.)?/;
1066
+ aMatch = sValue.match(rAMPM);
1067
+ bAMPMAlternativeCase = aMatch && aMatch.index === 0;
1068
+ function normalize(sValue) {
1069
+ return sValue.replace(/[\x20\xA0]/g, '').replace(/\./g, '');
1070
+ }
1071
+ if (bAMPMAlternativeCase) {
1072
+ sValue = normalize(sValue);
1073
+ }
1074
+ for (i = 0; i < aDayPeriodsVariants.length; i += 1) {
1075
+ aVariants = aDayPeriodsVariants[i];
1076
+ if (bAMPMAlternativeCase) {
1077
+ aVariants = aVariants.map(normalize);
1078
+ }
1079
+ oEntry = oParseHelper.findEntry(sValue, aVariants, oFormat.oLocaleData.sCLDRLocaleId);
1080
+ if (oEntry.index !== -1) {
1081
+ return {
1082
+ pm: oEntry.index === 1,
1083
+ length: bAMPMAlternativeCase ? aMatch[0].length : oEntry.length
1084
+ };
1085
+ }
1086
+ }
1087
+ return { valid: false };
1088
+ }
1089
+ }),
1090
+ 'B': DateFormat._createPatternSymbol({
1091
+ name: 'flexibleDayPeriod',
1092
+ format: function (oField, oDate, bUTC, oFormat) {
1093
+ var bContainsHour = oFormat.aFormatArray.some(function (oFormatElement) {
1094
+ return 'hHKk'.includes(oFormatElement.symbol);
1095
+ }), sFlexibleDayPeriod = oFormat.oLocaleData.getFlexibleDayPeriodOfTime(oDate.getUTCHours(), oDate.getUTCMinutes());
1096
+ if (bContainsHour) {
1097
+ if (oField.digits <= 3) {
1098
+ return oFormat.oFlexibleDayPeriodsAbbrev[sFlexibleDayPeriod];
1099
+ }
1100
+ if (oField.digits === 4) {
1101
+ return oFormat.oFlexibleDayPeriodsWide[sFlexibleDayPeriod];
1102
+ }
1103
+ return oFormat.oFlexibleDayPeriodsNarrow[sFlexibleDayPeriod];
1104
+ }
1105
+ if (oField.digits <= 3) {
1106
+ return oFormat.oFlexibleDayPeriodsAbbrevSt[sFlexibleDayPeriod];
1107
+ }
1108
+ if (oField.digits === 4) {
1109
+ return oFormat.oFlexibleDayPeriodsWideSt[sFlexibleDayPeriod];
1110
+ }
1111
+ return oFormat.oFlexibleDayPeriodsNarrowSt[sFlexibleDayPeriod];
935
1112
  },
936
1113
  parse: function (sValue, oPart, oFormat, oConfig) {
937
- var bPM;
938
- var iLength;
939
- var sAM = oFormat.aDayPeriods[0], sPM = oFormat.aDayPeriods[1];
940
- var rAMPM = /[aApP](?:\.)?[mM](?:\.)?/;
941
- var aMatch = sValue.match(rAMPM);
942
- var bVariant = aMatch && aMatch.index === 0;
943
- if (bVariant) {
944
- sValue = aMatch[0].replace(/\./g, '').toLowerCase() + sValue.substring(aMatch[0].length);
945
- sAM = sAM.replace(/\./g, '').toLowerCase();
946
- sPM = sPM.replace(/\./g, '').toLowerCase();
947
- }
948
- if (sValue.indexOf(sAM) === 0) {
949
- bPM = false;
950
- iLength = bVariant ? aMatch[0].length : sAM.length;
951
- } else if (sValue.indexOf(sPM) === 0) {
952
- bPM = true;
953
- iLength = bVariant ? aMatch[0].length : sPM.length;
1114
+ var i, oFound, oVariant, bContainsHour = oFormat.aFormatArray.some(function (oFormatElement) {
1115
+ return 'hHKk'.includes(oFormatElement.symbol);
1116
+ }), aFlexibleDayPeriodVariants = [
1117
+ oFormat.oFlexibleDayPeriodsWide,
1118
+ oFormat.oFlexibleDayPeriodsAbbrev,
1119
+ oFormat.oFlexibleDayPeriodsNarrow
1120
+ ];
1121
+ if (bContainsHour) {
1122
+ for (i = 0; i < aFlexibleDayPeriodVariants.length; i++) {
1123
+ oVariant = aFlexibleDayPeriodVariants[i];
1124
+ oFound = oParseHelper.findEntry(sValue, Object.values(oVariant), oFormat.oLocaleData.sCLDRLocaleId);
1125
+ if (oFound.index !== -1) {
1126
+ return {
1127
+ flexDayPeriod: Object.keys(oVariant)[oFound.index],
1128
+ length: oFound.length
1129
+ };
1130
+ }
1131
+ }
954
1132
  }
955
- return {
956
- pm: bPM,
957
- length: iLength
958
- };
1133
+ return { valid: false };
959
1134
  }
960
- },
961
- 'H': {
1135
+ }),
1136
+ 'H': DateFormat._createPatternSymbol({
962
1137
  name: 'hour0_23',
963
- format: function (oField, oDate, bUTC, oFormat) {
964
- var iHours = bUTC ? oDate.getUTCHours() : oDate.getHours();
1138
+ format: function (oField, oDate) {
1139
+ var iHours = oDate.getUTCHours();
965
1140
  return String(iHours).padStart(oField.digits, '0');
966
1141
  },
967
1142
  parse: function (sValue, oPart, oFormat, oConfig) {
968
- var bValid;
969
- var sPart = oParseHelper.findNumbers(sValue, Math.max(oPart.digits, 2));
970
- var iHours = parseInt(sPart);
971
- bValid = oParseHelper.checkValid(oPart.type, sPart === '', oFormat);
1143
+ var sPart = oParseHelper.findNumbers(sValue, Math.max(oPart.digits, 2)), iHours = parseInt(sPart), bPartInvalid = sPart === '' || oConfig.exactLength && sPart.length < 2, bValid = oParseHelper.checkValid(oPart.type, bPartInvalid, oFormat);
972
1144
  if (oConfig.strict && iHours > 23) {
973
1145
  bValid = false;
974
1146
  }
@@ -977,21 +1149,19 @@ DateFormat.prototype.oSymbols = {
977
1149
  length: sPart.length,
978
1150
  valid: bValid
979
1151
  };
980
- }
981
- },
982
- 'k': {
1152
+ },
1153
+ isNumeric: true
1154
+ }),
1155
+ 'k': DateFormat._createPatternSymbol({
983
1156
  name: 'hour1_24',
984
- format: function (oField, oDate, bUTC, oFormat) {
985
- var iHours = bUTC ? oDate.getUTCHours() : oDate.getHours();
1157
+ format: function (oField, oDate) {
1158
+ var iHours = oDate.getUTCHours();
986
1159
  var sHours = iHours === 0 ? '24' : String(iHours);
987
1160
  return sHours.padStart(oField.digits, '0');
988
1161
  },
989
1162
  parse: function (sValue, oPart, oFormat, oConfig) {
990
- var bValid;
991
- var sPart = oParseHelper.findNumbers(sValue, Math.max(oPart.digits, 2));
992
- var iHours = parseInt(sPart);
993
- bValid = oParseHelper.checkValid(oPart.type, sPart === '', oFormat);
994
- if (iHours == 24) {
1163
+ var sPart = oParseHelper.findNumbers(sValue, Math.max(oPart.digits, 2)), iHours = parseInt(sPart), bPartInvalid = sPart === '' || oConfig.exactLength && sPart.length < 2, bValid = oParseHelper.checkValid(oPart.type, bPartInvalid, oFormat);
1164
+ if (iHours === 24) {
995
1165
  iHours = 0;
996
1166
  }
997
1167
  if (oConfig.strict && iHours > 23) {
@@ -1002,20 +1172,18 @@ DateFormat.prototype.oSymbols = {
1002
1172
  length: sPart.length,
1003
1173
  valid: bValid
1004
1174
  };
1005
- }
1006
- },
1007
- 'K': {
1175
+ },
1176
+ isNumeric: true
1177
+ }),
1178
+ 'K': DateFormat._createPatternSymbol({
1008
1179
  name: 'hour0_11',
1009
- format: function (oField, oDate, bUTC, oFormat) {
1010
- var iHours = bUTC ? oDate.getUTCHours() : oDate.getHours();
1180
+ format: function (oField, oDate) {
1181
+ var iHours = oDate.getUTCHours();
1011
1182
  var sHours = String(iHours > 11 ? iHours - 12 : iHours);
1012
1183
  return sHours.padStart(oField.digits, '0');
1013
1184
  },
1014
1185
  parse: function (sValue, oPart, oFormat, oConfig) {
1015
- var bValid;
1016
- var sPart = oParseHelper.findNumbers(sValue, Math.max(oPart.digits, 2));
1017
- var iHours = parseInt(sPart);
1018
- bValid = oParseHelper.checkValid(oPart.type, sPart === '', oFormat);
1186
+ var sPart = oParseHelper.findNumbers(sValue, Math.max(oPart.digits, 2)), iHours = parseInt(sPart), bPartInvalid = sPart === '' || oConfig.exactLength && sPart.length < 2, bValid = oParseHelper.checkValid(oPart.type, bPartInvalid, oFormat);
1019
1187
  if (oConfig.strict && iHours > 11) {
1020
1188
  bValid = false;
1021
1189
  }
@@ -1024,16 +1192,17 @@ DateFormat.prototype.oSymbols = {
1024
1192
  length: sPart.length,
1025
1193
  valid: bValid
1026
1194
  };
1027
- }
1028
- },
1029
- 'h': {
1195
+ },
1196
+ isNumeric: true
1197
+ }),
1198
+ 'h': DateFormat._createPatternSymbol({
1030
1199
  name: 'hour1_12',
1031
- format: function (oField, oDate, bUTC, oFormat) {
1032
- var iHours = bUTC ? oDate.getUTCHours() : oDate.getHours();
1200
+ format: function (oField, oDate) {
1201
+ var iHours = oDate.getUTCHours();
1033
1202
  var sHours;
1034
1203
  if (iHours > 12) {
1035
1204
  sHours = String(iHours - 12);
1036
- } else if (iHours == 0) {
1205
+ } else if (iHours === 0) {
1037
1206
  sHours = '12';
1038
1207
  } else {
1039
1208
  sHours = String(iHours);
@@ -1041,11 +1210,8 @@ DateFormat.prototype.oSymbols = {
1041
1210
  return sHours.padStart(oField.digits, '0');
1042
1211
  },
1043
1212
  parse: function (sValue, oPart, oFormat, oConfig) {
1044
- var bPM = oConfig.dateValue.pm;
1045
- var sPart = oParseHelper.findNumbers(sValue, Math.max(oPart.digits, 2));
1046
- var iHours = parseInt(sPart);
1047
- var bValid = oParseHelper.checkValid(oPart.type, sPart === '', oFormat);
1048
- if (iHours == 12) {
1213
+ var bPM = oConfig.dateValue.pm, sPart = oParseHelper.findNumbers(sValue, Math.max(oPart.digits, 2)), iHours = parseInt(sPart), bPartInvalid = sPart === '' || oConfig.exactLength && sPart.length < 2, bValid = oParseHelper.checkValid(oPart.type, bPartInvalid, oFormat);
1214
+ if (iHours === 12) {
1049
1215
  iHours = 0;
1050
1216
  bPM = bPM === undefined ? true : bPM;
1051
1217
  }
@@ -1058,19 +1224,17 @@ DateFormat.prototype.oSymbols = {
1058
1224
  pm: bPM,
1059
1225
  valid: bValid
1060
1226
  };
1061
- }
1062
- },
1063
- 'm': {
1227
+ },
1228
+ isNumeric: true
1229
+ }),
1230
+ 'm': DateFormat._createPatternSymbol({
1064
1231
  name: 'minute',
1065
- format: function (oField, oDate, bUTC, oFormat) {
1066
- var iMinutes = bUTC ? oDate.getUTCMinutes() : oDate.getMinutes();
1232
+ format: function (oField, oDate) {
1233
+ var iMinutes = oDate.getUTCMinutes();
1067
1234
  return String(iMinutes).padStart(oField.digits, '0');
1068
1235
  },
1069
1236
  parse: function (sValue, oPart, oFormat, oConfig) {
1070
- var bValid;
1071
- var sPart = oParseHelper.findNumbers(sValue, Math.max(oPart.digits, 2));
1072
- var iMinutes = parseInt(sPart);
1073
- bValid = oParseHelper.checkValid(oPart.type, sPart === '', oFormat);
1237
+ var sPart = oParseHelper.findNumbers(sValue, Math.max(oPart.digits, 2)), iMinutes = parseInt(sPart), bPartInvalid = sPart === '' || oConfig.exactLength && sPart.length < 2, bValid = oParseHelper.checkValid(oPart.type, bPartInvalid, oFormat);
1074
1238
  if (oConfig.strict && iMinutes > 59) {
1075
1239
  bValid = false;
1076
1240
  }
@@ -1079,19 +1243,17 @@ DateFormat.prototype.oSymbols = {
1079
1243
  minute: iMinutes,
1080
1244
  valid: bValid
1081
1245
  };
1082
- }
1083
- },
1084
- 's': {
1246
+ },
1247
+ isNumeric: true
1248
+ }),
1249
+ 's': DateFormat._createPatternSymbol({
1085
1250
  name: 'second',
1086
- format: function (oField, oDate, bUTC, oFormat) {
1087
- var iSeconds = bUTC ? oDate.getUTCSeconds() : oDate.getSeconds();
1251
+ format: function (oField, oDate) {
1252
+ var iSeconds = oDate.getUTCSeconds();
1088
1253
  return String(iSeconds).padStart(oField.digits, '0');
1089
1254
  },
1090
1255
  parse: function (sValue, oPart, oFormat, oConfig) {
1091
- var bValid;
1092
- var sPart = oParseHelper.findNumbers(sValue, Math.max(oPart.digits, 2));
1093
- var iSeconds = parseInt(sPart);
1094
- bValid = oParseHelper.checkValid(oPart.type, sPart === '', oFormat);
1256
+ var iExpectedDigits = Math.max(oPart.digits, 2), sPart = oParseHelper.findNumbers(sValue, iExpectedDigits), bPartInvalid = sPart === '' || oConfig.exactLength && sPart.length < iExpectedDigits, iSeconds = parseInt(sPart), bValid = oParseHelper.checkValid(oPart.type, bPartInvalid, oFormat);
1095
1257
  if (oConfig.strict && iSeconds > 59) {
1096
1258
  bValid = false;
1097
1259
  }
@@ -1100,12 +1262,13 @@ DateFormat.prototype.oSymbols = {
1100
1262
  second: iSeconds,
1101
1263
  valid: bValid
1102
1264
  };
1103
- }
1104
- },
1105
- 'S': {
1265
+ },
1266
+ isNumeric: true
1267
+ }),
1268
+ 'S': DateFormat._createPatternSymbol({
1106
1269
  name: 'fractionalsecond',
1107
- format: function (oField, oDate, bUTC, oFormat) {
1108
- var iMilliseconds = bUTC ? oDate.getUTCMilliseconds() : oDate.getMilliseconds();
1270
+ format: function (oField, oDate) {
1271
+ var iMilliseconds = oDate.getUTCMilliseconds();
1109
1272
  var sMilliseconds = String(iMilliseconds);
1110
1273
  var sFractionalseconds = sMilliseconds.padStart(3, '0');
1111
1274
  sFractionalseconds = sFractionalseconds.substr(0, oField.digits);
@@ -1113,31 +1276,33 @@ DateFormat.prototype.oSymbols = {
1113
1276
  return sFractionalseconds;
1114
1277
  },
1115
1278
  parse: function (sValue, oPart, oFormat, oConfig) {
1116
- var sPart = oParseHelper.findNumbers(sValue, oPart.digits);
1117
- var iLength = sPart.length;
1279
+ var sPart = oParseHelper.findNumbers(sValue, oPart.digits), iLength = sPart.length, bPartInvalid = oConfig.exactLength && iLength < oPart.digits;
1118
1280
  sPart = sPart.substr(0, 3);
1119
1281
  sPart = sPart.padEnd(3, '0');
1120
1282
  var iMilliseconds = parseInt(sPart);
1121
1283
  return {
1122
1284
  length: iLength,
1123
- millisecond: iMilliseconds
1285
+ millisecond: iMilliseconds,
1286
+ valid: oParseHelper.checkValid(oPart.type, bPartInvalid, oFormat)
1124
1287
  };
1125
- }
1126
- },
1127
- 'z': {
1288
+ },
1289
+ isNumeric: true
1290
+ }),
1291
+ 'z': DateFormat._createPatternSymbol({
1128
1292
  name: 'timezoneGeneral',
1129
- format: function (oField, oDate, bUTC, oFormat) {
1130
- if (oField.digits > 3 && oDate.getTimezoneLong()) {
1293
+ format: function (oField, oDate, bUTC, oFormat, sTimezone) {
1294
+ if (oField.digits > 3 && oDate.getTimezoneLong && oDate.getTimezoneLong()) {
1131
1295
  return oDate.getTimezoneLong();
1132
- } else if (oDate.getTimezoneShort()) {
1296
+ } else if (oDate.getTimezoneShort && oDate.getTimezoneShort()) {
1133
1297
  return oDate.getTimezoneShort();
1134
1298
  }
1299
+ var iTimezoneOffset = TimezoneUtil.calculateOffset(oDate, sTimezone);
1135
1300
  var sTimeZone = 'GMT';
1136
- var iTZOffset = Math.abs(oDate.getTimezoneOffset());
1137
- var bPositiveOffset = oDate.getTimezoneOffset() > 0;
1301
+ var iTZOffset = Math.abs(iTimezoneOffset / 60);
1302
+ var bPositiveOffset = iTimezoneOffset > 0;
1138
1303
  var iHourOffset = Math.floor(iTZOffset / 60);
1139
- var iMinuteOffset = iTZOffset % 60;
1140
- if (!bUTC && iTZOffset != 0) {
1304
+ var iMinuteOffset = Math.floor(iTZOffset % 60);
1305
+ if (!bUTC && iTZOffset !== 0) {
1141
1306
  sTimeZone += bPositiveOffset ? '-' : '+';
1142
1307
  sTimeZone += String(iHourOffset).padStart(2, '0');
1143
1308
  sTimeZone += ':';
@@ -1155,13 +1320,13 @@ DateFormat.prototype.oSymbols = {
1155
1320
  iLength = 3;
1156
1321
  } else if (sValue.substring(0, 2) === 'UT') {
1157
1322
  iLength = 2;
1158
- } else if (sValue.charAt(0) == 'Z') {
1323
+ } else if (sValue.charAt(0) === 'Z') {
1159
1324
  iLength = 1;
1160
1325
  iTZDiff = 0;
1161
1326
  } else {
1162
- return { error: 'cannot be parsed correcly by sap.ui.core.format.DateFormat: The given timezone is not supported!' };
1327
+ return { error: 'cannot be parsed correctly by sap.ui.core.format.DateFormat: The given timezone is not supported!' };
1163
1328
  }
1164
- if (sValue.charAt(0) != 'Z') {
1329
+ if (sValue.charAt(0) !== 'Z') {
1165
1330
  var oParsedTZ = oParseHelper.parseTZ(sValue.substr(iLength), true);
1166
1331
  iLength += oParsedTZ.length;
1167
1332
  iTZDiff = oParsedTZ.tzDiff;
@@ -1171,16 +1336,17 @@ DateFormat.prototype.oSymbols = {
1171
1336
  tzDiff: iTZDiff
1172
1337
  };
1173
1338
  }
1174
- },
1175
- 'Z': {
1339
+ }),
1340
+ 'Z': DateFormat._createPatternSymbol({
1176
1341
  name: 'timezoneRFC822',
1177
- format: function (oField, oDate, bUTC, oFormat) {
1178
- var iTZOffset = Math.abs(oDate.getTimezoneOffset());
1179
- var bPositiveOffset = oDate.getTimezoneOffset() > 0;
1342
+ format: function (oField, oDate, bUTC, oFormat, sTimezone) {
1343
+ var iTimezoneOffset = TimezoneUtil.calculateOffset(oDate, sTimezone);
1344
+ var iTZOffset = Math.abs(iTimezoneOffset / 60);
1345
+ var bPositiveOffset = iTimezoneOffset > 0;
1180
1346
  var iHourOffset = Math.floor(iTZOffset / 60);
1181
- var iMinuteOffset = iTZOffset % 60;
1347
+ var iMinuteOffset = Math.floor(iTZOffset % 60);
1182
1348
  var sTimeZone = '';
1183
- if (!bUTC && iTZOffset != 0) {
1349
+ if (!bUTC) {
1184
1350
  sTimeZone += bPositiveOffset ? '-' : '+';
1185
1351
  sTimeZone += String(iHourOffset).padStart(2, '0');
1186
1352
  sTimeZone += String(iMinuteOffset).padStart(2, '0');
@@ -1190,40 +1356,86 @@ DateFormat.prototype.oSymbols = {
1190
1356
  parse: function (sValue, oPart, oFormat, oConfig) {
1191
1357
  return oParseHelper.parseTZ(sValue, false);
1192
1358
  }
1193
- },
1194
- 'X': {
1359
+ }),
1360
+ 'X': DateFormat._createPatternSymbol({
1195
1361
  name: 'timezoneISO8601',
1196
- format: function (oField, oDate, bUTC, oFormat) {
1197
- var iTZOffset = Math.abs(oDate.getTimezoneOffset());
1198
- var bPositiveOffset = oDate.getTimezoneOffset() > 0;
1362
+ format: function (oField, oDate, bUTC, oFormat, sTimezone) {
1363
+ var iTimezoneOffset = TimezoneUtil.calculateOffset(oDate, sTimezone);
1364
+ var iTZOffset = Math.abs(iTimezoneOffset / 60);
1365
+ var bPositiveOffset = iTimezoneOffset > 0;
1199
1366
  var iHourOffset = Math.floor(iTZOffset / 60);
1200
- var iMinuteOffset = iTZOffset % 60;
1367
+ var iMinuteOffset = Math.floor(iTZOffset % 60);
1201
1368
  var sTimeZone = '';
1202
- if (!bUTC && iTZOffset != 0) {
1369
+ if (!bUTC && iTZOffset !== 0) {
1203
1370
  sTimeZone += bPositiveOffset ? '-' : '+';
1204
1371
  sTimeZone += String(iHourOffset).padStart(2, '0');
1205
- sTimeZone += ':';
1206
- sTimeZone += String(iMinuteOffset).padStart(2, '0');
1372
+ if (oField.digits > 1 || iMinuteOffset > 0) {
1373
+ if (oField.digits === 3 || oField.digits === 5) {
1374
+ sTimeZone += ':';
1375
+ }
1376
+ sTimeZone += String(iMinuteOffset).padStart(2, '0');
1377
+ }
1207
1378
  } else {
1208
1379
  sTimeZone += 'Z';
1209
1380
  }
1210
1381
  return sTimeZone;
1211
1382
  },
1212
1383
  parse: function (sValue, oPart, oFormat, oConfig) {
1213
- if (sValue.charAt(0) == 'Z') {
1384
+ if (sValue.charAt(0) === 'Z') {
1214
1385
  return {
1215
1386
  length: 1,
1216
1387
  tzDiff: 0
1217
1388
  };
1218
1389
  } else {
1219
- return oParseHelper.parseTZ(sValue, true);
1390
+ return oParseHelper.parseTZ(sValue, oPart.digits === 3 || oPart.digits === 5);
1220
1391
  }
1221
1392
  }
1222
- }
1393
+ }),
1394
+ 'V': DateFormat._createPatternSymbol({
1395
+ name: 'timezoneID',
1396
+ format: function (oField, oDate, bUTC, oFormat, sTimezone) {
1397
+ if (!bUTC && oField.digits === 2) {
1398
+ return oFormat.oLocaleData.getTimezoneTranslations()[sTimezone] || sTimezone;
1399
+ }
1400
+ return '';
1401
+ },
1402
+ parse: function (sValue, oPart, oFormat, oConfig, sTimezone) {
1403
+ var oTimezoneParsed = {
1404
+ timezone: '',
1405
+ length: 0
1406
+ };
1407
+ if (oPart.digits === 2) {
1408
+ var mTimezoneTranslations = oFormat.oLocaleData.getTimezoneTranslations();
1409
+ if (sValue === mTimezoneTranslations[sTimezone]) {
1410
+ return {
1411
+ timezone: sTimezone,
1412
+ length: sValue.length
1413
+ };
1414
+ }
1415
+ var aTimezoneTranslations = Object.values(mTimezoneTranslations);
1416
+ var oTimezoneResult = oParseHelper.findEntry(sValue, aTimezoneTranslations, oFormat.oLocaleData.sCLDRLocaleId);
1417
+ if (oTimezoneResult.index !== -1) {
1418
+ return {
1419
+ timezone: Object.keys(mTimezoneTranslations)[oTimezoneResult.index],
1420
+ length: oTimezoneResult.length
1421
+ };
1422
+ }
1423
+ var sCurrentValue = '';
1424
+ for (var i = 0; i < sValue.length; i++) {
1425
+ sCurrentValue += sValue[i];
1426
+ if (TimezoneUtil.isValidTimezone(sCurrentValue)) {
1427
+ oTimezoneParsed.timezone = sCurrentValue;
1428
+ oTimezoneParsed.length = sCurrentValue.length;
1429
+ }
1430
+ }
1431
+ }
1432
+ return oTimezoneParsed;
1433
+ }
1434
+ })
1223
1435
  };
1224
- DateFormat.prototype._format = function (oJSDate, bUTC) {
1436
+ DateFormat.prototype._format = function (oJSDate, bUTC, sTimezone) {
1225
1437
  if (this.oFormatOptions.relative) {
1226
- var sRes = this.formatRelative(oJSDate, bUTC, this.oFormatOptions.relativeRange);
1438
+ var sRes = this.formatRelative(oJSDate, bUTC, this.oFormatOptions.relativeRange, sTimezone);
1227
1439
  if (sRes) {
1228
1440
  return sRes;
1229
1441
  }
@@ -1234,10 +1446,10 @@ DateFormat.prototype._format = function (oJSDate, bUTC) {
1234
1446
  for (var i = 0; i < this.aFormatArray.length; i++) {
1235
1447
  oPart = this.aFormatArray[i];
1236
1448
  sSymbol = oPart.symbol || '';
1237
- aBuffer.push(this.oSymbols[sSymbol].format(oPart, oDate, bUTC, this));
1449
+ aBuffer.push(this.oSymbols[sSymbol].format(oPart, oDate, bUTC, this, sTimezone));
1238
1450
  }
1239
1451
  sResult = aBuffer.join('');
1240
- if (Core.getConfiguration().getOriginInfo()) {
1452
+ if (Configuration.getOriginInfo()) {
1241
1453
  sResult = new String(sResult);
1242
1454
  sResult.originInfo = {
1243
1455
  source: 'Common Locale Data Repository',
@@ -1249,10 +1461,21 @@ DateFormat.prototype._format = function (oJSDate, bUTC) {
1249
1461
  return sResult;
1250
1462
  };
1251
1463
  DateFormat.prototype.format = function (vJSDate, bUTC) {
1464
+ var sTimezone;
1465
+ if (this.type === mDateFormatTypes.DATETIME_WITH_TIMEZONE) {
1466
+ sTimezone = bUTC;
1467
+ bUTC = false;
1468
+ checkTimezoneParameterType(sTimezone);
1469
+ if (sTimezone && !TimezoneUtil.isValidTimezone(sTimezone)) {
1470
+ Log.error('The given timezone isn\'t valid.');
1471
+ return '';
1472
+ }
1473
+ }
1252
1474
  var sCalendarType = this.oFormatOptions.calendarType, sResult;
1253
1475
  if (bUTC === undefined) {
1254
1476
  bUTC = this.oFormatOptions.UTC;
1255
1477
  }
1478
+ sTimezone = sTimezone || Configuration.getTimezone();
1256
1479
  if (Array.isArray(vJSDate)) {
1257
1480
  if (!this.oFormatOptions.interval) {
1258
1481
  Log.error('Non-interval DateFormat can\'t format more than one date instance.');
@@ -1262,27 +1485,30 @@ DateFormat.prototype.format = function (vJSDate, bUTC) {
1262
1485
  Log.error('Interval DateFormat can only format with 2 date instances but ' + vJSDate.length + ' is given.');
1263
1486
  return '';
1264
1487
  }
1488
+ vJSDate = vJSDate.map(function (oJSDate) {
1489
+ return convertToTimezone(oJSDate, sTimezone, bUTC);
1490
+ });
1265
1491
  if (this.oFormatOptions.singleIntervalValue) {
1266
1492
  if (vJSDate[0] === null) {
1267
1493
  Log.error('First date instance which is passed to the interval DateFormat shouldn\'t be null.');
1268
1494
  return '';
1269
1495
  }
1270
1496
  if (vJSDate[1] === null) {
1271
- sResult = this._format(vJSDate[0], bUTC);
1497
+ sResult = this._format(vJSDate[0], bUTC, sTimezone);
1272
1498
  }
1273
1499
  }
1274
1500
  if (sResult === undefined) {
1275
- var bValid = vJSDate.every(function (oJSDate) {
1276
- return oJSDate && !isNaN(oJSDate.getTime());
1277
- });
1278
- if (!bValid) {
1501
+ if (!vJSDate.every(isValidDateObject)) {
1279
1502
  Log.error('At least one date instance which is passed to the interval DateFormat isn\'t valid.');
1280
1503
  return '';
1281
1504
  }
1282
1505
  sResult = this._formatInterval(vJSDate, bUTC);
1283
1506
  }
1284
1507
  } else {
1285
- if (!vJSDate || isNaN(vJSDate.getTime())) {
1508
+ if (!isValidDateObject(vJSDate)) {
1509
+ if (this.type === mDateFormatTypes.DATETIME_WITH_TIMEZONE && this.oFormatOptions.pattern.includes('VV')) {
1510
+ return this.oLocaleData.getTimezoneTranslations()[sTimezone] || sTimezone;
1511
+ }
1286
1512
  Log.error('The given date instance isn\'t valid.');
1287
1513
  return '';
1288
1514
  }
@@ -1290,9 +1516,10 @@ DateFormat.prototype.format = function (vJSDate, bUTC) {
1290
1516
  Log.error('Interval DateFormat expects an array with two dates for the first argument but only one date is given.');
1291
1517
  return '';
1292
1518
  }
1293
- sResult = this._format(vJSDate, bUTC);
1519
+ vJSDate = convertToTimezone(vJSDate, sTimezone, bUTC);
1520
+ sResult = this._format(vJSDate, bUTC, sTimezone);
1294
1521
  }
1295
- if (sCalendarType == CalendarType.Japanese && this.oLocale.getLanguage() === 'ja') {
1522
+ if (sCalendarType === CalendarType.Japanese && this.oLocale.getLanguage() === 'ja') {
1296
1523
  sResult = sResult.replace(/(^|[^\d])1年/g, '$1元年');
1297
1524
  }
1298
1525
  return sResult;
@@ -1310,7 +1537,7 @@ DateFormat.prototype._formatInterval = function (aJSDates, bUTC) {
1310
1537
  var oDiffField = this._getGreatestDiffField([
1311
1538
  oFromDate,
1312
1539
  oToDate
1313
- ], bUTC);
1540
+ ]);
1314
1541
  if (!oDiffField) {
1315
1542
  return this._format(aJSDates[0], bUTC);
1316
1543
  }
@@ -1343,10 +1570,10 @@ var mFieldToGroup = {
1343
1570
  Minutes: 'Minute',
1344
1571
  Seconds: 'Second'
1345
1572
  };
1346
- DateFormat.prototype._getGreatestDiffField = function (aDates, bUTC) {
1573
+ DateFormat.prototype._getGreatestDiffField = function (aDates) {
1347
1574
  var bDiffFound = false, mDiff = {};
1348
1575
  this.aIntervalCompareFields.forEach(function (sField) {
1349
- 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]);
1576
+ var sGetterPrefix = 'getUTC', sMethodName = sGetterPrefix + sField, sFieldGroup = mFieldToGroup[sField], vFromValue = aDates[0][sMethodName].apply(aDates[0]), vToValue = aDates[1][sMethodName].apply(aDates[1]);
1350
1577
  if (!deepEqual(vFromValue, vToValue)) {
1351
1578
  bDiffFound = true;
1352
1579
  mDiff[sFieldGroup] = true;
@@ -1357,19 +1584,32 @@ DateFormat.prototype._getGreatestDiffField = function (aDates, bUTC) {
1357
1584
  }
1358
1585
  return null;
1359
1586
  };
1360
- DateFormat.prototype._parse = function (sValue, aFormatArray, bUTC, bStrict) {
1361
- var iIndex = 0, oPart, sSubValue, oResult;
1362
- var oDateValue = { valid: true };
1363
- var oParseConf = {
1364
- formatArray: aFormatArray,
1365
- dateValue: oDateValue,
1366
- strict: bStrict
1367
- };
1587
+ DateFormat.prototype._parse = function (sValue, aFormatArray, bUTC, bStrict, sTimezone) {
1588
+ var sFlexibleDayPeriod, oNextPart, oPart, bPM, oPrevPart, oResult, sSubValue, oDateValue = {
1589
+ valid: true,
1590
+ lastTimezonePatternSymbol: ''
1591
+ }, iIndex = 0, oParseConf = {
1592
+ formatArray: aFormatArray,
1593
+ dateValue: oDateValue,
1594
+ strict: bStrict
1595
+ }, that = this;
1596
+ function getSymbol(oPart0) {
1597
+ return that.oSymbols[oPart0.symbol || ''];
1598
+ }
1599
+ function isNumeric(oPart0) {
1600
+ return !!oPart0 && getSymbol(oPart0).isNumeric(oPart0.digits);
1601
+ }
1368
1602
  for (var i = 0; i < aFormatArray.length; i++) {
1369
1603
  sSubValue = sValue.substr(iIndex);
1370
1604
  oPart = aFormatArray[i];
1605
+ oPrevPart = aFormatArray[i - 1];
1606
+ oNextPart = aFormatArray[i + 1];
1371
1607
  oParseConf.index = i;
1372
- oResult = this.oSymbols[oPart.symbol || ''].parse(sSubValue, oPart, this, oParseConf) || {};
1608
+ oParseConf.exactLength = isNumeric(oPart) && (isNumeric(oPrevPart) || isNumeric(oNextPart));
1609
+ oResult = getSymbol(oPart).parse(sSubValue, oPart, this, oParseConf, sTimezone) || {};
1610
+ if (oResult.tzDiff !== undefined || oResult.timezone) {
1611
+ oResult.lastTimezonePatternSymbol = oPart.symbol;
1612
+ }
1373
1613
  oDateValue = extend(oDateValue, oResult);
1374
1614
  if (oResult.valid === false) {
1375
1615
  break;
@@ -1377,7 +1617,12 @@ DateFormat.prototype._parse = function (sValue, aFormatArray, bUTC, bStrict) {
1377
1617
  iIndex += oResult.length || 0;
1378
1618
  }
1379
1619
  oDateValue.index = iIndex;
1380
- if (oDateValue.pm) {
1620
+ bPM = oDateValue.pm;
1621
+ if (oDateValue.flexDayPeriod && oDateValue.hour * 60 + (oDateValue.minute || 0) < 720) {
1622
+ sFlexibleDayPeriod = this.oLocaleData.getFlexibleDayPeriodOfTime(oDateValue.hour + 12, oDateValue.minute || 0);
1623
+ bPM = oDateValue.flexDayPeriod === sFlexibleDayPeriod;
1624
+ }
1625
+ if (bPM) {
1381
1626
  oDateValue.hour += 12;
1382
1627
  }
1383
1628
  if (oDateValue.dayNumberOfWeek === undefined && oDateValue.dayOfWeek !== undefined) {
@@ -1389,7 +1634,7 @@ DateFormat.prototype._parse = function (sValue, aFormatArray, bUTC, bStrict) {
1389
1634
  }
1390
1635
  return oDateValue;
1391
1636
  };
1392
- DateFormat.prototype._parseInterval = function (sValue, sCalendarType, bUTC, bStrict) {
1637
+ DateFormat.prototype._parseInterval = function (sValue, sCalendarType, bUTC, bStrict, sTimezone) {
1393
1638
  var aDateValues, iRepeat, oDateValue;
1394
1639
  this.intervalPatterns.some(function (sPattern) {
1395
1640
  var aFormatArray = this.parseCldrDatePattern(sPattern);
@@ -1401,7 +1646,7 @@ DateFormat.prototype._parseInterval = function (sValue, sCalendarType, bUTC, bSt
1401
1646
  }
1402
1647
  }
1403
1648
  if (iRepeat === undefined) {
1404
- oDateValue = this._parse(sValue, aFormatArray, bUTC, bStrict);
1649
+ oDateValue = this._parse(sValue, aFormatArray, bUTC, bStrict, sTimezone);
1405
1650
  if (oDateValue.index === 0 || oDateValue.index < sValue.length) {
1406
1651
  oDateValue.valid = false;
1407
1652
  }
@@ -1415,13 +1660,13 @@ DateFormat.prototype._parseInterval = function (sValue, sCalendarType, bUTC, bSt
1415
1660
  return true;
1416
1661
  } else {
1417
1662
  aDateValues = [];
1418
- oDateValue = this._parse(sValue, aFormatArray.slice(0, iRepeat), bUTC, bStrict);
1663
+ oDateValue = this._parse(sValue, aFormatArray.slice(0, iRepeat), bUTC, bStrict, sTimezone);
1419
1664
  if (oDateValue.valid === false) {
1420
1665
  return;
1421
1666
  }
1422
1667
  aDateValues.push(oDateValue);
1423
1668
  var iLength = oDateValue.index;
1424
- oDateValue = this._parse(sValue.substring(iLength), aFormatArray.slice(iRepeat), bUTC, bStrict);
1669
+ oDateValue = this._parse(sValue.substring(iLength), aFormatArray.slice(iRepeat), bUTC, bStrict, sTimezone);
1425
1670
  if (oDateValue.index === 0 || oDateValue.index + iLength < sValue.length) {
1426
1671
  oDateValue.valid = false;
1427
1672
  }
@@ -1434,65 +1679,62 @@ DateFormat.prototype._parseInterval = function (sValue, sCalendarType, bUTC, bSt
1434
1679
  }.bind(this));
1435
1680
  return aDateValues;
1436
1681
  };
1437
- var fnCreateDate = function (oDateValue, sCalendarType, bUTC, bStrict) {
1682
+ function getCalendarWeekParameter(oFormatOptions) {
1683
+ if (oFormatOptions.calendarWeekNumbering) {
1684
+ return oFormatOptions.calendarWeekNumbering;
1685
+ } else if (oFormatOptions.firstDayOfWeek !== undefined && oFormatOptions.minimalDaysInFirstWeek !== undefined) {
1686
+ return {
1687
+ firstDayOfWeek: oFormatOptions.firstDayOfWeek,
1688
+ minimalDaysInFirstWeek: oFormatOptions.minimalDaysInFirstWeek
1689
+ };
1690
+ }
1691
+ return undefined;
1692
+ }
1693
+ var convertToTimezone = function (oJSDate, sTimezone, bUTC) {
1694
+ if (!bUTC && isValidDateObject(oJSDate)) {
1695
+ return TimezoneUtil.convertToTimezone(oJSDate, sTimezone);
1696
+ }
1697
+ return oJSDate;
1698
+ };
1699
+ var fnCreateDate = function (oDateValue, sCalendarType, bUTC, bStrict, sTimezone, oFormatOptions, oLocale) {
1700
+ if (!oDateValue.valid) {
1701
+ return null;
1702
+ }
1438
1703
  var oDate, iYear = typeof oDateValue.year === 'number' ? oDateValue.year : 1970;
1439
- if (oDateValue.valid) {
1440
- if (bUTC || oDateValue.tzDiff !== undefined) {
1441
- oDate = UniversalDate.getInstance(new Date(0), sCalendarType);
1442
- oDate.setUTCEra(oDateValue.era || UniversalDate.getCurrentEra(sCalendarType));
1443
- oDate.setUTCFullYear(iYear);
1444
- oDate.setUTCMonth(oDateValue.month || 0);
1445
- oDate.setUTCDate(oDateValue.day || 1);
1446
- oDate.setUTCHours(oDateValue.hour || 0);
1447
- oDate.setUTCMinutes(oDateValue.minute || 0);
1448
- oDate.setUTCSeconds(oDateValue.second || 0);
1449
- oDate.setUTCMilliseconds(oDateValue.millisecond || 0);
1450
- if (bStrict && (oDateValue.day || 1) !== oDate.getUTCDate()) {
1451
- oDateValue.valid = false;
1452
- oDate = undefined;
1453
- } else {
1454
- if (oDateValue.tzDiff) {
1455
- oDate.setUTCMinutes((oDateValue.minute || 0) + oDateValue.tzDiff);
1456
- }
1457
- if (oDateValue.week !== undefined && (oDateValue.month === undefined || oDateValue.day === undefined)) {
1458
- oDate.setUTCWeek({
1459
- year: oDateValue.weekYear || oDateValue.year,
1460
- week: oDateValue.week
1461
- });
1462
- if (oDateValue.dayNumberOfWeek !== undefined) {
1463
- oDate.setUTCDate(oDate.getUTCDate() + oDateValue.dayNumberOfWeek - 1);
1464
- }
1465
- }
1466
- }
1467
- } else {
1468
- oDate = UniversalDate.getInstance(new Date(1970, 0, 1, 0, 0, 0), sCalendarType);
1469
- oDate.setEra(oDateValue.era || UniversalDate.getCurrentEra(sCalendarType));
1470
- oDate.setFullYear(iYear);
1471
- oDate.setMonth(oDateValue.month || 0);
1472
- oDate.setDate(oDateValue.day || 1);
1473
- oDate.setHours(oDateValue.hour || 0);
1474
- oDate.setMinutes(oDateValue.minute || 0);
1475
- oDate.setSeconds(oDateValue.second || 0);
1476
- oDate.setMilliseconds(oDateValue.millisecond || 0);
1477
- if (bStrict && (oDateValue.day || 1) !== oDate.getDate()) {
1478
- oDateValue.valid = false;
1479
- oDate = undefined;
1480
- } else if (oDateValue.week !== undefined && (oDateValue.month === undefined || oDateValue.day === undefined)) {
1481
- oDate.setWeek({
1482
- year: oDateValue.weekYear || oDateValue.year,
1483
- week: oDateValue.week
1484
- });
1485
- if (oDateValue.dayNumberOfWeek !== undefined) {
1486
- oDate.setDate(oDate.getDate() + oDateValue.dayNumberOfWeek - 1);
1487
- }
1488
- }
1704
+ oDate = UniversalDate.getInstance(new Date(0), sCalendarType);
1705
+ oDate.setUTCEra(oDateValue.era || UniversalDate.getCurrentEra(sCalendarType));
1706
+ oDate.setUTCFullYear(iYear);
1707
+ oDate.setUTCMonth(oDateValue.month || 0);
1708
+ oDate.setUTCDate(oDateValue.day || 1);
1709
+ oDate.setUTCHours(oDateValue.hour || 0);
1710
+ oDate.setUTCMinutes(oDateValue.minute || 0);
1711
+ oDate.setUTCSeconds(oDateValue.second || 0);
1712
+ oDate.setUTCMilliseconds(oDateValue.millisecond || 0);
1713
+ if (bStrict && (oDateValue.day || 1) !== oDate.getUTCDate()) {
1714
+ return null;
1715
+ }
1716
+ if (oDateValue.week !== undefined && (oDateValue.month === undefined || oDateValue.day === undefined)) {
1717
+ oDate.setUTCWeek({
1718
+ year: oDateValue.weekYear || oDateValue.year,
1719
+ week: oDateValue.week
1720
+ }, oLocale, getCalendarWeekParameter(oFormatOptions));
1721
+ if (oDateValue.dayNumberOfWeek !== undefined) {
1722
+ oDate.setUTCDate(oDate.getUTCDate() + oDateValue.dayNumberOfWeek - 1);
1723
+ }
1724
+ }
1725
+ oDate = oDate.getJSDate();
1726
+ if (!bUTC && (oDateValue.lastTimezonePatternSymbol === 'V' && oDateValue.timezone || oDateValue.tzDiff === undefined)) {
1727
+ if (oDateValue.timezone) {
1728
+ sTimezone = oDateValue.timezone;
1489
1729
  }
1490
- if (oDateValue.valid) {
1491
- oDate = oDate.getJSDate();
1492
- return oDate;
1730
+ if (sTimezone) {
1731
+ oDateValue.tzDiff = TimezoneUtil.calculateOffset(oDate, sTimezone);
1493
1732
  }
1494
1733
  }
1495
- return null;
1734
+ if (oDateValue.tzDiff) {
1735
+ oDate.setUTCSeconds(oDate.getUTCSeconds() + oDateValue.tzDiff);
1736
+ }
1737
+ return oDate;
1496
1738
  };
1497
1739
  function mergeWithoutOverwrite(object1, object2) {
1498
1740
  if (object1 === object2) {
@@ -1515,17 +1757,37 @@ function isValidDateRange(oStartDate, oEndDate) {
1515
1757
  }
1516
1758
  return true;
1517
1759
  }
1760
+ function isValidDateObject(oDate) {
1761
+ return oDate && typeof oDate.getTime === 'function' && !isNaN(oDate.getTime());
1762
+ }
1518
1763
  DateFormat.prototype.parse = function (sValue, bUTC, bStrict) {
1764
+ var bShowDate = this.oFormatOptions.showDate === undefined || this.oFormatOptions.showDate;
1765
+ var bShowTime = this.oFormatOptions.showTime === undefined || this.oFormatOptions.showTime;
1766
+ if (this.type === mDateFormatTypes.DATETIME_WITH_TIMEZONE && (bShowDate && !bShowTime || !bShowDate && bShowTime)) {
1767
+ throw new TypeError('The input can only be parsed back to date if both date and time are supplied.');
1768
+ }
1769
+ var sTimezone;
1770
+ if (bUTC === undefined && this.type !== mDateFormatTypes.DATETIME_WITH_TIMEZONE) {
1771
+ bUTC = this.oFormatOptions.UTC;
1772
+ }
1773
+ var bUTCInputParameter = bUTC;
1774
+ if (this.type === mDateFormatTypes.DATETIME_WITH_TIMEZONE) {
1775
+ sTimezone = bUTC;
1776
+ bUTC = false;
1777
+ checkTimezoneParameterType(sTimezone);
1778
+ if (sTimezone && !TimezoneUtil.isValidTimezone(sTimezone)) {
1779
+ Log.error('The given timezone isn\'t valid.');
1780
+ return null;
1781
+ }
1782
+ }
1519
1783
  sValue = sValue == null ? '' : String(sValue).trim();
1520
1784
  var oDateValue;
1521
1785
  var sCalendarType = this.oFormatOptions.calendarType;
1522
- if (bUTC === undefined) {
1523
- bUTC = this.oFormatOptions.UTC;
1524
- }
1786
+ sTimezone = sTimezone || Configuration.getTimezone();
1525
1787
  if (bStrict === undefined) {
1526
1788
  bStrict = this.oFormatOptions.strictParsing;
1527
1789
  }
1528
- if (sCalendarType == CalendarType.Japanese && this.oLocale.getLanguage() === 'ja') {
1790
+ if (sCalendarType === CalendarType.Japanese && this.oLocale.getLanguage() === 'ja') {
1529
1791
  sValue = sValue.replace(/元年/g, '1年');
1530
1792
  }
1531
1793
  if (!this.oFormatOptions.interval) {
@@ -1533,22 +1795,40 @@ DateFormat.prototype.parse = function (sValue, bUTC, bStrict) {
1533
1795
  if (oJSDate) {
1534
1796
  return oJSDate;
1535
1797
  }
1536
- oDateValue = this._parse(sValue, this.aFormatArray, bUTC, bStrict);
1798
+ oDateValue = this._parse(sValue, this.aFormatArray, bUTC, bStrict, sTimezone);
1537
1799
  if (oDateValue.index === 0 || oDateValue.index < sValue.length) {
1538
1800
  oDateValue.valid = false;
1539
1801
  }
1540
- oJSDate = fnCreateDate(oDateValue, sCalendarType, bUTC, bStrict);
1802
+ oJSDate = fnCreateDate(oDateValue, sCalendarType, bUTC, bStrict, sTimezone, this.oFormatOptions, this.oLocale);
1541
1803
  if (oJSDate) {
1804
+ if (this.type === mDateFormatTypes.DATETIME_WITH_TIMEZONE) {
1805
+ var bShowTimezone = this.oFormatOptions.showTimezone === undefined || this.oFormatOptions.showTimezone;
1806
+ if (!bShowTimezone && bShowDate && bShowTime) {
1807
+ return [
1808
+ oJSDate,
1809
+ undefined
1810
+ ];
1811
+ } else if (bShowTimezone && !bShowDate && !bShowTime) {
1812
+ return [
1813
+ undefined,
1814
+ oDateValue.timezone
1815
+ ];
1816
+ }
1817
+ return [
1818
+ oJSDate,
1819
+ oDateValue.timezone || undefined
1820
+ ];
1821
+ }
1542
1822
  return oJSDate;
1543
1823
  }
1544
1824
  } else {
1545
- var aDateValues = this._parseInterval(sValue, sCalendarType, bUTC, bStrict);
1825
+ var aDateValues = this._parseInterval(sValue, sCalendarType, bUTC, bStrict, sTimezone);
1546
1826
  var oJSDate1, oJSDate2;
1547
- if (aDateValues && aDateValues.length == 2) {
1827
+ if (aDateValues && aDateValues.length === 2) {
1548
1828
  var oDateValue1 = mergeWithoutOverwrite(aDateValues[0], aDateValues[1]);
1549
1829
  var oDateValue2 = mergeWithoutOverwrite(aDateValues[1], aDateValues[0]);
1550
- oJSDate1 = fnCreateDate(oDateValue1, sCalendarType, bUTC, bStrict);
1551
- oJSDate2 = fnCreateDate(oDateValue2, sCalendarType, bUTC, bStrict);
1830
+ oJSDate1 = fnCreateDate(oDateValue1, sCalendarType, bUTC, bStrict, sTimezone, this.oFormatOptions, this.oLocale);
1831
+ oJSDate2 = fnCreateDate(oDateValue2, sCalendarType, bUTC, bStrict, sTimezone, this.oFormatOptions, this.oLocale);
1552
1832
  if (oJSDate1 && oJSDate2) {
1553
1833
  if (this.oFormatOptions.singleIntervalValue && oJSDate1.getTime() === oJSDate2.getTime()) {
1554
1834
  return [
@@ -1571,11 +1851,14 @@ DateFormat.prototype.parse = function (sValue, bUTC, bStrict) {
1571
1851
  }
1572
1852
  }
1573
1853
  }
1574
- if (!this.bIsFallback) {
1854
+ if (this.aFallbackFormats) {
1575
1855
  var vDate;
1576
1856
  this.aFallbackFormats.every(function (oFallbackFormat) {
1577
- vDate = oFallbackFormat.parse(sValue, bUTC, bStrict);
1857
+ vDate = oFallbackFormat.parse(sValue, bUTCInputParameter, bStrict);
1578
1858
  if (Array.isArray(vDate)) {
1859
+ if (oFallbackFormat.type === mDateFormatTypes.DATETIME_WITH_TIMEZONE) {
1860
+ return false;
1861
+ }
1579
1862
  return !(vDate[0] && vDate[1]);
1580
1863
  } else {
1581
1864
  return !vDate;
@@ -1600,20 +1883,20 @@ DateFormat.prototype.parseCldrDatePattern = function (sPattern) {
1600
1883
  for (i = 0; i < sPattern.length; i++) {
1601
1884
  var sCurChar = sPattern.charAt(i), sNextChar, sPrevChar, sPrevPrevChar;
1602
1885
  if (bQuoted) {
1603
- if (sCurChar == '\'') {
1886
+ if (sCurChar === '\'') {
1604
1887
  sPrevChar = sPattern.charAt(i - 1);
1605
1888
  sPrevPrevChar = sPattern.charAt(i - 2);
1606
1889
  sNextChar = sPattern.charAt(i + 1);
1607
- if (sPrevChar == '\'' && sPrevPrevChar != '\'') {
1890
+ if (sPrevChar === '\'' && sPrevPrevChar !== '\'') {
1608
1891
  bQuoted = false;
1609
- } else if (sNextChar == '\'') {
1892
+ } else if (sNextChar === '\'') {
1610
1893
  i += 1;
1611
1894
  } else {
1612
1895
  bQuoted = false;
1613
1896
  continue;
1614
1897
  }
1615
1898
  }
1616
- if (sState == 'text') {
1899
+ if (sState === 'text') {
1617
1900
  oCurrentObject.value += sCurChar;
1618
1901
  } else {
1619
1902
  oCurrentObject = {
@@ -1624,11 +1907,11 @@ DateFormat.prototype.parseCldrDatePattern = function (sPattern) {
1624
1907
  sState = 'text';
1625
1908
  }
1626
1909
  } else {
1627
- if (sCurChar == '\'') {
1910
+ if (sCurChar === '\'') {
1628
1911
  bQuoted = true;
1629
1912
  } else if (this.oSymbols[sCurChar]) {
1630
1913
  sNewState = this.oSymbols[sCurChar].name;
1631
- if (sState == sNewState) {
1914
+ if (sState === sNewState) {
1632
1915
  oCurrentObject.digits++;
1633
1916
  } else {
1634
1917
  oCurrentObject = {
@@ -1648,7 +1931,7 @@ DateFormat.prototype.parseCldrDatePattern = function (sPattern) {
1648
1931
  }
1649
1932
  }
1650
1933
  } else {
1651
- if (sState == 'text') {
1934
+ if (sState === 'text') {
1652
1935
  oCurrentObject.value += sCurChar;
1653
1936
  } else {
1654
1937
  oCurrentObject = {
@@ -1684,70 +1967,88 @@ DateFormat.prototype.parseRelative = function (sValue, bUTC) {
1684
1967
  }
1685
1968
  }
1686
1969
  function computeRelativeDate(iDiff, sScale) {
1687
- var iToday, oToday = new Date(), oJSDate;
1688
- if (bUTC) {
1689
- iToday = oToday.getTime();
1690
- } else {
1691
- iToday = Date.UTC(oToday.getFullYear(), oToday.getMonth(), oToday.getDate(), oToday.getHours(), oToday.getMinutes(), oToday.getSeconds(), oToday.getMilliseconds());
1692
- }
1693
- oJSDate = new Date(iToday);
1694
- switch (sScale) {
1695
- case 'second':
1696
- oJSDate.setUTCSeconds(oJSDate.getUTCSeconds() + iDiff);
1697
- break;
1698
- case 'minute':
1699
- oJSDate.setUTCMinutes(oJSDate.getUTCMinutes() + iDiff);
1700
- break;
1701
- case 'hour':
1702
- oJSDate.setUTCHours(oJSDate.getUTCHours() + iDiff);
1703
- break;
1704
- case 'day':
1705
- oJSDate.setUTCDate(oJSDate.getUTCDate() + iDiff);
1706
- break;
1707
- case 'week':
1708
- oJSDate.setUTCDate(oJSDate.getUTCDate() + iDiff * 7);
1709
- break;
1710
- case 'month':
1711
- oJSDate.setUTCMonth(oJSDate.getUTCMonth() + iDiff);
1712
- break;
1713
- case 'quarter':
1714
- oJSDate.setUTCMonth(oJSDate.getUTCMonth() + iDiff * 3);
1715
- break;
1716
- case 'year':
1717
- oJSDate.setUTCFullYear(oJSDate.getUTCFullYear() + iDiff);
1718
- break;
1719
- }
1970
+ var oResult = UI5Date.getInstance();
1720
1971
  if (bUTC) {
1721
- return oJSDate;
1972
+ oResult.setUTCFullYear(oResult.getFullYear(), oResult.getMonth(), oResult.getDate());
1973
+ oResult.setUTCHours(oResult.getHours(), oResult.getMinutes(), oResult.getSeconds(), oResult.getMilliseconds());
1974
+ switch (sScale) {
1975
+ case 'second':
1976
+ oResult.setUTCSeconds(oResult.getUTCSeconds() + iDiff);
1977
+ break;
1978
+ case 'minute':
1979
+ oResult.setUTCMinutes(oResult.getUTCMinutes() + iDiff);
1980
+ break;
1981
+ case 'hour':
1982
+ oResult.setUTCHours(oResult.getUTCHours() + iDiff);
1983
+ break;
1984
+ case 'day':
1985
+ oResult.setUTCDate(oResult.getUTCDate() + iDiff);
1986
+ break;
1987
+ case 'week':
1988
+ oResult.setUTCDate(oResult.getUTCDate() + iDiff * 7);
1989
+ break;
1990
+ case 'month':
1991
+ oResult.setUTCMonth(oResult.getUTCMonth() + iDiff);
1992
+ break;
1993
+ case 'quarter':
1994
+ oResult.setUTCMonth(oResult.getUTCMonth() + iDiff * 3);
1995
+ break;
1996
+ case 'year':
1997
+ oResult.setUTCFullYear(oResult.getUTCFullYear() + iDiff);
1998
+ break;
1999
+ }
1722
2000
  } else {
1723
- return new Date(oJSDate.getUTCFullYear(), oJSDate.getUTCMonth(), oJSDate.getUTCDate(), oJSDate.getUTCHours(), oJSDate.getUTCMinutes(), oJSDate.getUTCSeconds(), oJSDate.getUTCMilliseconds());
2001
+ switch (sScale) {
2002
+ case 'second':
2003
+ oResult.setSeconds(oResult.getSeconds() + iDiff);
2004
+ break;
2005
+ case 'minute':
2006
+ oResult.setMinutes(oResult.getMinutes() + iDiff);
2007
+ break;
2008
+ case 'hour':
2009
+ oResult.setHours(oResult.getHours() + iDiff);
2010
+ break;
2011
+ case 'day':
2012
+ oResult.setDate(oResult.getDate() + iDiff);
2013
+ break;
2014
+ case 'week':
2015
+ oResult.setDate(oResult.getDate() + iDiff * 7);
2016
+ break;
2017
+ case 'month':
2018
+ oResult.setMonth(oResult.getMonth() + iDiff);
2019
+ break;
2020
+ case 'quarter':
2021
+ oResult.setMonth(oResult.getMonth() + iDiff * 3);
2022
+ break;
2023
+ case 'year':
2024
+ oResult.setFullYear(oResult.getFullYear() + iDiff);
2025
+ break;
2026
+ }
1724
2027
  }
2028
+ return oResult;
1725
2029
  }
1726
2030
  };
1727
- DateFormat.prototype.formatRelative = function (oJSDate, bUTC, aRange) {
1728
- var oToday = new Date(), oDateUTC, sScale = this.oFormatOptions.relativeScale || 'day', iDiff, sPattern, iDiffSeconds;
2031
+ DateFormat.prototype.formatRelative = function (oJSDate, bUTC, aRange, sTimezone) {
2032
+ var oDateUTC, iDiff, iDiffSeconds, sPattern, oToday = convertToTimezone(new Date(), sTimezone), sScale = this.oFormatOptions.relativeScale || 'day';
1729
2033
  iDiffSeconds = (oJSDate.getTime() - oToday.getTime()) / 1000;
1730
- if (this.oFormatOptions.relativeScale == 'auto') {
2034
+ if (this.oFormatOptions.relativeScale === 'auto') {
1731
2035
  sScale = this._getScale(iDiffSeconds, this.aRelativeScales);
2036
+ sScale = fixScaleForMonths(oJSDate, oToday, sScale, iDiffSeconds);
1732
2037
  }
1733
2038
  if (!aRange) {
1734
2039
  aRange = this._mRanges[sScale];
1735
2040
  }
1736
- if (sScale == 'year' || sScale == 'month' || sScale == 'day') {
1737
- oToday = new Date(Date.UTC(oToday.getFullYear(), oToday.getMonth(), oToday.getDate()));
2041
+ if (sScale === 'year' || sScale === 'month' || sScale === 'day') {
2042
+ oToday = new Date(Date.UTC(oToday.getUTCFullYear(), oToday.getUTCMonth(), oToday.getUTCDate()));
1738
2043
  oDateUTC = new Date(0);
1739
- if (bUTC) {
1740
- oDateUTC.setUTCFullYear(oJSDate.getUTCFullYear(), oJSDate.getUTCMonth(), oJSDate.getUTCDate());
1741
- } else {
1742
- oDateUTC.setUTCFullYear(oJSDate.getFullYear(), oJSDate.getMonth(), oJSDate.getDate());
1743
- }
2044
+ oDateUTC.setUTCFullYear(oJSDate.getUTCFullYear(), oJSDate.getUTCMonth(), oJSDate.getUTCDate());
1744
2045
  oJSDate = oDateUTC;
1745
2046
  }
1746
2047
  iDiff = this._getDifference(sScale, [
1747
2048
  oToday,
1748
2049
  oJSDate
1749
2050
  ]);
1750
- if (this.oFormatOptions.relativeScale != 'auto' && (iDiff < aRange[0] || iDiff > aRange[1])) {
2051
+ if (this.oFormatOptions.relativeScale !== 'auto' && (iDiff < aRange[0] || iDiff > aRange[1])) {
1751
2052
  return null;
1752
2053
  }
1753
2054
  sPattern = this.oLocaleData.getRelativePattern(sScale, iDiff, iDiffSeconds > 0, this.oFormatOptions.relativeStyle);
@@ -1808,8 +2109,23 @@ DateFormat.prototype._getScale = function (iDiffSeconds, aScales) {
1808
2109
  }
1809
2110
  return sScale;
1810
2111
  };
2112
+ function fixScaleForMonths(oJSDate, oToday, sScale, iDiffSeconds) {
2113
+ var iMonthDiff = Math.abs(oJSDate.getUTCMonth() - oToday.getUTCMonth());
2114
+ if (sScale === 'week' && iMonthDiff === 2) {
2115
+ return 'month';
2116
+ } else if (sScale === 'week' && iMonthDiff === 1) {
2117
+ if (oJSDate.getUTCDate() === oToday.getUTCDate() || iDiffSeconds < 0 && oJSDate.getUTCDate() < oToday.getUTCDate() || iDiffSeconds > 0 && oJSDate.getUTCDate() > oToday.getUTCDate()) {
2118
+ return 'month';
2119
+ }
2120
+ } else if (sScale === 'month' && iMonthDiff === 1) {
2121
+ if (iDiffSeconds > 0 && oJSDate.getUTCDate() < oToday.getUTCDate() || iDiffSeconds < 0 && oJSDate.getUTCDate() > oToday.getUTCDate()) {
2122
+ return 'week';
2123
+ }
2124
+ }
2125
+ return sScale;
2126
+ }
1811
2127
  function cutDateFields(oDate, iStartIndex) {
1812
- var aFields = [
2128
+ var sMethodName, aFields = [
1813
2129
  'FullYear',
1814
2130
  'Month',
1815
2131
  'Date',
@@ -1817,49 +2133,55 @@ function cutDateFields(oDate, iStartIndex) {
1817
2133
  'Minutes',
1818
2134
  'Seconds',
1819
2135
  'Milliseconds'
1820
- ], sMethodName;
2136
+ ], oDateCopy = new Date(oDate.getTime());
1821
2137
  for (var i = iStartIndex; i < aFields.length; i++) {
1822
- sMethodName = 'set' + aFields[iStartIndex];
1823
- oDate[sMethodName].apply(oDate, [0]);
2138
+ sMethodName = 'setUTC' + aFields[iStartIndex];
2139
+ oDateCopy[sMethodName].apply(oDateCopy, [0]);
1824
2140
  }
2141
+ return oDateCopy;
1825
2142
  }
1826
2143
  var mRelativeDiffs = {
1827
2144
  year: function (oFromDate, oToDate) {
1828
- return oToDate.getFullYear() - oFromDate.getFullYear();
2145
+ return oToDate.getUTCFullYear() - oFromDate.getUTCFullYear();
1829
2146
  },
1830
2147
  month: function (oFromDate, oToDate) {
1831
- return oToDate.getMonth() - oFromDate.getMonth() + this.year(oFromDate, oToDate) * 12;
2148
+ return oToDate.getUTCMonth() - oFromDate.getUTCMonth() + this.year(oFromDate, oToDate) * 12;
1832
2149
  },
1833
2150
  week: function (oFromDate, oToDate, oFormat) {
1834
- var iFromDay = oFormat._adaptDayOfWeek(oFromDate.getDay());
1835
- var iToDay = oFormat._adaptDayOfWeek(oToDate.getDay());
1836
- cutDateFields(oFromDate, 3);
1837
- cutDateFields(oToDate, 3);
2151
+ var iFromDay = oFormat._adaptDayOfWeek(oFromDate.getUTCDay());
2152
+ var iToDay = oFormat._adaptDayOfWeek(oToDate.getUTCDay());
2153
+ oFromDate = cutDateFields(oFromDate, 3);
2154
+ oToDate = cutDateFields(oToDate, 3);
1838
2155
  return (oToDate.getTime() - oFromDate.getTime() - (iToDay - iFromDay) * oFormat._mScales.day * 1000) / (oFormat._mScales.week * 1000);
1839
2156
  },
1840
2157
  day: function (oFromDate, oToDate, oFormat) {
1841
- cutDateFields(oFromDate, 3);
1842
- cutDateFields(oToDate, 3);
2158
+ oFromDate = cutDateFields(oFromDate, 3);
2159
+ oToDate = cutDateFields(oToDate, 3);
1843
2160
  return (oToDate.getTime() - oFromDate.getTime()) / (oFormat._mScales.day * 1000);
1844
2161
  },
1845
2162
  hour: function (oFromDate, oToDate, oFormat) {
1846
- cutDateFields(oFromDate, 4);
1847
- cutDateFields(oToDate, 4);
2163
+ oFromDate = cutDateFields(oFromDate, 4);
2164
+ oToDate = cutDateFields(oToDate, 4);
1848
2165
  return (oToDate.getTime() - oFromDate.getTime()) / (oFormat._mScales.hour * 1000);
1849
2166
  },
1850
2167
  minute: function (oFromDate, oToDate, oFormat) {
1851
- cutDateFields(oFromDate, 5);
1852
- cutDateFields(oToDate, 5);
2168
+ oFromDate = cutDateFields(oFromDate, 5);
2169
+ oToDate = cutDateFields(oToDate, 5);
1853
2170
  return (oToDate.getTime() - oFromDate.getTime()) / (oFormat._mScales.minute * 1000);
1854
2171
  },
1855
2172
  second: function (oFromDate, oToDate, oFormat) {
1856
- cutDateFields(oFromDate, 6);
1857
- cutDateFields(oToDate, 6);
2173
+ oFromDate = cutDateFields(oFromDate, 6);
2174
+ oToDate = cutDateFields(oToDate, 6);
1858
2175
  return (oToDate.getTime() - oFromDate.getTime()) / (oFormat._mScales.second * 1000);
1859
2176
  }
1860
2177
  };
1861
2178
  DateFormat.prototype._adaptDayOfWeek = function (iDayOfWeek) {
1862
- var iFirstDayOfWeek = LocaleData.getInstance(Core.getConfiguration().getFormatSettings().getFormatLocale()).getFirstDayOfWeek();
2179
+ var vCalendarWeekParameter = getCalendarWeekParameter(this.oFormatOptions), iFirstDayOfWeek;
2180
+ if (typeof vCalendarWeekParameter === 'object') {
2181
+ iFirstDayOfWeek = vCalendarWeekParameter.firstDayOfWeek;
2182
+ } else {
2183
+ iFirstDayOfWeek = CalendarUtils.getWeekConfigurationValues(vCalendarWeekParameter, this.oLocale).firstDayOfWeek;
2184
+ }
1863
2185
  var iDayNumberOfWeek = iDayOfWeek - (iFirstDayOfWeek - 1);
1864
2186
  if (iDayNumberOfWeek <= 0) {
1865
2187
  iDayNumberOfWeek += 7;