Files
plane/web/helpers/date-time.helper.ts
rahulramesha a88a39fb1e [WEB-2442] feat: Revamp Timeline Layout (#5915)
* chore: added issue relations in issue listing

* chore: added pagination for issue detail endpoint

* chore: bulk date update endpoint

* chore: appended the target date

* chore: issue relation new types defined

* fix: order by and issue filters

* fix: passed order by in pagination

* chore: changed the key for issue dates

* Revamp Timeline Layout

* fix block dragging

* minor ui fixes

* improve auto scroll UX

* remove unused import

* fix timeline layout heights

* modify base timeline store

* Segregate issue relation types

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
2024-10-28 18:03:31 +05:30

408 lines
14 KiB
TypeScript

import { differenceInDays, format, formatDistanceToNow, isAfter, isEqual, isValid, parseISO } from "date-fns";
import isNumber from "lodash/isNumber";
// Format Date Helpers
/**
* @returns {string | null} formatted date in the desired format or platform default format (MMM dd, yyyy)
* @description Returns date in the formatted format
* @param {Date | string} date
* @param {string} formatToken (optional) // default MMM dd, yyyy
* @example renderFormattedDate("2024-01-01", "MM-DD-YYYY") // Jan 01, 2024
* @example renderFormattedDate("2024-01-01") // Jan 01, 2024
*/
export const renderFormattedDate = (
date: string | Date | undefined | null,
formatToken: string = "MMM dd, yyyy"
): string | undefined => {
// Parse the date to check if it is valid
const parsedDate = getDate(date);
// return if undefined
if (!parsedDate) return;
// Check if the parsed date is valid before formatting
if (!isValid(parsedDate)) return; // Return null for invalid dates
let formattedDate;
try {
// Format the date in the format provided or default format (MMM dd, yyyy)
formattedDate = format(parsedDate, formatToken);
} catch (e) {
// Format the date in format (MMM dd, yyyy) in case of any error
formattedDate = format(parsedDate, "MMM dd, yyyy");
}
return formattedDate;
};
/**
* @returns {string} formatted date in the format of MMM dd
* @description Returns date in the formatted format
* @param {string | Date} date
* @example renderShortDateFormat("2024-01-01") // Jan 01
*/
export const renderFormattedDateWithoutYear = (date: string | Date): string => {
// Parse the date to check if it is valid
const parsedDate = getDate(date);
// return if undefined
if (!parsedDate) return "";
// Check if the parsed date is valid before formatting
if (!isValid(parsedDate)) return ""; // Return empty string for invalid dates
// Format the date in short format (MMM dd)
const formattedDate = format(parsedDate, "MMM dd");
return formattedDate;
};
/**
* @returns {string | null} formatted date in the format of yyyy-mm-dd to be used in payload
* @description Returns date in the formatted format to be used in payload
* @param {Date | string} date
* @example renderFormattedPayloadDate("Jan 01, 20224") // "2024-01-01"
*/
export const renderFormattedPayloadDate = (date: Date | string | undefined | null): string | undefined => {
// Parse the date to check if it is valid
const parsedDate = getDate(date);
// return if undefined
if (!parsedDate) return;
// Check if the parsed date is valid before formatting
if (!isValid(parsedDate)) return; // Return null for invalid dates
// Format the date in payload format (yyyy-mm-dd)
const formattedDate = format(parsedDate, "yyyy-MM-dd");
return formattedDate;
};
// Format Time Helpers
/**
* @returns {string} formatted date in the format of hh:mm a or HH:mm
* @description Returns date in 12 hour format if in12HourFormat is true else 24 hour format
* @param {string | Date} date
* @param {boolean} timeFormat (optional) // default 24 hour
* @example renderFormattedTime("2024-01-01 13:00:00") // 13:00
* @example renderFormattedTime("2024-01-01 13:00:00", "12-hour") // 01:00 PM
*/
export const renderFormattedTime = (date: string | Date, timeFormat: "12-hour" | "24-hour" = "24-hour"): string => {
// Parse the date to check if it is valid
const parsedDate = new Date(date);
// return if undefined
if (!parsedDate) return "";
// Check if the parsed date is valid
if (!isValid(parsedDate)) return ""; // Return empty string for invalid dates
// Format the date in 12 hour format if in12HourFormat is true
if (timeFormat === "12-hour") {
const formattedTime = format(parsedDate, "hh:mm a");
return formattedTime;
}
// Format the date in 24 hour format
const formattedTime = format(parsedDate, "HH:mm");
return formattedTime;
};
// Date Difference Helpers
/**
* @returns {number} total number of days in range
* @description Returns total number of days in range
* @param {string} startDate
* @param {string} endDate
* @param {boolean} inclusive
* @example checkIfStringIsDate("2021-01-01", "2021-01-08") // 8
*/
export const findTotalDaysInRange = (
startDate: Date | string | undefined | null,
endDate: Date | string | undefined | null,
inclusive: boolean = true
): number | undefined => {
// Parse the dates to check if they are valid
const parsedStartDate = getDate(startDate);
const parsedEndDate = getDate(endDate);
// return if undefined
if (!parsedStartDate || !parsedEndDate) return;
// Check if the parsed dates are valid before calculating the difference
if (!isValid(parsedStartDate) || !isValid(parsedEndDate)) return 0; // Return 0 for invalid dates
// Calculate the difference in days
const diffInDays = differenceInDays(parsedEndDate, parsedStartDate);
// Return the difference in days based on inclusive flag
return inclusive ? diffInDays + 1 : diffInDays;
};
/**
* Add number of days to the provided date and return a resulting new date
* @param startDate
* @param numberOfDays
* @returns
*/
export const addDaysToDate = (startDate: Date | string | undefined | null, numberOfDays: number) => {
// Parse the dates to check if they are valid
const parsedStartDate = getDate(startDate);
// return if undefined
if (!parsedStartDate) return;
const newDate = new Date(parsedStartDate);
newDate.setDate(newDate.getDate() + numberOfDays);
return newDate;
};
/**
* @returns {number} number of days left from today
* @description Returns number of days left from today
* @param {string | Date} date
* @param {boolean} inclusive (optional) // default true
* @example findHowManyDaysLeft("2024-01-01") // 3
*/
export const findHowManyDaysLeft = (
date: Date | string | undefined | null,
inclusive: boolean = true
): number | undefined => {
if (!date) return undefined;
// Pass the date to findTotalDaysInRange function to find the total number of days in range from today
return findTotalDaysInRange(new Date(), date, inclusive);
};
// Time Difference Helpers
/**
* @returns {string} formatted date in the form of amount of time passed since the event happened
* @description Returns time passed since the event happened
* @param {string | Date} time
* @example calculateTimeAgo("2023-01-01") // 1 year ago
*/
export const calculateTimeAgo = (time: string | number | Date | null): string => {
if (!time) return "";
// Parse the time to check if it is valid
const parsedTime = typeof time === "string" || typeof time === "number" ? parseISO(String(time)) : time;
// return if undefined
if (!parsedTime) return ""; // Return empty string for invalid dates
// Format the time in the form of amount of time passed since the event happened
const distance = formatDistanceToNow(parsedTime, { addSuffix: true });
return distance;
};
export function calculateTimeAgoShort(date: string | number | Date | null): string {
if (!date) {
return "";
}
const parsedDate = typeof date === "string" ? parseISO(date) : new Date(date);
const now = new Date();
const diffInSeconds = (now.getTime() - parsedDate.getTime()) / 1000;
if (diffInSeconds < 60) {
return `${Math.floor(diffInSeconds)}s`;
}
const diffInMinutes = diffInSeconds / 60;
if (diffInMinutes < 60) {
return `${Math.floor(diffInMinutes)}m`;
}
const diffInHours = diffInMinutes / 60;
if (diffInHours < 24) {
return `${Math.floor(diffInHours)}h`;
}
const diffInDays = diffInHours / 24;
if (diffInDays < 30) {
return `${Math.floor(diffInDays)}d`;
}
const diffInMonths = diffInDays / 30;
if (diffInMonths < 12) {
return `${Math.floor(diffInMonths)}mo`;
}
const diffInYears = diffInMonths / 12;
return `${Math.floor(diffInYears)}y`;
}
// Date Validation Helpers
/**
* @returns {string} boolean value depending on whether the date is greater than today
* @description Returns boolean value depending on whether the date is greater than today
* @param {string} dateStr
* @example isDateGreaterThanToday("2024-01-01") // true
*/
export const isDateGreaterThanToday = (dateStr: string): boolean => {
// Return false if dateStr is not present
if (!dateStr) return false;
// Parse the date to check if it is valid
const date = parseISO(dateStr);
const today = new Date();
// Check if the parsed date is valid
if (!isValid(date)) return false; // Return false for invalid dates
// Return true if the date is greater than today
return isAfter(date, today);
};
// Week Related Helpers
/**
* @returns {number} week number of date
* @description Returns week number of date
* @param {Date} date
* @example getWeekNumber(new Date("2023-09-01")) // 35
*/
export const getWeekNumberOfDate = (date: Date): number => {
const currentDate = date;
// Adjust the starting day to Sunday (0) instead of Monday (1)
const startDate = new Date(currentDate.getFullYear(), 0, 1);
// Calculate the number of days between currentDate and startDate
const days = Math.floor((currentDate.getTime() - startDate.getTime()) / (24 * 60 * 60 * 1000));
// Adjust the calculation for weekNumber
const weekNumber = Math.ceil((days + 1) / 7);
return weekNumber;
};
/**
* @returns {boolean} boolean value depending on whether the dates are equal
* @description Returns boolean value depending on whether the dates are equal
* @param date1
* @param date2
* @example checkIfDatesAreEqual("2024-01-01", "2024-01-01") // true
* @example checkIfDatesAreEqual("2024-01-01", "2024-01-02") // false
*/
export const checkIfDatesAreEqual = (
date1: Date | string | null | undefined,
date2: Date | string | null | undefined
): boolean => {
const parsedDate1 = getDate(date1);
const parsedDate2 = getDate(date2);
// return if undefined
if (!parsedDate1 && !parsedDate2) return true;
if (!parsedDate1 || !parsedDate2) return false;
return isEqual(parsedDate1, parsedDate2);
};
/**
* This method returns a date from string of type yyyy-mm-dd
* This method is recommended to use instead of new Date() as this does not introduce any timezone offsets
* @param date
* @returns date or undefined
*/
export const getDate = (date: string | Date | undefined | null): Date | undefined => {
try {
if (!date || date === "") return;
if (typeof date !== "string" && !(date instanceof String)) return date;
const [yearString, monthString, dayString] = date.substring(0, 10).split("-");
const year = parseInt(yearString);
const month = parseInt(monthString);
const day = parseInt(dayString);
if (!isNumber(year) || !isNumber(month) || !isNumber(day)) return;
return new Date(year, month - 1, day);
} catch (e) {
return undefined;
}
};
export const isInDateFormat = (date: string) => {
const datePattern = /^\d{4}-\d{2}-\d{2}$/;
return datePattern.test(date);
};
/**
* returns the date string in ISO format regardless of the timezone in input date string
* @param dateString
* @returns
*/
export const convertToISODateString = (dateString: string | undefined) => {
if (!dateString) return dateString;
const date = new Date(dateString);
return date.toISOString();
};
/**
* returns the date string in Epoch regardless of the timezone in input date string
* @param dateString
* @returns
*/
export const convertToEpoch = (dateString: string | undefined) => {
if (!dateString) return dateString;
const date = new Date(dateString);
return date.getTime();
};
/**
* get current Date time in UTC ISO format
* @returns
*/
export const getCurrentDateTimeInISO = () => {
const date = new Date();
return date.toISOString();
};
/**
* @description converts hours and minutes to minutes
* @param { number } hours
* @param { number } minutes
* @returns { number } minutes
* @example convertHoursMinutesToMinutes(2, 30) // Output: 150
*/
export const convertHoursMinutesToMinutes = (hours: number, minutes: number): number => hours * 60 + minutes;
/**
* @description converts minutes to hours and minutes
* @param { number } mins
* @returns { number, number } hours and minutes
* @example convertMinutesToHoursAndMinutes(150) // Output: { hours: 2, minutes: 30 }
*/
export const convertMinutesToHoursAndMinutes = (mins: number): { hours: number; minutes: number } => {
const hours = Math.floor(mins / 60);
const minutes = Math.floor(mins % 60);
return { hours: hours, minutes: minutes };
};
/**
* @description converts minutes to hours and minutes string
* @param { number } totalMinutes
* @returns { string } 0h 0m
* @example convertMinutesToHoursAndMinutes(150) // Output: 2h 10m
*/
export const convertMinutesToHoursMinutesString = (totalMinutes: number): string => {
const { hours, minutes } = convertMinutesToHoursAndMinutes(totalMinutes);
return `${hours ? `${hours}h ` : ``}${minutes ? `${minutes}m ` : ``}`;
};
/**
* @description calculates the read time for a document using the words count
* @param {number} wordsCount
* @returns {number} total number of seconds
* @example getReadTimeFromWordsCount(400) // Output: 120
* @example getReadTimeFromWordsCount(100) // Output: 30s
*/
export const getReadTimeFromWordsCount = (wordsCount: number): number => {
const wordsPerMinute = 200;
const minutes = wordsCount / wordsPerMinute;
return minutes * 60;
};
/**
* @description generates an array of dates between the start and end dates
* @param startDate
* @param endDate
* @returns
*/
export const generateDateArray = (startDate: string | Date, endDate: string | Date) => {
// Convert the start and end dates to Date objects if they aren't already
const start = new Date(startDate);
// start.setDate(start.getDate() + 1);
const end = new Date(endDate);
end.setDate(end.getDate() + 1);
// Create an empty array to store the dates
const dateArray = [];
// Use a while loop to generate dates between the range
while (start <= end) {
// Increment the date by 1 day (86400000 milliseconds)
start.setDate(start.getDate() + 1);
// Push the current date (converted to ISO string for consistency)
dateArray.push({
date: new Date(start).toISOString().split("T")[0],
});
}
return dateArray;
};