expression.js 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514
  1. var test = require('tap').test;
  2. var CronExpression = require('../lib/expression');
  3. var CronDate = require('../lib/date');
  4. test('empty expression test', function(t) {
  5. try {
  6. var interval = CronExpression.parse('');
  7. t.ok(interval, 'Interval parsed');
  8. var date = new CronDate();
  9. date.addMinute();
  10. var next = interval.next();
  11. t.ok(next, 'Found next scheduled interval');
  12. t.equal(next.getMinutes(), date.getMinutes(), 'Schedule matches');
  13. t.end();
  14. } catch (err) {
  15. t.error(err, 'Interval parse error');
  16. }
  17. });
  18. test('default expression test', function(t) {
  19. try {
  20. var interval = CronExpression.parse('* * * * *');
  21. t.ok(interval, 'Interval parsed');
  22. var date = new CronDate();
  23. date.addMinute();
  24. var next = interval.next();
  25. t.ok(next, 'Found next scheduled interval');
  26. t.equal(next.getMinutes(), date.getMinutes(), 'Schedule matches');
  27. } catch (err) {
  28. t.error(err, 'Interval parse error');
  29. }
  30. t.end();
  31. });
  32. test('default expression (tab separate) test', function(t) {
  33. try {
  34. var interval = CronExpression.parse('* * * * *');
  35. t.ok(interval, 'Interval parsed');
  36. var date = new CronDate();
  37. date.addMinute();
  38. var next = interval.next();
  39. t.ok(next, 'Found next scheduled interval');
  40. t.equal(next.getMinutes(), date.getMinutes(), 'Schedule matches');
  41. } catch (err) {
  42. t.error(err, 'Interval parse error');
  43. }
  44. t.end();
  45. });
  46. test('default expression (multi-space separated) test 1', function(t) {
  47. try {
  48. var interval = CronExpression.parse('* \t*\t\t *\t * \t\t*');
  49. t.ok(interval, 'Interval parsed');
  50. var date = new CronDate();
  51. date.addMinute();
  52. var next = interval.next();
  53. t.ok(next, 'Found next scheduled interval');
  54. t.equal(next.getMinutes(), date.getMinutes(), 'Schedule matches');
  55. } catch (err) {
  56. t.error(err, 'Interval parse error');
  57. }
  58. t.end();
  59. });
  60. test('default expression (multi-space separated) test 1', function(t) {
  61. try {
  62. var interval = CronExpression.parse('* \t *\t \t * * \t \t *');
  63. t.ok(interval, 'Interval parsed');
  64. var date = new CronDate();
  65. date.addMinute();
  66. var next = interval.next();
  67. t.ok(next, 'Found next scheduled interval');
  68. t.equal(next.getMinutes(), date.getMinutes(), 'Schedule matches');
  69. } catch (err) {
  70. t.error(err, 'Interval parse error');
  71. }
  72. t.end();
  73. });
  74. test('value out of the range', function(t) {
  75. t.throws(function() {
  76. CronExpression.parse('61 * * * * *');
  77. }, new Error('Constraint error, got value 61 expected range 0-59'));
  78. t.end();
  79. });
  80. test('second value out of the range', function(t) {
  81. t.throws(function() {
  82. CronExpression.parse('-1 * * * * *');
  83. }, new Error('Constraint error, got value -1 expected range 0-59'));
  84. t.end();
  85. });
  86. test('invalid range', function(t) {
  87. t.throws(function() {
  88. CronExpression.parse('- * * * * *');
  89. }, new Error('Invalid range: -'));
  90. t.end();
  91. });
  92. test('minute value out of the range', function(t) {
  93. t.throws(function() {
  94. CronExpression.parse('* 32,72 * * * *');
  95. }, new Error('Constraint error, got value 72 expected range 0-59'));
  96. t.end();
  97. });
  98. test('hour value out of the range', function(t) {
  99. t.throws(function() {
  100. CronExpression.parse('* * 12-36 * * *');
  101. }, new Error('Constraint error, got range 12-36 expected range 0-23'));
  102. t.end();
  103. });
  104. test('day of the month value out of the range', function(t) {
  105. t.throws(function() {
  106. CronExpression.parse('* * * 10-15,40 * *');
  107. }, ('Constraint error, got value 40 expected range 1-31'));
  108. t.end();
  109. });
  110. test('month value out of the range', function(t) {
  111. t.throws(function() {
  112. CronExpression.parse('* * * * */10,12-13 *');
  113. }, new Error('Constraint error, got range 12-13 expected range 1-12'));
  114. t.end();
  115. });
  116. test('day of the week value out of the range', function(t) {
  117. t.throws(function() {
  118. CronExpression.parse('* * * * * 9');
  119. }, new Error('Constraint error, got value 9 expected range 0-7'));
  120. t.end();
  121. });
  122. test('invalid expression that contains too many fields', function (t) {
  123. t.throws(function() {
  124. CronExpression.parse('* * * * * * * *ASD');
  125. }, new Error('Invalid cron expression'));
  126. t.end();
  127. });
  128. test('invalid explicit day of month definition', function(t) {
  129. t.throws(function() {
  130. const iter = CronExpression.parse('0 0 31 4 *');
  131. iter.next();
  132. }, new Error('Invalid explicit day of month definition'));
  133. t.end();
  134. });
  135. test('incremental minutes expression test', function(t) {
  136. try {
  137. var interval = CronExpression.parse('*/3 * * * *');
  138. t.ok(interval, 'Interval parsed');
  139. var next = interval.next();
  140. t.ok(next, 'Found next scheduled interval');
  141. t.equal(next.getMinutes() % 3, 0, 'Schedule matches');
  142. } catch (err) {
  143. t.error(err, 'Interval parse error');
  144. }
  145. t.end();
  146. });
  147. test('fixed expression test', function(t) {
  148. try {
  149. var interval = CronExpression.parse('10 2 12 8 0');
  150. t.ok(interval, 'Interval parsed');
  151. var next = interval.next();
  152. t.ok(next, 'Found next scheduled interval');
  153. t.ok(next.getDay() === 0 || next.getDate() === 12, 'Day or day of Month matches');
  154. t.equal(next.getMonth(), 7, 'Month matches');
  155. t.equal(next.getHours(), 2, 'Hour matches');
  156. t.equal(next.getMinutes(), 10, 'Minute matches');
  157. } catch (err) {
  158. t.error(err, 'Interval parse error');
  159. }
  160. t.end();
  161. });
  162. test('invalid characters test - symbol', function(t) {
  163. t.throws(function() {
  164. CronExpression.parse('10 ! 12 8 0');
  165. }, new Error('Invalid characters, got value: !'));
  166. t.end();
  167. });
  168. test('invalid characters test - letter', function(t) {
  169. t.throws(function() {
  170. CronExpression.parse('10 x 12 8 0');
  171. }, new Error('Invalid characters, got value: x'));
  172. t.end();
  173. });
  174. test('invalid characters test - parentheses', function(t) {
  175. t.throws(function() {
  176. CronExpression.parse('10 ) 12 8 0');
  177. }, new Error('Invalid characters, got value: )'));
  178. t.end();
  179. });
  180. test('interval with invalid characters test', function(t) {
  181. t.throws(function() {
  182. CronExpression.parse('10 */A 12 8 0');
  183. }, new Error('Invalid characters, got value: */A'));
  184. t.end();
  185. });
  186. test('range with invalid characters test', function(t) {
  187. t.throws(function() {
  188. CronExpression.parse('10 0-z 12 8 0');
  189. }, new Error('Invalid characters, got value: 0-z'));
  190. t.end();
  191. });
  192. test('group with invalid characters test', function(t) {
  193. t.throws(function() {
  194. CronExpression.parse('10 0,1,z 12 8 0');
  195. }, new Error('Invalid characters, got value: 0,1,z'));
  196. t.end();
  197. });
  198. test('invalid expression which has repeat 0 times', function(t) {
  199. t.throws(function() {
  200. CronExpression.parse('0 */0 * * *');
  201. }, new Error('Constraint error, cannot repeat at every 0 time.'));
  202. t.end();
  203. });
  204. test('invalid expression which has repeat negative number times', function(t) {
  205. t.throws(function() {
  206. CronExpression.parse('0 */-5 * * *');
  207. }, new Error('Constraint error, cannot repeat at every -5 time.'));
  208. t.end();
  209. });
  210. test('invalid expression which has multiple combined repeat cycles', function(t) {
  211. t.throws(function() {
  212. CronExpression.parse('0 5/5/5 * * *');
  213. }, new Error('Invalid repeat: 5/5/5'));
  214. t.end();
  215. });
  216. test('range test with value and repeat (second)', function(t) {
  217. var options = {
  218. currentDate: new CronDate('Wed, 26 Dec 2012 14:38:53')
  219. };
  220. var interval = CronExpression.parse('0/30 * * * * ?', options);
  221. t.ok(interval, 'Interval parsed');
  222. var next = interval.next();
  223. t.equal(next.getSeconds(), 0);
  224. next = interval.next();
  225. t.equal(next.getSeconds(), 30);
  226. next = interval.next();
  227. t.equal(next.getSeconds(), 0);
  228. t.end();
  229. });
  230. test('range test with value and repeat (minute)', function(t) {
  231. var options = {
  232. currentDate: new CronDate('Wed, 26 Dec 2012 14:38:53')
  233. };
  234. var interval = CronExpression.parse('6/23 * * * *', options);
  235. t.ok(interval, 'Interval parsed');
  236. var next = interval.next();
  237. t.equal(next.getMinutes(), 52);
  238. next = interval.next();
  239. t.equal(next.getMinutes(), 6);
  240. next = interval.next();
  241. t.equal(next.getMinutes(), 29);
  242. next = interval.next();
  243. t.equal(next.getMinutes(), 52);
  244. t.end();
  245. });
  246. test('range test with iterator', function(t) {
  247. try {
  248. var interval = CronExpression.parse('10-30 2 12 8 0');
  249. t.ok(interval, 'Interval parsed');
  250. var intervals = interval.iterate(20);
  251. t.ok(intervals, 'Found intervals');
  252. for (var i = 0, c = intervals.length; i < c; i++) {
  253. var next = intervals[i];
  254. t.ok(next, 'Found next scheduled interval');
  255. t.ok(next.getDay() === 0 || next.getDate() === 12, 'Day or day of month matches');
  256. t.equal(next.getMonth(), 7, 'Month matches');
  257. t.equal(next.getHours(), 2, 'Hour matches');
  258. t.equal(next.getMinutes(), 10 + i, 'Minute matches');
  259. }
  260. } catch (err) {
  261. t.error(err, 'Interval parse error');
  262. }
  263. t.end();
  264. });
  265. test('incremental range test with iterator', function(t) {
  266. try {
  267. var interval = CronExpression.parse('10-30/2 2 12 8 0');
  268. t.ok(interval, 'Interval parsed');
  269. var intervals = interval.iterate(10);
  270. t.ok(intervals, 'Found intervals');
  271. for (var i = 0, c = intervals.length; i < c; i++) {
  272. var next = intervals[i];
  273. t.ok(next, 'Found next scheduled interval');
  274. t.ok(next.getDay() === 0 || next.getDate() === 12, 'Day or day of month matches');
  275. t.equal(next.getMonth(), 7, 'Month matches');
  276. t.equal(next.getHours(), 2, 'Hour matches');
  277. t.equal(next.getMinutes(), 10 + (i * 2), 'Minute matches');
  278. }
  279. } catch (err) {
  280. t.error(err, 'Interval parse error');
  281. }
  282. t.end();
  283. });
  284. test('range with the same start and end value', function(t) {
  285. try {
  286. var interval = CronExpression.parse('*/10 2-2 * * *');
  287. t.ok(interval, 'Interval parsed');
  288. } catch (err) {
  289. t.error(err, 'Interval parse error');
  290. }
  291. t.end();
  292. });
  293. test('predefined expression', function(t) {
  294. try {
  295. var interval = CronExpression.parse('@yearly');
  296. t.ok(interval, 'Interval parsed');
  297. var date = new CronDate();
  298. date.addYear();
  299. var next = interval.next();
  300. t.ok(next, 'Found next scheduled interval');
  301. t.equal(next.getFullYear(), date.getFullYear(), 'Year matches');
  302. } catch (err) {
  303. t.error(err, 'Interval parse error');
  304. }
  305. t.end();
  306. });
  307. test('expression limited with start and end date', function(t) {
  308. try {
  309. var options = {
  310. currentDate: new CronDate('Wed, 26 Dec 2012 14:38:53'),
  311. startDate: new CronDate('Wed, 26 Dec 2012 12:40:00'),
  312. endDate: new CronDate('Wed, 26 Dec 2012 16:40:00')
  313. };
  314. var interval = CronExpression.parse('*/20 * * * *', options);
  315. t.ok(interval, 'Interval parsed');
  316. var dates = interval.iterate(10);
  317. t.equal(dates.length, 7, 'Dates count matches for positive iteration');
  318. interval.reset();
  319. var dates = interval.iterate(-10);
  320. t.equal(dates.length, 6, 'Dates count matches for negative iteration');
  321. interval.reset();
  322. // Forward iteration
  323. var next = interval.next();
  324. t.equal(next.getHours(), 14, 'Hour matches');
  325. t.equal(next.getMinutes(), 40, 'Minute matches');
  326. next = interval.next();
  327. t.equal(next.getHours(), 15, 'Hour matches');
  328. t.equal(next.getMinutes(), 0, 'Minute matches');
  329. next = interval.next();
  330. t.equal(next.getHours(), 15, 'Hour matches');
  331. t.equal(next.getMinutes(), 20, 'Minute matches');
  332. next = interval.next();
  333. t.equal(next.getHours(), 15, 'Hour matches');
  334. t.equal(next.getMinutes(), 40, 'Minute matches');
  335. next = interval.next();
  336. t.equal(next.getHours(), 16, 'Hour matches');
  337. t.equal(next.getMinutes(), 0, 'Minute matches');
  338. next = interval.next();
  339. t.equal(next.getHours(), 16, 'Hour matches');
  340. t.equal(next.getMinutes(), 20, 'Minute matches');
  341. next = interval.next();
  342. t.equal(next.getHours(), 16, 'Hour matches');
  343. t.equal(next.getMinutes(), 40, 'Minute matches');
  344. try {
  345. interval.next();
  346. t.ok(false, 'Should fail');
  347. } catch (e) {
  348. t.ok(true, 'Failed as expected');
  349. }
  350. next = interval.prev();
  351. t.equal(next.getHours(), 16, 'Hour matches');
  352. t.equal(next.getMinutes(), 20, 'Minute matches');
  353. interval.reset();
  354. // Backward iteration
  355. var prev = interval.prev();
  356. t.equal(prev.getHours(), 14, 'Hour matches');
  357. t.equal(prev.getMinutes(), 20, 'Minute matches');
  358. prev = interval.prev();
  359. t.equal(prev.getHours(), 14, 'Hour matches');
  360. t.equal(prev.getMinutes(), 0, 'Minute matches');
  361. prev = interval.prev();
  362. t.equal(prev.getHours(), 13, 'Hour matches');
  363. t.equal(prev.getMinutes(), 40, 'Minute matches');
  364. prev = interval.prev();
  365. t.equal(prev.getHours(), 13, 'Hour matches');
  366. t.equal(prev.getMinutes(), 20, 'Minute matches');
  367. prev = interval.prev();
  368. t.equal(prev.getHours(), 13, 'Hour matches');
  369. t.equal(prev.getMinutes(), 0, 'Minute matches');
  370. prev = interval.prev();
  371. t.equal(prev.getHours(), 12, 'Hour matches');
  372. t.equal(prev.getMinutes(), 40, 'Minute matches');
  373. try {
  374. interval.prev();
  375. t.ok(false, 'Should fail');
  376. } catch (e) {
  377. t.ok(true, 'Failed as expected');
  378. }
  379. } catch (err) {
  380. t.error(err, 'Interval parse error');
  381. }
  382. t.end();
  383. });
  384. test('reset to given date', function(t){
  385. try {
  386. var options = {
  387. currentDate: new CronDate('Wed, 26 Dec 2012 14:38:53')
  388. };
  389. var interval = CronExpression.parse('*/20 * * * *', options);
  390. t.ok(interval, 'Interval parsed');
  391. // Forward iteration
  392. var next = interval.next();
  393. t.equal(next.getHours(), 14, 'Hour matches');
  394. t.equal(next.getMinutes(), 40, 'Minute matches');
  395. interval.reset(); // defaults to initial currentDate
  396. next = interval.next();
  397. t.equal(next.getHours(), 14, 'Hour matches');
  398. t.equal(next.getMinutes(), 40, 'Minute matches');
  399. interval.reset(new CronDate('Wed, 26 Dec 2012 17:23:53'));
  400. next = interval.next();
  401. t.equal(next.getHours(), 17, 'Hour matches');
  402. t.equal(next.getMinutes(), 40, 'Minute matches');
  403. next = interval.next();
  404. t.equal(next.getHours(), 18, 'Hour matches');
  405. t.equal(next.getMinutes(), 0, 'Minute matches');
  406. interval.reset(new Date('2019-06-18T08:18:36.000'));
  407. next = interval.prev();
  408. t.equal(next.getDate(), 18, 'Date matches');
  409. t.equal(next.getHours(), 8, 'Hour matches');
  410. t.equal(next.getMinutes(), 0, 'Minute matches');
  411. next = interval.prev();
  412. t.equal(next.getDate(), 18, 'Date matches');
  413. t.equal(next.getHours(), 7, 'Hour matches');
  414. t.equal(next.getMinutes(), 40, 'Minute matches');
  415. t.end();
  416. } catch (err) {
  417. t.error(err, 'Reset error');
  418. }
  419. });
  420. test('parse expression as UTC', function(t) {
  421. try {
  422. var options = {
  423. utc: true
  424. };
  425. var interval = CronExpression.parse('0 0 10 * * *', options);
  426. var date = interval.next();
  427. t.equal(date.getUTCHours(), 10, 'Correct UTC hour value');
  428. t.equal(date.getHours(), 10, 'Correct UTC hour value');
  429. interval = CronExpression.parse('0 */5 * * * *', options);
  430. var date = interval.next(), now = new Date();
  431. now.setMinutes(now.getMinutes() + 5 - (now.getMinutes() % 5));
  432. t.equal(date.getHours(), now.getUTCHours(), 'Correct local time for 5 minute interval');
  433. } catch (err) {
  434. t.error(err, 'UTC parse error');
  435. }
  436. t.end();
  437. });
  438. test('expression using days of week strings', function(t) {
  439. try {
  440. var interval = CronExpression.parse('15 10 * * MON-TUE');
  441. t.ok(interval, 'Interval parsed');
  442. var intervals = interval.iterate(8);
  443. t.ok(intervals, 'Found intervals');
  444. for (var i = 0, c = intervals.length; i < c; i++) {
  445. var next = intervals[i];
  446. var day = next.getDay();
  447. t.ok(next, 'Found next scheduled interval');
  448. t.ok(day == 1 || day == 2, 'Day matches');
  449. t.equal(next.getHours(), 10, 'Hour matches');
  450. t.equal(next.getMinutes(), 15, 'Minute matches');
  451. }
  452. } catch (err) {
  453. t.error(err, 'Interval parse error');
  454. }
  455. t.end();
  456. });
  457. test('expression using days of week strings - wrong alias', function(t) {
  458. t.throws(function () {
  459. CronExpression.parse('15 10 * * MON-TUR');
  460. }, new Error('Validation error, cannot resolve alias "tur"'));
  461. t.end();
  462. });
  463. test('expression using mixed days of week strings', function(t) {
  464. try {
  465. var options = {
  466. currentDate: new CronDate('Wed, 26 Dec 2012 14:38:53')
  467. };
  468. var interval = CronExpression.parse('15 10 * jAn-FeB mOn-tUE', options);
  469. t.ok(interval, 'Interval parsed');
  470. var intervals = interval.iterate(8);
  471. t.ok(intervals, 'Found intervals');
  472. for (var i = 0, c = intervals.length; i < c; i++) {
  473. var next = intervals[i];
  474. var day = next.getDay();
  475. var month = next.getMonth();
  476. t.ok(next, 'Found next scheduled interval');
  477. t.ok(month == 0 || month == 2, 'Month Matches');
  478. t.ok(day == 1 || day == 2, 'Day matches');
  479. t.equal(next.getHours(), 10, 'Hour matches');
  480. t.equal(next.getMinutes(), 15, 'Minute matches');
  481. }
  482. } catch (err) {
  483. t.error(err, 'Interval parse error');
  484. }
  485. t.end();
  486. });
  487. test('expression using non-standard second field (wildcard)', function(t) {
  488. try {
  489. var options = {
  490. currentDate: new CronDate('Wed, 26 Dec 2012 14:38:00'),
  491. endDate: new CronDate('Wed, 26 Dec 2012 15:40:00')
  492. };
  493. var interval = CronExpression.parse('* * * * * *', options);
  494. t.ok(interval, 'Interval parsed');
  495. var intervals = interval.iterate(10);
  496. t.ok(intervals, 'Found intervals');
  497. for (var i = 0, c = intervals.length; i < c; i++) {
  498. var next = intervals[i];
  499. t.ok(next, 'Found next scheduled interval');
  500. t.equal(next.getSeconds(), i + 1, 'Second matches');
  501. }
  502. } catch (err) {
  503. t.error(err, 'Interval parse error');
  504. }
  505. t.end();
  506. });
  507. test('expression using non-standard second field (step)', function(t) {
  508. try {
  509. var options = {
  510. currentDate: new CronDate('Wed, 26 Dec 2012 14:38:00'),
  511. endDate: new CronDate('Wed, 26 Dec 2012 15:40:00')
  512. };
  513. var interval = CronExpression.parse('*/20 * * * * *', options);
  514. t.ok(interval, 'Interval parsed');
  515. var intervals = interval.iterate(3);
  516. t.ok(intervals, 'Found intervals');
  517. t.equal(intervals[0].getSeconds(), 20, 'Second matches');
  518. t.equal(intervals[1].getSeconds(), 40, 'Second matches');
  519. t.equal(intervals[2].getSeconds(), 0, 'Second matches');
  520. } catch (err) {
  521. t.error(err, 'Interval parse error');
  522. }
  523. t.end();
  524. });
  525. test('expression using non-standard second field (range)', function(t) {
  526. try {
  527. var options = {
  528. currentDate: new CronDate('Wed, 26 Dec 2012 14:38:00'),
  529. endDate: new CronDate('Wed, 26 Dec 2012 15:40:00')
  530. };
  531. var interval = CronExpression.parse('20-40/10 * * * * *', options);
  532. t.ok(interval, 'Interval parsed');
  533. var intervals = interval.iterate(3);
  534. t.ok(intervals, 'Found intervals');
  535. for (var i = 0, c = intervals.length; i < c; i++) {
  536. var next = intervals[i];
  537. t.ok(next, 'Found next scheduled interval');
  538. t.equal(next.getSeconds(), 20 + (i * 10), 'Second matches');
  539. }
  540. } catch (err) {
  541. t.error(err, 'Interval parse error');
  542. }
  543. t.end();
  544. });
  545. test('expression using explicit month definition and */5 day of month step', function(t) {
  546. var firstIterator = CronExpression.parse('0 12 */5 6 *', {
  547. currentDate: '2019-06-01T11:00:00.000'
  548. });
  549. var firstExpectedDates = [
  550. new CronDate('2019-06-01T12:00:00.000'),
  551. new CronDate('2019-06-06T12:00:00.000'),
  552. new CronDate('2019-06-11T12:00:00.000'),
  553. new CronDate('2019-06-16T12:00:00.000'),
  554. new CronDate('2019-06-21T12:00:00.000'),
  555. new CronDate('2019-06-26T12:00:00.000'),
  556. new CronDate('2020-06-01T12:00:00.000')
  557. ];
  558. firstExpectedDates.forEach(function(expectedDate) {
  559. t.equal(expectedDate.toISOString(), firstIterator.next().toISOString());
  560. });
  561. var secondIterator = CronExpression.parse('0 15 */5 5 *', {
  562. currentDate: '2019-05-01T11:00:00.000'
  563. });
  564. var secondExpectedDates = [
  565. new CronDate('2019-05-01T15:00:00.000'),
  566. new CronDate('2019-05-06T15:00:00.000'),
  567. new CronDate('2019-05-11T15:00:00.000'),
  568. new CronDate('2019-05-16T15:00:00.000'),
  569. new CronDate('2019-05-21T15:00:00.000'),
  570. new CronDate('2019-05-26T15:00:00.000'),
  571. new CronDate('2019-05-31T15:00:00.000'),
  572. new CronDate('2020-05-01T15:00:00.000')
  573. ];
  574. secondExpectedDates.forEach(function(expectedDate) {
  575. t.equal(expectedDate.toISOString(), secondIterator.next().toISOString());
  576. });
  577. t.end();
  578. });
  579. test('day of month and week are both set', function(t) {
  580. try {
  581. var interval = CronExpression.parse('10 2 12 8 0');
  582. t.ok(interval, 'Interval parsed');
  583. var next = interval.next();
  584. t.ok(next, 'Found next scheduled interval');
  585. t.ok(next.getDay() === 0 || next.getDate() === 12, 'Day or day of month matches');
  586. t.equal(next.getMonth(), 7, 'Month matches');
  587. next = interval.next();
  588. t.ok(next, 'Found next scheduled interval');
  589. t.ok(next.getDay() === 0 || next.getDate() === 12, 'Day or day of month matches');
  590. t.equal(next.getMonth(), 7, 'Month matches');
  591. next = interval.next();
  592. t.ok(next, 'Found next scheduled interval');
  593. t.ok(next.getDay() === 0 || next.getDate() === 12, 'Day or day of month matches');
  594. t.equal(next.getMonth(), 7, 'Month matches');
  595. next = interval.next();
  596. t.ok(next, 'Found next scheduled interval');
  597. t.ok(next.getDay() === 0 || next.getDate() === 12, 'Day or day of month matches');
  598. t.equal(next.getMonth(), 7, 'Month matches');
  599. } catch (err) {
  600. t.error(err, 'Interval parse error');
  601. }
  602. t.end();
  603. });
  604. test('day of month is unspecified', function(t) {
  605. try {
  606. var interval = CronExpression.parse('10 2 ? * 3');
  607. t.ok(interval, 'Interval parsed');
  608. var next = interval.next();
  609. t.ok(next, 'Found next scheduled interal');
  610. t.ok(next.getDay() === 3, 'day of week matches');
  611. next = interval.next();
  612. t.ok(next, 'Found next scheduled interal');
  613. t.ok(next.getDay() === 3, 'day of week matches');
  614. next = interval.next();
  615. t.ok(next, 'Found next scheduled interal');
  616. t.ok(next.getDay() === 3, 'day of week matches');
  617. next = interval.next();
  618. t.ok(next, 'Found next scheduled interal');
  619. t.ok(next.getDay() === 3, 'day of week matches');
  620. } catch (err) {
  621. t.error(err, 'Interval parse error');
  622. }
  623. t.end();
  624. });
  625. test('day of week is unspecified', function(t) {
  626. try {
  627. var interval = CronExpression.parse('10 2 3,6 * ?');
  628. t.ok(interval, 'Interval parsed');
  629. var next = interval.next();
  630. t.ok(next, 'Found next scheduled interal');
  631. t.ok(next.getDate() === 3 || next.getDate() === 6, 'date matches');
  632. var prevDate = next.getDate();
  633. next = interval.next();
  634. t.ok(next, 'Found next scheduled interal');
  635. t.ok((next.getDate() === 3 || next.getDate() === 6) &&
  636. next.getDate() !== prevDate, 'date matches and is not previous date');
  637. prevDate = next.getDate();
  638. next = interval.next();
  639. t.ok(next, 'Found next scheduled interal');
  640. t.ok((next.getDate() === 3 || next.getDate() === 6) &&
  641. next.getDate() !== prevDate, 'date matches and is not previous date');
  642. prevDate = next.getDate();
  643. next = interval.next();
  644. t.ok(next, 'Found next scheduled interal');
  645. t.ok((next.getDate() === 3 || next.getDate() === 6) &&
  646. next.getDate() !== prevDate, 'date matches and is not previous date');
  647. } catch (err) {
  648. t.error(err, 'Interval parse error');
  649. }
  650. t.end();
  651. });
  652. test('Summertime bug test', function(t) {
  653. try {
  654. var month = new CronDate().getMonth() + 1;
  655. var interval = CronExpression.parse('0 0 0 1 '+month+' *');
  656. t.ok(interval, 'Interval parsed');
  657. var next = interval.next();
  658. // Before fix the bug it was getting a timeout error if you are
  659. // in a timezone that changes the DST to ST in the hour 00:00h.
  660. t.ok(next, 'Found next scheduled interval');
  661. } catch (err) {
  662. t.error(err, 'Interval parse error');
  663. }
  664. t.end();
  665. });
  666. test('day of month and week are both set and dow is 7', function(t) {
  667. try {
  668. var interval = CronExpression.parse('10 2 12 8 7');
  669. t.ok(interval, 'Interval parsed');
  670. var next = interval.next();
  671. t.ok(next, 'Found next scheduled interval');
  672. t.ok(next.getDay() === 0 || next.getDate() === 12, 'Day or day of month matches');
  673. t.equal(next.getMonth(), 7, 'Month matches');
  674. next = interval.next();
  675. t.ok(next, 'Found next scheduled interval');
  676. t.ok(next.getDay() === 0 || next.getDate() === 12, 'Day or day of month matches');
  677. t.equal(next.getMonth(), 7, 'Month matches');
  678. next = interval.next();
  679. t.ok(next, 'Found next scheduled interval');
  680. t.ok(next.getDay() === 0 || next.getDate() === 12, 'Day or day of month matches');
  681. t.equal(next.getMonth(), 7, 'Month matches');
  682. next = interval.next();
  683. t.ok(next, 'Found next scheduled interval');
  684. t.ok(next.getDay() === 0 || next.getDate() === 12, 'Day or day of month matches');
  685. t.equal(next.getMonth(), 7, 'Month matches');
  686. } catch (err) {
  687. t.error(err, 'Interval parse error');
  688. }
  689. t.end();
  690. });
  691. test('day of month is wildcard, month and day of week are both set', function(t) {
  692. try {
  693. var options = {
  694. currentDate: new CronDate('Mon, 31 May 2021 12:00:00')
  695. };
  696. var interval = CronExpression.parse('0 0 * 6 2', options);
  697. t.ok(interval, 'Interval parsed');
  698. var expectedDayMatches = [1, 8, 15, 22, 29];
  699. expectedDayMatches.forEach(function(dayOfMonth) {
  700. var next = interval.next();
  701. t.ok(next, 'Found next scheduled interval');
  702. t.equal(next.getDay(), 2, 'Day of week matches');
  703. t.equal(next.getDate(), dayOfMonth, 'Day of month matches');
  704. t.equal(next.getMonth(), 5, 'Month matches');
  705. });
  706. } catch (err) {
  707. t.error(err, 'Interval parse error');
  708. }
  709. t.end();
  710. });
  711. test('day of month contains multiple ranges and day of week is wildcard', function(t) {
  712. try {
  713. var options = {
  714. currentDate: new CronDate('Sat, 1 Dec 2012 14:38:53')
  715. };
  716. var interval = CronExpression.parse('0 0 0 2-4,7-31 * *', options);
  717. t.ok(interval, 'Interval parsed');
  718. var next = interval.next();
  719. t.ok(next, 'Found next scheduled interval');
  720. t.ok(next.getDate() === 2, 'Day of month matches');
  721. t.equal(next.getMonth(), 11, 'Month matches');
  722. next = interval.next();
  723. t.ok(next, 'Found next scheduled interval');
  724. t.ok(next.getDate() === 3, 'Day of month matches');
  725. t.equal(next.getMonth(), 11, 'Month matches');
  726. next = interval.next();
  727. t.ok(next, 'Found next scheduled interval');
  728. t.ok(next.getDate() === 4, 'Day of month matches');
  729. t.equal(next.getMonth(), 11, 'Month matches');
  730. next = interval.next();
  731. t.ok(next, 'Found next scheduled interval');
  732. t.ok(next.getDate() === 7, 'Day of month matches');
  733. t.equal(next.getMonth(), 11, 'Month matches');
  734. next = interval.next();
  735. t.ok(next, 'Found next scheduled interval');
  736. t.ok(next.getDate() === 8, 'Day of month matches');
  737. t.equal(next.getMonth(), 11, 'Month matches');
  738. next = interval.next();
  739. } catch (err) {
  740. t.error(err, 'Interval parse error');
  741. }
  742. t.end();
  743. });
  744. test('day of month and week are both set and dow is 6,0', function(t) {
  745. try {
  746. var options = {
  747. currentDate: new CronDate('Wed, 26 Dec 2012 14:38:53')
  748. };
  749. var interval = CronExpression.parse('10 2 12 8 6,0', options);
  750. t.ok(interval, 'Interval parsed');
  751. var next = interval.next();
  752. t.ok(next, 'Found next scheduled interval');
  753. t.ok(next.getDay() === 6 || next.getDate() === 12, 'Day or day of month matches');
  754. t.equal(next.getMonth(), 7, 'Month matches');
  755. next = interval.next();
  756. t.ok(next, 'Found next scheduled interval');
  757. t.ok(next.getDay() === 0 || next.getDate() === 12, 'Day or day of month matches');
  758. t.equal(next.getMonth(), 7, 'Month matches');
  759. next = interval.next();
  760. t.ok(next, 'Found next scheduled interval');
  761. t.ok(next.getDay() === 6 || next.getDate() === 12, 'Day or day of month matches');
  762. t.equal(next.getMonth(), 7, 'Month matches');
  763. next = interval.next();
  764. t.ok(next, 'Found next scheduled interval');
  765. t.ok(next.getDay() === 0 || next.getDate() === 12, 'Day or day of month matches');
  766. t.equal(next.getMonth(), 7, 'Month matches');
  767. } catch (err) {
  768. t.error(err, 'Interval parse error');
  769. }
  770. t.end();
  771. });
  772. test('day of month and week are both set and dow is 6-7', function(t) {
  773. try {
  774. var options = {
  775. currentDate: new CronDate('Wed, 26 Dec 2012 14:38:53')
  776. };
  777. var interval = CronExpression.parse('10 2 12 8 6-7', options);
  778. t.ok(interval, 'Interval parsed');
  779. var next = interval.next();
  780. t.ok(next, 'Found next scheduled interval');
  781. t.ok(next.getDay() === 6, 'Day of week matches');
  782. t.equal(next.getMonth(), 7, 'Month matches');
  783. next = interval.next();
  784. t.ok(next, 'Found next scheduled interval');
  785. t.ok(next.getDay() === 0, 'Day of week matches');
  786. t.equal(next.getMonth(), 7, 'Month matches');
  787. next = interval.next();
  788. t.ok(next, 'Found next scheduled interval');
  789. t.ok(next.getDay() === 6, 'Day of week matches');
  790. t.equal(next.getMonth(), 7, 'Month matches');
  791. next = interval.next();
  792. t.ok(next, 'Found next scheduled interval');
  793. t.ok(next.getDay() === 0, 'Day of week matches');
  794. t.equal(next.getMonth(), 7, 'Month matches');
  795. next = interval.next();
  796. t.ok(next, 'Found next scheduled interval');
  797. t.ok(next.getDate() === 12, 'Day of month matches');
  798. t.ok(next.getDay() === 1, 'Day of week matches');
  799. t.equal(next.getMonth(), 7, 'Month matches');
  800. next = interval.next();
  801. t.ok(next, 'Found next scheduled interval');
  802. t.ok(next.getDay() === 6, 'Day of week matches');
  803. t.equal(next.getMonth(), 7, 'Month matches');
  804. } catch (err) {
  805. t.ifError(err, 'Interval parse error');
  806. }
  807. t.end();
  808. });
  809. test('day of month validation should be ignored when day of month is wildcard and month is set', function (t) {
  810. try {
  811. var options = {
  812. currentDate: new CronDate('2020-05-01T15:00:00.000')
  813. };
  814. var interval = CronExpression.parse('* * * * 2 *', options);
  815. t.ok(interval, 'Interval parsed');
  816. var next = interval.next();
  817. t.ok(next, 'Found next scheduled interval');
  818. t.equal(next.getHours(), 0, 'Hours matches');
  819. t.equal(next.getDate(), 1, 'Day of month matches');
  820. t.equal(next.getMonth() + 1, 2, 'Month matches');
  821. t.ok(next, 'Found next scheduled interval');
  822. } catch (err) {
  823. t.error(err, 'Interval parse error');
  824. }
  825. t.end();
  826. });
  827. test('day and date in week should matches', function(t){
  828. try {
  829. var interval = CronExpression.parse('0 1 1 1 * 1');
  830. t.ok(interval, 'Interval parsed');
  831. var next = interval.next();
  832. t.ok(next, 'Found next scheduled interval');
  833. t.equal(next.getHours(), 1, 'Hours matches');
  834. t.ok(next.getDay() === 1 || next.getDate() === 1, 'Day or day of month matches');
  835. next = interval.next();
  836. t.ok(next, 'Found next scheduled interval');
  837. t.equal(next.getHours(), 1, 'Hours matches');
  838. t.ok(next.getDay() === 1 || next.getDate() === 1, 'Day or day of month matches');
  839. next = interval.next();
  840. t.ok(next, 'Found next scheduled interval');
  841. t.equal(next.getHours(), 1, 'Hours matches');
  842. t.ok(next.getDay() === 1 || next.getDate() === 1, 'Day or day of month matches');
  843. } catch (err) {
  844. t.error(err, 'Interval parse error');
  845. }
  846. t.end();
  847. });
  848. test('should sort ranges and values in ascending order', function(t) {
  849. var options = {
  850. currentDate: new CronDate('Wed, 26 Dec 2012 14:38:53')
  851. };
  852. var interval = CronExpression.parse('0 12,13,10,1-3 * * *', options);
  853. t.ok(interval, 'Interval parsed');
  854. var hours = [ 1, 2, 3, 10, 12, 13 ];
  855. for (var i in hours) {
  856. next = interval.next();
  857. t.ok(next, 'Found next scheduled interval');
  858. t.equal(next.getHours(), hours[i], 'Hours matches');
  859. }
  860. t.end();
  861. });
  862. test('valid ES6 iterator should be returned if iterator options is set to true', function(t) {
  863. try {
  864. var options = {
  865. currentDate: new CronDate('Wed, 26 Dec 2012 14:38:53'),
  866. endDate: new CronDate('Wed, 26 Dec 2012 15:40:00'),
  867. iterator: true
  868. };
  869. var val = null;
  870. var interval = CronExpression.parse('*/25 * * * *', options);
  871. t.ok(interval, 'Interval parsed');
  872. val = interval.next();
  873. t.ok(val, 'Next iteration resolved');
  874. t.ok(val.value, 'Iterator value is set');
  875. t.notOk(val.done, 'Iterator is not finished');
  876. val = interval.next();
  877. t.ok(val, 'Next iteration resolved');
  878. t.ok(val.value, 'Iterator value is set');
  879. t.notOk(val.done, 'Iterator is not finished');
  880. val = interval.next();
  881. t.ok(val, 'Next iteration resolved');
  882. t.ok(val.value, 'Iterator value is set');
  883. t.ok(val.done, 'Iterator is finished');
  884. } catch (err) {
  885. t.error(err, 'Interval parse error');
  886. }
  887. t.end();
  888. });
  889. test('dow 6,7 6,0 0,6 7,6 should be equivalent', function(t) {
  890. try {
  891. var options = {
  892. currentDate: new CronDate('Wed, 26 Dec 2012 14:38:53'),
  893. };
  894. var expressions = [
  895. '30 16 * * 6,7',
  896. '30 16 * * 6,0',
  897. '30 16 * * 0,6',
  898. '30 16 * * 7,6'
  899. ];
  900. expressions.forEach(function(expression) {
  901. var interval = CronExpression.parse(expression, options);
  902. t.ok(interval, 'Interval parsed');
  903. var val = interval.next();
  904. t.equal(val.getDay(), 6, 'Day matches');
  905. val = interval.next();
  906. t.equal(val.getDay(), 0, 'Day matches');
  907. val = interval.next();
  908. t.equal(val.getDay(), 6, 'Day matches');
  909. });
  910. } catch (err) {
  911. t.error(err, 'Interval parse error');
  912. }
  913. t.end();
  914. });
  915. test('hour 0 9,11,1 * * * and 0 1,9,11 * * * should be equivalent', function(t) {
  916. try {
  917. var options = {
  918. currentDate: new CronDate('Wed, 26 Dec 2012 00:00:00'),
  919. };
  920. var expressions = [
  921. '0 9,11,1 * * *',
  922. '0 1,9,11 * * *'
  923. ];
  924. expressions.forEach(function(expression) {
  925. var interval = CronExpression.parse(expression, options);
  926. t.ok(interval, 'Interval parsed');
  927. var val = interval.next();
  928. t.equal(val.getHours(), 1, 'Hour matches');
  929. val = interval.next();
  930. t.equal(val.getHours(), 9, 'Hour matches');
  931. val = interval.next();
  932. t.equal(val.getHours(), 11, 'Hour matches');
  933. val = interval.next();
  934. t.equal(val.getHours(), 1, 'Hour matches');
  935. val = interval.next();
  936. t.equal(val.getHours(), 9, 'Hour matches');
  937. val = interval.next();
  938. t.equal(val.getHours(), 11, 'Hour matches');
  939. });
  940. } catch (err) {
  941. t.error(err, 'Interval parse error');
  942. }
  943. t.end();
  944. });
  945. test('it will work with #139 issue case', function(t) {
  946. var options = {
  947. currentDate : new Date('2018-11-15T16:15:33.522Z'),
  948. tz: 'Europe/Madrid'
  949. };
  950. var interval = CronExpression.parse('0 0 0 1,2 * *', options);
  951. var date = interval.next();
  952. t.equal(date.getFullYear(), 2018);
  953. t.equal(date.getDate(), 1);
  954. t.equal(date.getMonth(), 11);
  955. t.end();
  956. });
  957. test('should work for valid first/second/third/fourth/fifth occurence dayOfWeek (# char)', function(t) {
  958. try {
  959. var options = {
  960. currentDate: new CronDate('2019-04-30')
  961. };
  962. var expectedFirstDates = [
  963. new CronDate('2019-05-05'),
  964. new CronDate('2019-06-02'),
  965. new CronDate('2019-07-07'),
  966. new CronDate('2019-08-04')
  967. ];
  968. var expectedSecondDates = [
  969. new CronDate('2019-05-12'),
  970. new CronDate('2019-06-09'),
  971. new CronDate('2019-07-14'),
  972. new CronDate('2019-08-11')
  973. ];
  974. var expectedThirdDates = [
  975. new CronDate('2019-05-19'),
  976. new CronDate('2019-06-16'),
  977. new CronDate('2019-07-21'),
  978. new CronDate('2019-08-18')
  979. ];
  980. var expectedFourthDates = [
  981. new CronDate('2019-05-26'),
  982. new CronDate('2019-06-23'),
  983. new CronDate('2019-07-28'),
  984. new CronDate('2019-08-25')
  985. ];
  986. var expectedFifthDates = [
  987. new CronDate('2019-06-30'),
  988. new CronDate('2019-09-29'),
  989. new CronDate('2019-12-29'),
  990. new CronDate('2020-03-29')
  991. ];
  992. var allExpectedDates = [
  993. expectedFirstDates,
  994. expectedSecondDates,
  995. expectedThirdDates,
  996. expectedFourthDates,
  997. expectedFifthDates
  998. ];
  999. var expressions = [
  1000. '0 0 0 ? * 0#1',
  1001. '0 0 0 ? * 0#2',
  1002. '0 0 0 ? * 0#3',
  1003. '0 0 0 ? * 0#4',
  1004. '0 0 0 ? * 0#5'
  1005. ];
  1006. expressions.forEach(function(expression, index) {
  1007. var interval = CronExpression.parse(expression, options);
  1008. var expectedDates = allExpectedDates[index];
  1009. expectedDates.forEach(function(expected) {
  1010. var date = interval.next();
  1011. t.equal(
  1012. date.toISOString(),
  1013. expected.toISOString(),
  1014. 'Expression "' + expression + '" has next() that matches expected: ' + expected.toISOString()
  1015. );
  1016. });
  1017. expectedDates
  1018. .slice(0, expectedDates.length - 1)
  1019. .reverse()
  1020. .forEach(function(expected) {
  1021. var date = interval.prev();
  1022. t.equal(
  1023. date.toISOString(),
  1024. expected.toISOString(),
  1025. 'Expression "' + expression + '" has prev() that matches expected: ' + expected.toISOString()
  1026. );
  1027. });
  1028. });
  1029. } catch (err) {
  1030. t.error(err, 'Interval parse error');
  1031. }
  1032. t.end();
  1033. });
  1034. test('should work for valid second sunday in may', function(t) {
  1035. try {
  1036. var options = {
  1037. currentDate: new CronDate('2019-01-30')
  1038. };
  1039. var expectedDates = [
  1040. new CronDate('2019-05-12'),
  1041. new CronDate('2020-05-10'),
  1042. new CronDate('2021-05-09'),
  1043. new CronDate('2022-05-08')
  1044. ];
  1045. var interval = CronExpression.parse('0 0 0 ? MAY 0#2', options);
  1046. expectedDates.forEach(function(expected) {
  1047. var date = interval.next();
  1048. t.equal(
  1049. date.toISOString(),
  1050. expected.toISOString(),
  1051. 'Expression "0 0 0 ? MAY 0#2" has next() that matches expected: ' + expected.toISOString()
  1052. );
  1053. });
  1054. expectedDates
  1055. .slice(0, expectedDates.length - 1)
  1056. .reverse()
  1057. .forEach(function(expected) {
  1058. var date = interval.prev();
  1059. t.equal(
  1060. date.toISOString(),
  1061. expected.toISOString(),
  1062. 'Expression "0 0 0 ? MAY 0#2" has prev() that matches expected: ' + expected.toISOString()
  1063. );
  1064. });
  1065. } catch (err) {
  1066. t.error(err, 'Interval parse error');
  1067. }
  1068. t.end();
  1069. });
  1070. test('should work for valid second sunday at noon in may', function(t) {
  1071. try {
  1072. var options = {
  1073. currentDate: new CronDate('2019-05-12T11:59:00.000')
  1074. };
  1075. var expected = new CronDate('2019-05-12T12:00:00.000');
  1076. var interval = CronExpression.parse('0 0 12 ? MAY 0#2', options);
  1077. var date = interval.next();
  1078. t.equal(
  1079. date.toISOString(),
  1080. expected.toISOString(),
  1081. 'Expression "0 0 12 ? MAY 0#2" has next() that matches expected: ' + expected.toISOString()
  1082. );
  1083. } catch (err) {
  1084. t.error(err, 'Interval parse error');
  1085. }
  1086. t.end();
  1087. });
  1088. test('should work for valid second sunday at noon in may (UTC+3)', function(t) {
  1089. try {
  1090. var options = {
  1091. currentDate: new CronDate('2019-05-12T11:59:00.000', 'Europe/Sofia')
  1092. };
  1093. var expected = new CronDate('2019-05-12T12:00:00.000', 'Europe/Sofia');
  1094. var interval = CronExpression.parse('0 0 12 ? MAY 0#2', options);
  1095. var date = interval.next();
  1096. t.equal(
  1097. date.toISOString(),
  1098. expected.toISOString(),
  1099. 'Expression "0 0 12 ? MAY 0#2" has next() that matches expected: ' + expected.toISOString()
  1100. );
  1101. } catch (err) {
  1102. t.error(err, 'Interval parse error');
  1103. }
  1104. t.end();
  1105. });
  1106. test('should work with both dayOfMonth and nth occurence of dayOfWeek', function(t) {
  1107. try {
  1108. var options = {
  1109. currentDate: new CronDate('2019-04-01')
  1110. };
  1111. var expectedDates = [
  1112. new CronDate('2019-04-16'),
  1113. new CronDate('2019-04-17'),
  1114. new CronDate('2019-04-18'),
  1115. new CronDate('2019-05-15'),
  1116. new CronDate('2019-05-16'),
  1117. new CronDate('2019-05-18'),
  1118. ];
  1119. var interval = CronExpression.parse('0 0 0 16,18 * 3#3', options);
  1120. expectedDates.forEach(function(expected) {
  1121. var date = interval.next();
  1122. t.equal(
  1123. date.toISOString(),
  1124. expected.toISOString(),
  1125. 'Expression "0 0 0 16,18 * 3#3" has next() that matches expected: ' + expected.toISOString()
  1126. );
  1127. });
  1128. expectedDates
  1129. .slice(0, expectedDates.length - 1)
  1130. .reverse()
  1131. .forEach(function(expected) {
  1132. var date = interval.prev();
  1133. t.equal(
  1134. date.toISOString(),
  1135. expected.toISOString(),
  1136. 'Expression "0 0 0 16,18 * 3#3" has prev() that matches expected: ' + expected.toISOString()
  1137. );
  1138. });
  1139. } catch (err) {
  1140. t.error(err, 'Interval parse error');
  1141. }
  1142. t.end();
  1143. });
  1144. test('should error when passed invalid occurence value', function(t) {
  1145. var expressions = [
  1146. '0 0 0 ? * 1#',
  1147. '0 0 0 ? * 1#0',
  1148. '0 0 0 ? * 4#6',
  1149. '0 0 0 ? * 0##4',
  1150. ];
  1151. expressions.forEach(function(expression) {
  1152. t.throws(function() {
  1153. CronExpression.parse(expression);
  1154. }, new Error('Constraint error, invalid dayOfWeek occurrence number (#)'), expression);
  1155. });
  1156. t.end();
  1157. });
  1158. // The Quartz documentation says that if the # character is used then no other expression can be used in the dayOfWeek term: http://www.quartz-scheduler.org/api/2.3.0/index.html
  1159. test('cannot combine `-` range and # occurrence special characters', function(t) {
  1160. var expression = '0 0 0 ? * 2-4#2';
  1161. t.throws(function() {
  1162. CronExpression.parse(expression);
  1163. }, new Error('Constraint error, invalid dayOfWeek `#` and `-` special characters are incompatible'));
  1164. t.end();
  1165. });
  1166. test('cannot combine `/` repeat interval and # occurrence special characters', function(t) {
  1167. var expression = '0 0 0 ? * 1/2#3';
  1168. t.throws(function() {
  1169. CronExpression.parse(expression);
  1170. }, new Error('Constraint error, invalid dayOfWeek `#` and `/` special characters are incompatible'));
  1171. t.end();
  1172. });
  1173. test('cannot combine `,` list and # occurrence special characters', function(t) {
  1174. var expression = '0 0 0 ? * 0,6#4';
  1175. t.throws(function() {
  1176. CronExpression.parse(expression);
  1177. }, new Error('Constraint error, invalid dayOfWeek `#` and `,` special characters are incompatible'));
  1178. t.end();
  1179. });