DateUtils.js

/** @module DateUtils */
/**
 * @function daysInMonth
 * @description Get the number of days in a month
 * 
 * @param {Number} month from 0 to 11
 * @param {Number} year from 1970 YYYY format
 * 
 * @returns {Number} Number of days in the request moth
*/
export const daysInMonth = function (month, year) {
    return new Date(Date.UTC(year, month + 1, 0)).getDate();
}
/**
 * @function getLastDayOfTheMonth
 * @description Get the a Date object of the last day in specified month
 * 
 * @param {Number} month from 0 to 11
 * @param {Number} year from 1970 YYYY format
 * 
 * @returns {Date} Date
 */
export const getLastDayOfTheMonth = function (month, year) {
    return new Date(Date.UTC(year, month, daysInMonth(month, year)));
}
/**
 * @function getFirstDayOfTheMonth
 * @description Get the a Date object of the first day in specified month 
 * 
 * @param {Number} month from 0 to 11
 * @param {Number} year from 1970 YYYY format
 * 
 * @returns {Date} Date
*/
export const getFirstDayOfTheMonth = function (month, year) {
    return new Date(Date.UTC(year, month, 1));
}
/**
 * @typedef formatObject
 * @property {String} locale country locale
 * @property {Object<string, string>} [options] toLocaleDateString format object
*/
/**  
 * @typedef dateFormatType
 * @enum {"iso"|"string"|formatObject}
 */
/**
 * @function dateFromObjectId
 * @description Convert a Mongo ObjectID to a Date Object or a Date String depending on format param
 * 
 * @param {ObjectID} objectId object id to transform
 * @param {dateFormatType} [format] if undefined return Date object
 * 
 * @return {String|Number} formatted date string or timestamp in ms
 */
export const dateFromObjectId = function (objectId, format) {

    if (typeof format == 'object' && format.locale)
        return (new Date(parseInt(objectId.substring(0, 8), 16) * 1000)).toLocaleDateString(format.locale, format.options || {})

    if (format == 'iso')
        return (new Date(parseInt(objectId.substring(0, 8), 16) * 1000)).toISOString()

    if (format == 'string')
        return (new Date(parseInt(objectId.substring(0, 8), 16) * 1000)).toDateString()

    return new Date(parseInt(objectId.substring(0, 8), 16) * 1000).getTime()

};
/**
 * @function timeAgo
 * @description Returns a date or a timestamp to elapsed time format
 * 
 * @param {Date|Number} time 
 * @param {Object} options template property allow you to use different string for all time frames, prefix, and suffix 
 * @param {templates} [options.templates]
 * 
 * @returns {String} formatted time ago
 */
export const timeAgo = function (time, { templates } = {}) {
    /**
     * @typedef {Object} templates
     * @property {string} prefix
     * @property {string} suffix
     * @property {string} seconds
     * @property {string} minute
     * @property {string} minutes
     * @property {string} hour
     * @property {string} hours
     * @property {string} day
     * @property {string} days
     * @property {string} month
     * @property {string} months
     * @property {string} year
     * @property {string} years
    */
    const defaultTemplates = {
        prefix: "",
        suffix: " ago",
        seconds: "less than a minute",
        minute: "about a minute",
        minutes: "%d minutes",
        hour: "about an hour",
        hours: "about %d hours",
        day: "a day",
        days: "%d days",
        month: "about a month",
        months: "%d months",
        year: "about a year",
        years: "%d years"
    };

    const currentTemplates = { ...defaultTemplates, ...templates }

    const getTemplate = function (t, n) {
        return currentTemplates[t] && currentTemplates[t].replace(/%d/i, Math.abs(Math.round(n)));
    };

    const timer = function (time) {

        if (!time) return;
        if (!isNaN(Number(time))) time = new Date(time);
        if (time instanceof Date) time = String(time);

        time = time.replace(/\.\d+/, ""); // remove milliseconds
        time = time.replace(/-/, "/").replace(/-/, "/");
        time = time.replace(/T/, " ").replace(/Z/, " UTC");
        time = time.replace(/([\+\-]\d\d)\:?(\d\d)/, " $1$2"); // -04:00 -> -0400
        time = new Date(time * 1000 || time);

        const now = new Date();
        const seconds = ((now.getTime() - time) * .001) >> 0;
        const minutes = seconds / 60;
        const hours = minutes / 60;
        const days = hours / 24;
        const years = days / 365;

        return currentTemplates.prefix + (
            seconds < 45 && getTemplate('seconds', seconds) ||
            seconds < 90 && getTemplate('minute', 1) ||
            minutes < 45 && getTemplate('minutes', minutes) ||
            minutes < 90 && getTemplate('hour', 1) ||
            hours < 24 && getTemplate('hours', hours) ||
            hours < 42 && getTemplate('day', 1) ||
            days < 30 && getTemplate('days', days) ||
            days < 45 && getTemplate('month', 1) ||
            days < 365 && getTemplate('months', days / 30) ||
            years < 1.5 && getTemplate('year', 1) ||
            getTemplate('years', years)) + currentTemplates.suffix;
    };
    return timer(time);
};

export default {
    dateFromObjectId,
    timeAgo,
    daysInMonth,
    getLastDayOfTheMonth,
    getFirstDayOfTheMonth,
}