var totalDaysInMonth = require('./totalDaysInMonth'); var totalDaysInYear = require('./totalDaysInYear'); var convert = require('../time/convert'); /** * calculate the difference between dates (range) */ function diff(start, end, unitName){ // sort the dates to make it easier to process (specially year/month) if (start > end) { var swap = start; start = end; end = swap; } var output; if (unitName === 'month') { output = getMonthsDiff(start, end); } else if (unitName === 'year'){ output = getYearsDiff(start, end); } else if (unitName != null) { if (unitName === 'day') { // ignore timezone difference because of daylight savings time start = toUtc(start); end = toUtc(end); } output = convert(end - start, 'ms', unitName); } else { output = end - start; } return output; } function toUtc(d){ // we ignore timezone differences on purpose because of daylight // savings time, otherwise it would return fractional days/weeks even // if a full day elapsed. eg: // Wed Feb 12 2014 00:00:00 GMT-0200 (BRST) // Sun Feb 16 2014 00:00:00 GMT-0300 (BRT) // diff should be 4 days and not 4.041666666666667 return Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds()); } function getMonthsDiff(start, end){ return getElapsedMonths(start, end) + getElapsedYears(start, end) * 12 + getFractionalMonth(start, end); } function getYearsDiff(start, end){ var elapsedYears = getElapsedYears(start, end); return elapsedYears + getFractionalYear(start, end, elapsedYears); } function getElapsedMonths(start, end){ var monthDiff = end.getMonth() - start.getMonth(); if (monthDiff < 0) { monthDiff += 12; } // less than a full month if (start.getDate() > end.getDate()) { monthDiff -= 1; } return monthDiff; } function getElapsedYears(start, end){ var yearDiff = end.getFullYear() - start.getFullYear(); // less than a full year if (start.getMonth() > end.getMonth()) { yearDiff -= 1; } return yearDiff; } function getFractionalMonth(start, end){ var fractionalDiff = 0; var startDay = start.getDate(); var endDay = end.getDate(); if (startDay !== endDay) { var startTotalDays = totalDaysInMonth(start); var endTotalDays = totalDaysInMonth(end); var totalDays; var daysElapsed; if (startDay > endDay) { // eg: Jan 29 - Feb 27 (29 days elapsed but not a full month) var baseDay = startTotalDays - startDay; daysElapsed = endDay + baseDay; // total days should be relative to 1st day of next month if // startDay > endTotalDays totalDays = (startDay > endTotalDays)? endTotalDays + baseDay + 1 : startDay + baseDay; } else { // fractional is only based on endMonth eg: Jan 12 - Feb 18 // (6 fractional days, 28 days until next full month) daysElapsed = endDay - startDay; totalDays = endTotalDays; } fractionalDiff = daysElapsed / totalDays; } return fractionalDiff; } function getFractionalYear(start, end, elapsedYears){ var base = elapsedYears? new Date(end.getFullYear(), start.getMonth(), start.getDate()) : start; var elapsedDays = diff(base, end, 'day'); return elapsedDays / totalDaysInYear(end); } module.exports = diff;