UNPKG

@fto-consult/common

Version:

Un ensemble de bibliothèques et d'utilistaires communs pour le développement d'applications javascript

1,394 lines (1,323 loc) 60.1 kB
// Copyright 2022 @fto-consult/Boris Fouomene. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. /** * @see : https://www.npmjs.com/package/dateformat @see : https://en.wikipedia.org/wiki/ISO_8601 @see : https://momentjs.com/docs/#/use-it */ import appConfig from "$capp/config"; import i18n from "../i18n"; import defaultStr from "$cutils/defaultStr"; import isDateObj from "$cutils/isDateObj"; import moment from 'moment'; try { moment.updateLocale("fr-FR"); } catch{} export const SQLDateFormat = "yyyy-mm-dd"; export const SQLDateTimeFormat = "yyyy-mm-dd HH:MM:ss" export const SQLTimeFormat = "HH:MM:ss"; const isBool = x=> typeof x=='boolean'; const isNonNullString = x => x && typeof x =="string"; export const isoDateRegExp = new RegExp( /(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))/ ); var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZWN]|'[^']*'|'[^']*'/g; if(typeof Date.prototype.getDays != 'function'){ Date.prototype.getDays = function() { return new Date(this.getFullYear(), this.getMonth() + 1, 0).getDate(); }; } Date.prototype.withoutTime = function () { var d = new Date(this); d.setHours(0, 0, 0, 0); return d; } if(typeof Date.prototype.toUTCDateTime !== 'function'){ Date.prototype.toUTCDateTime = function(){ return ISOSQLDateTime(new Date(this)); } } if(typeof Date.prototype.toISOSQLDateTime !== 'function'){ Date.prototype.ISOSQLDateTime = function(){ return ISOSQLDateTime(new Date(this)); } } if(typeof Date.prototype.toUTCDate !== 'function'){ Date.prototype.toUTCDate = function(){ return ISOSQLDate(new Date(this)); } } if(typeof Date.prototype.toISOSQLDate !== 'function'){ Date.prototype.ISOSQLDateTime = function(){ return ISOSQLDate(new Date(this)); } } Date.isLeapYear = function (year) { return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0)); }; if(typeof Date.prototype.isLeapYear != 'function'){ Date.prototype.isLeapYear = function () { return Date.isLeapYear(this.getFullYear()); }; } if(typeof Date.prototype.getDaysInMonth != 'function'){ Date.getDaysInMonth = function (year, month) { return [31, (Date.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]; }; Date.prototype.getDaysInMonth = function () { return Date.getDaysInMonth(this.getFullYear(), this.getMonth()); }; } Date.prototype.addMonths = function (value) { var n = this.getDate(); this.setDate(1); this.setMonth(this.getMonth() + value); this.setDate(Math.min(n, this.getDaysInMonth())); return this; }; Date.prototype.toSQL = Date.prototype.toISODate = Date.prototype.toSQLDate = function(){ return DateLib.SQLDate(this.valueOf()); } Date.prototype.toSQLTime = Date.prototype.toISOTime = function(){ return DateLib.SQLTime(this.valueOf()); } /*** formate la date passé en paramètre à un format spécifique */ Date.prototype.format = Date.prototype.toFormat = Date.prototype.toDateFormat = function(format){ return dateFormat(this.valueOf(),format); } /*** formate la date passé en paramètre à un format spécifique */ Date.prototype.toSQLFormat = Date.prototype.toSQLDateFormat = function(){ return dateFormat(this.valueOf(),DateLib.SQLDateFormat); } Date.prototype.toSQLDateTimeFormat = Date.prototype.toSQLDateTime = function(){ return dateFormat(this.valueOf(),DateLib.SQLDateTimeFormat); } Date.prototype.toSQLTimeFormat = function(){ return dateFormat(this.valueOf(),DateLib.SQLTimeFormat); } Date.prototype.toDefaultFormat = Date.prototype.toDefaultDateFormat = function(){ return dateFormat(this.valueOf(),DateLib.defaultDateFormat); } Date.prototype.toDefaultTimeFormat = function(){ return dateFormat(this.valueOf(),DateLib.defaultTimeFormat); } Date.prototype.toDefaultDateTimeFormat = function(){ return dateFormat(this.valueOf(),DateLib.defaultDateTimeFormat); } Date.prototype.resetHours = function(){ this.setHours(0); return this; } Date.prototype.resetMinutes = function(){ this.setMinutes(0); return this; } Date.prototype.resetSeconds = function(){ this.setSeconds(0); return this; } Date.prototype.resetHours2Minutes2Seconds = Date.prototype.resetHoursMinutesSeconds =function(){ this.setHours(0); this.setMinutes(0); this.setSeconds(0); this.setMilliseconds(0); return this; } const dateFormat = (function() { var timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g; var timezoneClip = /[^-+\dA-Z]/g; // Regexes and supporting functions are cached through closure return function (date, mask, utc, gmt) { if(isNullOrEmpty(date)) { date = undefined; } if (typeof date === 'number') { date = new Date(date); } else if(typeof date == 'string'){ var defM = (dateFormat.masks[mask] || mask); if(!isNonNullString(defM)){ defM = dateFormat.masks['default']; } var p = parse(date,defM,false); if(isDateObj(p)){ date = p; } else { if(isNonNullString(date) && !isNonNullString(mask)){ mask = date; } date = undefined; } } if(!(isDateObj(date))) { date = new Date(); } mask = (dateFormat.masks[mask] || mask); if(!isNonNullString(mask)){ mask = dateFormat.masks['default']; } // Allow setting the utc/gmt argument via the mask var maskSlice = mask.slice(0, 4); if (maskSlice.toUpperCase() === 'UTC:' || maskSlice.toUpperCase() === 'GMT:') { mask = mask.slice(4); utc = true; if (maskSlice.toUpperCase() === 'GMT:') { gmt = true; } } var _ = utc ? 'getUTC' : 'get'; var d = date[_ + 'Date'](); var D = date[_ + 'Day'](); var m = date[_ + 'Month'](); var y = date[_ + 'FullYear'](); var H = date[_ + 'Hours'](); var M = date[_ + 'Minutes'](); var s = date[_ + 'Seconds'](); var L = date[_ + 'Milliseconds'](); var o = utc ? 0 : date.getTimezoneOffset(); var W = getWeek(date); var N = getDayOfWeek(date); if(!isNonNullString(DaysAndMonths.monthNames[m])){ resetDaysAndMonth(); } var flags = { d: d, dd: pad(d), ddd: DaysAndMonths.dayNames[D], dddd: DaysAndMonths.dayNames[D + 7], m: m + 1, mm: pad(m + 1), mmm: defaultStr(DaysAndMonths.monthNames[m]), mmmm: defaultStr(DaysAndMonths.monthNames[m + 12]), yy: String(y).slice(2), yyyy: y, h: H % 12 || 12, hh: pad(H % 12 || 12), H: H, HH: pad(H), M: M, MM: pad(M), s: s, ss: pad(s), l: pad(L, 3), L: pad(Math.round(L / 10)), t: H < 12 ? 'a' : 'p', tt: H < 12 ? 'am' : 'pm', T: H < 12 ? 'A' : 'P', TT: H < 12 ? 'AM' : 'PM', Z: gmt ? 'GMT' : utc ? 'UTC' : (String(date).match(timezone) || ['']).pop().replace(timezoneClip, ''), o: (o > 0 ? '-' : '+') + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4), S: i18n.lang("ms_dateformat_dofn")(d), W: W, N: N }; var _r = mask.replace(token, function (match) { if (match in flags) { if(match === "mmm" || match === "mmmm"){ if(!isNonNullString(flags[match])){ resetDaysAndMonth(); flags.mmm = defaultStr(DaysAndMonths.monthNames[m]); flags.mmmm = defaultStr(DaysAndMonths.monthNames[m + 12]); } } return flags[match]; } return match.slice(1, match.length - 1); }); return _r; }; })(); var hoursTok = ["h","hh","H","HH","M","MM","o","s",'l','L','t',"tt","TT",'Z'] //retourne le format des heures dans le formattage passé en paramètres var retrieveTimeFormat = function(format){ if(!isNonNullString(format)){ format = dateFormat.masks.default; } var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZWN]|'[^']*'|'[^']*'/g; return format.replace(token, function (match) { if(arrayValueExists(match,hoursTok)) return match; return ''; }); } function shorten(arr, sLen) { var newArr = []; for (var i = 0, len = arr.length; i < len; i++) { newArr.push(arr[i].substr(0, sLen)); } return newArr; } //const SQLDateFormat = "yyyy-mm-dd",SQLDateTimeFormat = "yyyy-mm-dd\'T\'HH:MM:sso",SQLTimeFormat = "HH:MM:ss" dateFormat.masks = { "day1" : "dd", hour1 : "HH:MM", hour2 : "HH", hour3 : "MM", "shortDay1" : "ddd", "longDay2":"dddd", "long1" : "ddd dd", "long3" : "ddd dd mmm yyyy", month1 : "mmm", month2 : "mmmm", month3 : "mm", get default(){ const def = appConfig.get("defaultDateTimeFormat"); return isNonNullString(def)? def : 'dd/mm/yyyy HH:MM:ss'; }, get defaultDate(){ const def = appConfig.get("defaultDateFormat"); return isNonNullString(def)? def : 'dd/mm/yyyy'; }, get defaultTime(){ const def = appConfig.get("defaultTimeFormat"); return isNonNullString(def)? def : 'HH:MM:ss'; }, 'defaultDat1' : "dd/mm", 'defaultDat1' : "dd mm", 'shortDate': 'm/d/yy', "monthO1" : "mm/yyyy", "monthO2" : "mm/yy", "monthO3" : "mm-yyyy", "monthO4" : "mm-yy", "month3333" : "mm yyyy", "month3332" : "mm yy", "monthO5" : "mmm yyyy", "monthO6" : "mmm yy", "monthO7" : "mmmm yyyy", "monthO8" : "mmmm yy", 'mediumDate': 'mmm d, yyyy', 'mediumDate1': 'dd mmm yyyy', 'mediumDate2': 'dd mmm', "year1" : "yyyy", "year2" : "yy", 'longDate': 'mmmm d, yyyy', 'fullDate': 'dddd, mmmm d, yyyy', 'shortTime': 'h:MM TT', 'mediumTime': 'h:MM:ss TT', 'longTime': 'h:MM:ss TT Z', 'isoDate': SQLDateFormat, 'isoTime': SQLTimeFormat, 'isoDateTime': SQLDateTimeFormat, 'expiresHeaderFormat':'ddd, dd mmm yyyy HH:MM:ss Z', //format personalisés, qui ont été ajoutés 'Short Date2': 'd/m/yy', 'court3': 'yy/m/d', 'court4': 'dmyy', 'court5': 'mdyy', "variantF1" : "ddmmyy", "variantF2" : "m/dd", "variantF3" : "m/dd/yy", "variantF4" : "mm/dd/yy", "variantF5" : "dd-mmm", "variantF6" : "dd-mmm-yy", "variant2" : "dd mmm yy", "variant3" : "ddd dd mmm", "variant4" : "ddd dd mmmm", "variant5" : "ddd dd mmm yy", "variant6" : "ddd dd mmmm yy", "variantF7" : "mmm-dd", "variantF8" : "mmm-dd, yyyy", "variantF9" : "m/dd/yyyy HH:MM", "variantF10" : "dd-mmm-yyyy", }; /*** la liste des formats ordonnés parmis ceux ci */ const keys = []; const values = {}; const dFormats = {}; for(let key in dateFormat.masks){ const value = dateFormat.masks[key]; if(!values[value]){ keys.push(key); dFormats[key] = value; values [value] = true; } } const sValues = keys.map(key=>dFormats[key]) .sort((a, b) => { a = a.replaceAll(":","").replaceAll("/","-") b = b.replaceAll(":","").replaceAll("/","-") return (a < b ? -1 : +(a > b)) }); export const sortedFormats = {}; sValues.map((v,i)=>{ sortedFormats[i] = v; }); dateFormat.masks.isoUtcDateTime = sortedFormats.isoUtcDateTime = 'UTC:yyyy-mm-dd\'T\'HH:MM:ss\'Z\''; /*** la liste des formats de type date, avec possibilité de produire un exemple, utile si l'on veut par exemple * demander à l'utilisateur via un FormDataProvider, de sélectionner un format pour le formattage d'une date */ export const formatsObjects = {} // Internationalization strings export const DaysAndMonths = { dayNames: [], monthNames: [] }; export const DaysAndMonthsObject = {}; const resetDaysAndMonth = ()=>{ const dayNames = i18n.lang("ms_date_daynames"); const monthNames = i18n.lang("ms_date_monthnames"); const monthNamesShort = shorten(monthNames, 3); const dayNamesShort = shorten(dayNames, 3); DaysAndMonths.dayNames = dayNamesShort; DaysAndMonths.monthNames = monthNamesShort; DaysAndMonths.dayNamesLong = dayNames; DaysAndMonths.monthNamesLong = monthNames; for(var i in dayNames){ DaysAndMonths.dayNames.push(dayNames[i]); } for(var i in monthNames){ DaysAndMonths.monthNames.push(monthNames[i]); } ///on initialise les dates objects for(let i in DaysAndMonths){ const dates = DaysAndMonths[i]; if(Array.isArray(dates)){ dates.map((value,index)=>{ if(typeof value !=='string' || !value) { return; } DaysAndMonthsObject[i] = typeof DaysAndMonthsObject[i] ==='object' && DaysAndMonthsObject[i]? DaysAndMonthsObject[i] : {}; DaysAndMonthsObject[i][value.toUpperCase().trim()] = index; }); if(Array.isArray(DaysAndMonths[i])){ //on permute les dates de fin et de début const first = DaysAndMonths[i][0].toUpperCase(); const last = DaysAndMonths[i][1].toUpperCase(); const length = DaysAndMonths[i].length; if(first && last && ((first.startsWith("SUN") || first.startsWith("LUN")) || (last.startsWith("LUN") || last.startsWith("MON")))){ DaysAndMonthsObject[i][first] = length; DaysAndMonthsObject[i][last] = 0; } } } } } export const sort = (values)=>{ if(!Array.isArray(values)) return values; //on cherche le nom de colonne correspondante au type de données recherché let keyName = null; for(let i in values){ const v = values[i]?.toString()?.toUpperCase(); if(v){ for(let key in DaysAndMonthsObject){ if(v in DaysAndMonthsObject[key]){ keyName = key; break; } } break; } } if(keyName){ const keys = DaysAndMonthsObject[keyName]; return values.sort((a,b)=>{ a = keys[a?.toString().toUpperCase().trim()]; b = keys[b?.toString().toUpperCase().trim()]; return a < b ? -1 : +(a > b); }) } return values.sort(); } function pad(val, len) { val = String(val); len = len || 2; while (val.length < len) { val = '0' + val; } return val; } var isInteger = function (str) { if (str.match(/^(\d+)$/)) { return true; } return false; }; var getInt = function (str, i, minlength, maxlength) { for (var x = maxlength; x >= minlength; x--) { var token = str.substring(i, i + x); if (token.length < minlength) { return null; } if (isInteger(token)) { return token; } } return null; }; export function isIsoTimeStr(str) { if(!isNonNullString(str)) return; try { return moment(str.trim(),["HH:mm:ss.SSS","HH:mm:ss","HH:mm"])?.isValid(); } catch{} return false; } export function isIsoDateStr(str) { if(!isNonNullString(str) || str.trim().length<6){ return false; } str = str.trim(); if(isoDateRegExp.test(str) || !/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/.test(str)){ try { return moment(str.trim())?.isValid(); } catch{ return false; } } return bool; } export const isTimeStr = (timeStr)=>{ if(!isNonNullString(timeStr)) return false; return timeStr.match(/^\s*(\d\d?)(?::?(\d\d))?(?::(\d\d))?(?!\d)(\.\d+)?\s*(pm?|am?)?/i); } /** @see : https://stackoverflow.com/questions/141348/how-to-parse-a-time-into-a-date-object-from-user-input-in-javascript * Parses a string into a Date. Supports several formats: "12", "1234", * "12:34", "12:34pm", "12:34 PM", "12:34:56 pm", and "12:34:56.789". * The time must be at the beginning of the string but can have leading spaces. * Anything is allowed after the time as long as the time itself appears to * be valid, e.g. "12:34*Z" is OK but "12345" is not. * @param {string} t Time string, e.g. "1435" or "2:35 PM" or "14:35:00.0" * is called on it. Otherwise, setUTCHours is called on 1970/1/1. * @returns {Date|undefined} The parsed date, if parsing succeeded. */ export function parseTime(t) { if(isDateObj(t)) return t; if(!isNonNullString(t)) return null; t = t.trim(); // ?: means non-capturing group and ?! is zero-width negative lookahead var time = isTimeStr(t); if (time) { var hour = parseInt(time[1]), pm = (time[5] || ' ')[0].toUpperCase(); var min = time[2] ? parseInt(time[2]) : 0; var sec = time[3] ? parseInt(time[3]) : 0; var ms = (time[4] ? parseFloat(time[4]) * 1000 : 0); if (pm !== ' ' && (hour == 0 || hour > 12) || hour > 24 || min >= 60 || sec >= 60) return undefined; if (pm === 'A' && hour === 12) hour = 0; if (pm === 'P' && hour !== 12) hour += 12; if (hour === 24) hour = 0; const date = new Date(); date.setHours(hour,min,sec,ms); return date; } else { var b = t.match(/\d+/g); if(b){ const date = new Date(); date.setHours( b[0]>12? b[0] : b[0]%12 + (/p/i.test(t)? 12 : 0), //hours /\d/.test(b[1])? b[1] : 0,//minutes /\d/.test(b[2])? b[2] : 0, //seconds, 0, ); return date; } } return undefined; } /** * parsing a date string * @param {String} val - date string * @param {String} format - format string || null, if null, default format will be get * @param {boolelan} returnObj - check if object containaing date,hour,minute, * @returns {Object} || NaN the constructed date */ export const parse = function (val, format,returnObj) { val = isNonNullString(val)? val.trim() : val; if(isIsoDateStr(val)){ val = moment(val.trim())?.toDate(); } else if(isNullOrEmpty(val)){ val = new Date(); } if(isNonNullString(val) && !isNonNullString(format) && isTimeStr(val)){ const vv = parseTime(val); if(isDateObj(vv)){ val = vv; } } if(isDateObj(val)){ var date = new Date(val); if(returnObj === true){ let month = date.getMonth()+1; month = month < 10 ? ('0' + month) : ('' + month); return {date: date, year: date.getFullYear(), month,day: ((date.getDate() < 10 ? '0' : '') + date.getDate()),hour:date.getHours(),minute:date.getMinutes(),second:date.getSeconds(),milli:date.getMilliseconds()}; } return date; } if(!isNonNullString(val)){ if(returnObj) return null; return NaN; //throw new Error("invalid date string to parse",date); } format = defaultStr(format,DateLib.SQLDateFormat); val = val + ""; var iVal = 0; var iFormat = 0; var c = ""; var token = ""; var token2 = ""; var x, y; var now = new Date(); var year = now.getYear(); var month = now.getMonth() + 1; var date = 1; var hh = 0; var mm = 0; var ss = 0; var millis = 0; var ampm = ""; let returnValue = undefined,newDate = null; while (iFormat < format.length) { // Get next token from format string c = format.charAt(iFormat); token = ""; while ((format.charAt(iFormat) === c) && (iFormat < format.length)) { token += format.charAt(iFormat++); } // Extract contents of value based on format token if (token === "yyyy" || token === "yy" || token === "y") { if (token === "yyyy") { x = 4; y = 4; } if (token === "yy") { x = 2; y = 2; } if (token === "y") { x = 2; y = 4; } year = getInt(val, iVal, x, y); if (year === null) { returnValue = NaN; break; } iVal += year.length; if (year.length === 2) { if (year > 70) { year = 1900 + (year - 0); } else { year = 2000 + (year - 0); } } } else if (token === "mmm" | token === "mmmm") { month = 0; for (var i = 0; i < DaysAndMonths.monthNames.length; i++) { var monthName = DaysAndMonths.monthNames[i]; if (val.substring(iVal, iVal + monthName.length).toLowerCase() === monthName.toLowerCase()) { month = i + 1; if (month > 12) { month -= 12; } iVal += monthName.length; break; } } if ((month < 1) || (month > 12)) { returnValue = NaN; break; } } else if (token === "dddd" || token === "ddd") { for (var n = 0; n < DaysAndMonths.dayNames.length; n++) { var dayName = DaysAndMonths.dayNames[n]; if (val.substring(iVal, iVal + dayName.length).toLowerCase() === dayName.toLowerCase()) { iVal += dayName.length; break; } } } else if (token === "m" || token === "mm") { month = getInt(val, iVal, token.length, 2); if (month === null || (month < 1) || (month > 12)) { returnValue = NaN; break; } iVal += month.length; } else if (token === "dd" || token === "d") { date = getInt(val, iVal, token.length, 2); if (date === null || (date < 1) || (date > 31)) { returnValue = NaN; break; } iVal += date.length; } else if (token === "hh" || token === "h") { hh = getInt(val, iVal, token.length, 2); if (hh === null || (hh < 1) || (hh > 12)) { returnValue = NaN; break; } iVal += hh.length; } else if (token === "HH" || token === "H") { hh = getInt(val, iVal, token.length, 2); if (hh === null || (hh < 0) || (hh > 23)) { returnValue = NaN; break; } iVal += hh.length; } else if (token === "KK" || token === "K") { hh = getInt(val, iVal, token.length, 2); if (hh === null || (hh < 0) || (hh > 11)) { returnValue = NaN; break; } iVal += hh.length; } else if (token === "kk" || token === "k") { hh = getInt(val, iVal, token.length, 2); if (hh === null || (hh < 1) || (hh > 24)) { returnValue = NaN; break; } iVal += hh.length; hh--; } else if (token === "MM" || token === "M") { mm = getInt(val, iVal, token.length, 2); if (mm === null || (mm < 0) || (mm > 59)) { returnValue = NaN; break; } iVal += mm.length; } else if (token === "l") {//Milliseconds; gives 3 digits. millis = getInt(val, iVal, 1, 3); if (millis === null || (millis < 0) || (millis >= 1000)) { returnValue = NaN; break; } iVal += millis.length; } else if (token === "L") {//Milliseconds; gives 2 digits. millis = getInt(val, iVal, 1, 2); if (millis === null || (millis < 0) || (millis >= 100)) { returnValue = NaN; break; } iVal += millis.length; } else if (token === "ss" || token === "s") { ss = getInt(val, iVal, token.length, 2); if (ss === null || (ss < 0) || (ss > 59)) { returnValue = NaN; break; } iVal += ss.length; } else if (token =="tt" | token =='TT') { if (val.substring(iVal, iVal + 2).toLowerCase() === "am") { ampm = (token == 'tt')? "am":"AM"; } else if (val.substring(iVal, iVal + 2).toLowerCase() === "pm") { ampm = (token === 'tt')? 'pm':"PM"; } else { returnValue = NaN; break; } iVal += 2; } else if (token =="t" | token =='T') { if (val.substring(iVal, iVal + 1).toLowerCase() === "a") { ampm = (token == 't')? "a":"A"; } else if (val.substring(iVal, iVal + 1).toLowerCase() === "p") { ampm = (token === 't')? 'p':"P"; } else { returnValue = NaN; break; } iVal += 2; } else { if (val.substring(iVal, iVal + token.length) !== token) { returnValue = NaN; break; } else { iVal += token.length; } } } // If there are any trailing characters left in the value, it doesn't match if (iVal !== val.length) { returnValue = NaN; } if(returnValue != NaN){ // Is date valid for month? if (month === 2) { // Check for leap year if (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0)) { // leap year if (date > 29) { returnValue = NaN; } } else { if (date > 28) { returnValue = NaN; } } } if(returnValue != NaN && (month === 4) || (month === 6) || (month === 9) || (month === 11)){ if (date > 30) { returnValue = NaN; } } if(returnValue != NaN){ // Correct hours value if (hh < 12 && ampm && arrayValueExists(ampm,["p","pm","PM"])) { hh = hh - 0 + 12; } else if (hh > 11 && ampm && arrayValueExists(ampm,["a","am","AM"])) { hh -= 12; } newDate = new Date(year, month - 1, date, hh, mm, ss,millis); } } if(returnValue == NaN){ try { let d = new Date(val); if(d && d != NaN){ newDate = d; } } catch{} } if(returnValue != NaN && newDate){ if(returnObj === true) return {date: newDate, year: year, month: month ,day: date,hour:hh,minute:mm,second:ss,milli:millis}; return newDate; } if(returnObj === true){ return {}; } return NaN; }; /** * Get the ISO 8601 week number * Based on comments from * http://techblog.procurios.nl/k/n618/news/view/33796/14863/Calculate-ISO-8601-week-and-year-in-javascript.html * * @param {Object} `date` * @return {Number} */ function getWeek(date) { // Remove time components of date var targetThursday = new Date(date.getFullYear(), date.getMonth(), date.getDate()); // Change date to Thursday same week targetThursday.setDate(targetThursday.getDate() - ((targetThursday.getDay() + 6) % 7) + 3); // Take January 4th as it is always in week 1 (see ISO 8601) var firstThursday = new Date(targetThursday.getFullYear(), 0, 4); // Change date to Thursday same week firstThursday.setDate(firstThursday.getDate() - ((firstThursday.getDay() + 6) % 7) + 3); // Check if daylight-saving-time-switch occurred and correct for it var ds = targetThursday.getTimezoneOffset() - firstThursday.getTimezoneOffset(); targetThursday.setHours(targetThursday.getHours() - ds); // Number of weeks between target Thursday and first Thursday var weekDiff = (targetThursday - firstThursday) / (86400000*7); return 1 + Math.floor(weekDiff); } /** * Get ISO-8601 numeric representation of the day of the week * 1 (for Monday) through 7 (for Sunday) * * @param {Object} `date` * @return {Number} */ function getDayOfWeek(date) { var dow = date.getDay(); if(dow === 0) { dow = 7; } return dow; } // return the number of days in a date's month var daysInMonth = function ( dt ) { if(isNullOrEmpty(dt)) td = new Date(); if(isNonNullString(dt) && isValidDate(dt)){ dt = new Date(dt); } if(!isDateObj(dt)) { console.error("could not retrieve days in month of invalid date object ",dt); return 0; } return dt.getDaysInMonth(); }; var __addToDate = function(days,date,format,returnDateObj,type) { if(!isDecimal(days)) days = 0; if(isNullOrEmpty(date)){ date = new Date(); } if(isValidDate(date) && isNonNullString(date)){ date = new Date(date); } if(isBool(format)){ let t = format; format = defaultStr(returnDateObj); returnDateObj = t; } if(!isValidDate(date)&& isNonNullString(date)){ var t = date; if(isValidDate(format)){ date = new Date(format); } format = t; } if(!isValidDate(date)){ date = isNonNullString(date)? new Date(date): new Date(); } let set = 'set'+type, get = 'get'+type; date = date[set](date[get]() + days); if(returnDateObj === true) return new Date(date); return dateFormat(date,format); }; /*** retourne l'objet date à partir d'une chaine de caractère où un objet date * @param date : string, dateObj, l'objet date * @param format : le format source de la date à parser si c'est une date en chaine de caractère * @param returnObj : si un objet sera retourné en lieu et place de l'objet date * @return : dateObj or null */ export const toDateObj = function(date,format,returnObj){ if(isBool(format)){ let t = returnObj; returnObj = format; format = t; } format = defaultStr(format,SQLDateFormat); const isDateTime = isValidSQLDateTime(date); if(isNonNullString(date)){ date = date.trim(); } if((isDateTime || isValidSQLDate(date))){ format = SQLDateTimeFormat; ///on ramène au format SQLDateTime if(!isDateTime){ date += " 00:00:00"; } return parse(new Date(date),format,returnObj) } if(isDateObj(date)) { return parse(date,format,returnObj) } if(!isNonNullString(date)) return null; date = parse(date,format,returnObj); if(returnObj){ return isObj(date) && isDateObj(date.date)? date : null; } if(isDateObj(date)) return date; return null; } export const isValidDate = function (sDate,format) { if(isBool(sDate)) return false; //if(isNullOrEmpty(sDate)) return false; if(isDateObj(sDate)) return true; if(isNonNullString(sDate)){ var f = parse(sDate,format,true); return (f && isPlainObject(f) && Object.size(f,true)>0); } if(sDate?.toString() == parseInt(sDate).toString()) return false; var tryDate = new Date(sDate); return (tryDate && tryDate.toString() != "NaN" && tryDate != "Invalid Date"); } export const isValidIsoTime = (strTime) =>{ let timeFormat = /^(?:[01]?\d|2[0-3]):[0-5]\d:[0-5]\d$/; return timeFormat.test(strTime); }; /**** retourne le temps GMT eu format SQLDateTime @param {object|string} date, la date à partir de laquelle on veut convertir, si date n'est pas définie, alors la date en cours est exploiée @return {string} */ export const ISOSQLDateTime = (date)=>{ if(!date){ date = new Date(); } else date = parse(date); if(isDateObj(date)){ return date.toISOString().slice(0, 19).replace('T', ' '); } return ""; } export function ISOSQLDate (date){ return defaultStr(ISOSQLDateTime(date)).split(":")[0].trim(); } const DateLib = {} export const isValidSQLDate = (date)=>{ if(!isNonNullString(date)) return false; var regEx = /^\d{4}-\d{2}-\d{2}$/; if(!date.match(regEx)) return false; // Invalid format var d = new Date(date); if(typeof d !='object' || !d) return false; var dNum = d.getTime(); if(!dNum && dNum !== 0) return false; // NaN value, Invalid date return d.toISOString().slice(0,10) === date; } export const isValidSQLDateTime = (dateTime)=>{ if(!isNonNullString(dateTime)) return false; var regEx = /^\d{4}-\d{2}-\d{2} (?:[01]?\d|2[0-3]):[0-5]\d:[0-5]\d/; if(!dateTime.match(regEx)) return false; // Invalid format var d = new Date(dateTime); if(typeof d !== 'object' || !d) return false; var dNum = d.getTime(); if(!dNum && dNum !== 0) return false; // NaN value, Invalid date return true; } export const isSQLDate = function isISODate(value){ if(isDateObj(value)) return true; var dateReg = /^\d{2}([./-])\d{2}\1\d{4}$/; if(!isNonNullString(value)) return false; // STRING FORMAT yyyy-mm-dd var str = value; // m[1] is year 'YYYY' * m[2] is month 'MM' * m[3] is day 'DD' var m = str.match(/(\d{4})-(\d{2})-(\d{2})/); // STR IS NOT FIT m IS NOT OBJECT if( m === null || typeof m !== 'object'){return false;} // CHECK m TYPE if (typeof m !== 'object' && m !== null && m.size!==3){return false;} var ret = true; //RETURN VALUE var thisYear = new Date().getFullYear(); //YEAR NOW var minYear = 1700; //MIN YEAR // YEAR CHECK if( (m[1].length < 4) || m[1] < minYear || m[1] > thisYear){ret = false;} // MONTH CHECK if( (m[2].length < 2) || m[2] < 1 || m[2] > 12){ret = false;} // DAY CHECK if( (m[3].length < 2) || m[3] < 1 || m[3] > 31){ret = false;} return ret; } /*** retourne la première date courante du mois * @param {int|Date} year, l'année où la date pour laquelle on veut récuperer la date * @param {int} month, le mois pour lequel on veut récupérer la date (valeur comprise entre 0 et 11) */ export function getFirstDayOfMonth(year, month) { if(year && isDateObj(year)){ year = date.getFullYear(); month = date.getMonth(); } else { if(typeof year =='object') year = undefined; if(!year && typeof year !='number'){ year = new Date().getFullYear(); } if(!month || typeof month !='number' || month <0 || month>11){ month = new Date().getMonth(); } } return new Date(year, month, 1); } /**** prend en paramètre une date au format from =>to et formatte au format français par défaut * @param {string} periodDate, la date au format [from] => [to] où from est soit au format SQLDateTime|SQLDate et to est soit au format SQLDateTime|SQLTime * @param {boolean} {isDateTime} si la date est au format dateTime où non * @return {string} chaine de caractère au format parsé */ export function formatDatePeriod(periodDate,isDateTime){ if(isNonNullString(periodDate)){ const lang = i18n.getLang(); const isFR = lang && typeof lang =="string" && lang.toLowerCase() == 'fr'; const split = periodDate.split("=>"); let from = split[0], to = split[1]; if(isValidSQLDateTime(from) || isValidSQLDate(from)){ if(!isDateTime)isDateTime = isValidSQLDateTime(from); from = new Date(from); } if(isValidSQLDateTime(to) || isValidSQLDate(from)){ if(!isDateTime) isDateTime = isValidSQLDateTime(to); to = new Date(to); } const dateFormat = isDateTime ? (isFR?DateLib.defaultDateTimeFormat:SQLDateTimeFormat) : (isFR?DateLib.defaultDateFormat:SQLDateFormat); if(isValidDate(from) && isValidDate(to)){ return "{0} {1} {2} {3}".sprintf(isFR?"Du":"From",new Date(from).toFormat(dateFormat),isFR?"au":"to",new Date(to).toFormat(dateFormat)); } } return ""; } export const parseFromToDate = formatDatePeriod; /*** * retourne les dates limites de la semaine courante à partir de la date passée en paramètre * @param {date}, l'objet date à partir duquel retourner les limites * @param {string} format le format de données de la valeur résultat */ export const currentWeekDaysLimits = (date,format)=>{ const currentDate = isValidDate(date)? new Date(date) : new Date().resetHoursMinutesSeconds(); const day = currentDate.getDay(), diff = currentDate.getDate() - day + (day == 0 ? -6:1); // adjust when day is sunday const last = new Date(currentDate); const first = new Date(currentDate.setDate(diff)); if(isNonNullString(format)){ format = format.trim(); return first.toDateFormat(format) +"=>"+last.toDateFormat(format); } return {first,last} } /*** * retourne les dates limites du mois courant * @param {date}, l'objet date à partir duquel retourner les limites * @param {string} format le format de données de la valeur résultat */ export const currentMonthDaysLimits = (date,format)=>{ const currentDate = isValidDate(date)? new Date(date) : new Date().resetHoursMinutesSeconds(); const first = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1); const last = currentDate; if(isNonNullString(format)){ format = format.trim(); return first.toDateFormat(format) +"=>"+last.toDateFormat(format); } return {first,last} } /*** * retourne les dates limites de la semaine passée à partir de la date passées en une semaine * @param {date}, l'objet date à partir duquel retourner les limites * @param {string} format le format de données de la valeur résultat */ export const previousWeekDaysLimits = (date,format)=>{ let cDate = isValidDate(date)? new Date(date) : new Date().resetHoursMinutesSeconds(); var beforeOneWeek = new Date(cDate.getTime() - 60 * 60 * 24 * 7 * 1000) var beforeOneWeek2 = new Date(beforeOneWeek); let day = beforeOneWeek.getDay() let diffToMonday = beforeOneWeek.getDate() - day + (day === 0 ? -6 : 1) const first = new Date(beforeOneWeek.setDate(diffToMonday)); const last = new Date(beforeOneWeek2.setDate(diffToMonday + 6)); if(isNonNullString(format)){ format = format.trim(); return first.toDateFormat(format) +"=>"+last.toDateFormat(format); } return {first,last} }; Object.defineProperties(DateLib,{ daysInMonth : { value : daysInMonth, override : false, writable : false }, getFirstDayOfMonth : { value : getFirstDayOfMonth, }, UTCDateTime : { value : ISOSQLDateTime, }, toUTCDateTime : { value : ISOSQLDateTime, }, UTCDate : { value : ISOSQLDate, }, ISOSQLDate : { value : ISOSQLDate, }, toUTCDate : { value : ISOSQLDate, }, dateDiff : { /*** determine difference of from date to toDate * @param fromDate {date} : la date de départ * @param toDate {date} : la date à utiliser pour soustraite à fromDate et déterminer la différence * si toDate est nulle où on définie alors il prendra la valeur de la date actuelle */ value : function datediff(fromDate, toDate) { if (!fromDate || !isDateObj(fromDate)) { console.log(fromDate,' bad date object to evaluate dateDiff funcion'); //throw new Error('Date should be specified'); return {}; } var startDate = new Date(1970, 0, 1, 0).getTime(), now = new Date(), toDate = isDateObj(toDate)? toDate : now, diff = toDate - fromDate, date = new Date(startDate + diff), years = date.getFullYear() - 1970, months = date.getMonth(), days = date.getDate() - 1, hours = date.getHours(), minutes = date.getMinutes(), seconds = date.getSeconds(), diffDate = { years: 0, months: 0, days: 0, hours: 0, minutes: 0, seconds: 0 }; if (years < 0) return diffDate; diffDate.years = years > 0 ? years : 0; diffDate.months = months > 0 ? months : 0; diffDate.days = days > 0 ? days : 0; diffDate.hours = hours > 0 ? hours : 0; diffDate.minutes = minutes > 0 ? minutes : 0; diffDate.seconds = seconds > 0 ? seconds : 0; return diffDate; }, override:false, writable : false }, format : { value : (date,toFormat,fromFormat,dateFormatArg1,dateFormatArg2)=>{ if(isNonNullString(date)){ date = DateLib.toObj(date,fromFormat); } return dateFormat(date,toFormat,dateFormatArg1,dateFormatArg2); }, override : false, writable : false }, parseTime : {value:parseTime}, isTimeStr : {value : isTimeStr}, isTimeString : {value:isTimeStr}, parse : { value : parse, override : false, writable : false }, parseFromToDate : {value : parseFromToDate}, formatDatePeriod : { value : formatDatePeriod,override : false, writable :false }, formatPeriod : { value : formatDatePeriod, override : false, writable : false, }, /*** parse une chaine de caractère de type date passée en paramètre issue du format fromFormat et le formate * en la date parsée au forma toFormat * @param : string : la date passé en paramètre au format formFormat * @param : string {undefined} : le format source de la date, par défaut : la date au format sql * @param : string {undefined} l: le format destination de la date à retourner * @return : la date formatté au format toFormat où une chaine de caractère vide */ parse2Format : { value : function(date,fromFormat,toFormat){ fromFormat = typeof fromFormat =='string' && fromFormat ? fromFormat : typeof SQLDateFormat =='string' && SQLDateFormat ? SQLDateFormat :''; toFormat = typeof toFormat =='string' && toFormat ? toFormat : DateLib.defaultDateFormat; let d = toDateObj(date,fromFormat,false); if(d){ return dateFormat(d,toFormat); } return defaultStr(date); },override:false,writable:false }, formats : { value : dateFormat.masks,override:false,writable:false }, sortedFormats : { value : sortedFormats,override:false,writable:false }, masks : { value : dateFormat.masks, override : false, writable : false }, isDateObj : { value : isDateObj, override : false, writable : false }, isObj : { value : isDateObj, override : false, writable : false }, isSQLDate : { value : isSQLDate, override : false, writable : false }, retrieveTimeFormat : { value : retrieveTimeFormat, override : false, writable : false }, addDays : { /**ajoute le nombre de jours jours à l'objet date * @param days : number le nombre de jour à ajouter à la date * @param date : null la date courante * @param format : string or null, le format de la date à retourner * format et date peuvent avoir des rôles inverses * @param {bool}, si la date objet sera retournée où la date formattée * @return string */ value : function(days,date,format,returnDateObj){ return __addToDate(days,date,format,returnDateObj,'Date'); }, override : false, writable : false }, removeDays : { value : function(){ var args = Array.prototype.slice.call(arguments,0); if(!isDecimal(args[0])) args[0] = 0; args[0] = -1*args[0]; return DateLib.addDays.apply(this,args); }, override : false, writable : false }, //les paramètres systèmes, renseignés au niveau de la base de données system : { writable : false, override:false, value : { time_format : SQLTimeFormat, date_format : SQLDateFormat, date_time_format : SQLDateTimeFormat, }, }, isoDateRegExp : {value:isoDateRegExp}, isIsoDateStr : {value:isIsoDateStr}, isIsoTimeStr : {value:isIsoTimeStr}, addMonths : { /**ajoute le nombre de mois à l'objet date * @param months : number le nombre de mois à ajouter à la date * @param date : null la date courante * @param format : string or null, le format de la date à retourner * format et date peuvent avoir des rôles inverses * @param {bool}, si la date objet sera retournée où la date formattée * @return string */ value : function(months,date,format,returnDateObj){ return __addToDate(months,date,format,returnDateObj,'Month'); }, override : false, writable : false }, addWeeks : { value : function(weeks,date,format,returnDateObj){ weeks = defaultDecimal(weeks)* 7; return __addToDate(weeks,date,format,returnDateObj,'Date'); },override : false, writable : false }, removeWeeks : { value : function(weeks,date,format,returnDateObj){ weeks = -1*Math.abs(defaultDecimal(weeks))* 7; return __addToDate(weeks,date,format,returnDateObj,'Date'); },override : false, writable : false }, removeMonths : { value : function(){ var args = Array.prototype.slice.call(arguments,0); if(!isDecimal(args[0])) args[0] = 0; args[0] = -1*args[0]; return DateLib.addMonths.apply(this,args); }, override : false, writable : false }, addYears : { /**ajoute le nombre d'années à l'objet date * @param years : number le nombre de mois à ajouter à la date * @param date : null la date courante * @param format : string or null, le format de la date à retourner * format et date peuvent avoir des rôles inverses * @return string */ value : function(years,date,format,returnObj){ if(!isDecimal(years)) years = 0; if(isBool(format)){ let t = format; format = defaultStr(returnObj); returnObj = t; } date = new Date(DateLib.addDays(0,date,true)); if(isDateObj(date)){ var year = date.getFullYear(); if((year+years)<0) years = 0; else years+= year; date = date.setFullYear(years); if(returnObj){ return new Date(date); } return DateLib.format(date,format) } return null; }, override : false, writable : false }, removeYears : { value : function(){ var args = Array.prototype.slice.call(arguments,0); if(!isDecimal(args[0])) args[0] = 0; args[0] = -1*args[0]; return DateLib.addYears.apply(this,args); }, override : false, writable : false }, addHours : { value : function(hours,dateObj){ if(!isDecimal(hours)){ hours = 0; } return DateLib.addMilliseconds(hours * 3600000,dateObj); }, override : false, writable:false }, removeHours : { value : function(){ var args = Array.prototype.slice.call(arguments,0); if(!isDecimal(args[0])) args[0] = 0; args[0] = -1*args[0];