From 1f5b6490a36f77ffb68cb5afd620fdcc2c612b87 Mon Sep 17 00:00:00 2001 From: Adam Shaw Date: Sun, 2 Feb 2014 13:07:52 -0800 Subject: [PATCH] break apart date_utils.js --- lumbar.json | 3 +- src/date-formatting.js | 214 +++++++++++++++++ src/date_util.js | 487 -------------------------------------- src/moment-ext.js | 264 +++++++++++++++++++++ src/util.js | 31 +++ tests/date_util_test.html | 4 +- 6 files changed, 514 insertions(+), 489 deletions(-) create mode 100644 src/date-formatting.js delete mode 100644 src/date_util.js create mode 100644 src/moment-ext.js diff --git a/lumbar.json b/lumbar.json index 9bb73c6..0699fad 100644 --- a/lumbar.json +++ b/lumbar.json @@ -20,7 +20,8 @@ "src/Header.js", "src/EventManager.js", "src/util.js", - "src/date_util.js", + "src/moment-ext.js", + "src/date-formatting.js", "src/basic/MonthView.js", "src/basic/BasicWeekView.js", "src/basic/BasicDayView.js", diff --git a/src/date-formatting.js b/src/date-formatting.js new file mode 100644 index 0000000..729bc37 --- /dev/null +++ b/src/date-formatting.js @@ -0,0 +1,214 @@ + +// Single Date Formatting +// ------------------------------------------------------------------------------------------------- + + +// call this if you want Moment's original format method to be used +function momentFormat(mom, formatStr) { + return moment.fn.format.call(mom, formatStr); +} + + +// Formats `date` with a Moment formatting string, but allow our non-zero areas and +// additional token. +function formatDate(date, formatStr) { + return formatDateWithChunks(date, getFormatStringChunks(formatStr)); +} + + +function formatDateWithChunks(date, chunks) { + var s = ''; + var i; + + for (i=0; i "MMMM D YYYY" + formatStr = date1.lang().longDateFormat(formatStr) || formatStr; + // BTW, this is not important for `formatDate` because it is impossible to put custom tokens + // or non-zero areas in Moment's localized format strings. + + separator = separator || ' - '; + + return formatRangeWithChunks( + date1, + date2, + getFormatStringChunks(formatStr), + separator, + isRTL + ); +} +fc.formatRange = formatRange; // expose + + +function formatRangeWithChunks(date1, date2, chunks, separator, isRTL) { + var chunkStr; // the rendering of the chunk + var leftI; + var leftStr = ''; + var rightI; + var rightStr = ''; + var middleI; + var middleStr1 = ''; + var middleStr2 = ''; + var middleStr = ''; + + // Start at the leftmost side of the formatting string and continue until you hit a token + // that is not the same between dates. + for (leftI=0; leftIleftI; rightI--) { + chunkStr = formatSimilarChunk(date1, date2, chunks[rightI]); + if (chunkStr === false) { + break; + } + rightStr = chunkStr + rightStr; + } + + // The area in the middle is different for both of the dates. + // Collect them distinctly so we can jam them together later. + for (middleI=leftI; middleI<=rightI; middleI++) { + middleStr1 += formatDateWithChunk(date1, chunks[middleI]); + middleStr2 += formatDateWithChunk(date2, chunks[middleI]); + } + + if (middleStr1 || middleStr2) { + if (isRTL) { + middleStr = middleStr2 + separator + middleStr1; + } + else { + middleStr = middleStr1 + separator + middleStr2; + } + } + + return leftStr + middleStr + rightStr; +} + + +var similarUnitMap = { + Y: 'year', + M: 'month', + D: 'day', // day of month + d: 'day' // day of week +}; +// don't go any further than day, because we don't want to break apart times like "12:30:00" +// TODO: week maybe? + + +// Given a formatting chunk, and given that both dates are similar in the regard the +// formatting chunk is concerned, format date1 against `chunk`. Otherwise, return `false`. +function formatSimilarChunk(date1, date2, chunk) { + var token; + var unit; + + if (typeof chunk === 'string') { // a literal string + return chunk; + } + else if ((token = chunk.token)) { + unit = similarUnitMap[token.charAt(0)]; + // are the dates the same for this unit of measurement? + if (unit && date1.isSame(date2, unit)) { + return momentFormat(date1, token); // would be the same if we used `date2` + // BTW, don't support custom tokens + } + } + + return false; // the chunk is NOT the same for the two dates + // BTW, don't support splitting on non-zero areas +} + + +// Chunking Utils +// ------------------------------------------------------------------------------------------------- + + +var formatStringChunkCache = {}; + + +function getFormatStringChunks(formatStr) { + if (formatStr in formatStringChunkCache) { + return formatStringChunkCache[formatStr]; + } + return (formatStringChunkCache[formatStr] = chunkFormatString(formatStr)); +} + + +// Break the formatting string into an array of chunks +function chunkFormatString(formatStr) { + var chunks = []; + var chunker = /\[([^\]]*)\]|\(([^\)]*)\)|((\w)\4*o?T?)|([^\w\[\(]+)/g; // TODO: more descrimination + var match; + + while ((match = chunker.exec(formatStr))) { + if (match[1]) { // a literal string instead [ ... ] + chunks.push(match[1]); + } + else if (match[2]) { // non-zero formatting inside ( ... ) + chunks.push({ maybe: chunkFormatString(match[2]) }); + } + else if (match[3]) { // a formatting token + chunks.push({ token: match[3] }); + } + else if (match[5]) { // an unenclosed literal string + chunks.push(match[5]); + } + } + + return chunks; +} diff --git a/src/date_util.js b/src/date_util.js deleted file mode 100644 index b9c566b..0000000 --- a/src/date_util.js +++ /dev/null @@ -1,487 +0,0 @@ - -var dayIDs = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']; -var ambigTimeRegex = /^\s*\d{4}-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))$/; -var ambigZoneRegex = /^\s*\d{4}-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?)?$/; -var momentCloneMethod = moment.fn.clone; -var momentFormatMethod = moment.fn.format; -var momentToISOStringMethod = moment.fn.toISOString; - - -// diffs the two moments into a Duration where full-days are recorded first, -// then the remaining time. -function dayishDiff(d1, d0) { - return moment.duration({ - days: d1.clone().stripTime().diff(d0.clone().stripTime(), 'days'), - ms: d1.time() - d0.time() - }); -} - -function isNativeDate(input) { - return Object.prototype.toString.call(input) === '[object Date]' || - input instanceof Date; -} - - -// MOMENT: creating -// ------------------------------------------------------------------------------------------------- - -// Creates a moment in the local timezone, similar to the vanilla moment(...) constructor, -// but with extra features: -// - ambiguous times -// - enhanced formatting (TODO) -fc.moment = function() { - return buildMoment(arguments); -}; - -// Sames as fc.moment, but creates a moment in the UTC timezone. -fc.moment.utc = function() { - return buildMoment(arguments, true); -}; - -// Creates a moment and preserves the timezone offset of the ISO8601 string, -// allowing for ambigous timezones. If the string is not an ISO8601 string, -// the moment is processed in UTC-mode (a departure from moment's method). -fc.moment.parseZone = function() { - return buildMoment(arguments, true, true); -}; - -// when parseZone==true, if can't figure it out, fall back to parseUTC -function buildMoment(args, parseUTC, parseZone) { - var isSingleArg = args.length == 1; - var isSingleMoment = isSingleArg && moment.isMoment(args[0]); - var isSingleString = isSingleArg && typeof args[0] === 'string'; - var isSingleArray = isSingleArg && $.isArray(args[0]); - var isSingleNativeDate = isSingleArg && isNativeDate(args[0]); - var isAmbigTime = isSingleString && ambigTimeRegex.test(args[0]); - var isAmbigZone = isAmbigTime || isSingleArray || isSingleString && ambigZoneRegex.test(args[0]); - var mom; - - if (parseUTC || parseZone || isAmbigTime) { - mom = moment.utc.apply(moment, args); - } - else { - mom = moment.apply(null, args); - } - - // if we are essentially cloning a moment, transfer over existing _ambig properties - if (isSingleMoment) { - transferAmbigs(args[0], mom); - } - - if (isAmbigTime) { - mom._ambigTime = true; - mom._ambigZone = true; // if ambiguous time, also ambiguous timezone offset - } - - if (parseZone) { - if (isAmbigZone) { - mom._ambigZone = true; - } - else if (isSingleString) { - mom.zone(args[0]); // if fails, will set it to 0, which it already was - } - else if (isSingleNativeDate || args[0] === undefined) { - // native Date object? - // specified with no arguments? - // then consider the moment to be local - mom.local(); - } - } - - mom._fc = true; // flag for use other extended functionality (only formatting at this point) - - return mom; -} - -// we need to patch moment's clone method because it will not copy our _ambig properties -moment.fn.clone = function() { - var res = momentCloneMethod.apply(this, arguments); - transferAmbigs(this, res); - return res; -}; - -// transfers our internal _ambig properties from one moment to another, but only if true -function transferAmbigs(srcMoment, destMoment) { - if (srcMoment._ambigTime) { - destMoment._ambigTime = true; - } - if (srcMoment._ambigZone) { - destMoment._ambigZone = true; - } -} - - -// MOMENT: time-of-day -// ------------------------------------------------------------------------------------------------- - - -// GETTER -// Returns a Duration with the hours/minutes/seconds/ms values of the moment. -// If the moment has an ambiguous time, a duration of 00:00 will be returned. -// -// SETTER -// You can supply a Duration, a Moment, or a Duration-like argument. -// When setting the time, and the moment has an ambiguous time, it then becomes unambiguous. -moment.fn.time = function(time) { - if (time === undefined) { // getter - return moment.duration({ - hours: this.hours(), - minutes: this.minutes(), - seconds: this.seconds(), - milliseconds: this.milliseconds() - }); - } - else { // setter - - delete this._ambigTime; // mark that the moment now has a time - - if (!moment.isDuration(time) && !moment.isMoment(time)) { - time = moment.duration(time); - } - - return this.hours(time.hours() + time.days() * 24) // day value will cause overflow (so 24 hours becomes 00:00:00 of next day) - .minutes(time.minutes()) - .seconds(time.seconds()) - .milliseconds(time.milliseconds()); - } -}; - -// Converts the moment to UTC, stripping out its time-of-day and timezone offset, -// but preserving its YMD. A moment with a stripped time will display no time -// nor timezone offset when .format() is called. -moment.fn.stripTime = function() { - var a = this.toArray(); // year,month,date,hours,minutes,seconds as an array - - this._ambigTime = true; - this._ambigZone = true; // if ambiguous time, also ambiguous timezone offset - - return this.utc() - .year(a[0]) - .month(a[1]) - .date(a[2]) - .hours(0) - .minutes(0) - .seconds(0) - .milliseconds(0); -}; - -// Returns if the moment has a non-ambiguous time (boolean) -moment.fn.hasTime = function() { - return !this._ambigTime; -}; - - -// MOMENT: timezone offset -// ------------------------------------------------------------------------------------------------- - -// Converts the moment to UTC, stripping out its timezone offset, but preserving its -// YMD and time-of-day. A moment with a stripped timezone offset will display no -// timezone offset when .format() is called. -moment.fn.stripZone = function() { - var a = this.toArray(); // year,month,date,hours,minutes,seconds as an array - - this._ambigZone = true; - - return this.utc() - .year(a[0]) - .month(a[1]) - .date(a[2]) - .hours(a[3]) - .minutes(a[4]) - .seconds(a[5]) - .milliseconds(a[6]); -}; - -// Returns of the moment has a non-ambiguous timezone offset (boolean) -moment.fn.hasZone = function() { - return !this._ambigZone; -}; - - -// MOMENT: formatting mods -// ------------------------------------------------------------------------------------------------- - -moment.fn.format = function() { - if (!arguments[0]) { - if (this._ambigTime) { - return momentFormat(this, 'YYYY-MM-DD'); - } - if (this._ambigZone) { - return momentFormat(this, 'YYYY-MM-DD[T]HH:mm:ss'); - } - } - if (this._fc) { - return formatDate(this, arguments[0]); // our extended formatting - } - else { - return momentFormatMethod.apply(this, arguments); // pass along all arguments - } -}; - -moment.fn.toISOString = function() { - if (this._ambigTime) { - return momentFormat(this, 'YYYY-MM-DD'); - } - if (this._ambigZone) { - return momentFormat(this, 'YYYY-MM-DD[T]HH:mm:ss'); - } - return momentToISOStringMethod.apply(this, arguments); // pass along all arguments -}; - -// call this if you want Moment's original format method to be used -function momentFormat(moment, formatStr) { - return momentFormatMethod.call(moment, formatStr); -} - - -// MOMENT: misc utils -// ------------------------------------------------------------------------------------------------- - -// Is the moment within the specified range? `end` is exclusive. -// TODO: rename for collision reasons? -moment.fn.isWithin = function(start, end) { - return this >= moment(start) && this < moment(end); -}; - -$.each([ - 'isBefore', - 'isAfter', - //'isSame', // nevermind. moment handles normalization to UTC - 'isWithin' -], function(i, methodName) { - var origMethod = moment.fn[methodName]; - var momentCount = methodName == 'isWithin' ? 2 : 1; - - moment.fn[methodName] = function() { - var newThis; - var args = Array.prototype.slice.call(arguments); - var i; - - if (this._ambigZone) { - for (i=0; i "MMMM D YYYY" - formatStr = date1.lang().longDateFormat(formatStr) || formatStr; - // BTW, this is not important for `formatDate` because it is impossible to put custom tokens - // or non-zero areas in Moment's localized format strings. - - separator = separator || ' - '; - - return formatRangeWithChunks( - date1, - date2, - getFormatStringChunks(formatStr), - separator, - isRTL - ); -} -fc.formatRange = formatRange; // expose - - -function formatRangeWithChunks(date1, date2, chunks, separator, isRTL) { - var chunkStr; // the rendering of the chunk - var leftI; - var leftStr = ''; - var rightI; - var rightStr = ''; - var middleI; - var middleStr1 = ''; - var middleStr2 = ''; - var middleStr = ''; - - // Start at the leftmost side of the formatting string and continue until you hit a token - // that is not the same between dates. - for (leftI=0; leftIleftI; rightI--) { - chunkStr = formatSimilarChunk(date1, date2, chunks[rightI]); - if (chunkStr === false) { - break; - } - rightStr = chunkStr + rightStr; - } - - // The area in the middle is different for both of the dates. - // Collect them distinctly so we can jam them together later. - for (middleI=leftI; middleI<=rightI; middleI++) { - middleStr1 += formatDateWithChunk(date1, chunks[middleI]); - middleStr2 += formatDateWithChunk(date2, chunks[middleI]); - } - - if (middleStr1 || middleStr2) { - if (isRTL) { - middleStr = middleStr2 + separator + middleStr1; - } - else { - middleStr = middleStr1 + separator + middleStr2; - } - } - - return leftStr + middleStr + rightStr; -} - - -var similarUnitMap = { - Y: 'year', - M: 'month', - D: 'day', // day of month - d: 'day' // day of week -}; -// don't go any further than day, because we don't want to break apart times like "12:30:00" -// TODO: week maybe? - - -// Given a formatting chunk, and given that both dates are similar in the regard the -// formatting chunk is concerned, format date1 against `chunk`. Otherwise, return `false`. -function formatSimilarChunk(date1, date2, chunk) { - var token; - var unit; - - if (typeof chunk === 'string') { // a literal string - return chunk; - } - else if ((token = chunk.token)) { - unit = similarUnitMap[token.charAt(0)]; - // are the dates the same for this unit of measurement? - if (unit && date1.isSame(date2, unit)) { - return momentFormat(date1, token); // would be the same if we used `date2` - // BTW, don't support custom tokens - } - } - - return false; // the chunk is NOT the same for the two dates - // BTW, don't support splitting on non-zero areas -} - - -// Chunking Utils -// ------------------------------------------------------------------------------------------------- - - -var formatStringChunkCache = {}; - - -function getFormatStringChunks(formatStr) { - if (formatStr in formatStringChunkCache) { - return formatStringChunkCache[formatStr]; - } - return (formatStringChunkCache[formatStr] = chunkFormatString(formatStr)); -} - - -// Break the formatting string into an array of chunks -function chunkFormatString(formatStr) { - var chunks = []; - var chunker = /\[([^\]]*)\]|\(([^\)]*)\)|((\w)\4*o?T?)|([^\w\[\(]+)/g; // TODO: more descrimination - var match; - - while ((match = chunker.exec(formatStr))) { - if (match[1]) { // a literal string instead [ ... ] - chunks.push(match[1]); - } - else if (match[2]) { // non-zero formatting inside ( ... ) - chunks.push({ maybe: chunkFormatString(match[2]) }); - } - else if (match[3]) { // a formatting token - chunks.push({ token: match[3] }); - } - else if (match[5]) { // an unenclosed literal string - chunks.push(match[5]); - } - } - - return chunks; -} - diff --git a/src/moment-ext.js b/src/moment-ext.js new file mode 100644 index 0000000..094c230 --- /dev/null +++ b/src/moment-ext.js @@ -0,0 +1,264 @@ + +var ambigTimeRegex = /^\s*\d{4}-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))$/; +var ambigZoneRegex = /^\s*\d{4}-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?)?$/; + + +// MOMENT: creating +// ------------------------------------------------------------------------------------------------- + +// Creates a moment in the local timezone, similar to the vanilla moment(...) constructor, +// but with extra features: +// - ambiguous times +// - enhanced formatting (TODO) +fc.moment = function() { + return makeMoment(arguments); +}; + +// Sames as fc.moment, but creates a moment in the UTC timezone. +fc.moment.utc = function() { + return makeMoment(arguments, true); +}; + +// Creates a moment and preserves the timezone offset of the ISO8601 string, +// allowing for ambigous timezones. If the string is not an ISO8601 string, +// the moment is processed in UTC-mode (a departure from moment's method). +fc.moment.parseZone = function() { + return makeMoment(arguments, true, true); +}; + +function FCMoment(config) { + extend(this, config); +} + +FCMoment.prototype = createObject(moment.fn); + +FCMoment.prototype.clone = function() { + return makeMoment([ this ]); +}; + +// when parseZone==true, if can't figure it out, fall back to parseUTC +function makeMoment(args, parseUTC, parseZone) { + var isSingleArg = args.length == 1; + var isSingleMoment = isSingleArg && moment.isMoment(args[0]); + var isSingleString = isSingleArg && typeof args[0] === 'string'; + var isSingleArray = isSingleArg && $.isArray(args[0]); + var isSingleNativeDate = isSingleArg && isNativeDate(args[0]); + var isAmbigTime = isSingleString && ambigTimeRegex.test(args[0]); + var isAmbigZone = isAmbigTime || isSingleArray || isSingleString && ambigZoneRegex.test(args[0]); + var mom; + + if (parseUTC || parseZone || isAmbigTime) { + mom = moment.utc.apply(moment, args); + } + else { + mom = moment.apply(null, args); + } + + if (isSingleMoment) { + transferAmbigs(args[0], mom); + } + + if (isAmbigTime) { + mom._ambigTime = true; + mom._ambigZone = true; // if ambiguous time, also ambiguous timezone offset + } + + if (parseZone) { + if (isAmbigZone) { + mom._ambigZone = true; + } + else if (isSingleString) { + mom.zone(args[0]); // if fails, will set it to 0, which it already was + } + else if (isSingleNativeDate || args[0] === undefined) { + // native Date object? + // specified with no arguments? + // then consider the moment to be local + mom.local(); + } + } + + return new FCMoment(mom); +} + +// transfers our internal _ambig properties from one moment to another +function transferAmbigs(src, dest) { + if (src._ambigTime) { + dest._ambigTime = true; + } + else if (dest._ambigTime) { + delete dest._ambigTime; + } + + if (src._ambigZone) { + dest._ambigZone = true; + } + else if (dest._ambigZone) { + delete dest._ambigZone; + } +} + +function makeMomentAs(input, model) { + var output = fc.moment(input); + if (model._ambigTime) { + output.stripTime(); + } + else if (model._ambigZone) { + output.stripZone(); + } + return output; +} + + + +// MOMENT: time-of-day +// ------------------------------------------------------------------------------------------------- + + +// GETTER +// Returns a Duration with the hours/minutes/seconds/ms values of the moment. +// If the moment has an ambiguous time, a duration of 00:00 will be returned. +// +// SETTER +// You can supply a Duration, a Moment, or a Duration-like argument. +// When setting the time, and the moment has an ambiguous time, it then becomes unambiguous. +FCMoment.prototype.time = function(time) { + if (time === undefined) { // getter + return moment.duration({ + hours: this.hours(), + minutes: this.minutes(), + seconds: this.seconds(), + milliseconds: this.milliseconds() + }); + } + else { // setter + + delete this._ambigTime; // mark that the moment now has a time + + if (!moment.isDuration(time) && !moment.isMoment(time)) { + time = moment.duration(time); + } + + return this.hours(time.hours() + time.days() * 24) // day value will cause overflow (so 24 hours becomes 00:00:00 of next day) + .minutes(time.minutes()) + .seconds(time.seconds()) + .milliseconds(time.milliseconds()); + } +}; + +// Converts the moment to UTC, stripping out its time-of-day and timezone offset, +// but preserving its YMD. A moment with a stripped time will display no time +// nor timezone offset when .format() is called. +FCMoment.prototype.stripTime = function() { + var a = this.toArray(); // year,month,date,hours,minutes,seconds as an array + + this._ambigTime = true; + this._ambigZone = true; // if ambiguous time, also ambiguous timezone offset + + return this.utc() + .year(a[0]) + .month(a[1]) + .date(a[2]) + .hours(0) + .minutes(0) + .seconds(0) + .milliseconds(0); +}; + +// Returns if the moment has a non-ambiguous time (boolean) +FCMoment.prototype.hasTime = function() { + return !this._ambigTime; +}; + + +// MOMENT: timezone offset +// ------------------------------------------------------------------------------------------------- + +// Converts the moment to UTC, stripping out its timezone offset, but preserving its +// YMD and time-of-day. A moment with a stripped timezone offset will display no +// timezone offset when .format() is called. +FCMoment.prototype.stripZone = function() { + var a = this.toArray(); // year,month,date,hours,minutes,seconds as an array + + this._ambigZone = true; + + return this.utc() + .year(a[0]) + .month(a[1]) + .date(a[2]) + .hours(a[3]) + .minutes(a[4]) + .seconds(a[5]) + .milliseconds(a[6]); +}; + +// Returns of the moment has a non-ambiguous timezone offset (boolean) +FCMoment.prototype.hasZone = function() { + return !this._ambigZone; +}; + +FCMoment.prototype.zone = function(tzo) { + if (tzo != undefined) { + this._ambigZone = false; + } + return moment.fn.zone.apply(this, arguments); +}; + +FCMoment.prototype.local = function() { + this._ambigZone = false; + return moment.fn.local.apply(this, arguments); +}; + +FCMoment.prototype.utc = function() { + this._ambigZone = false; + return moment.fn.utc.apply(this, arguments); +}; + + +// MOMENT: formatting mods +// ------------------------------------------------------------------------------------------------- + +FCMoment.prototype.format = function() { + if (arguments[0]) { + return formatDate(this, arguments[0]); // our extended formatting + } + if (this._ambigTime) { + return momentFormat(this, 'YYYY-MM-DD'); + } + if (this._ambigZone) { + return momentFormat(this, 'YYYY-MM-DD[T]HH:mm:ss'); + } + return momentFormat(this); // default moment original formatting +}; + +FCMoment.prototype.toISOString = function() { + if (this._ambigTime) { + return momentFormat(this, 'YYYY-MM-DD'); + } + if (this._ambigZone) { + return momentFormat(this, 'YYYY-MM-DD[T]HH:mm:ss'); + } + return moment.fn.toISOString.apply(this, arguments); +}; + + +// MOMENT: misc utils +// ------------------------------------------------------------------------------------------------- + +// Is the moment within the specified range? `end` is exclusive. +FCMoment.prototype.isWithin = function(start, end) { + return this >= makeMomentAs(start, this) && this < makeMomentAs(end, this); +}; + +$.each([ + 'isBefore', + 'isAfter' +], function(i, methodName) { + FCMoment.prototype[methodName] = function(input, units) { + moment.fn[methodName].call( + this, + makeMomentAs(input, this), + units + ); + }; +}); diff --git a/src/util.js b/src/util.js index 6382b20..2be2e09 100644 --- a/src/util.js +++ b/src/util.js @@ -9,6 +9,37 @@ function createObject(proto) { // like Object.create return new f(); } +function extend(a, b) { + for (var i in b) { + if (b.hasOwnProperty(i)) { + a[i] = b[i]; + } + } +} + + + +/* Date +-----------------------------------------------------------------------------*/ + + +var dayIDs = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']; + + +// diffs the two moments into a Duration where full-days are recorded first, +// then the remaining time. +function dayishDiff(d1, d0) { + return moment.duration({ + days: d1.clone().stripTime().diff(d0.clone().stripTime(), 'days'), + ms: d1.time() - d0.time() + }); +} + + +function isNativeDate(input) { + return Object.prototype.toString.call(input) === '[object Date]' || + input instanceof Date; +} diff --git a/tests/date_util_test.html b/tests/date_util_test.html index 152d866..4bb6e77 100644 --- a/tests/date_util_test.html +++ b/tests/date_util_test.html @@ -5,7 +5,9 @@ - + + +