PNG  IHDRX cHRMz&u0`:pQ<bKGD pHYsodtIME MeqIDATxw]Wug^Qd˶ 6`!N:!@xI~)%7%@Bh&`lnjVF29gΨ4E$|>cɚ{gk= %,a KX%,a KX%,a KX%,a KX%,a KX%,a KX%, b` ǟzeאfp]<!SJmɤY޲ڿ,%c ~ع9VH.!Ͳz&QynֺTkRR.BLHi٪:l;@(!MԴ=žI,:o&N'Kù\vRmJ雵֫AWic H@" !: Cé||]k-Ha oݜ:y F())u]aG7*JV@J415p=sZH!=!DRʯvɱh~V\}v/GKY$n]"X"}t@ xS76^[bw4dsce)2dU0 CkMa-U5tvLƀ~mlMwfGE/-]7XAƟ`׮g ewxwC4\[~7@O-Q( a*XGƒ{ ՟}$_y3tĐƤatgvێi|K=uVyrŲlLӪuܿzwk$m87k( `múcE)"@rK( z4$D; 2kW=Xb$V[Ru819קR~qloѱDyįݎ*mxw]y5e4K@ЃI0A D@"BDk_)N\8͜9dz"fK0zɿvM /.:2O{ Nb=M=7>??Zuo32 DLD@D| &+֎C #B8ַ`bOb $D#ͮҪtx]%`ES`Ru[=¾!@Od37LJ0!OIR4m]GZRJu$‡c=%~s@6SKy?CeIh:[vR@Lh | (BhAMy=݃  G"'wzn޺~8ԽSh ~T*A:xR[ܹ?X[uKL_=fDȊ؂p0}7=D$Ekq!/t.*2ʼnDbŞ}DijYaȲ(""6HA;:LzxQ‘(SQQ}*PL*fc\s `/d'QXW, e`#kPGZuŞuO{{wm[&NBTiiI0bukcA9<4@SӊH*؎4U/'2U5.(9JuDfrޱtycU%j(:RUbArLֺN)udA':uGQN"-"Is.*+k@ `Ojs@yU/ H:l;@yyTn}_yw!VkRJ4P)~y#)r,D =ě"Q]ci'%HI4ZL0"MJy 8A{ aN<8D"1#IJi >XjX֔#@>-{vN!8tRݻ^)N_╗FJEk]CT՟ YP:_|H1@ CBk]yKYp|og?*dGvzنzӴzjֺNkC~AbZƷ`.H)=!QͷVTT(| u78y֮}|[8-Vjp%2JPk[}ԉaH8Wpqhwr:vWª<}l77_~{s۴V+RCģ%WRZ\AqHifɤL36: #F:p]Bq/z{0CU6ݳEv_^k7'>sq*+kH%a`0ԣisqにtү04gVgW΂iJiS'3w.w}l6MC2uԯ|>JF5`fV5m`Y**Db1FKNttu]4ccsQNnex/87+}xaUW9y>ͯ骵G{䩓Գ3+vU}~jJ.NFRD7<aJDB1#ҳgSb,+CS?/ VG J?|?,2#M9}B)MiE+G`-wo߫V`fio(}S^4e~V4bHOYb"b#E)dda:'?}׮4繏`{7Z"uny-?ǹ;0MKx{:_pÚmFמ:F " .LFQLG)Q8qN q¯¯3wOvxDb\. BKD9_NN &L:4D{mm o^tֽ:q!ƥ}K+<"m78N< ywsard5+вz~mnG)=}lYݧNj'QJS{S :UYS-952?&O-:W}(!6Mk4+>A>j+i|<<|;ر^߉=HE|V#F)Emm#}/"y GII웻Jі94+v뾧xu~5C95~ūH>c@덉pʃ1/4-A2G%7>m;–Y,cyyaln" ?ƻ!ʪ<{~h~i y.zZB̃/,雋SiC/JFMmBH&&FAbϓO^tubbb_hZ{_QZ-sύodFgO(6]TJA˯#`۶ɟ( %$&+V'~hiYy>922 Wp74Zkq+Ovn錄c>8~GqܲcWꂎz@"1A.}T)uiW4="jJ2W7mU/N0gcqܗOO}?9/wìXžΏ0 >֩(V^Rh32!Hj5`;O28؇2#ݕf3 ?sJd8NJ@7O0 b־?lldщ̡&|9C.8RTWwxWy46ah嘦mh٤&l zCy!PY?: CJyв]dm4ǜҐR޻RլhX{FƯanшQI@x' ao(kUUuxW_Ñ줮[w8 FRJ(8˼)_mQ _!RJhm=!cVmm ?sFOnll6Qk}alY}; "baӌ~M0w,Ggw2W:G/k2%R,_=u`WU R.9T"v,<\Ik޽/2110Ӿxc0gyC&Ny޽JҢrV6N ``یeA16"J³+Rj*;BϜkZPJaÍ<Jyw:NP8/D$ 011z֊Ⱳ3ι֘k1V_"h!JPIΣ'ɜ* aEAd:ݺ>y<}Lp&PlRfTb1]o .2EW\ͮ]38؋rTJsǏP@芎sF\> P^+dYJLbJ C-xϐn> ι$nj,;Ǖa FU *择|h ~izť3ᤓ`K'-f tL7JK+vf2)V'-sFuB4i+m+@My=O҈0"|Yxoj,3]:cо3 $#uŘ%Y"y죯LebqtҢVzq¼X)~>4L׶m~[1_k?kxֺQ`\ |ٛY4Ѯr!)N9{56(iNq}O()Em]=F&u?$HypWUeB\k]JɩSع9 Zqg4ZĊo oMcjZBU]B\TUd34ݝ~:7ڶSUsB0Z3srx 7`:5xcx !qZA!;%͚7&P H<WL!džOb5kF)xor^aujƍ7 Ǡ8/p^(L>ὴ-B,{ۇWzֺ^k]3\EE@7>lYBȝR.oHnXO/}sB|.i@ɥDB4tcm,@ӣgdtJ!lH$_vN166L__'Z)y&kH;:,Y7=J 9cG) V\hjiE;gya~%ks_nC~Er er)muuMg2;֫R)Md) ,¶ 2-wr#F7<-BBn~_(o=KO㭇[Xv eN_SMgSҐ BS헃D%g_N:/pe -wkG*9yYSZS.9cREL !k}<4_Xs#FmҶ:7R$i,fi!~' # !6/S6y@kZkZcX)%5V4P]VGYq%H1!;e1MV<!ϐHO021Dp= HMs~~a)ަu7G^];git!Frl]H/L$=AeUvZE4P\.,xi {-~p?2b#amXAHq)MWǾI_r`S Hz&|{ +ʖ_= (YS(_g0a03M`I&'9vl?MM+m~}*xT۲(fY*V4x@29s{DaY"toGNTO+xCAO~4Ϳ;p`Ѫ:>Ҵ7K 3}+0 387x\)a"/E>qpWB=1 ¨"MP(\xp߫́A3+J] n[ʼnӼaTbZUWb={~2ooKױӰp(CS\S筐R*JغV&&"FA}J>G֐p1ٸbk7 ŘH$JoN <8s^yk_[;gy-;߉DV{c B yce% aJhDȶ 2IdйIB/^n0tNtџdcKj4϶v~- CBcgqx9= PJ) dMsjpYB] GD4RDWX +h{y`,3ꊕ$`zj*N^TP4L:Iz9~6s) Ga:?y*J~?OrMwP\](21sZUD ?ܟQ5Q%ggW6QdO+\@ ̪X'GxN @'4=ˋ+*VwN ne_|(/BDfj5(Dq<*tNt1х!MV.C0 32b#?n0pzj#!38}޴o1KovCJ`8ŗ_"]] rDUy޲@ Ȗ-;xџ'^Y`zEd?0„ DAL18IS]VGq\4o !swV7ˣι%4FѮ~}6)OgS[~Q vcYbL!wG3 7띸*E Pql8=jT\꘿I(z<[6OrR8ºC~ډ]=rNl[g|v TMTղb-o}OrP^Q]<98S¤!k)G(Vkwyqyr޽Nv`N/e p/~NAOk \I:G6]4+K;j$R:Mi #*[AȚT,ʰ,;N{HZTGMoּy) ]%dHء9Պ䠬|<45,\=[bƟ8QXeB3- &dҩ^{>/86bXmZ]]yޚN[(WAHL$YAgDKp=5GHjU&99v簪C0vygln*P)9^͞}lMuiH!̍#DoRBn9l@ xA/_v=ȺT{7Yt2N"4!YN`ae >Q<XMydEB`VU}u]嫇.%e^ánE87Mu\t`cP=AD/G)sI"@MP;)]%fH9'FNsj1pVhY&9=0pfuJ&gޤx+k:!r˭wkl03׼Ku C &ѓYt{.O.zҏ z}/tf_wEp2gvX)GN#I ݭ߽v/ .& и(ZF{e"=V!{zW`, ]+LGz"(UJp|j( #V4, 8B 0 9OkRrlɱl94)'VH9=9W|>PS['G(*I1==C<5"Pg+x'K5EMd؞Af8lG ?D FtoB[je?{k3zQ vZ;%Ɠ,]E>KZ+T/ EJxOZ1i #T<@ I}q9/t'zi(EMqw`mYkU6;[t4DPeckeM;H}_g pMww}k6#H㶏+b8雡Sxp)&C $@'b,fPߑt$RbJ'vznuS ~8='72_`{q纶|Q)Xk}cPz9p7O:'|G~8wx(a 0QCko|0ASD>Ip=4Q, d|F8RcU"/KM opKle M3#i0c%<7׿p&pZq[TR"BpqauIp$ 8~Ĩ!8Սx\ւdT>>Z40ks7 z2IQ}ItԀ<-%S⍤};zIb$I 5K}Q͙D8UguWE$Jh )cu4N tZl+[]M4k8֦Zeq֮M7uIqG 1==tLtR,ƜSrHYt&QP윯Lg' I,3@P'}'R˪e/%-Auv·ñ\> vDJzlӾNv5:|K/Jb6KI9)Zh*ZAi`?S {aiVDԲuy5W7pWeQJk֤#5&V<̺@/GH?^τZL|IJNvI:'P=Ϛt"¨=cud S Q.Ki0 !cJy;LJR;G{BJy޺[^8fK6)=yʊ+(k|&xQ2`L?Ȓ2@Mf 0C`6-%pKpm')c$׻K5[J*U[/#hH!6acB JA _|uMvDyk y)6OPYjœ50VT K}cǻP[ $:]4MEA.y)|B)cf-A?(e|lɉ#P9V)[9t.EiQPDѠ3ϴ;E:+Օ t ȥ~|_N2,ZJLt4! %ա]u {+=p.GhNcŞQI?Nd'yeh n7zi1DB)1S | S#ًZs2|Ɛy$F SxeX{7Vl.Src3E℃Q>b6G ўYCmtկ~=K0f(=LrAS GN'ɹ9<\!a`)֕y[uՍ[09` 9 +57ts6}b4{oqd+J5fa/,97J#6yν99mRWxJyѡyu_TJc`~W>l^q#Ts#2"nD1%fS)FU w{ܯ R{ ˎ󅃏џDsZSQS;LV;7 Od1&1n$ N /.q3~eNɪ]E#oM~}v֯FڦwyZ=<<>Xo稯lfMFV6p02|*=tV!c~]fa5Y^Q_WN|Vs 0ҘދU97OI'N2'8N֭fgg-}V%y]U4 峧p*91#9U kCac_AFңĪy뚇Y_AiuYyTTYЗ-(!JFLt›17uTozc. S;7A&&<ԋ5y;Ro+:' *eYJkWR[@F %SHWP 72k4 qLd'J "zB6{AC0ƁA6U.'F3:Ȅ(9ΜL;D]m8ڥ9}dU "v!;*13Rg^fJyShyy5auA?ɩGHRjo^]׽S)Fm\toy 4WQS@mE#%5ʈfFYDX ~D5Ϡ9tE9So_aU4?Ѽm%&c{n>.KW1Tlb}:j uGi(JgcYj0qn+>) %\!4{LaJso d||u//P_y7iRJ߬nHOy) l+@$($VFIQ9%EeKʈU. ia&FY̒mZ=)+qqoQn >L!qCiDB;Y<%} OgBxB!ØuG)WG9y(Ą{_yesuZmZZey'Wg#C~1Cev@0D $a@˲(.._GimA:uyw֬%;@!JkQVM_Ow:P.s\)ot- ˹"`B,e CRtaEUP<0'}r3[>?G8xU~Nqu;Wm8\RIkբ^5@k+5(By'L&'gBJ3ݶ!/㮻w҅ yqPWUg<e"Qy*167΃sJ\oz]T*UQ<\FԎ`HaNmڜ6DysCask8wP8y9``GJ9lF\G g's Nn͵MLN֪u$| /|7=]O)6s !ĴAKh]q_ap $HH'\1jB^s\|- W1:=6lJBqjY^LsPk""`]w)󭃈,(HC ?䔨Y$Sʣ{4Z+0NvQkhol6C.婧/u]FwiVjZka&%6\F*Ny#8O,22+|Db~d ~Çwc N:FuuCe&oZ(l;@ee-+Wn`44AMK➝2BRՈt7g*1gph9N) *"TF*R(#'88pm=}X]u[i7bEc|\~EMn}P瘊J)K.0i1M6=7'_\kaZ(Th{K*GJyytw"IO-PWJk)..axӝ47"89Cc7ĐBiZx 7m!fy|ϿF9CbȩV 9V-՛^pV̌ɄS#Bv4-@]Vxt-Z, &ֺ*diؠ2^VXbs֔Ìl.jQ]Y[47gj=幽ex)A0ip׳ W2[ᎇhuE^~q흙L} #-b۸oFJ_QP3r6jr+"nfzRJTUqoaۍ /$d8Mx'ݓ= OՃ| )$2mcM*cЙj}f };n YG w0Ia!1Q.oYfr]DyISaP}"dIӗթO67jqR ҊƐƈaɤGG|h;t]䗖oSv|iZqX)oalv;۩meEJ\!8=$4QU4Xo&VEĊ YS^E#d,yX_> ۘ-e\ "Wa6uLĜZi`aD9.% w~mB(02G[6y.773a7 /=o7D)$Z 66 $bY^\CuP. (x'"J60׿Y:Oi;F{w佩b+\Yi`TDWa~|VH)8q/=9!g߆2Y)?ND)%?Ǐ`k/sn:;O299yB=a[Ng 3˲N}vLNy;*?x?~L&=xyӴ~}q{qE*IQ^^ͧvü{Huu=R|>JyUlZV, B~/YF!Y\u_ݼF{_C)LD]m {H 0ihhadd nUkf3oٺCvE\)QJi+֥@tDJkB$1!Đr0XQ|q?d2) Ӣ_}qv-< FŊ߫%roppVBwü~JidY4:}L6M7f٬F "?71<2#?Jyy4뷢<_a7_=Q E=S1И/9{+93֮E{ǂw{))?maÆm(uLE#lïZ  ~d];+]h j?!|$F}*"4(v'8s<ŏUkm7^7no1w2ؗ}TrͿEk>p'8OB7d7R(A 9.*Mi^ͳ; eeUwS+C)uO@ =Sy]` }l8^ZzRXj[^iUɺ$tj))<sbDJfg=Pk_{xaKo1:-uyG0M ԃ\0Lvuy'ȱc2Ji AdyVgVh!{]/&}}ċJ#%d !+87<;qN޼Nفl|1N:8ya  8}k¾+-$4FiZYÔXk*I&'@iI99)HSh4+2G:tGhS^繿 Kتm0 вDk}֚+QT4;sC}rՅE,8CX-e~>G&'9xpW,%Fh,Ry56Y–hW-(v_,? ; qrBk4-V7HQ;ˇ^Gv1JVV%,ik;D_W!))+BoS4QsTM;gt+ndS-~:11Sgv!0qRVh!"Ȋ(̦Yl.]PQWgٳE'`%W1{ndΗBk|Ž7ʒR~,lnoa&:ü$ 3<a[CBݮwt"o\ePJ=Hz"_c^Z.#ˆ*x z̝grY]tdkP*:97YľXyBkD4N.C_[;F9`8& !AMO c `@BA& Ost\-\NX+Xp < !bj3C&QL+*&kAQ=04}cC!9~820G'PC9xa!w&bo_1 Sw"ܱ V )Yl3+ס2KoXOx]"`^WOy :3GO0g;%Yv㐫(R/r (s } u B &FeYZh0y> =2<Ϟc/ -u= c&׭,.0"g"7 6T!vl#sc>{u/Oh Bᾈ)۴74]x7 gMӒ"d]U)}" v4co[ ɡs 5Gg=XR14?5A}D "b{0$L .\4y{_fe:kVS\\O]c^W52LSBDM! C3Dhr̦RtArx4&agaN3Cf<Ԉp4~ B'"1@.b_/xQ} _߃҉/gٓ2Qkqp0շpZ2fԫYz< 4L.Cyυι1t@鎫Fe sYfsF}^ V}N<_`p)alٶ "(XEAVZ<)2},:Ir*#m_YӼ R%a||EƼIJ,,+f"96r/}0jE/)s)cjW#w'Sʯ5<66lj$a~3Kʛy 2:cZ:Yh))+a߭K::N,Q F'qB]={.]h85C9cr=}*rk?vwV렵ٸW Rs%}rNAkDv|uFLBkWY YkX מ|)1!$#3%y?pF<@<Rr0}: }\J [5FRxY<9"SQdE(Q*Qʻ)q1E0B_O24[U'],lOb ]~WjHޏTQ5Syu wq)xnw8~)c 쫬gٲߠ H% k5dƝk> kEj,0% b"vi2Wس_CuK)K{n|>t{P1򨾜j>'kEkƗBg*H%'_aY6Bn!TL&ɌOb{c`'d^{t\i^[uɐ[}q0lM˕G:‚4kb祔c^:?bpg… +37stH:0}en6x˟%/<]BL&* 5&fK9Mq)/iyqtA%kUe[ڛKN]Ě^,"`/ s[EQQm?|XJ߅92m]G.E΃ח U*Cn.j_)Tѧj̿30ڇ!A0=͜ar I3$C^-9#|pk!)?7.x9 @OO;WƝZBFU keZ75F6Tc6"ZȚs2y/1 ʵ:u4xa`C>6Rb/Yм)^=+~uRd`/|_8xbB0?Ft||Z\##|K 0>>zxv8۴吅q 8ĥ)"6>~\8:qM}#͚'ĉ#p\׶ l#bA?)|g g9|8jP(cr,BwV (WliVxxᡁ@0Okn;ɥh$_ckCgriv}>=wGzβ KkBɛ[˪ !J)h&k2%07δt}!d<9;I&0wV/ v 0<H}L&8ob%Hi|޶o&h1L|u֦y~󛱢8fٲUsւ)0oiFx2}X[zVYr_;N(w]_4B@OanC?gĦx>мgx>ΛToZoOMp>40>V Oy V9iq!4 LN,ˢu{jsz]|"R޻&'ƚ{53ўFu(<٪9:΋]B;)B>1::8;~)Yt|0(pw2N%&X,URBK)3\zz&}ax4;ǟ(tLNg{N|Ǽ\G#C9g$^\}p?556]/RP.90 k,U8/u776s ʪ_01چ|\N 0VV*3H鴃J7iI!wG_^ypl}r*jɤSR 5QN@ iZ#1ٰy;_\3\BQQ x:WJv츟ٯ$"@6 S#qe딇(/P( Dy~TOϻ<4:-+F`0||;Xl-"uw$Цi󼕝mKʩorz"mϺ$F:~E'ҐvD\y?Rr8_He@ e~O,T.(ފR*cY^m|cVR[8 JҡSm!ΆԨb)RHG{?MpqrmN>߶Y)\p,d#xۆWY*,l6]v0h15M˙MS8+EdI='LBJIH7_9{Caз*Lq,dt >+~ّeʏ?xԕ4bBAŚjﵫ!'\Ը$WNvKO}ӽmSşذqsOy?\[,d@'73'j%kOe`1.g2"e =YIzS2|zŐƄa\U,dP;jhhhaxǶ?КZ՚.q SE+XrbOu%\GتX(H,N^~]JyEZQKceTQ]VGYqnah;y$cQahT&QPZ*iZ8UQQM.qo/T\7X"u?Mttl2Xq(IoW{R^ ux*SYJ! 4S.Jy~ BROS[V|žKNɛP(L6V^|cR7i7nZW1Fd@ Ara{詑|(T*dN]Ko?s=@ |_EvF]׍kR)eBJc" MUUbY6`~V޴dJKß&~'d3i WWWWWW
Current Directory: /usr/lib/node_modules/pm2/node_modules/croner/dist
Viewing File: /usr/lib/node_modules/pm2/node_modules/croner/dist/croner.cjs
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Cron = factory()); })(this, (function () { 'use strict'; /** * "Converts" a date to a specific time zone * * Note: This is only for specific and controlled usage, * as the internal UTC time of the resulting object will be off. * * Example: * let normalDate = new Date(); // d is a normal Date instance, with local timezone and correct utc representation * tzDate = convertTZ(d, 'America/New_York') // d is a tainted Date instance, where getHours() * (for example) will return local time in new york, but getUTCHours() * will return something irrelevant. * * @param {Date} date - Input date * @param {string} tzString - Timezone string in Europe/Stockholm format * @returns {Date} */ function convertTZ(date, tzString) { return new Date(date.toLocaleString("en-US", {timeZone: tzString})); } /** * Converts date to CronDate * @constructor * * @param {CronDate|date|string} [date] - Input date, if using string representation ISO 8001 (2015-11-24T19:40:00) local timezone is expected * @param {string} [timezone] - String representation of target timezone in Europe/Stockholm format. */ function CronDate (date, timezone) { this.timezone = timezone; if (date && date instanceof Date) { this.fromDate(date); } else if (date === void 0) { this.fromDate(new Date()); } else if (date && typeof date === "string") { this.fromString(date); } else if (date instanceof CronDate) { this.fromCronDate(date); } else { throw new TypeError("CronDate: Invalid type (" + typeof date + ") passed as parameter to CronDate constructor"); } } /** * Sets internals using a Date * @private * * @param {Date} date - Input date */ CronDate.prototype.fromDate = function (date) { if (this.timezone) { date = convertTZ(date, this.timezone); } this.milliseconds = date.getMilliseconds(); this.seconds = date.getSeconds(); this.minutes = date.getMinutes(); this.hours = date.getHours(); this.days = date.getDate(); this.months = date.getMonth(); this.years = date.getFullYear(); }; /** * Sets internals by deep copying another CronDate * @private * * @param {CronDate} date - Input date */ CronDate.prototype.fromCronDate = function (date) { this.timezone = date.timezone; this.milliseconds = date.milliseconds; this.seconds = date.seconds; this.minutes = date.minutes; this.hours = date.hours; this.days = date.days; this.months = date.months; this.years = date.years; }; /** * Reset internal parameters (seconds, minutes, hours) that may have exceeded their ranges * @private * * @param {Date} date - Input date */ CronDate.prototype.apply = function () { const newDate = new Date(this.years, this.months, this.days, this.hours, this.minutes, this.seconds, this.milliseconds); this.milliseconds = newDate.getMilliseconds(); this.seconds = newDate.getSeconds(); this.minutes = newDate.getMinutes(); this.hours = newDate.getHours(); this.days = newDate.getDate(); this.months = newDate.getMonth(); this.years = newDate.getFullYear(); }; /** * Sets internals by parsing a string * @private * * @param {Date} date - Input date */ CronDate.prototype.fromString = function (str) { const parsedDate = this.parseISOLocal(str); // Throw if we did get an invalid date if( isNaN(parsedDate) ) { throw new TypeError("CronDate: Provided string value for CronDate could not be parsed as date."); } this.fromDate(parsedDate); }; /** * Increment to next run time * @public * * @param {string} pattern - The pattern used to increment current state * @param {boolean} [rerun=false] - If this is an internal incremental run * @return {CronDate|null} - Returns itself for chaining, or null if increment wasnt possible */ CronDate.prototype.increment = function (pattern, rerun) { if (!rerun) { this.seconds += 1; } this.milliseconds = 0; const origTime = this.getTime(), /** * Find next * * @param {string} target * @param {string} pattern * @param {string} offset * @param {string} override * * @returns {boolean} * */ findNext = (target, pattern, offset, override) => { const startPos = (override === void 0) ? this[target] + offset : 0 + offset; for( let i = startPos; i < pattern[target].length; i++ ) { // If pattern matches and, in case of days, weekday matches, go on if( pattern[target][i] ) { // Special handling for L (last day of month), when we are searching for days if (target === "days" && pattern.lastDayOfMonth) { let baseDate = this.getDate(true); // Set days to one day after today, if month changes, then we are at the last day of the month baseDate.setDate(i-offset+1); if (baseDate.getMonth() !== this["months"]) { this[target] = i-offset; return true; } // Normal handling } else { this[target] = i-offset; return true; } } } return false; }, resetPrevious = (offset) => { // Now when we have gone to next minute, we have to set seconds to the first match // Now we are at 00:01:05 following the same example. // // This goes all the way back to seconds, hence the reverse loop. while(doing + offset >= 0) { // Ok, reset current member(e.g. seconds) to first match in pattern, using // the same method as aerlier // // Note the fourth parameter, stating that we should start matching the pattern // from zero, instead of current time. findNext(toDo[doing + offset][0], pattern, toDo[doing + offset][2], 0); // Go back up, days -> hours -> minutes -> seconds doing--; } }; // Array of work to be done, consisting of subarrays described below: // [ // First item is which member to process, // Second item is which member to increment if we didn't find a mathch in current item, // Third item is an offset. if months is handled 0-11 in js date object, and we get 1-12 // from pattern. Offset should be -1 // ] const toDo = [ ["seconds", "minutes", 0], ["minutes", "hours", 0], ["hours", "days", 0], ["days", "months", -1], ["months", "years", 0] ]; // Ok, we're working our way trough the toDo array, top to bottom // If we reach 5, work is done let doing = 0; while(doing < 5) { // findNext sets the current member to next match in pattern // If time is 00:00:01 and pattern says *:*:05, seconds will // be set to 5 // Store current value at current level let currentValue = this[toDo[doing][0]]; // If pattern didn't provide a match, increment next value (e.g. minues) if(!findNext(toDo[doing][0], pattern, toDo[doing][2])) { this[toDo[doing][1]]++; // Reset current level and previous levels resetPrevious(0); // If pattern provided a match, but changed current value ... } else if (currentValue !== this[toDo[doing][0]]) { // Reset previous levels resetPrevious(-1); } // Bail out if an impossible pattern is used if (this.years >= 4000) { return null; } // Gp down, seconds -> minutes -> hours -> days -> months -> year doing++; } // This is a special case for weekday, as the user isn't able to combine date/month patterns // with weekday patterns, it's just to increment days until we get a match. while (!pattern.daysOfWeek[this.getDate(true).getDay()]) { this.days += 1; // Reset everything before days doing = 2; resetPrevious(); } // If anything changed, recreate this CronDate and run again without incrementing if (origTime != this.getTime()) { this.apply(); return this.increment(pattern, true); } else { return this; } }; /** * Convert current state back to a javascript Date() * @public * * @param {boolean} internal - If this is an internal call * @returns {Date} */ CronDate.prototype.getDate = function (internal) { const targetDate = new Date(this.years, this.months, this.days, this.hours, this.minutes, this.seconds, this.milliseconds); if (internal || !this.timezone) { return targetDate; } else { const offset = convertTZ(targetDate, this.timezone).getTime()-targetDate.getTime(); return new Date(targetDate.getTime()-offset); } }; /** * Convert current state back to a javascript Date() and return UTC milliseconds * @public * * @param {boolean} internal - If this is an internal call * @returns {Date} */ CronDate.prototype.getTime = function (internal) { return this.getDate(internal).getTime(); }; /** * Takes a iso 8001 local date time string and creates a Date object * @private * * @param {string} dateTimeString - an ISO 8001 format date and time string * with all components, e.g. 2015-11-24T19:40:00 * @returns {Date|number} - Date instance from parsing the string. May be NaN. */ CronDate.prototype.parseISOLocal = function (dateTimeString) { const dateTimeStringSplit = dateTimeString.split(/\D/); // Check for completeness if (dateTimeStringSplit.length < 6) { return NaN; } const year = parseInt(dateTimeStringSplit[0], 10), month = parseInt(dateTimeStringSplit[1], 10), day = parseInt(dateTimeStringSplit[2], 10), hour = parseInt(dateTimeStringSplit[3], 10), minute = parseInt(dateTimeStringSplit[4], 10), second = parseInt(dateTimeStringSplit[5], 10); // Check parts for numeric if( isNaN(year) || isNaN(month) || isNaN(day) || isNaN(hour) || isNaN(minute) || isNaN(second) ) { return NaN; } else { let generatedDate; // Check for UTC flag if ((dateTimeString.indexOf("Z") > 0)) { // Handle date as UTC generatedDate = new Date(Date.UTC(year, month-1, day, hour, minute, second)); // Check generated date if (year == generatedDate.getUTCFullYear() && month == generatedDate.getUTCMonth()+1 && day == generatedDate.getUTCDate() && hour == generatedDate.getUTCHours() && minute == generatedDate.getUTCMinutes() && second == generatedDate.getUTCSeconds()) { return generatedDate; } else { return NaN; } } else { // Handle date as local time generatedDate = new Date(year, month-1, day, hour, minute, second); // Check generated date if (year == generatedDate.getFullYear() && month == generatedDate.getMonth()+1 && day == generatedDate.getDate() && hour == generatedDate.getHours() && minute == generatedDate.getMinutes() && second == generatedDate.getSeconds()) { return generatedDate; } else { return NaN; } } } }; /** * Name for each part of the cron pattern * @typedef {("seconds" | "minutes" | "hours" | "days" | "months" | "daysOfWeek")} CronPatternPart */ /** * Offset, 0 or -1. * * 0 for seconds,minutes and hours as they start on 1. * -1 on days and months, as the start on 0 * * @typedef {Number} CronIndexOffset */ /** * Create a CronPattern instance from pattern string ('* * * * * *') * @constructor * @param {string} pattern - Input pattern * @param {string} timezone - Input timezone, used for '?'-substitution */ function CronPattern (pattern, timezone) { this.pattern = pattern; this.timezone = timezone; this.seconds = Array(60).fill(0); // 0-59 this.minutes = Array(60).fill(0); // 0-59 this.hours = Array(24).fill(0); // 0-23 this.days = Array(31).fill(0); // 0-30 in array, 1-31 in config this.months = Array(12).fill(0); // 0-11 in array, 1-12 in config this.daysOfWeek = Array(8).fill(0); // 0-7 Where 0 = Sunday and 7=Sunday; this.lastDayOfMonth = false; this.parse(); } /** * Parse current pattern, will throw on any type of failure * @private */ CronPattern.prototype.parse = function () { // Sanity check if( !(typeof this.pattern === "string" || this.pattern.constructor === String) ) { throw new TypeError("CronPattern: Pattern has to be of type string."); } // Split configuration on whitespace const parts = this.pattern.trim().replace(/\s+/g, " ").split(" "); // Validite number of configuration entries if( parts.length < 5 || parts.length > 6 ) { throw new TypeError("CronPattern: invalid configuration format ('" + this.pattern + "'), exacly five or six space separated parts required."); } // If seconds is omitted, insert 0 for seconds if( parts.length === 5) { parts.unshift("0"); } // Convert 'L' to '*' and add lastDayOfMonth flag, // and set days to 28,29,30,31 as those are the only days that can be the last day of month if(parts[3].toUpperCase() == "L") { parts[3] = "28,29,30,31"; this.lastDayOfMonth = true; } // Replace alpha representations parts[4] = this.replaceAlphaMonths(parts[4]); parts[5] = this.replaceAlphaDays(parts[5]); // Implement '?' in the simplest possible way - replace ? with current value, before further processing let initDate = new CronDate(new Date(),this.timezone).getDate(true); parts[0] = parts[0].replace("?", initDate.getSeconds()); parts[1] = parts[1].replace("?", initDate.getMinutes()); parts[2] = parts[2].replace("?", initDate.getHours()); parts[3] = parts[3].replace("?", initDate.getDate()); parts[4] = parts[4].replace("?", initDate.getMonth()+1); // getMonth is zero indexed while pattern starts from 1 parts[5] = parts[5].replace("?", initDate.getDay()); // Check part content this.throwAtIllegalCharacters(parts); // Parse parts into arrays, validates as we go this.partToArray("seconds", parts[0], 0); this.partToArray("minutes", parts[1], 0); this.partToArray("hours", parts[2], 0); this.partToArray("days", parts[3], -1); this.partToArray("months", parts[4], -1); this.partToArray("daysOfWeek", parts[5], 0); // 0 = Sunday, 7 = Sunday if( this.daysOfWeek[7] ) { this.daysOfWeek[0] = 1; } }; /** * Convert current part (seconds/minutes etc) to an array of 1 or 0 depending on if the part is about to trigger a run or not. * @private * * @param {CronPatternPart} type - Seconds/minutes etc * @param {string} conf - Current pattern part - *, 0-1 etc * @param {CronIndexOffset} valueIndexOffset * @param {boolean} [recursed] - Is this a recursed call */ CronPattern.prototype.partToArray = function (type, conf, valueIndexOffset, recursed) { const arr = this[type]; // First off, handle wildcard if( conf === "*" ) { for( let i = 0; i < arr.length; i++ ) { arr[i] = 1; } return; } // Handle separated entries (,) by recursion const split = conf.split(","); if( split.length > 1 ) { for( let i = 0; i < split.length; i++ ) { this.partToArray(type, split[i], valueIndexOffset, true); } // Handle range with stepping (x-y/z) } else if( conf.indexOf("-") !== -1 && conf.indexOf("/") !== -1 ) { if (recursed) throw new Error("CronPattern: Range with stepping cannot coexist with ,"); this.handleRangeWithStepping(conf, type, valueIndexOffset); // Handle range } else if( conf.indexOf("-") !== -1 ) { if (recursed) throw new Error("CronPattern: Range with stepping cannot coexist with ,"); this.handleRange(conf, type, valueIndexOffset); // Handle stepping } else if( conf.indexOf("/") !== -1 ) { if (recursed) throw new Error("CronPattern: Range with stepping cannot coexist with ,"); this.handleStepping(conf, type, valueIndexOffset); } else { this.handleNumber(conf, type, valueIndexOffset); } }; /** * After converting JAN-DEC, SUN-SAT only 0-9 * , / - are allowed, throw if anything else pops up * @private * * @param {string[]} parts - Each part split as strings */ CronPattern.prototype.throwAtIllegalCharacters = function (parts) { const reValidCron = /[^/*0-9,-]+/; for(let i = 0; i < parts.length; i++) { if( reValidCron.test(parts[i]) ) { throw new TypeError("CronPattern: configuration entry " + i + " (" + parts[i] + ") contains illegal characters."); } } }; /** * Nothing but a number left, handle that * @private * * @param {string} conf - Current part, expected to be a number, as a string * @param {string} type - One of "seconds", "minutes" etc * @param {number} valueIndexOffset - -1 for day of month, and month, as they start at 1. 0 for seconds, hours, minutes */ CronPattern.prototype.handleNumber = function (conf, type, valueIndexOffset) { const i = (parseInt(conf, 10) + valueIndexOffset); if( i < 0 || i >= this[type].length ) { throw new TypeError("CronPattern: " + type + " value out of range: '" + conf + "'"); } this[type][i] = 1; }; /** * Take care of ranges with stepping (e.g. 3-23/5) * @private * * @param {string} conf - Current part, expected to be a string like 3-23/5 * @param {string} type - One of "seconds", "minutes" etc * @param {number} valueIndexOffset - -1 for day of month, and month, as they start at 1. 0 for seconds, hours, minutes */ CronPattern.prototype.handleRangeWithStepping = function (conf, type, valueIndexOffset) { const matches = conf.match(/^(\d+)-(\d+)\/(\d+)$/); if( matches === null ) throw new TypeError("CronPattern: Syntax error, illegal range with stepping: '" + conf + "'"); let [, lower, upper, steps] = matches; lower = parseInt(lower, 10) + valueIndexOffset; upper = parseInt(upper, 10) + valueIndexOffset; steps = parseInt(steps, 10); if( isNaN(lower) ) throw new TypeError("CronPattern: Syntax error, illegal lower range (NaN)"); if( isNaN(upper) ) throw new TypeError("CronPattern: Syntax error, illegal upper range (NaN)"); if( isNaN(steps) ) throw new TypeError("CronPattern: Syntax error, illegal stepping: (NaN)"); if( steps === 0 ) throw new TypeError("CronPattern: Syntax error, illegal stepping: 0"); if( steps > this[type].length ) throw new TypeError("CronPattern: Syntax error, steps cannot be greater than maximum value of part ("+this[type].length+")"); if( lower < 0 || upper >= this[type].length ) throw new TypeError("CronPattern: Value out of range: '" + conf + "'"); if( lower > upper ) throw new TypeError("CronPattern: From value is larger than to value: '" + conf + "'"); for (let i = lower; i <= upper; i += steps) { this[type][i] = 1; } }; /** * Take care of ranges (e.g. 1-20) * @private * * @param {string} conf - Current part, expected to be a string like 1-20 * @param {string} type - One of "seconds", "minutes" etc * @param {number} valueIndexOffset - -1 for day of month, and month, as they start at 1. 0 for seconds, hours, minutes */ CronPattern.prototype.handleRange = function (conf, type, valueIndexOffset) { const split = conf.split("-"); if( split.length !== 2 ) { throw new TypeError("CronPattern: Syntax error, illegal range: '" + conf + "'"); } const lower = parseInt(split[0], 10) + valueIndexOffset, upper = parseInt(split[1], 10) + valueIndexOffset; if( isNaN(lower) ) { throw new TypeError("CronPattern: Syntax error, illegal lower range (NaN)"); } else if( isNaN(upper) ) { throw new TypeError("CronPattern: Syntax error, illegal upper range (NaN)"); } // Check that value is within range if( lower < 0 || upper >= this[type].length ) { throw new TypeError("CronPattern: Value out of range: '" + conf + "'"); } // if( lower > upper ) { throw new TypeError("CronPattern: From value is larger than to value: '" + conf + "'"); } for( let i = lower; i <= upper; i++ ) { this[type][i] = 1; } }; /** * Handle stepping (e.g. * / 14) * @private * * @param {string} conf - Current part, expected to be a string like * /20 (without the space) * @param {string} type - One of "seconds", "minutes" etc */ CronPattern.prototype.handleStepping = function (conf, type) { const split = conf.split("/"); if( split.length !== 2 ) { throw new TypeError("CronPattern: Syntax error, illegal stepping: '" + conf + "'"); } let start = 0; if( split[0] !== "*" ) { start = parseInt(split[0], 10); } const steps = parseInt(split[1], 10); if( isNaN(steps) ) throw new TypeError("CronPattern: Syntax error, illegal stepping: (NaN)"); if( steps === 0 ) throw new TypeError("CronPattern: Syntax error, illegal stepping: 0"); if( steps > this[type].length ) throw new TypeError("CronPattern: Syntax error, steps cannot be greater than maximum value of part ("+this[type].length+")"); for( let i = start; i < this[type].length; i+= steps ) { this[type][i] = 1; } }; /** * Replace day name with day numbers * @private * * @param {string} conf - Current part, expected to be a string that might contain sun,mon etc. * * @returns {string} - conf with 0 instead of sun etc. */ CronPattern.prototype.replaceAlphaDays = function (conf) { return conf .replace(/sun/gi, "0") .replace(/mon/gi, "1") .replace(/tue/gi, "2") .replace(/wed/gi, "3") .replace(/thu/gi, "4") .replace(/fri/gi, "5") .replace(/sat/gi, "6"); }; /** * Replace month name with month numbers * @private * * @param {string} conf - Current part, expected to be a string that might contain jan,feb etc. * * @returns {string} - conf with 0 instead of sun etc. */ CronPattern.prototype.replaceAlphaMonths = function (conf) { return conf .replace(/jan/gi, "1") .replace(/feb/gi, "2") .replace(/mar/gi, "3") .replace(/apr/gi, "4") .replace(/may/gi, "5") .replace(/jun/gi, "6") .replace(/jul/gi, "7") .replace(/aug/gi, "8") .replace(/sep/gi, "9") .replace(/oct/gi, "10") .replace(/nov/gi, "11") .replace(/dec/gi, "12"); }; /* ------------------------------------------------------------------------------------ Croner - MIT License - Hexagon <github.com/Hexagon> Pure JavaScript Isomorphic cron parser and scheduler without dependencies. ------------------------------------------------------------------------------------ License: Copyright (c) 2015-2021 Hexagon <github.com/Hexagon> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------------ */ /** * @typedef {Object} CronOptions - Cron scheduler options * @property {boolean} [paused] - Job is paused * @property {boolean} [kill] - Job is about to be killed or killed * @property {boolean} [catch] - Continue exection even if a unhandled error is thrown by triggered function * @property {number} [maxRuns] - Maximum nuber of executions * @property {string | Date} [startAt] - When to start running * @property {string | Date} [stopAt] - When to stop running * @property {string} [timezone] - Time zone in Europe/Stockholm format * @property {?} [context] - Used to pass any object to scheduled function */ /** * Many JS engines stores the delay as a 32-bit signed integer internally. * This causes an integer overflow when using delays larger than 2147483647, * resulting in the timeout being executed immediately. * * All JS engines implements an immediate execution of delays larger that a 32-bit * int to keep the behaviour concistent. * * @type {number} */ const maxDelay = Math.pow(2, 32 - 1) - 1; /** * Cron entrypoint * * @constructor * @param {string|Date} pattern - Input pattern, input date, or input ISO 8601 time string * @param {CronOptions|Function} [options] - Options * @param {Function} [func] - Function to be run each iteration of pattern * @returns {Cron} */ function Cron (pattern, options, func) { // Optional "new" keyword if( !(this instanceof Cron) ) { return new Cron(pattern, options, func); } // Make options optional if( typeof options === "function" ) { func = options; options = void 0; } /** @type {CronOptions} */ this.options = this.processOptions(options); // Check if we got a date, or a pattern supplied as first argument if (pattern && (pattern instanceof Date)) { this.once = new CronDate(pattern, this.options.timezone); } else if (pattern && (typeof pattern === "string") && pattern.indexOf(":") > 0) { /** @type {CronDate} */ this.once = new CronDate(pattern, this.options.timezone); } else { /** @type {CronPattern} */ this.pattern = new CronPattern(pattern, this.options.timezone); } /** * Allow shorthand scheduling */ if( func !== void 0 ) { this.fn = func; this.schedule(); } return this; } /** * Internal function that validates options, and sets defaults * @private * * @param {CronOptions} options * @returns {CronOptions} */ Cron.prototype.processOptions = function (options) { // If no options are passed, create empty object if (options === void 0) { options = {}; } // Keep options, or set defaults options.paused = (options.paused === void 0) ? false : options.paused; options.maxRuns = (options.maxRuns === void 0) ? Infinity : options.maxRuns; options.catch = (options.catch === void 0) ? false : options.catch; options.kill = false; // startAt is set, validate it if( options.startAt ) { options.startAt = new CronDate(options.startAt, options.timezone); } if( options.stopAt ) { options.stopAt = new CronDate(options.stopAt, options.timezone); } return options; }; /** * Find next runtime, based on supplied date. Strips milliseconds. * * @param {Date|string} [prev] - Date to start from * @returns {Date | null} - Next run time */ Cron.prototype.next = function (prev) { prev = new CronDate(prev, this.options.timezone); const next = this._next(prev); return next ? next.getDate() : null; }; /** * Find next n runs, based on supplied date. Strips milliseconds. * * @param {number} n - Number of runs to enumerate * @param {Date|string} [previous] - Date to start from * @returns {Date[]} - Next n run times */ Cron.prototype.enumerate = function (n, previous) { let enumeration = []; while(n-- && (previous = this.next(previous))) { enumeration.push(previous); } return enumeration; }; /** * Is running? * @public * * @returns {boolean} - Running or not */ Cron.prototype.running = function () { const msLeft = this.msToNext(this.previousrun); const running = !this.options.paused && this.fn !== void 0; return msLeft !== null && running; }; /** * Return previous run time * @public * * @returns {Date | null} - Previous run time */ Cron.prototype.previous = function () { return this.previousrun ? this.previousrun.getDate() : null; }; /** * Internal version of next. Cron needs millseconds internally, hence _next. * @private * * @param {CronDate} prev - Input pattern * @returns {CronDate | null} - Next run time */ Cron.prototype._next = function (prev) { // Previous run should never be before startAt if( this.options.startAt && prev && prev.getTime(true) < this.options.startAt.getTime(true) ) { prev = this.options.startAt; } // Calculate next run according to pattern or one-off timestamp const nextRun = this.once || new CronDate(prev, this.options.timezone).increment(this.pattern); if (this.once && this.once.getTime(true) <= prev.getTime(true)) { return null; } else if ((nextRun === null) || (this.options.maxRuns <= 0) || (this.options.kill) || (this.options.stopAt && nextRun.getTime(true) >= this.options.stopAt.getTime(true) )) { return null; } else { // All seem good, return next run return nextRun; } }; /** * Returns number of milliseconds to next run * @public * * @param {Date} [prev] - Starting date, defaults to now * @returns {number | null} */ Cron.prototype.msToNext = function (prev) { prev = new CronDate(prev, this.options.timezone); const next = this._next(prev); if( next ) { return (next.getTime(true) - prev.getTime(true)); } else { return null; } }; /** * Stop execution * @public */ Cron.prototype.stop = function () { this.options.kill = true; // Stop any awaiting call if( this.currentTimeout ) { clearTimeout( this.currentTimeout ); } }; /** * Pause executionR * @public * * @returns {boolean} - Wether pause was successful */ Cron.prototype.pause = function () { return (this.options.paused = true) && !this.options.kill; }; /** * Pause execution * @public * * @returns {boolean} - Wether resume was successful */ Cron.prototype.resume = function () { return !(this.options.paused = false) && !this.options.kill; }; /** * Schedule a new job * @public * * @param {Function} func - Function to be run each iteration of pattern * @returns {Cron} */ Cron.prototype.schedule = function (func) { // If a function is already scheduled, bail out if (func && this.fn) { throw new Error("Cron: It is not allowed to schedule two functions using the same Croner instance."); // Update function if passed } else if (func) { this.fn = func; } // Get ms to next run, bail out early if waitMs is null (no next run) let waitMs = this.msToNext(this.previousrun); if ( waitMs === null ) return this; // setTimeout cant handle more than Math.pow(2, 32 - 1) - 1 ms if( waitMs > maxDelay ) { waitMs = maxDelay; } // Ok, go! this.currentTimeout = setTimeout(() => { if( waitMs !== maxDelay && !this.options.paused ) { this.options.maxRuns--; // Always catch errors, but only re-throw if options.catch is not set if (this.options.catch) { try { this.fn(this, this.options.context); } catch (_e) { // Ignore } } else { this.fn(this, this.options.context); } this.previousrun = new CronDate(void 0, this.options.timezone); } // Recurse this.schedule(); }, waitMs ); return this; }; return Cron; }));