Created
December 16, 2019 17:07
-
-
Save siddharthvp/dcdc51e05aa79a5856e63ecc116dcacf to your computer and use it in GitHub Desktop.
Date class for Morebits
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*************** Morebits.date ********************/ | |
/** | |
* @constructor | |
* Create a date object. MediaWiki timestamp format is also acceptable, | |
* in addition to everything that JS Date() accepts. | |
*/ | |
Morebits.date = function() { | |
var args = Array.prototype.slice.call(arguments); | |
this._d = new (Function.prototype.bind.apply(Date, [Date].concat(args))); | |
if (isNaN(this._d.getTime()) && typeof args[0] === 'string') { | |
// Try again after removing a comma, to get MediaWiki timestamps to parse | |
this._d = new (Function.prototype.bind.call(Date, Date, args[0].replace(/(\d\d:\d\d),/, '$1'))); | |
} | |
}; | |
Morebits.date.prototype = { | |
/** @param {(Date|Morebits.date)} date */ | |
isBefore: function(date) { | |
return this.getTime() < date.getTime(); | |
}, | |
isAfter: function(date) { | |
return this.getTime() > date.getTime(); | |
}, | |
/** | |
* Add a given number of minutes, hours, days, months or years to the date. | |
* This is done in-place. The modified date object is also returned, allowing chaining. | |
* @param {number} number | |
* @param {string} unit | |
* @throws {Error} if invalid or unsupported unit is given | |
* @returns {Morebits.date} | |
*/ | |
add: function(number, unit) { | |
// mapping time units with getter/setter function names | |
var unitMap = { | |
minutes: 'Minutes', | |
hours: 'Hours', | |
days: 'Date', | |
months: 'Month', | |
years: 'FullYear' | |
}; | |
var unitNorm = unitMap[unit] || unitMap[unit + 's']; | |
if (unitNorm) { | |
this['set' + unitNorm](this['get' + unitNorm]() + number); | |
return this; | |
} | |
throw new Error('Invalid unit "' + unit + '": Only ' + Object.keys(unitMap).join(', ') + ' are allowed.'); | |
}, | |
/** | |
* Subtracts a given number of minutes, hours, days, months or years to the date. | |
* This is done in-place. The modified date object is also returned, allowing chaining. | |
* @param {number} number | |
* @param {string} unit | |
* @throws {Error} if invalid or unsupported unit is given | |
* @returns {Morebits.date} | |
*/ | |
subtract: function(number, unit) { | |
return this.add(-number, unit); | |
}, | |
/** | |
* Formats the date into a string per the given format string, and as per a given time zone, | |
* text between square brackets is kept as is. | |
* @param {string} formatstr | |
* @param {string|number} [zone=system] - 'system' (for browser-default time zone), 'utc' (for UTC), | |
* or 'user' (for time zone according to user's site preferences), or just specify a timezone as number | |
* of minutes past UTC. | |
* @returns {string} | |
* @requires mediawiki.user - if 'user' zone is used | |
*/ | |
format: function(formatstr, zone) { | |
zone = zone || 'system'; | |
var udate = this; | |
// create a new date object that will contain the date to display as system time | |
if (zone === 'utc') { | |
udate = new Morebits.date(this.getTime()).add(this.getTimezoneOffset(), 'minutes'); | |
} else if (zone === 'user') { | |
var offset = parseInt(mw.user.options.get('timecorrection').split('|')[1]); | |
udate = new Morebits.date(this.getTime()).add(offset, 'minutes'); | |
} else if (typeof zone === 'number') { | |
var offset = this.getTimezoneOffset() /* convert to utc */ + zone; /* add the utc offset */ | |
udate = new Morebits.date(this.getTime()).add(offset, 'minutes'); | |
} | |
var pad = function(num) { | |
return num < 10 ? '0' + num : num; | |
}; | |
var unbinder = new Morebits.unbinder(formatstr); // escape stuff between [...] | |
unbinder.unbind('\\[', '\\]'); | |
unbinder.content = unbinder.content | |
.replace(/HH/g, pad(udate.getHours())) | |
.replace(/H/g, udate.getHours()) | |
.replace(/mm/g, pad(udate.getMinutes())) | |
.replace(/SS/g, pad(udate.getSeconds())) | |
.replace(/DD/g, pad(udate.getDate())) | |
.replace(/D/g, udate.getDate()) | |
.replace(/MMMM/g, mw.config.get('wgMonthNames')[udate.getMonth() + 1]) | |
.replace(/MMM/g, mw.config.get('wgMonthNamesShort')[udate.getMonth() + 1]) | |
.replace(/MM/g, pad(udate.getMonth() + 1)) | |
.replace(/M/g, udate.getMonth() + 1) | |
.replace(/YYYY/g, udate.getFullYear()); | |
return unbinder.rebind().replace(/\[(.*?)\]/g, '$1'); | |
}, | |
// Redundant to `format`, hence probably unnecessary: | |
// /** | |
// * Returns a "HH:mm, D MMMM YYYY" UTC timestamp. The default MediaWiki timestamp format. | |
// * @returns {string} | |
// */ | |
// toTimestamp: function() { | |
// var h = this.getUTCHours(), m = this.getUTCMinutes(); | |
// return (h < 10 ? '0' + h : h) + ':' + (m < 10 ? '0' + m : m) + ', ' + | |
// this.getUTCDate() + ' ' + this.getUTCMonthName() + ' ' + this.getUTCFullYear(); | |
// }, | |
// /** | |
// * @param {string} [format=dmy] - 'dmy', 'mdy' or 'ymd' | |
// * @returns {string} | |
// */ | |
// dateString: function(format) { | |
// var D = this.getUTCDate(), M = this.getUTCMonthName(), Y = this.getUTCFullYear(); | |
// switch (format) { | |
// case 'ymd': | |
// return Y + ' ' + M + ' ' + D; | |
// case 'mdy': | |
// return M + ' ' + D + ', ' + Y; | |
// default: // dmy | |
// return D + ' ' + M + ' ' + Y; | |
// } | |
// }, | |
/** | |
* Gives a readable relative time string such as "Yesterday at 6:43 PM" or "Last Thursday at 11:45 AM". | |
* @returns {string} | |
*/ | |
calendar: function() { | |
var days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; | |
var now = new Date(), thisdate = new Morebits.date(this.getTime()); // create a copy | |
now.setHours(0, 0, 0, 0); // zero out the hours, minutes, seconds and milliseconds - keeping only the date | |
thisdate.setHours(0, 0, 0, 0); | |
var datediff = Math.abs((now.getTime() - thisdate.getTime())) / 86400000; | |
if (datediff >= 7) { | |
return thisdate.format('DD/MM/YYYY'); | |
} | |
var result; | |
if (datediff === 0) { | |
result = 'Today'; | |
} else if (datediff === 1) { | |
result = thisdate.isBefore(now) ? 'Yesterday' : 'Tomorrow'; | |
} else if (datediff < 7) { | |
result = (thisdate.isBefore(now) ? 'Last ' : '') + days[thisdate.getDay()]; | |
} | |
var h = this.getHours(), m = this.getMinutes(); | |
result += ' at ' + (h > 12 ? h - 12 : h) + ':' + (m < 10 ? '0' + m : m) + ' ' + (h >= 12 ? 'PM' : 'AM'); | |
return result; | |
}, | |
/** | |
* Returns a regex that matches wikitext section titles such as ==December 2019== or | |
* === Jan 2018 === | |
* @returns {RegExp} | |
*/ | |
monthHeaderRegex: function() { | |
return new RegExp('^==+\\s*(?:' + this.getUTCMonthName() + '|' + this.getUTCMonthNameAbbrev() + | |
')\\s+' + this.getUTCFullYear() + '\\s*==+', 'mg'); | |
}, | |
/** | |
* Creates a wikitext section header with the month and year. | |
* @param {number} [level=2] - Header level (default 2) | |
* @returns {string} | |
*/ | |
monthHeader: function(level) { | |
level = level || 2; | |
var header = ''; // String.prototype.repeat is not supported in IE 11, so... | |
for (var i = 0; i < level; i++) { | |
header += '='; | |
} | |
return header + ' ' + this.getUTCMonthName() + ' ' + this.getUTCFullYear() + ' ' + header; | |
} | |
}; | |
// Allow native Date.prototype methods to be used on Morebits.date objects | |
// Note that this includes getUTCMonthName() and getUTCMonthNameAbbrev(), which should probably be removed | |
// from Date.prototype and moved above. | |
Object.getOwnPropertyNames(Date.prototype).forEach(function(func) { | |
Morebits.date.prototype[func] = function() { | |
return this._d[func].apply(this._d, Array.prototype.slice.call(arguments)); | |
}; | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment