tui-calendar
Version:
TOAST UI Calendar
484 lines (395 loc) • 11.4 kB
JavaScript
/**
* @fileoverview timezone
* @author NHN FE Development Lab <dl_javascript@nhn.com>
*/
;
var util = require('tui-code-snippet');
var intlUtil = require('./intlUtil');
var MIN_TO_MS = 60 * 1000;
var nativeOffsetMs = getTimezoneOffset();
var customOffsetMs = nativeOffsetMs;
var timezoneOffsetCallback = null;
var setByTimezoneOption = false;
var offsetCalculator = null;
var primaryOffset, primaryTimezoneName;
var getterMethods = [
'getDate',
'getDay',
'getFullYear',
'getHours',
'getMilliseconds',
'getMinutes',
'getMonth',
'getSeconds'
];
var setterMethods = [
'setDate',
'setFullYear',
'setHours',
'setMilliseconds',
'setMinutes',
'setMonth',
'setSeconds'
];
var STANDARD_TO_DST = 1;
var DST_TO_STANDARD = -1;
/**
* Get the timezone offset by timestamp
* @param {number} timestamp - timestamp
* @returns {number} timezone offset
* @private
*/
function getTimezoneOffset(timestamp) {
timestamp = !util.isUndefined(timestamp) ? timestamp : Date.now();
return new Date(timestamp).getTimezoneOffset() * MIN_TO_MS;
}
/**
* Get the custom timezone offset by timestamp
* @param {number} timestamp - timestamp
* @returns {number} timezone offset
* @private
*/
function getCustomTimezoneOffset(timestamp) {
if (!setByTimezoneOption && timezoneOffsetCallback) {
return timezoneOffsetCallback(timestamp) * MIN_TO_MS;
}
return customOffsetMs;
}
/**
* Convert to local time
* @param {number} time - time
* @returns {number} local time
*/
function getLocalTime(time) {
var customTimezoneOffset, localTime;
if (!setByTimezoneOption) {
return time;
}
customTimezoneOffset = getCustomTimezoneOffset(time);
localTime = time - customTimezoneOffset + nativeOffsetMs;
return localTime;
}
/**
* Create a Date instance with multiple arguments
* @param {Array} args - arguments
* @returns {Date}
* @private
*/
function createDateWithMultipleArgs(args) {
var utc = Date.UTC.apply(null, args);
return new Date(utc + getTimezoneOffset(utc));
}
/**
* To convert a Date to TZDate as it is.
* @param {TZDate|number|null} arg - date
* @returns {Date}
*/
function createDateWithUTCTime(arg) {
var time;
if (arg instanceof TZDate) {
time = arg.getUTCTime();
} else if (typeof arg === 'number') {
time = arg;
} else if (arg === null) {
time = 0;
} else {
throw new Error('Invalid Type');
}
return new Date(time);
}
/**
* Convert time to local time. Those times are only from API and not from inner source code.
* @param {Date|string} arg - date
* @returns {Date}
*/
function createDateAsLocalTime(arg) {
var time;
if (arg instanceof Date) {
time = arg.getTime();
} else if (typeof arg === 'string') {
time = Date.parse(arg);
} else {
throw new Error('Invalid Type');
}
time = getLocalTime(time);
return new Date(time);
}
/**
* is it for local time? These type can be used from Calendar API.
* @param {Date|string} arg - date
* @returns {boolean}
*/
function useLocalTimeConverter(arg) {
return arg instanceof Date || typeof arg === 'string';
}
/**
* Timezone Date Class
* @param {number|TZDate|Date|string} date - date to be converted
* @constructor
*/
function TZDate(date) {
var nativeDate;
if (util.isUndefined(date)) {
date = Date.now();
}
if (arguments.length > 1) {
nativeDate = createDateWithMultipleArgs(arguments);
} else if (useLocalTimeConverter(date)) {
nativeDate = createDateAsLocalTime(date);
} else {
nativeDate = createDateWithUTCTime(date);
}
this._date = nativeDate;
}
/**
* Get milliseconds which is converted by timezone
* @returns {number} milliseconds
*/
TZDate.prototype.getTime = function() {
var time = this._date.getTime();
return time + getCustomTimezoneOffset(time) - getTimezoneOffset(time);
};
/**
* Get UTC milliseconds
* @returns {number} milliseconds
*/
TZDate.prototype.getUTCTime = function() {
return this._date.getTime();
};
/**
* toUTCString
* @returns {string}
*/
TZDate.prototype.toUTCString = function() {
return this._date.toUTCString();
};
/**
* to Date
* @returns {Date}
*/
TZDate.prototype.toDate = function() {
return this._date;
};
TZDate.prototype.valueOf = function() {
return this.getTime();
};
TZDate.prototype.addDate = function(day) {
this.setDate(this.getDate() + day);
return this;
};
TZDate.prototype.addMinutes = function(minutes) {
this.setMinutes(this.getMinutes() + minutes);
return this;
};
TZDate.prototype.addMilliseconds = function(milliseconds) {
this.setMilliseconds(this.getMilliseconds() + milliseconds);
return this;
};
/* eslint-disable max-params*/
TZDate.prototype.setWithRaw = function(y, M, d, h, m, s, ms) {
this.setFullYear(y, M, d);
this.setHours(h, m, s, ms);
return this;
};
/**
* @returns {TZDate} local time
*/
TZDate.prototype.toLocalTime = function() {
var time = this.getTime();
var utcTime = this.getUTCTime();
var diff = time - utcTime;
return new TZDate(utcTime - diff);
};
getterMethods.forEach(function(methodName) {
TZDate.prototype[methodName] = function() {
return this._date[methodName].apply(this._date, arguments);
};
});
setterMethods.forEach(function(methodName) {
TZDate.prototype[methodName] = function() {
this._date[methodName].apply(this._date, arguments);
return this.getTime();
};
});
/**
* Set offset
* @param {number} offset - timezone offset based on minutes
*/
function setOffset(offset) {
customOffsetMs = offset * MIN_TO_MS;
}
/**
* Set primary offset
* @param {number} offset - offset
*/
function setPrimaryOffset(offset) {
primaryOffset = offset;
setOffset(offset);
}
/**
* Return primary offset
* @returns {number} offset
*/
function getPrimaryOffset() {
return util.isNumber(primaryOffset) ? primaryOffset : new Date().getTimezoneOffset();
}
/**
* Set primary timezone name
* @param {string} timezoneName - timezone name (time zone names of the IANA time zone database, such as 'Asia/Seoul', 'America/New_York')
*/
function setPrimaryTimezoneCode(timezoneName) {
primaryTimezoneName = timezoneName;
}
/**
* Get offset by timezoneName
* @param {string} timezoneName - timezone name (time zone names of the IANA time zone database, such as 'Asia/Seoul', 'America/New_York')
* @param {number} timestamp - timestamp
* @returns {number} timezone offset
*/
function getOffsetByTimezoneName(timezoneName, timestamp) {
var offset = getPrimaryOffset();
var calculator;
if (!timezoneName) {
return offset;
}
calculator = getOffsetCalculator(timezoneName);
return calculator ? calculator(timezoneName, timestamp) : offset;
}
/**
* Set a calculator function to get timezone offset by timestamp
* @param {function} calculator - offset calculator
*/
function setOffsetCalculator(calculator) {
offsetCalculator = calculator;
}
/**
* Return a function to calculate timezone offset by timestamp
* @param {string} timezoneName - timezone name
* @returns {function | null} offset calculator
*/
function getOffsetCalculator(timezoneName) {
if (util.isFunction(offsetCalculator)) {
return offsetCalculator;
}
if (intlUtil.supportIntl(timezoneName)) {
return intlUtil.offsetCalculator;
}
return null;
}
/**
* Set timezone and offset by timezone option
* @param {Timezone} timezoneObj - {@link Timezone}
*/
function setPrimaryTimezoneByOption(timezoneObj) {
var timezoneName, offset;
if (!(timezoneObj && timezoneObj.timezoneName)) {
return;
}
timezoneName = timezoneObj.timezoneName;
setByTimezoneOption = true;
setPrimaryTimezoneCode(timezoneName);
offset = getOffsetByTimezoneName(timezoneName, Date.now());
if (offset === nativeOffsetMs / MIN_TO_MS) {
setByTimezoneOption = false;
}
setPrimaryOffset(offset);
}
/**
* Get primary timezone name
* @returns {string} primary timezone name (time zone names of the IANA time zone database, such as 'Asia/Seoul', 'America/New_York')
*/
function getPrimaryTimezoneName() {
return primaryTimezoneName;
}
/**
* Compare the start and end times to see if the time zone is changing.
* @param {number} startTime - start timestamp
* @param {number} endTime - end timestamp
* @returns {object} whether to change the offset and offset difference value
*/
function isDifferentOffsetStartAndEndTime(startTime, endTime) {
var offset1 = getOffsetByTimezoneName(primaryTimezoneName, startTime);
var offset2 = getOffsetByTimezoneName(primaryTimezoneName, endTime);
var result = 0;
if (offset1 > offset2) {
result = STANDARD_TO_DST;
} else if (offset1 < offset2) {
result = DST_TO_STANDARD;
}
return {
isOffsetChanged: result,
offsetDiff: offset1 - offset2
};
}
module.exports = {
Date: TZDate,
setOffset: setOffset,
/**
* Set offset
* @param {number} offset - timezone offset based on minutes
*/
setOffsetByTimezoneOption: function(offset) {
this.setOffset(-offset);
primaryOffset = -offset;
setByTimezoneOption = true;
},
/**
* Get offset in case of `setByTimezoneOption`. Or return 0.
* @returns {number} timezone offset offset minutes
*/
getOffset: function() {
if (setByTimezoneOption) {
return customOffsetMs / MIN_TO_MS;
}
return 0;
},
/**
* Set a callback function to get timezone offset by timestamp
* @param {function} callback - callback function
*/
setOffsetCallback: function(callback) {
timezoneOffsetCallback = callback;
},
/**
* (Use this method only for testing)
* Reset system timezone and custom timezone
*/
restoreOffset: function() {
customOffsetMs = getTimezoneOffset();
},
getNativeOffsetMs: function() {
return nativeOffsetMs;
},
/**
* Check to use custom timezone option
* @returns {boolean} use custom timezone option
*/
hasPrimaryTimezoneCustomSetting: function() {
return setByTimezoneOption;
},
resetCustomSetting: function() {
setByTimezoneOption = false;
},
setOffsetCalculator: setOffsetCalculator,
setPrimaryTimezoneByOption: setPrimaryTimezoneByOption,
getPrimaryOffset: getPrimaryOffset,
getOffsetByTimezoneName: getOffsetByTimezoneName,
getPrimaryTimezoneName: getPrimaryTimezoneName,
isNativeOsUsingDSTTimezone: function() {
var year = new Date().getFullYear();
var jan = new Date(year, 0, 1).getTimezoneOffset();
var jul = new Date(year, 6, 1).getTimezoneOffset();
return jan !== jul;
},
isPrimaryUsingDSTTimezone: function() {
var year = new Date().getFullYear();
var jan = new Date(year, 0, 1);
var jul = new Date(year, 6, 1);
return (
getOffsetByTimezoneName(primaryTimezoneName, jan) !==
getOffsetByTimezoneName(primaryTimezoneName, jul)
);
},
isDifferentOffsetStartAndEndTime: isDifferentOffsetStartAndEndTime,
setPrimaryTimezoneCode: setPrimaryTimezoneCode
};