UNPKG

@xassist/xassist-date

Version:

helper functions for date manipulation

519 lines (491 loc) 16.6 kB
"use strict" import { default as duration } from "./xassist-duration.js" var _dateDict = { days:{ defaultKey:"long", "abbreviation" : ["S", "M", "T", "W", "T", "F", "S"], "short":["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], "long":["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] }, month:{ defaultKey:"long", "abbreviation" : ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"], "short": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], "long": ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"] } }, _leapYear=function(year) { //year not divisible by 4 => no leapyear (year % 4 == year & 3 (bitwise AND x%2^n===x&2^(n-1) bit wise and) //else: year not divisible by 100 => leapyear (year % 4 => check year%25 //else year divisble by 400 => leapyear ( (year %400) knwoing that year%25==0=>year%16 or year & 15) year=((typeof year==="undefined")?new Date().getFullYear():year); if(year>0||year<0){ return ((year & 3) == 0 && ((year % 25) != 0 || (year & 15) == 0)); } return undefined; }, _maxNumberofDays=function(month,year){ var leap; month=((typeof month==="undefined")?new Date().getMonth()+1:month); if(month===2){ leap=_leapYear(year); return (typeof leap==="undefined"?leap:(leap?29:28)) } else { return [31,28,31,30,31,30,31,31,30,31,30,31][month-1] } }, _validDate = function (d) { if (Object.prototype.toString.call(d) === "[object Date]") { // it is a date if (isNaN(d.getTime())) { // d.valueOf() could also work // date is not valid return false; } else { // date is valid return true; } } else { // not a date return false; } }, _dateRegExp=/^(\d{4}|\d{2}|[1-2]?\d{1}|3[0-1])[-/\\.](0?[1-9]|[1-2]\d{1}|3[0-1])[-/\\.](\d{4}|\d{2}|[1-2]?\d{1}|3[0-1])(?:[T ]([0-1]\d|2[0-3]):([0-5]\d)(?::([0-5]\d)(?:[.,](\d*)?)?)?)?$/i; /*regexp for datetime matching ^ //strings starts with ( //capturing group 1 (year or day) \d{4} //YYYY | //or \d{2} // YY or DD (01-31), difference not captured | //or [1-2]?\d{1} // D (1-29) ! 0 is allowed | //or 3[0-1] // D (30-31) ) [ //date-split:match single character from list - //dash / //forward slash \\ //back slash escaped . //point ] //end list ( //capturing group 2 (month) 0?[1-9] //optional 0 and 1-9 (1-9 or 01-09) | [1-2]?\d{1} // D (1-29) ! 0 is allowed | //or 3[0-1] // D (30-31) ) [ //date-split:match single character from list - //dash / //forward slash \\ //back slash escaped . //point ] //end list ( //capturing group 3 (year or day) \d{4} //YYYY | //or \d{2} // YY or DD (01-31), difference not captured | //or [1-2]?\d{1} // D (1-29) ! 0 is allowed | //or 3[0-1] // D (30-31) ) (?: //non capturing group (time) [T ] //1 character from list (T or space) ( //captruing group 4: hour [0-1]\d|2[0-3] //00-19 or 20-23 ) : //matches colon ":" ( //capturing group 5: minutes [0-5]\d //00-59 ) (?: //non capturing group (seconds and milliseconds) : //matches colon ":" ( //capturing group 6: seconds [0-5]\d //00-59 ) (?: //non capturing group (milliseconds) [.,] //matches single character (point or ,) ( //capturing group 7 \d* //any number of digits )? //optional could be omitted )? //optional could be omitted )? //optional could be omitted )? //optional, could be omitted $ //string end here */ //date functionality //date functionality var _getWeekDay= function (/*index,*/type, startindexOfWeek, zeroBased) { var onDateObj=0,index; if (this && this.constructor && this.constructor.name==="XaDate") { onDateObj=1; } index = (onDateObj?this.getDay():arguments[0]||0); type = (""+arguments[1-onDateObj]||_dateDict.days.defaultKey).toLowerCase(); startindexOfWeek = (onDateObj?0:arguments[2]||0); zeroBased = (onDateObj?true:!!(typeof arguments[3]!=="undefined"? arguments[3]:true)); index =((index - (zeroBased ? 0 : 1) + startindexOfWeek) % 7); type=(!_dateDict.days.hasOwnProperty(type)?_dateDict.days.defaultKey:type); index=(index<0?7+index:index); return _dateDict.days[type][index]; }; var _getMonth=function (/*index,*/type, zeroBased) { var onDateObj=0,index; if (this &&this.constructor && this.constructor.name==="XaDate") { onDateObj=1; } index = (onDateObj?this.getMonth():arguments[0]||0); type = (""+arguments[1-onDateObj]||_dateDict.month.defaultKey).toLowerCase(); type=(!_dateDict.month.hasOwnProperty(type)?_dateDict.month.defaultKey:type); zeroBased = (onDateObj?true:!!(typeof arguments[2]!=="undefined"? arguments[2]:true)); index = ((index - (zeroBased ? 0 : 1)) % 12); index=index<0?12+index:index; return _dateDict.month[type][index]; }; var _testDateFormat=function(day,month,year){ return day<=_maxNumberofDays(month,year); } var date=function() { return new XaDate([].slice.call(arguments)); }; date.isValidDateString=function (str) { var dateObj={ valid:false, format:[] }, matchRes=str.match(_dateRegExp), p1,p3,p2,p1Year,p3Year,possibleFormats=[]; if(matchRes){ p1=parseInt(matchRes[1]); //dateObj.month=parseInt(matchRes[2]); p2=parseInt(matchRes[2]) p3=parseInt(matchRes[3]); //we assume hours and minutes is (mostly) defined //seconds and milliseconds is not (always) defined dateObj.hours=parseInt(matchRes[4]||0); //fastest comparison if matchRes[4] is defined dateObj.minutes=parseInt(matchRes[5]||0); //fastest comparison if matchRes[5] is defined dateObj.seconds=(matchRes[6]?parseInt(matchRes[6]):0);//faster comparison for undefined in stead of "or" dateObj.milliSeconds=(matchRes[7]?parseInt(matchRes[7]):0); //faster comparison for undefined in stead of "or" dateObj.valid=true; dateObj.multipleFormats=false; var numberToYear=function(y){ return (y<100?(y>50?1900+y:2000+y):y) } p1Year=numberToYear(p1); p3Year=numberToYear(p3); possibleFormats=[ { day:p1,month:p2,year:p3Year,format:"DMY"}, { day:p3,month:p2,year:p1Year,format:"YMD"}, { day:p2,month:p1,year:p3Year,format:"MDY"}, { day:p2,month:p3,year:p1Year,format:"YDM"} ].filter(function(x){return (x.day<32&&x.month<13&&_testDateFormat(x.day,x.month,x.year));}); if(!possibleFormats.length){ return false; } else { dateObj.day=[]; dateObj.month=[]; dateObj.year=[]; dateObj.multipleFormats=true; dateObj.format=[]; possibleFormats.forEach(function(x){ dateObj.day.push(x.day); dateObj.month.push(x.month); dateObj.year.push(x.year); dateObj.format.push(x.format); }); return dateObj; } } else{ return false; } }; date.stringToDate = function (str,format) { var dateObj=date.isValidDateString(str); if(dateObj){ //rewrite format to index of assigned format if(typeof format!=="string"){ format=0; } else { format=dateObj.format.indexOf(format.toUpperCase()); if(format===-1){ return false; } } return date(dateObj.year[format],dateObj.month[format]-1,dateObj.day[format],dateObj.hours,dateObj.minutes,dateObj.seconds,dateObj.milliSeconds); } else{ return false; } }; function XaDate(inputArray) { var x = new(Function.prototype.bind.apply( Date, [Date].concat(inputArray))); Object.setPrototypeOf(x, XaDate.prototype); return x; } //Object.setPrototypeOf(XaDate, XaDate.prototype); Object.setPrototypeOf(XaDate.prototype, Date.prototype); XaDate.prototype.isValid = function () { return _validDate(this); }; /*if date is not set:4arguments: index of weekday,type,startindexOfWeek and zeroBased else index=date.getDay();*/ /*TODO adapt arguments optional, ...*/ date.getWeekDay=_getWeekDay.bind(null); date.month =_getMonth.bind(null); XaDate.prototype.getWeekDay =_getWeekDay XaDate.prototype.month =_getMonth; XaDate.prototype.isLeapYear =function(){ return (this.isValid()?_leapYear(this.getFullYear()):undefined); }; date.isLeapYear = _leapYear; XaDate.prototype.daysInMonth =function(){ return (this.isValid()?_maxNumberofDays(this.getMonth()+1,this.getFullYear()):undefined); }; date.daysInMonth = _maxNumberofDays; XaDate.prototype._addSmall=function(dur){ this.addDays(dur.removeIntervalOfType("day")); this.addHours(dur.removeIntervalOfType("hour")); this.addMinutes(dur.removeIntervalOfType("minute")); this.addSeconds(dur.removeIntervalOfType("second")); this.addMilliseconds(dur.removeIntervalOfType("millisecond")); return this; } XaDate.prototype._addBig=function(dur){ //console.log("before: "+this.toLocaleString()) var currentDay=this.getDate(),currentMonth; var decMonth=dur.month*10%10/10; var groundMonth=dur.month-decMonth; this.addYears(dur.removeIntervalOfType("year")); this.addMonths(dur.removeIntervalOfType("month",groundMonth)); //remove rounding errors dur.month=decMonth //we get month and set date date currentMonth=this.getMonth(); this.setDate(currentDay); if (this.getMonth() !== currentMonth){ this.setDate(0) //go back to last day of previous month; } //console.log("after: "+this.toLocaleString()+") } XaDate.prototype.format=function(formatStr){ /* //formatStr can be //d: day without leading zero //dd: day with leading zero //ddd: short day string //dddd: long day string //ddddd:single letter day string (may be more d's) //M:month without leading zero //MM or Mm:month with leading zero //mmm: short month string //mmmm: long month string //mmmmm: single letter month (may be more m's) //y or yy: 2digit year //yyy or yyyyyy: 4digit year //h: hour without leading zero //hh:hour with leading zero (or more h's) //m: minute without leading zero //mm: minute with leading zero //s: second without leading zero //ss: second with leading zero (or more s's) //.000 or ,000: any number of zero's is for the deci,centi,milliseconds, ... //all other characters are repeated as such in the string //the difference between m for minutes or month is made by the capitalization, at least one of the m's for (a one or two letter match) should be capitalized for months //all other strings could be capitalized or not. //to escape the characters use a ^ before the matching character eg ^mmm prints mmm */ /*var matchingChars=["d","D","m","M","y","Y","h","H","s","S",".",","]; var result=""; var matchingCombos={ day:/(?:[^\\/dD]|^)([dD]+)/, month:/(?:[^\\/Mm]|^)(M[Mm]?|[Mm]{3,})/, year:/(?:[^\\/yY]|^)([yY]+)/, hour:/(?:[^\\/hH]|^)([hH]+)/, minute:/(?:[^\\/m]|^)(m{1,2})/, second:/(?:[^\\/sS]|^)([sS]+)/, millisecond:/(?:[^\\/,.]|^)([,.]0+)/ }*/ var matchResult={ month:[ function(d){return (d.getMonth()+1).toString();}, function(d){return ("0"+(d.getMonth()+1)).slice(-2);}, function(d){return d.month("short");}, function(d){return d.month("long");}, function(d){return d.month("abbreviation");} ], day:[ function(d){return (d.getDate()).toString();}, function(d){return ("0"+d.getDate()).slice(-2);}, function(d){return d.getWeekDay("short");}, function(d){return d.getWeekDay("long");}, function(d){return d.getWeekDay("abbreviation");} ], year:[ function(d){return d.getFullYear().toString().slice(-2)}, function(d){return d.getFullYear().toString();}, ], /*minute:[ function(d){return (d.getMinutes()).toString();}, function(d){return ("0"+d.getMinutes()).slice(-2);} ], hour:[ function(d){return (d.getHours()).toString();}, function(d){return ("0"+d.getHours()).slice(-2);} ], second:[ function(d){return (d.getSeconds()).toString();}, function(d){return ("0"+d.getSeconds()).slice(-2);} ],*/ time:[ function(d,fn){return (d[fn]()).toString();}, function(d,fn){return ("0"+d[fn]()).slice(-2);} ], millisecond:[ function(d,len){return d.getMilliseconds().toString().slice(0,len);} ] }; var me=this; function getFormattedString(matchType){ var firstChar=matchType[0]; var matchLength=matchType.length; if (firstChar==="M"||(firstChar==="m"&&matchLength>2)){ return matchResult.month[Math.min(matchLength,5)-1](me); } else if (firstChar==="d"||firstChar==="D"){ return matchResult.day[Math.min(matchLength,5)-1](me); } else if (firstChar==="y"||firstChar==="Y"){ return matchResult.year[(matchLength>2)+0](me); } else if (firstChar==="m"&&matchLength<3){ return matchResult.time[matchLength-1](me,"getMinutes"); } else if (firstChar==="s"||firstChar==="S"){ return matchResult.time[Math.min(matchLength,2)-1](me,"getSeconds"); } else if (firstChar==="h"||firstChar==="H"){ return matchResult.time[Math.min(matchLength,2)-1](me,"getHours"); } else if (firstChar==="."||firstChar===","){ return matchResult.millisecond[0](me,matchLength-1); } } //var reDateString=/(?:[^\\/dD]|^)[dD]+|(?:[^\\/Mm]|^)M[Mm]?|[Mm]{3,}|(?:[^\\/yY]|^)[yY]+|(?:[^\\/hH]|^)[hH]+|(?:[^\\/m]|^)m{1,2}|(?:[^\\/sS]|^)[sS]+|(?:[^\\/,.]|^)[,.]0+/g; var reDateString=/[\s\S]([dD]+|M[Mm]?|[Mm]{3,}|[yY]+|[hH]+|m{1,2}|[sS]+|[,.]0+)/g; return ("1"+formatStr).replace(reDateString,function(m){ var firstChar=m[0],match=m.slice(1); if(firstChar==="^"){ return match; } else{ return firstChar+getFormattedString(match); } }).slice(1) } XaDate.prototype.until=function(otherDate){ if(!_validDate(otherDate)){ //try to create other date object otherDate=new XaDate([].slice.call(arguments)); } if(!otherDate.isValid()){ throw new TypeError('until() needs a date or parseable dateargumenrs'); } return duration(otherDate.valueOf()-this.valueOf()); } XaDate.prototype.add=function(dur/*,firstBig*/){ //console.log(dur); var args=[].slice.call(arguments); var firstBig=args.pop(); if(typeof firstBig!=="boolean"){ args.push(firstBig) firstBig=true; //this makes a difference in subtracting durations } //console.log(dur); if(dur.constructor.name!=="XaDuration"){ dur=duration.apply(null,args) } dur.normalizeDown(); if (firstBig){ this._addBig(dur) dur.normalizeMonth(this.daysInMonth()); } //console.log(dur) this._addSmall(dur); if(!firstBig){ dur.normalizeMonth(this.daysInMonth()); this._addSmall(dur); this._addBig(dur) } return this; } XaDate.prototype.addMonths=function(m){ //faster implementation than datejs var day,month; if(typeof m!=="number"){ return this; } day=this.getDate() this.setMonth(this.getMonth() + m, 1); month=this.getMonth(); this.setDate(day); if (this.getMonth() !== month){ this.setDate(0) //go back to last day of previous month; } return this; } XaDate.prototype.addYears=function(y){ //faster implementation than datejs var month; if(typeof y!=="number"){ return this; } month=this.getMonth(); //day=this.getDate() this.setFullYear(this.getFullYear() + y,month); if (this.getMonth() !== month){ this.setDate(0) //go back to last day of previous month; } return this; } XaDate.prototype.addDays=function(d){ if(typeof d!=="number"){ return this; } this.setDate(this.getDate()+d) return this; } XaDate.prototype.addHours=function(h){ if(typeof h!=="number"){ return this; } this.setHours(this.getHours()+h) return this; } XaDate.prototype.addMinutes=function(m){ if(typeof m!=="number"){ return this; } this.setMinutes(this.getMinutes()+m) return this; } XaDate.prototype.addSeconds=function(s){ if(typeof s!=="number"){ return this; } this.setSeconds(this.getSeconds()+s) return this; } XaDate.prototype.addMilliseconds=function(m){ if(typeof m!=="number"){ return this; } this.setMilliseconds(this.getMilliseconds()+m) return this; } export default date;