date-with-offset
Version:
Work with timezone-aware dates
158 lines (123 loc) • 4.86 kB
JavaScript
(function(window, undefined) {
var slice = Array.prototype.slice,
MILLISECONDS_PER_MINUTE = 60 * 1000,
OFFSET_SUFFIX = /(((GMT)?[\+\-]\d\d:?\d\d)|Z)(\s*\(.+\))?$/;
function isNumber(x) { return typeof(x) === 'number'; }
function isFunction(x) { return typeof(x) === 'function'; }
function isOffset(x) {
if (x == null) { return false; }
if (isFunction(x.valueOf)) {
x = x.valueOf();
}
return isNumber(x);
}
function applyOffset(date, offset) {
date.setTime( date.getTime() + MILLISECONDS_PER_MINUTE * offset );
return date;
}
function buildDate(args, offset) {
if (args.length === 0) { return new Date(); }
if (args.length === 1 && args[0] instanceof Date) { return args[0]; }
if (args.length === 1 && isNumber(args[0])) { return new Date(args[0]); }
if (args.length > 1) {
args[3] = args[3] || null;
args[4] = args[4] || null;
args[5] = args[5] || null;
args[6] = args[6] || null;
date = new Date(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
return applyOffset( date, -date.getTimezoneOffset() - offset );
}
var string = args[0].toString(),
date = new Date(string),
isYYYYmmdd = /\d\d\d\d-\d\d-\d\d/.test(string),
isOffsetSpecified = OFFSET_SUFFIX.test(string),
isLocal = !isYYYYmmdd && !isOffsetSpecified;
if (isLocal) {
date = applyOffset(date, -date.getTimezoneOffset() - offset);
}
return date;
}
function setTime(date) {
this.date = function() { return new Date(date); };
return this;
}
function formattedOffset(offsetInMinutes) {
var sign = offsetInMinutes >= 0 ? '+' : '-';
offsetInMinutes = Math.abs(offsetInMinutes);
var hours = Math.floor(offsetInMinutes / 60),
minutes = offsetInMinutes - 60 * hours;
if (hours < 10) { hours = '0' + hours; }
if (minutes < 10) { minutes = '0' + minutes; }
return 'GMT' + sign + hours + minutes;
}
function DateWithOffset() {
var args = slice.call(arguments, 0),
offset = args.pop();
if ( !isOffset(offset) ) {
throw new TypeError('DateWithOffset requires an offset');
}
setTime.call(this, buildDate(args, offset));
this.offset = function() { return offset; };
}
DateWithOffset.prototype = {
// A Date whose UTC time is the local time of this object's real time.
// That is, it is incorrect by `offset` minutes. Used for `getDate` et al.
localDate: function() {
return applyOffset(this.date(), this.offset());
},
withOffset: function(offset) {
return new DateWithOffset(this.getTime(), offset);
},
getTime: function() { return this.date().getTime(); },
getTimezoneOffset: function() { return -this.offset(); },
toISOString: function() { return this.date().toISOString(); },
valueOf: function() { return this.getTime(); },
toString: function() {
var localDate = this.localDate(),
plusBrowserOffset = applyOffset(localDate, localDate.getTimezoneOffset()),
asString = plusBrowserOffset.toString();
return asString.replace(OFFSET_SUFFIX, formattedOffset(this.offset()));
},
getYear: function() {
return this.localDate().getUTCFullYear() - 1900;
},
setYear: function(year) {
return this.setFullYear(1900 + year);
},
setTime: setTime
};
function addGetters(property) {
DateWithOffset.prototype['get' + property] = function() {
return this.localDate()['getUTC' + property]();
};
DateWithOffset.prototype['getUTC' + property] = function() {
return this.date()['getUTC' + property]();
};
}
function addSetters(property) {
DateWithOffset.prototype['set' + property] = function(newValue) {
var localDate = this.localDate();
localDate['setUTC' + property](newValue);
return this.setTime( applyOffset(localDate, -this.offset()) );
};
DateWithOffset.prototype['setUTC' + property] = function(newValue) {
var date = this.date();
date['setUTC' + property](newValue);
return this.setTime(date);
};
}
addGetters('Date'); addSetters('Date');
addGetters('Day'); // can't set day of week
addGetters('FullYear'); addSetters('FullYear');
addGetters('Hours'); addSetters('Hours');
addGetters('Milliseconds'); addSetters('Milliseconds');
addGetters('Minutes'); addSetters('Minutes');
addGetters('Month'); addSetters('Month');
addGetters('Seconds'); addSetters('Seconds');
if (typeof(module) !== 'undefined') {
module.exports = DateWithOffset;
}
if (typeof(window) !== 'undefined') {
window.DateWithOffset = DateWithOffset;
}
}(this));