text: 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;
  
  
  