text: (function() {
    'use strict';
  
    /**
     * Utility for performing date calculations to facilitate operation of the calendar and
     * datepicker.
     */
    angular.module('material.components.datepicker').factory('$$mdDateUtil', function() {
      return {
        getFirstDateOfMonth: getFirstDateOfMonth,
        getNumberOfDaysInMonth: getNumberOfDaysInMonth,
        getDateInNextMonth: getDateInNextMonth,
        getDateInPreviousMonth: getDateInPreviousMonth,
        isInNextMonth: isInNextMonth,
        isInPreviousMonth: isInPreviousMonth,
        getDateMidpoint: getDateMidpoint,
        isSameMonthAndYear: isSameMonthAndYear,
        getWeekOfMonth: getWeekOfMonth,
        incrementDays: incrementDays,
        incrementMonths: incrementMonths,
        getLastDateOfMonth: getLastDateOfMonth,
        isSameDay: isSameDay,
        getMonthDistance: getMonthDistance,
        isValidDate: isValidDate,
        setDateTimeToMidnight: setDateTimeToMidnight,
        createDateAtMidnight: createDateAtMidnight,
        isDateWithinRange: isDateWithinRange
      };
  
      /**
       * Gets the first day of the month for the given date's month.
       * @param {Date} date
       * @returns {Date}
       */
      function getFirstDateOfMonth(date) {
        return new Date(date.getFullYear(), date.getMonth(), 1);
      }
  
      /**
       * Gets the number of days in the month for the given date's month.
       * @param date
       * @returns {number}
       */
      function getNumberOfDaysInMonth(date) {
        return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
      }
  
      /**
       * Get an arbitrary date in the month after the given date's month.
       * @param date
       * @returns {Date}
       */
      function getDateInNextMonth(date) {
        return new Date(date.getFullYear(), date.getMonth() + 1, 1);
      }
  
      /**
       * Get an arbitrary date in the month before the given date's month.
       * @param date
       * @returns {Date}
       */
      function getDateInPreviousMonth(date) {
        return new Date(date.getFullYear(), date.getMonth() - 1, 1);
      }
  
      /**
       * Gets whether two dates have the same month and year.
       * @param {Date} d1
       * @param {Date} d2
       * @returns {boolean}
       */
      function isSameMonthAndYear(d1, d2) {
        return d1.getFullYear() === d2.getFullYear() && d1.getMonth() === d2.getMonth();
      }
  
      /**
       * Gets whether two dates are the same day (not not necesarily the same time).
       * @param {Date} d1
       * @param {Date} d2
       * @returns {boolean}
       */
      function isSameDay(d1, d2) {
        return d1.getDate() == d2.getDate() && isSameMonthAndYear(d1, d2);
      }
  
      /**
       * Gets whether a date is in the month immediately after some date.
       * @param {Date} startDate The date from which to compare.
       * @param {Date} endDate The date to check.
       * @returns {boolean}
       */
      function isInNextMonth(startDate, endDate) {
        var nextMonth = getDateInNextMonth(startDate);
        return isSameMonthAndYear(nextMonth, endDate);
      }
  
      /**
       * Gets whether a date is in the month immediately before some date.
       * @param {Date} startDate The date from which to compare.
       * @param {Date} endDate The date to check.
       * @returns {boolean}
       */
      function isInPreviousMonth(startDate, endDate) {
        var previousMonth = getDateInPreviousMonth(startDate);
        return isSameMonthAndYear(endDate, previousMonth);
      }
  
      /**
       * Gets the midpoint between two dates.
       * @param {Date} d1
       * @param {Date} d2
       * @returns {Date}
       */
      function getDateMidpoint(d1, d2) {
        return createDateAtMidnight((d1.getTime() + d2.getTime()) / 2);
      }
  
      /**
       * Gets the week of the month that a given date occurs in.
       * @param {Date} date
       * @returns {number} Index of the week of the month (zero-based).
       */
      function getWeekOfMonth(date) {
        var firstDayOfMonth = getFirstDateOfMonth(date);
        return Math.floor((firstDayOfMonth.getDay() + date.getDate() - 1) / 7);
      }
  
      /**
       * Gets a new date incremented by the given number of days. Number of days can be negative.
       * @param {Date} date
       * @param {number} numberOfDays
       * @returns {Date}
       */
      function incrementDays(date, numberOfDays) {
        return new Date(date.getFullYear(), date.getMonth(), date.getDate() + numberOfDays);
      }
  
      /**
       * Gets a new date incremented by the given number of months. Number of months can be negative.
       * If the date of the given month does not match the target month, the date will be set to the
       * last day of the month.
       * @param {Date} date
       * @param {number} numberOfMonths
       * @returns {Date}
       */
      function incrementMonths(date, numberOfMonths) {
        // If the same date in the target month does not actually exist, the Date object will
        // automatically advance *another* month by the number of missing days.
        // For example, if you try to go from Jan. 30 to Feb. 30, you'll end up on March 2.
        // So, we check if the month overflowed and go to the last day of the target month instead.
        var dateInTargetMonth = new Date(date.getFullYear(), date.getMonth() + numberOfMonths, 1);
        var numberOfDaysInMonth = getNumberOfDaysInMonth(dateInTargetMonth);
        if (numberOfDaysInMonth < date.getDate()) {
          dateInTargetMonth.setDate(numberOfDaysInMonth);
        } else {
          dateInTargetMonth.setDate(date.getDate());
        }
  
        return dateInTargetMonth;
      }
  
      /**
       * Get the integer distance between two months. This *only* considers the month and year
       * portion of the Date instances.
       *
       * @param {Date} start
       * @param {Date} end
       * @returns {number} Number of months between `start` and `end`. If `end` is before `start`
       *     chronologically, this number will be negative.
       */
      function getMonthDistance(start, end) {
        return (12 * (end.getFullYear() - start.getFullYear())) + (end.getMonth() - start.getMonth());
      }
  
      /**
       * Gets the last day of the month for the given date.
       * @param {Date} date
       * @returns {Date}
       */
      function getLastDateOfMonth(date) {
        return new Date(date.getFullYear(), date.getMonth(), getNumberOfDaysInMonth(date));
      }
  
      /**
       * Checks whether a date is valid.
       * @param {Date} date
       * @return {boolean} Whether the date is a valid Date.
       */
      function isValidDate(date) {
        return date != null && date.getTime && !isNaN(date.getTime());
      }
  
      /**
       * Sets a date's time to midnight.
       * @param {Date} date
       */
      function setDateTimeToMidnight(date) {
        if (isValidDate(date)) {
          date.setHours(0, 0, 0, 0);
        }
      }
  
      /**
       * Creates a date with the time set to midnight.
       * Drop-in replacement for two forms of the Date constructor:
       * 1. No argument for Date representing now.
       * 2. Single-argument value representing number of seconds since Unix Epoch.
       * @param {number=} opt_value
       * @return {Date} New date with time set to midnight.
       */
      function createDateAtMidnight(opt_value) {
        var date;
        if (angular.isUndefined(opt_value)) {
          date = new Date();
        } else {
          date = new Date(opt_value);
        }
        setDateTimeToMidnight(date);
        return date;
      }
  
       /**
        * Checks if a date is within a min and max range.
        * If minDate or maxDate are not dates, they are ignored.
        * @param {Date} date
        * @param {Date} minDate
        * @param {Date} maxDate
        */
       function isDateWithinRange(date, minDate, maxDate) {
         return (!angular.isDate(minDate) || minDate <= date) &&
             (!angular.isDate(maxDate) || maxDate >= date);
       }
    });
  })();
  