/** * A helper class responsible in assisting the formatting and unformatting of * date/time. */ define( [ 'dojo/_base/declare', 'dojo/date', 'dojox/date/timezone', 'dojo/date/locale', "dojo/_base/kernel", 'dojo/i18n' ], function(declare, dojoDate, dojoxDate, dateLocale, kernel, i18n) { var DateTimeFormatterHelper = declare( null, { DSTIndicatorPosition : Object.freeze({ AFTER : "AFTER", BEFORE : "BEFORE" }), hourPatterns : [ "hh", "h", "HH", "H", "kk", "k" ], constructor : function() { this.dstHourIndicator = { position : this.DSTIndicatorPosition.AFTER, A : '', B : '*' } this.dateTimeDelimiter = " "; // Override dojo's default localized short format // to consider our delimiter i18n.cache["dojo/cldr/nls/gregorian/" + dojo.config.locale]["dateTimeFormat-short"] = "{1}" + this.dateTimeDelimiter + "{0}"; }, getSelector : function(datePattern, timePattern) { if (!datePattern || datePattern === "") { return "time"; } if (!this.selector && (!timePattern || timePattern === "")) { return "date"; } return null; }, dateTimeToString : function(date, configuration) { var datePattern = configuration.datePattern; var timePattern = configuration.timePattern; var timezone = configuration.timezone; if (date == null || (!(datePattern || datePattern === '') && (!timePattern || timePattern === ''))) { return ""; } if (typeof date == 'number') { date = new Date(date); } if (!timezone) { timezone = dojo.config.timezone; } var selector = this.getSelector(datePattern, timePattern); // This adds the dst indicator to the pattern. timePattern = this.getDSTTimePattern(date, timezone, timePattern); // This indicates if the datestamp needs to be // rendered for the previous date. This is used as // we want to render // the midnight as 24:00. var switchDayBack = false; if (timePattern && timePattern !== '') { // If we are at midnight on a case with k // rendering, set the timestamp to 24 and the // day switchback to true. if (timePattern.indexOf("k") != -1 && date.getHours() == 0 && date.getMinutes() == 0 && date.getSeconds() == 0 && date.getMilliseconds() == 0) { // Replace cases of two k with one so the // next step can just replace the remaining // k. timePattern = timePattern .replace("kk", "k"); // Replace the k with 24. timePattern = timePattern.replace("k", "'24'"); // Set the day to be switched back a day // when rendering since we are midnight of // the previous day. switchDayBack = true; } else { timePattern = timePattern .replace(/k/g, "H"); } } var tmpDate = new Date(date.getTime()); if (switchDayBack) { tmpDate = dojoDate.add(tmpDate, "day", -1); } var result = dateLocale.format(tmpDate, { datePattern : datePattern, timePattern : timePattern, timezone : timezone, selector : selector }); return result; }, stringToDateTime : function(dateString, configuration) { var datePattern = configuration.datePattern; var timePattern = configuration.timePattern; var timezone = configuration.timezone; var selector = this.getSelector(datePattern, timePattern); if (!dateString || dateString === '') { return null; } if (!timezone) { timezone = dojo.config.timezone; } var dateTimePattern = ""; var tmpTimePattern = timePattern; if (datePattern !== '') { dateTimePattern += datePattern; } if (datePattern !== '' && timePattern !== '') { dateTimePattern += this.dateTimeDelimiter; } if (timePattern !== '') { dateTimePattern += timePattern; } if (timePattern.indexOf("kk") != -1) { tmpTimePattern = timePattern .replace("kk", "HH"); dateTimePattern = dateTimePattern.replace("kk", "HH"); } else if (timePattern.indexOf("k") != -1) { tmpTimePattern = timePattern.replace("k", "H"); dateTimePattern = dateTimePattern.replace("k", "H"); } var isHourA = dateString .indexOf(this.dstHourIndicator.A) != -1; if (isHourA) { dateString = dateString.replace( this.dstHourIndicator.A, ""); if (this.dstHourIndicator.A !== '') { dateString = dateString.replace("kk", "k"); dateString = dateString.replace("HH", "H"); } } var isHourB = dateString .indexOf(this.dstHourIndicator.B) != -1; if (isHourB) { dateString = dateString.replace( this.dstHourIndicator.B, ""); if (this.dstHourIndicator.B !== '') { dateString = dateString.replace("kk", "k"); dateString = dateString.replace("HH", "H"); } } var switchDate = false; if (timePattern != null && timePattern.indexOf("k") != -1) { var index = dateTimePattern.indexOf("H"); if (index != -1) { var str = dateString.substring(index); if (str.length > 1) { if ("24" === str.substring(0, 2)) { dateString = dateString.substring( 0, index) + "00" + dateString.substring( index + 2, dateString.length); switchDate = true; } } } } var date = dateLocale.parse(dateString, { datePattern : datePattern, timePattern : tmpTimePattern, timezone : timezone, selector : selector }); // Dojo's formatter always uses the current timezone // when parsing. Need to adjust the necessary amount // of minutes from the // timezone offset of the current timezone and the // provided timezone. var tzInfo = dojoxDate.getTzInfo(date, timezone); date = dojoDate.add(date, "minute", tzInfo.tzOffset - date.getTimezoneOffset()); if (switchDate) { date = dojoDate.add(date, "day", 1); } if (timezoneInfo.enableDST) { var daylightTime = timezoneInfo.dstChangeDate[date .getFullYear()]; if (daylightTime != null) { // if dst end hour, subtract hour from // date for ( var i in daylightTime.endDates) { var endDate = new Date( daylightTime.endDates[i]); var endHour = endDate.getHours() == 0 ? 24 : endDate.getHours(); if ((date.getDate() == endDate .getDate()) && (date.getMonth() == endDate .getMonth()) && (date.getHours() == endHour - 1)) { if (date.getTimezoneOffset() >= endDate .getTimezoneOffset()) { date = dojoDate.add(date, "hour", -1); } else if (isHourB) { date = dojoDate.add(date, "hour", 1); } break; } } } } return date; }, getDSTTimePattern : function(date, timezone, timePattern) { if (timePattern !== '') { // if extra hour, add hourBIndicator if (this.isDstSwitchbackHour(date, timezone)) { timePattern = this.applyIndicator( timePattern, this.dstHourIndicator.position, "'" + this.dstHourIndicator.B + "'"); timePattern = timePattern .replace("kk", "k"); timePattern = timePattern .replace("HH", "H"); timePattern = timePattern .replace("hh", "h"); } else { // if one hour before the extra // hour, add hourAIndicator var tmpDate = new Date(date.getTime()); tmpDate = dojoDate.add(tmpDate, "hour", 1); if (this.isDstSwitchbackHour(tmpDate, timezone)) { timePattern = this.applyIndicator( timePattern, this.dstHourIndicator.position, "'" + this.dstHourIndicator.A + "'"); timePattern = timePattern.replace("kk", "k"); timePattern = timePattern.replace("HH", "H"); timePattern = timePattern.replace("hh", "h"); } } } return timePattern; }, applyIndicator : function(timePattern, dstIndicatorPosition, dstIndicator) { for ( var i in this.hourPatterns) { var pattern = this.hourPatterns[i]; if (timePattern.indexOf(pattern) != -1) { timePattern = timePattern .replace( pattern, dstIndicatorPosition == this.DSTIndicatorPosition.BEFORE ? dstIndicator + pattern : pattern + dstIndicator); break; } } return timePattern; }, isDstSwitchbackHour : function(date, timezone) { // check for DST end hour if (timezoneInfo.enableDST) { var daylightTime = timezoneInfo.dstChangeDate[date .getFullYear()]; if (!daylightTime) { return false; } // check if DST end hour for ( var i in daylightTime.endDates) { var endDate = new Date( daylightTime.endDates[i]); var endHour = endDate.getHours() == 0 ? 24 : endDate.getHours(); if ((date.getDate() == endDate.getDate()) && (date.getMonth() == endDate .getMonth()) && (date.getHours() == endHour - 1)) { if (date.getTimezoneOffset() >= endDate .getTimezoneOffset()) { var tmpDate = dojoDate .add(new Date(endDate .getTime()), "hour", -2); if (date.getTimezoneOffset() != tmpDate .getTimezoneOffset()) { // this is the extra hour return true; } } } } } return false; } }); return new DateTimeFormatterHelper(); });