diff-ymd-package Source

diff-ymd-package provides APIs for calculating the difference between two dates in formatted ways like (aY bM cD)(aYears bMonths cDays) or customized desired formats like aY-bM-cD or aYears-bMonths-cDays or kDays or mWeeks or nMonths etc.

/**
 * Represents a javascript class for calculating the difference between two dates in formatted ways like (aY bM cD)(aYears bMonths cDays) or customized desired formats like aY-bM-cD or aYears-bMonths-cDays or nDays or mWeeks etc.
 *
 * @class
 */
class DatesYMD {
  /**
   * Creates an instance of DatesYMD.
   *
   * @constructor
   * @param {string} firstDate (type- String but Number for epoch, and Object for dateObject) - The first date in the format 'yyyy-mm-dd' or 'yyyy/mm/dd' or yyyy.mm.dd or dateString or dateObject
  or Timestamp(epoch).
   * @param {string} secondDate (type- String but Number for epoch, and Object for dateObject) - The second date in the format 'yyyy-mm-dd' or 'yyyy/mm/dd' or yyyy.mm.dd or dateString or dateObject
  or Timestamp(epoch).
   */
  constructor(firstDate, secondDate) {
    this.firstDate = firstDate;
    this.secondDate = secondDate;

    // Handle empty date inputs by defaulting to the current date
    this.handleEmpty = function () {
      var firstD, secondD;
      if (this.firstDate === '' && this.secondDate === '') {
        firstD = new Date();
        secondD = new Date();
      } else if (this.firstDate === '') {
        firstD = new Date();
        secondD = new Date(this.secondDate);
      } else if (this.secondDate === '') {
        firstD = new Date(this.firstDate);
        secondD = new Date();
      } else {
        firstD = new Date(this.firstDate);
        secondD = new Date(this.secondDate);
      }
      return [firstD, secondD];
    };

    // Swap dates if the first date is smaller than the second date
    this.swapDates = function (firstD, secondD) {
      if (firstD.getTime() < secondD.getTime()) {
        var negativeHandle = firstD;
        firstD = secondD;
        secondD = negativeHandle;
      }
      return [firstD, secondD];
    };

    // initializing dates difference
    this.initDiff = function () {
      var dates, firstD, secondD;

      // Handle empty date inputs by defaulting to the current date
      dates = this.handleEmpty();
      (firstD = dates[0]), (secondD = dates[1]);

      // Swap dates if the first date is smaller than the second date
      var swapped = this.swapDates(firstD, secondD);
      if (swapped) {
        (firstD = swapped[0]), (secondD = swapped[1]);
      }

      return [firstD, secondD];
    };
  }

  /**
   * Calculates the difference between two dates and returns an array containing Y, M, D, and a formatted 'aY bM cD' difference string.
   *
   * @method
   * @returns {Array} An array containing the calculated years, months, days, and the formatted difference.
   */
  diffArray() {
    var datesDiff, year, month, day, monthAdjuster, firstD, secondD;
    var dates, yearS, yearF, monthS, monthF, dateS, dateF;

    // initializing dates difference
    dates = this.initDiff();
    (firstD = dates[0]), (secondD = dates[1]);

    // Extract year, month, and date components of the two dates
    yearS = secondD.getFullYear();
    yearF = firstD.getFullYear();

    monthS = secondD.getMonth() + 1;
    monthF = firstD.getMonth() + 1;

    dateS = secondD.getDate();
    dateF = firstD.getDate();

    // Calculate the difference in years, months, and days
    if (monthF >= monthS && dateF >= dateS) {
      year = yearF - yearS;
      month = monthF - monthS;
      day = dateF - dateS;
    } else if (monthF < monthS && dateF >= dateS) {
      // Handle the case where the months are different, and the first date's day is greater than or equal to the second date's day
      day = dateF - dateS;
      month = monthF + 12 - monthS;
      year = yearF - 1 - yearS;
    } else if (monthF > monthS && dateF < dateS) {
      // Handle the case where the months are different, and the first date's day is less than the second date's day
      month = monthF - monthS - 1;
      year = yearF - yearS;

      // Save the first last month of monthF for correct aY bM cD following mathematical finding algorithm for calculating aY bM cD correctly like if 2020-05-22 to 2021-03-20, then firstly decide monthAdjuster which is (greaterDate month - 1), that is 03 - 1 = 02, then find month from 20-05 to 21-02(02 is monthAdjuster) i.e 9M, and  then decide days from 2021-02-22 to 2021-03-20
      monthAdjuster = monthF - 1; // for getting intuitively correct - aY bM cD in all cases

      // Further adjustments based on specific conditions
      if (monthAdjuster === 2) {
        // Handle February
        if (yearF % 4 === 0) {
          // Leap year logic
          day = dateF > 29 ? dateF : 29 - dateS + dateF;
        } else {
          // Non-leap year logic
          day = dateF > 28 ? dateF : 28 - dateS + dateF;
        }
      } else if (
        // handle 30 days months
        monthAdjuster === 4 ||
        monthAdjuster === 6 ||
        monthAdjuster === 8 ||
        monthAdjuster === 11
      ) {
        // Handle months with 30 days
        day = dateF > 30 ? dateF : 30 - dateS + dateF;
      } else {
        // Handle months with 31 days
        day = 31 - dateS + dateF;
      }
    } else if (monthF <= monthS && dateF < dateS) {
      // Handle the case where the months are either different or equal, and the first date's day is less than the second date's day
      month = monthF + 11 - monthS;
      year = yearF - 1 - yearS;
      monthAdjuster = monthF - 1; // for getting intuitively correct - aY bM cD

      // Further adjustments based on specific conditions
      if (monthAdjuster === 2) {
        // Handle February
        if (yearF % 4 === 0) {
          // Leap year logic
          day = dateF > 29 ? dateF : 29 - dateS + dateF;
        } else {
          // Non-leap year logic
          day = dateF > 28 ? dateF : 28 - dateS + dateF;
        }
      } else if (
        // handle 30 days months
        monthAdjuster === 4 ||
        monthAdjuster === 6 ||
        monthAdjuster === 8 ||
        monthAdjuster === 11
      ) {
        // Handle months with 30 days
        day = dateF > 30 ? dateF : 30 - dateS + dateF;
      } else {
        // Handle months with 31 days
        day = 31 - dateS + dateF;
      }
    }

    // Format the difference and return the result
    const diffFormat = year + 'Y ' + month + 'M ' + day + 'D';
    datesDiff = [year, month, day, diffFormat];
    return datesDiff;
  }

  /**
   * Returns the formatted difference between two dates.
   *
   * @method
   * @returns {string} The formatted difference in the format 'aY bM cD'.
   */
  formattedYMD() {
    return this.diffArray()[3];
  }

  /**
   * Customizes the difference using specified units and separators like (a + yearUnit + partSeparator + b + monthUnit + partSeparator  + c + dayUnit), eg. aYs-bMs-cDs etc.
   *
   * @method
   * @param {string} yearUnit - The unit for years.
   * @param {string} monthUnit - The unit for months.
   * @param {string} dayUnit - The unit for days.
   * @param {string} partSeparator - The separator between year, month, and day parts.
   * @returns {string} The customized formatted difference.
   */
  customizeFormat(yearUnit, monthUnit, dayUnit, partSeparator) {
    let Y, M, D;
    Y = this.diffArray()[0];
    M = this.diffArray()[1];
    D = this.diffArray()[2];
    const customized =
      Y +
      yearUnit +
      partSeparator +
      M +
      monthUnit +
      partSeparator +
      D +
      dayUnit;
    return customized;
  }

  /**
   * Calculates the difference in months between two dates.
   *
   * @method
   * @returns {number} The difference in months.
   */
  diffInMonths() {
    var ymdArray, year, month;

    // initializing dates difference
    ymdArray = this.diffArray();

    // Extract year, month, and date components of the difference
    (year = ymdArray[0]), (month = ymdArray[1]);

    // calculate months
    const months = year * 12 + month;

    return months;
  }

  /**
   * Calculates the difference in weeks between two dates.
   *
   * @method
   * @returns {number} The difference in weeks.
   */
  diffInWeeks() {
    let dates, weeks, firstD, secondD;

    // initializing dates difference
    dates = this.initDiff();
    (firstD = dates[0]), (secondD = dates[1]);

    // calculate weeks
    weeks = Math.floor(
      (firstD.getTime() - secondD.getTime()) / (7 * 24 * 60 * 60 * 1000),
    );

    return weeks;
  }

  /**
   * Calculates the difference in days between two dates.
   *
   * @method
   * @returns {number} The difference in days.
   */
  diffInDays() {
    let dates, days, firstD, secondD;

    // initializing dates difference
    dates = this.initDiff();
    (firstD = dates[0]), (secondD = dates[1]);

    // calculate days
    days = Math.floor(
      (firstD.getTime() - secondD.getTime()) / (24 * 60 * 60 * 1000),
    );

    return days;
  }

  /**
   * Calculates the difference in years between two dates.
   *
   * @method
   * @returns {number} The difference in years.
   */
  diffInYears() {
    let ymdArray, years;

    // get dates difference array
    ymdArray = this.diffArray();

    // Extract year component of the difference
    years = ymdArray[0];

    return years;
  }

  /**
   * Calculates the difference in hours between two dates.
   *
   * @method
   * @returns {number} The difference in hours.
   */
  diffInHours() {
    let days, hours;

    // get dates difference days
    days = this.diffInDays();

    // calculate hours
    hours = days * 24;

    return hours;
  }

  /**
   * Calculates the difference in minutes between two dates.
   *
   * @method
   * @returns {number} The difference in minutes.
   */
  diffInMinutes() {
    let minutes, hours;

    // get dates difference hours
    hours = this.diffInHours();

    // calculate minutes
    minutes = hours * 60;

    return minutes;
  }

  /**
   * Calculates the difference in seconds between two dates.
   *
   * @method
   * @returns {number} The difference in seconds.
   */
  diffInSeconds() {
    let seconds, minutes;

    // get dates difference minutes
    minutes = this.diffInMinutes();

    // calculate seconds
    seconds = minutes * 60;

    return seconds;
  }
}

/**
 * Represents a utility for calculating the difference between two dates in formatted or desired customized ways.
 *
 * @typedef {Object} DatesYMD
 * @property {Function} diffArray - Calculates the difference between two dates and returns an array containing years, months, days, and a formatted difference string.
 * @property {Function} formattedYMD - Returns the formatted difference between two dates.
 * @property {Function} customizeFormat - Customizes the difference using specified units and separators.
 * @property {Function} diffInMonths - Calculates the difference in months between two dates.
 * @property {Function} diffInWeeks - Calculates the difference in weeks between two dates.
 * @property {Function} diffInDays - Calculates the difference in days between two dates.
 * @property {Function} diffInYears - Calculates the difference in years between two dates.
 * @property {Function} diffInHours - Calculates the difference in hours between two dates.
 * @property {Function} diffInMinutes - Calculates the difference in minutes between two dates.
 * @property {Function} diffInSeconds - Calculates the difference in seconds between two dates.
 */

/**
 * Creates an instance of DatesYMD.
 *
 * @param {string} firstDate (type- String but Number for epoch, and Object for dateObject) - The first date in the format 'yyyy-mm-dd' or 'yyyy/mm/dd' or yyyy.mm.dd or dateString or dateObject
  or Timestamp(epoch).
 * @param {string} secondDate (type- String but Number for epoch, and Object for dateObject) - The second date in the format 'yyyy-mm-dd' or 'yyyy/mm/dd' or yyyy.mm.dd or dateString or dateObject
  or Timestamp(epoch).
 *
 * @returns {DatesYMD} An object containing methods for date difference calculations.
 */
function diffDates(firstDate, secondDate) {
  /**
   * Handles empty date inputs by defaulting to the current date.
   *
   * @function
   * @returns {Array} An array containing the first and second date objects.
   */
  function handleEmpty() {
    var firstD, secondD;
    if (firstDate === '' && secondDate === '') {
      firstD = new Date();
      secondD = new Date();
    } else if (firstDate === '') {
      firstD = new Date();
      secondD = new Date(secondDate);
    } else if (secondDate === '') {
      firstD = new Date(firstDate);
      secondD = new Date();
    } else {
      firstD = new Date(firstDate);
      secondD = new Date(secondDate);
    }
    return [firstD, secondD];
  }

  /**
   * Swaps dates if the first date is smaller than the second date.
   *
   * @function
   * @param {Date} firstD - The first date object.
   * @param {Date} secondD - The second date object.
   * @returns {Array} An array containing the first and second date objects.
   */
  function swapDates(firstD, secondD) {
    if (firstD.getTime() < secondD.getTime()) {
      var negativeHandle = firstD;
      firstD = secondD;
      secondD = negativeHandle;
    }
    return [firstD, secondD];
  }

  /**
   * Initializes dates difference.
   *
   * @function
   * @returns {Array} An array containing the first and second date objects.
   */
  function initDiff() {
    var dates, firstD, secondD;

    // Handle empty date inputs by defaulting to the current date
    dates = handleEmpty();
    [firstD, secondD] = dates;

    // Swap dates if the first date is smaller than the second date
    var swapped = swapDates(firstD, secondD);
    if (swapped) {
      [firstD, secondD] = swapped;
    }

    return [firstD, secondD];
  }

  return {
    /**
     * Calculates the difference between two dates and returns an array containing years, months, days, and a formatted difference string.
     *
     * @returns {Array} An array containing the calculated years, months, days, and the formatted difference.
     */
    diffArray: function () {
      var datesDiff, year, month, day, monthAdjuster, firstD, secondD;
      var dates, yearS, yearF, monthS, monthF, dateS, dateF;

      // initializing dates difference
      dates = initDiff();
      (firstD = dates[0]), (secondD = dates[1]);

      // Extract year, month, and date components of the two dates
      yearS = secondD.getFullYear();
      yearF = firstD.getFullYear();

      monthS = secondD.getMonth() + 1;
      monthF = firstD.getMonth() + 1;

      dateS = secondD.getDate();
      dateF = firstD.getDate();

      // Calculate the difference in years, months, and days
      if (monthF >= monthS && dateF >= dateS) {
        year = yearF - yearS;
        month = monthF - monthS;
        day = dateF - dateS;
      } else if (monthF < monthS && dateF >= dateS) {
        // Handle the case where the months are different, and the first date's day is greater than or equal to the second date's day
        day = dateF - dateS;
        month = monthF + 12 - monthS;
        year = yearF - 1 - yearS;
      } else if (monthF > monthS && dateF < dateS) {
        // Handle the case where the months are different, and the first date's day is less than the second date's day
        month = monthF - monthS - 1;
        year = yearF - yearS;

        // Save the first last month of monthF for correct aY bM cD following mathematical finding algorithm for calculating aY bM cD correctly like if 2020-05-22 to 2021-03-20, then firstly decide monthAdjuster which is (greaterDate month - 1), that is 03 - 1 = 02, then find month from 20-05 to 21-02(02 is monthAdjuster) i.e 9M, and  then decide days from 2021-02-22 to 2021-03-20
        monthAdjuster = monthF - 1; // for getting intuitively correct - aY bM cD in all cases

        // Further adjustments based on specific conditions
        if (monthAdjuster === 2) {
          // Handle February
          if (yearF % 4 === 0) {
            // Leap year logic
            day = dateF > 29 ? dateF : 29 - dateS + dateF;
          } else {
            // Non-leap year logic
            day = dateF > 28 ? dateF : 28 - dateS + dateF;
          }
        } else if (
          // handle 30 days months
          monthAdjuster === 4 ||
          monthAdjuster === 6 ||
          monthAdjuster === 8 ||
          monthAdjuster === 11
        ) {
          // Handle months with 30 days
          day = dateF > 30 ? dateF : 30 - dateS + dateF;
        } else {
          // Handle months with 31 days
          day = 31 - dateS + dateF;
        }
      } else if (monthF <= monthS && dateF < dateS) {
        // Handle the case where the months are either different or equal, and the first date's day is less than the second date's day
        month = monthF + 11 - monthS;
        year = yearF - 1 - yearS;
        monthAdjuster = monthF - 1; // for getting intuitively correct - aY bM cD

        // Further adjustments based on specific conditions
        if (monthAdjuster === 2) {
          // Handle February
          if (yearF % 4 === 0) {
            // Leap year logic
            day = dateF > 29 ? dateF : 29 - dateS + dateF;
          } else {
            // Non-leap year logic
            day = dateF > 28 ? dateF : 28 - dateS + dateF;
          }
        } else if (
          // handle 30 days months
          monthAdjuster === 4 ||
          monthAdjuster === 6 ||
          monthAdjuster === 8 ||
          monthAdjuster === 11
        ) {
          // Handle months with 30 days
          day = dateF > 30 ? dateF : 30 - dateS + dateF;
        } else {
          // Handle months with 31 days
          day = 31 - dateS + dateF;
        }
      }

      // Format the difference and return the result
      const diffFormat = year + 'Y ' + month + 'M ' + day + 'D';
      datesDiff = [year, month, day, diffFormat];
      return datesDiff;
    },

    /**
     * Returns the formatted difference between two dates.
     *
     * @returns {string} The formatted difference in the format 'aY bM cD'.
     */
    formattedYMD: function () {
      return this.diffArray()[3];
    },

    /**
     * Customizes the difference using specified units and separators like (a + yearUnit + partSeparator + b + monthUnit + partSeparator + c + dayUnit), eg. aYs-bMs-cDs etc.
     *
     * @param {string} yearUnit - The unit for years.
     * @param {string} monthUnit - The unit for months.
     * @param {string} dayUnit - The unit for days.
     * @param {string} partSeparator - The separator between year, month, and day parts.
     * @returns {string} The customized formatted difference.
     */
    customizeFormat: function (yearUnit, monthUnit, dayUnit, partSeparator) {
      let Y, M, D;
      Y = this.diffArray()[0];
      M = this.diffArray()[1];
      D = this.diffArray()[2];
      const customized =
        Y +
        yearUnit +
        partSeparator +
        M +
        monthUnit +
        partSeparator +
        D +
        dayUnit;
      return customized;
    },

    /**
     * Calculates the difference in months between two dates.
     *
     * @returns {number} The difference in months.
     */
    diffInMonths: function () {
      var ymdArray, year, month;

      // initializing dates difference
      ymdArray = this.diffArray();

      // Extract year, month, and date components of the difference
      (year = ymdArray[0]), (month = ymdArray[1]);

      // calculate months
      const months = year * 12 + month;

      return months;
    },

    /**
     * Calculates the difference in weeks between two dates.
     *
     * @returns {number} The difference in weeks.
     */
    diffInWeeks: function () {
      let dates, weeks, firstD, secondD;

      // initializing dates difference
      dates = initDiff();
      (firstD = dates[0]), (secondD = dates[1]);

      // calculate weeks
      weeks = Math.floor(
        (firstD.getTime() - secondD.getTime()) / (7 * 24 * 60 * 60 * 1000),
      );

      return weeks;
    },

    /**
     * Calculates the difference in days between two dates.
     *
     * @returns {number} The difference in days.
     */
    diffInDays: function () {
      let dates, days, firstD, secondD;

      // initializing dates difference
      dates = initDiff();
      (firstD = dates[0]), (secondD = dates[1]);

      // calculate days
      days = Math.floor(
        (firstD.getTime() - secondD.getTime()) / (24 * 60 * 60 * 1000),
      );

      return days;
    },

    /**
     * Calculates the difference in years between two dates.
     *
     * @returns {number} The difference in years.
     */
    diffInYears: function () {
      let ymdArray, years;

      // get dates difference array
      ymdArray = this.diffArray();

      // Extract year component of the difference
      years = ymdArray[0];

      return years;
    },

    /**
     * Calculates the difference in hours between two dates.
     *
     * @returns {number} The difference in hours.
     */
    diffInHours: function () {
      let days, hours;

      // get dates difference days
      days = this.diffInDays();

      // calculate hours
      hours = days * 24;

      return hours;
    },

    /**
     * Calculates the difference in minutes between two dates.
     *
     * @returns {number} The difference in minutes.
     */
    diffInMinutes: function () {
      let minutes, hours;

      // get dates difference hours
      hours = this.diffInHours();

      // calculate minutes
      minutes = hours * 60;

      return minutes;
    },

    /**
     * Calculates the difference in seconds between two dates.
     *
     * @returns {number} The difference in seconds.
     */
    diffInSeconds: function () {
      let seconds, minutes;

      // get dates difference minutes
      minutes = this.diffInMinutes();

      // calculate seconds
      seconds = minutes * 60;

      return seconds;
    },
  };
}

// Export the DatesYMD class and closure function diffDates for usages in other modules
module.exports = DatesYMD; // default export for class
module.exports.diffDates = diffDates; // named export for closure