utils.recurrence.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. /**
  2. * DevExtreme (ui/scheduler/utils.recurrence.js)
  3. * Version: 19.1.16
  4. * Build date: Tue Oct 18 2022
  5. *
  6. * Copyright (c) 2012 - 2022 Developer Express Inc. ALL RIGHTS RESERVED
  7. * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
  8. */
  9. "use strict";
  10. var errors = require("../../core/errors");
  11. var extend = require("../../core/utils/extend").extend;
  12. var each = require("../../core/utils/iterator").each;
  13. var inArray = require("../../core/utils/array").inArray;
  14. var isDefined = require("../../core/utils/type").isDefined;
  15. var dateUtils = require("../../core/utils/date");
  16. var toMs = dateUtils.dateToMilliseconds;
  17. var leastDaysInWeek = 4;
  18. var intervalMap = {
  19. secondly: "seconds",
  20. minutely: "minutes",
  21. hourly: "hours",
  22. daily: "days",
  23. weekly: "weeks",
  24. monthly: "months",
  25. yearly: "years"
  26. };
  27. var dateSetterMap = {
  28. bysecond: function(date, value) {
  29. date.setSeconds(value)
  30. },
  31. byminute: function(date, value) {
  32. date.setMinutes(value)
  33. },
  34. byhour: function(date, value) {
  35. date.setHours(value)
  36. },
  37. bymonth: function(date, value) {
  38. date.setMonth(value)
  39. },
  40. bymonthday: function(date, value) {
  41. if (value < 0) {
  42. var initialDate = new Date(date);
  43. setDateByNegativeValue(initialDate, 1, -1);
  44. var daysInMonth = initialDate.getDate();
  45. if (daysInMonth >= Math.abs(value)) {
  46. setDateByNegativeValue(date, 1, value)
  47. } else {
  48. setDateByNegativeValue(date, 2, value)
  49. }
  50. } else {
  51. date.setDate(value);
  52. correctDate(date, value)
  53. }
  54. },
  55. byday: function(date, byDay, appointmentWeekStart, frequency, firstDayOfWeek) {
  56. var appointmentDayOfWeek = date.getDay();
  57. var weekStart = days[appointmentWeekStart];
  58. byDay += byDay >= weekStart === weekStart > appointmentDayOfWeek ? 7 : 0;
  59. date.setDate(date.getDate() - appointmentDayOfWeek + byDay)
  60. },
  61. byweekno: function(date, weekNumber, weekStart) {
  62. var initialDate = new Date(date);
  63. var firstYearDate = new Date(initialDate.setMonth(0, 1));
  64. var dayShift = firstYearDate.getDay() - days[weekStart];
  65. var firstDayOfYear = firstYearDate.getTime() - dayShift * toMs("day");
  66. var newFirstYearDate = dayShift + 1;
  67. if (newFirstYearDate > leastDaysInWeek) {
  68. date.setTime(firstDayOfYear + 7 * weekNumber * toMs("day"))
  69. } else {
  70. date.setTime(firstDayOfYear + 7 * (weekNumber - 1) * toMs("day"))
  71. }
  72. var timezoneDiff = (date.getTimezoneOffset() - firstYearDate.getTimezoneOffset()) * toMs("minute");
  73. timezoneDiff && date.setTime(date.getTime() + timezoneDiff)
  74. },
  75. byyearday: function(date, dayOfYear) {
  76. date.setMonth(0, 1);
  77. date.setDate(dayOfYear)
  78. }
  79. };
  80. var setDateByNegativeValue = function(date, month, value) {
  81. var initialDate = new Date(date);
  82. date.setMonth(date.getMonth() + month);
  83. if (date.getMonth() - initialDate.getMonth() > month) {
  84. date.setDate(value + 1)
  85. }
  86. date.setDate(value + 1)
  87. };
  88. var dateGetterMap = {
  89. bysecond: function(date) {
  90. return date.getSeconds()
  91. },
  92. byminute: function(date) {
  93. return date.getMinutes()
  94. },
  95. byhour: function(date) {
  96. return date.getHours()
  97. },
  98. bymonth: function(date) {
  99. return date.getMonth()
  100. },
  101. bymonthday: function(date) {
  102. return date.getDate()
  103. },
  104. byday: function(date) {
  105. return date.getDay()
  106. },
  107. byweekno: function(date, weekStart) {
  108. var current = new Date(date);
  109. var diff = leastDaysInWeek - current.getDay() + days[weekStart] - 1;
  110. var dayInMilliseconds = toMs("day");
  111. if (date.getDay() < days[weekStart]) {
  112. diff -= 7
  113. }
  114. current.setHours(0, 0, 0);
  115. current.setDate(current.getDate() + diff);
  116. var yearStart = new Date(current.getFullYear(), 0, 1);
  117. var timezoneDiff = (yearStart.getTimezoneOffset() - current.getTimezoneOffset()) * toMs("minute");
  118. var daysFromYearStart = 1 + (current - yearStart + timezoneDiff) / dayInMilliseconds;
  119. return Math.ceil(daysFromYearStart / 7)
  120. },
  121. byyearday: function(date) {
  122. var yearStart = new Date(date.getFullYear(), 0, 0);
  123. var timezoneDiff = date.getTimezoneOffset() - yearStart.getTimezoneOffset();
  124. var diff = date - yearStart - timezoneDiff * toMs("minute");
  125. var dayLength = toMs("day");
  126. return Math.floor(diff / dayLength)
  127. }
  128. };
  129. var ruleNames = ["freq", "interval", "byday", "byweekno", "byyearday", "bymonth", "bymonthday", "count", "until", "byhour", "byminute", "bysecond", "bysetpos", "wkst"];
  130. var freqNames = ["DAILY", "WEEKLY", "MONTHLY", "YEARLY", "SECONDLY", "MINUTELY", "HOURLY"];
  131. var days = {
  132. SU: 0,
  133. MO: 1,
  134. TU: 2,
  135. WE: 3,
  136. TH: 4,
  137. FR: 5,
  138. SA: 6
  139. };
  140. var daysNames = {
  141. 0: "SU",
  142. 1: "MO",
  143. 2: "TU",
  144. 3: "WE",
  145. 4: "TH",
  146. 5: "FR",
  147. 6: "SA"
  148. };
  149. var getTimeZoneOffset = function() {
  150. return (new Date).getTimezoneOffset()
  151. };
  152. var dateInRecurrenceRange = function(options) {
  153. var result = [];
  154. if (options.rule) {
  155. result = getDatesByRecurrence(options)
  156. }
  157. return !!result.length
  158. };
  159. var normalizeInterval = function(rule) {
  160. var interval = rule.interval;
  161. var freq = rule.freq;
  162. var intervalObject = {};
  163. var intervalField = intervalMap[freq.toLowerCase()];
  164. if ("MONTHLY" === freq && rule.byday) {
  165. intervalField = intervalMap.daily
  166. }
  167. intervalObject[intervalField] = interval;
  168. return intervalObject
  169. };
  170. var getDatesByRecurrenceException = function(ruleValues, date) {
  171. var result = [];
  172. for (var i = 0, len = ruleValues.length; i < len; i++) {
  173. result[i] = getDateByAsciiString(ruleValues[i], date)
  174. }
  175. return result
  176. };
  177. var dateIsRecurrenceException = function(date, recurrenceException) {
  178. var result = false;
  179. if (!recurrenceException) {
  180. return result
  181. }
  182. var splitDates = recurrenceException.split(",");
  183. var exceptDates = getDatesByRecurrenceException(splitDates, date);
  184. var shortFormat = /\d{8}$/;
  185. for (var i = 0, len = exceptDates.length; i < len; i++) {
  186. if (splitDates[i].match(shortFormat)) {
  187. var diffs = getDatePartDiffs(date, exceptDates[i]);
  188. if (0 === diffs.years && 0 === diffs.months && 0 === diffs.days) {
  189. result = true
  190. }
  191. } else {
  192. if (date.getTime() === exceptDates[i].getTime()) {
  193. result = true
  194. }
  195. }
  196. }
  197. return result
  198. };
  199. var doNextIteration = function(date, startIntervalDate, endIntervalDate, recurrenceRule, iterationCount) {
  200. var matchCountIsCorrect = true;
  201. endIntervalDate = endIntervalDate.getTime();
  202. if (recurrenceRule.until) {
  203. if (recurrenceRule.until.getTime() < endIntervalDate) {
  204. endIntervalDate = recurrenceRule.until.getTime()
  205. }
  206. }
  207. if (recurrenceRule.count) {
  208. if (iterationCount === recurrenceRule.count) {
  209. matchCountIsCorrect = false
  210. }
  211. }
  212. var dateInInterval = date.getTime() <= endIntervalDate;
  213. return dateInInterval && matchCountIsCorrect
  214. };
  215. var getDatesByRecurrence = function(options) {
  216. var result = [];
  217. var recurrenceRule = getRecurrenceRule(options.rule);
  218. var iterationResult = {};
  219. var rule = recurrenceRule.rule;
  220. var recurrenceStartDate = options.start;
  221. if (!recurrenceRule.isValid || !rule.freq) {
  222. return result
  223. }
  224. rule.interval = normalizeInterval(rule);
  225. var dateRules = splitDateRules(rule, options.firstDayOfWeek);
  226. var duration = options.end ? options.end.getTime() - options.start.getTime() : toMs("day");
  227. var config = {
  228. exception: options.exception,
  229. min: options.min,
  230. dateRules: dateRules,
  231. rule: rule,
  232. recurrenceStartDate: recurrenceStartDate,
  233. recurrenceEndDate: options.end,
  234. duration: duration
  235. };
  236. if (dateRules.length && rule.count) {
  237. var iteration = 0;
  238. getDatesByCount(dateRules, new Date(recurrenceStartDate), new Date(recurrenceStartDate), rule).forEach(function(currentDate, i) {
  239. if (currentDate < options.max) {
  240. iteration++;
  241. iterationResult = pushToResult(iteration, iterationResult, currentDate, i, config, true)
  242. }
  243. })
  244. } else {
  245. getDatesByRules(dateRules, new Date(recurrenceStartDate), rule).forEach(function(currentDate, i) {
  246. var iteration = 0;
  247. while (doNextIteration(currentDate, recurrenceStartDate, options.max, rule, iteration)) {
  248. iteration++;
  249. iterationResult = pushToResult(iteration, iterationResult, currentDate, i, config);
  250. currentDate = incrementDate(currentDate, recurrenceStartDate, rule, i)
  251. }
  252. })
  253. }
  254. if (rule.bysetpos) {
  255. each(iterationResult, function(iterationIndex, iterationDates) {
  256. iterationResult[iterationIndex] = filterDatesBySetPos(iterationDates, rule.bysetpos)
  257. })
  258. }
  259. each(iterationResult, function(_, iterationDates) {
  260. result = result.concat(iterationDates)
  261. });
  262. result.sort(function(a, b) {
  263. return a - b
  264. });
  265. return result
  266. };
  267. var pushToResult = function(iteration, iterationResult, currentDate, i, config, verifiedField) {
  268. if (!iterationResult[iteration]) {
  269. iterationResult[iteration] = []
  270. }
  271. if (checkDate(currentDate, i, config, verifiedField)) {
  272. iterationResult[iteration].push(currentDate)
  273. }
  274. return iterationResult
  275. };
  276. var checkDate = function(currentDate, i, config, verifiedField) {
  277. if (!dateIsRecurrenceException(currentDate, config.exception)) {
  278. var duration = dateUtils.sameDate(currentDate, config.recurrenceEndDate) && config.recurrenceEndDate.getTime() > currentDate.getTime() ? config.recurrenceEndDate.getTime() - currentDate.getTime() : config.duration;
  279. if (currentDate.getTime() >= config.recurrenceStartDate.getTime() && currentDate.getTime() + duration > config.min.getTime()) {
  280. return verifiedField || checkDateByRule(currentDate, [config.dateRules[i]], config.rule.wkst)
  281. }
  282. }
  283. return false
  284. };
  285. var filterDatesBySetPos = function(dates, bySetPos) {
  286. var resultArray = [];
  287. bySetPos.split(",").forEach(function(index) {
  288. index = Number(index);
  289. var dateIndex = index > 0 ? index - 1 : dates.length + index;
  290. if (dates[dateIndex]) {
  291. resultArray.push(dates[dateIndex])
  292. }
  293. });
  294. return resultArray
  295. };
  296. var correctDate = function(originalDate, date) {
  297. if (originalDate.getDate() !== date) {
  298. originalDate.setDate(date)
  299. }
  300. };
  301. var incrementDate = function(date, originalStartDate, rule, iterationStep) {
  302. var initialDate = new Date(date);
  303. var needCorrect = true;
  304. date = dateUtils.addInterval(date, rule.interval);
  305. if ("MONTHLY" === rule.freq && !rule.byday) {
  306. var expectedDate = originalStartDate.getDate();
  307. if (rule.bymonthday) {
  308. expectedDate = Number(rule.bymonthday.split(",")[iterationStep]);
  309. if (expectedDate < 0) {
  310. initialDate.setMonth(initialDate.getMonth() + 1, 1);
  311. dateSetterMap.bymonthday(initialDate, expectedDate);
  312. date = initialDate;
  313. needCorrect = false
  314. }
  315. }
  316. needCorrect && correctDate(date, expectedDate)
  317. }
  318. if ("YEARLY" === rule.freq) {
  319. if (rule.byyearday) {
  320. var dayNumber = Number(rule.byyearday.split(",")[iterationStep]);
  321. dateSetterMap.byyearday(date, dayNumber)
  322. }
  323. var dateRules = splitDateRules(rule);
  324. for (var field in dateRules[iterationStep]) {
  325. dateSetterMap[field] && dateSetterMap[field](date, dateRules[iterationStep][field], rule.wkst)
  326. }
  327. }
  328. return date
  329. };
  330. var getDatePartDiffs = function(date1, date2) {
  331. return {
  332. years: date1.getFullYear() - date2.getFullYear(),
  333. months: date1.getMonth() - date2.getMonth(),
  334. days: date1.getDate() - date2.getDate(),
  335. hours: date1.getHours() - date2.getHours(),
  336. minutes: date1.getMinutes() - date2.getMinutes(),
  337. seconds: date1.getSeconds() - date2.getSeconds()
  338. }
  339. };
  340. var getRecurrenceRule = function(recurrence) {
  341. var result = {
  342. rule: {},
  343. isValid: false
  344. };
  345. if (recurrence) {
  346. result.rule = parseRecurrenceRule(recurrence);
  347. result.isValid = validateRRule(result.rule, recurrence)
  348. }
  349. return result
  350. };
  351. var loggedWarnings = [];
  352. var validateRRule = function(rule, recurrence) {
  353. if (brokenRuleNameExists(rule) || inArray(rule.freq, freqNames) === -1 || wrongCountRule(rule) || wrongIntervalRule(rule) || wrongDayOfWeek(rule) || wrongByMonthDayRule(rule) || wrongByMonth(rule) || wrongUntilRule(rule)) {
  354. logBrokenRule(recurrence);
  355. return false
  356. }
  357. return true
  358. };
  359. var wrongUntilRule = function(rule) {
  360. var wrongUntil = false;
  361. var until = rule.until;
  362. if (void 0 !== until && !(until instanceof Date)) {
  363. wrongUntil = true
  364. }
  365. return wrongUntil
  366. };
  367. var wrongCountRule = function(rule) {
  368. var wrongCount = false;
  369. var count = rule.count;
  370. if (count && "string" === typeof count) {
  371. wrongCount = true
  372. }
  373. return wrongCount
  374. };
  375. var wrongByMonthDayRule = function(rule) {
  376. var wrongByMonthDay = false;
  377. var byMonthDay = rule.bymonthday;
  378. if (byMonthDay && isNaN(parseInt(byMonthDay))) {
  379. wrongByMonthDay = true
  380. }
  381. return wrongByMonthDay
  382. };
  383. var wrongByMonth = function wrongByMonth(rule) {
  384. var wrongByMonth = false;
  385. var byMonth = rule.bymonth;
  386. if (byMonth && isNaN(parseInt(byMonth))) {
  387. wrongByMonth = true
  388. }
  389. return wrongByMonth
  390. };
  391. var wrongIntervalRule = function(rule) {
  392. var wrongInterval = false;
  393. var interval = rule.interval;
  394. if (interval && "string" === typeof interval) {
  395. wrongInterval = true
  396. }
  397. return wrongInterval
  398. };
  399. var wrongDayOfWeek = function(rule) {
  400. var daysByRule = daysFromByDayRule(rule);
  401. var brokenDaysExist = false;
  402. each(daysByRule, function(_, day) {
  403. if (!Object.prototype.hasOwnProperty.call(days, day)) {
  404. brokenDaysExist = true;
  405. return false
  406. }
  407. });
  408. return brokenDaysExist
  409. };
  410. var brokenRuleNameExists = function(rule) {
  411. var brokenRuleExists = false;
  412. each(rule, function(ruleName) {
  413. if (inArray(ruleName, ruleNames) === -1) {
  414. brokenRuleExists = true;
  415. return false
  416. }
  417. });
  418. return brokenRuleExists
  419. };
  420. var logBrokenRule = function(recurrence) {
  421. if (inArray(recurrence, loggedWarnings) === -1) {
  422. errors.log("W0006", recurrence);
  423. loggedWarnings.push(recurrence)
  424. }
  425. };
  426. var parseRecurrenceRule = function(recurrence) {
  427. var ruleObject = {};
  428. var ruleParts = recurrence.split(";");
  429. for (var i = 0, len = ruleParts.length; i < len; i++) {
  430. var rule = ruleParts[i].split("=");
  431. var ruleName = rule[0].toLowerCase();
  432. var ruleValue = rule[1];
  433. ruleObject[ruleName] = ruleValue
  434. }
  435. var count = parseInt(ruleObject.count);
  436. if (!isNaN(count)) {
  437. ruleObject.count = count
  438. }
  439. if (ruleObject.interval) {
  440. var interval = parseInt(ruleObject.interval);
  441. if (!isNaN(interval)) {
  442. ruleObject.interval = interval
  443. }
  444. } else {
  445. ruleObject.interval = 1
  446. }
  447. if (ruleObject.freq && ruleObject.until) {
  448. ruleObject.until = getDateByAsciiString(ruleObject.until)
  449. }
  450. return ruleObject
  451. };
  452. var getDateByAsciiString = function(string, initialDate) {
  453. if ("string" !== typeof string) {
  454. return string
  455. }
  456. var arrayDate = string.match(/(\d{4})(\d{2})(\d{2})(T(\d{2})(\d{2})(\d{2}))?(Z)?/);
  457. if (!arrayDate) {
  458. return null
  459. }
  460. var isUTC = void 0 !== arrayDate[8];
  461. var currentOffset = initialDate ? initialDate.getTimezoneOffset() : resultUtils.getTimeZoneOffset();
  462. var date = new(Function.prototype.bind.apply(Date, prepareDateArrayToParse(arrayDate)));
  463. currentOffset = 6e4 * currentOffset;
  464. if (isUTC) {
  465. date = new Date(date.getTime() - currentOffset)
  466. }
  467. return date
  468. };
  469. var prepareDateArrayToParse = function(arrayDate) {
  470. arrayDate.shift();
  471. if (void 0 === arrayDate[3]) {
  472. arrayDate.splice(3)
  473. } else {
  474. arrayDate.splice(3, 1);
  475. arrayDate.splice(6)
  476. }
  477. arrayDate[1]--;
  478. arrayDate.unshift(null);
  479. return arrayDate
  480. };
  481. var daysFromByDayRule = function(rule) {
  482. var result = [];
  483. if (rule.byday) {
  484. if (Array.isArray(rule.byday)) {
  485. result = rule.byday
  486. } else {
  487. result = rule.byday.split(",")
  488. }
  489. }
  490. return result
  491. };
  492. var getAsciiStringByDate = function(date) {
  493. var currentOffset = 6e4 * resultUtils.getTimeZoneOffset();
  494. date = new Date(date.getTime() + currentOffset);
  495. return date.getFullYear() + ("0" + (date.getMonth() + 1)).slice(-2) + ("0" + date.getDate()).slice(-2) + "T" + ("0" + date.getHours()).slice(-2) + ("0" + date.getMinutes()).slice(-2) + ("0" + date.getSeconds()).slice(-2) + "Z"
  496. };
  497. var splitDateRules = function(rule) {
  498. var firstDayOfWeek = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : null;
  499. var result = [];
  500. if (isDefined(firstDayOfWeek)) {
  501. rule.fdow = firstDayOfWeek
  502. }
  503. if (!rule.wkst) {
  504. rule.wkst = isDefined(firstDayOfWeek) ? daysNames[firstDayOfWeek] : "MO"
  505. }
  506. if (rule.byweekno && !rule.byday) {
  507. var dayNames = Object.keys(days);
  508. for (var i = 0; i < days[rule.wkst]; i++) {
  509. dayNames.push(dayNames.shift())
  510. }
  511. rule.byday = dayNames.join(",")
  512. }
  513. for (var field in dateSetterMap) {
  514. if (!rule[field]) {
  515. continue
  516. }
  517. var ruleFieldValues = rule[field].split(",");
  518. var ruleArray = getDateRuleArray(field, ruleFieldValues);
  519. result = result.length ? extendObjectArray(ruleArray, result) : ruleArray
  520. }
  521. return result
  522. };
  523. var getDateRuleArray = function(field, values) {
  524. var result = [];
  525. for (var i = 0, length = values.length; i < length; i++) {
  526. var dateRule = {};
  527. dateRule[field] = handleRuleFieldValue(field, values[i]);
  528. result.push(dateRule)
  529. }
  530. return result
  531. };
  532. var handleRuleFieldValue = function(field, value) {
  533. var result = parseInt(value);
  534. if ("bymonth" === field) {
  535. result -= 1
  536. }
  537. if ("byday" === field) {
  538. result = days[value]
  539. }
  540. return result
  541. };
  542. var extendObjectArray = function(firstArray, secondArray) {
  543. var result = [];
  544. for (var i = 0, firstArrayLength = firstArray.length; i < firstArrayLength; i++) {
  545. for (var j = 0, secondArrayLength = secondArray.length; j < secondArrayLength; j++) {
  546. result.push(extend({}, firstArray[i], secondArray[j]))
  547. }
  548. }
  549. return result
  550. };
  551. var getDatesByRules = function(dateRules, startDate, rule) {
  552. var result = [];
  553. for (var i = 0, len = dateRules.length; i < len; i++) {
  554. var current = dateRules[i];
  555. var updatedDate = prepareDate(startDate, dateRules, rule.wkst);
  556. for (var field in current) {
  557. dateSetterMap[field] && dateSetterMap[field](updatedDate, current[field], rule.wkst, rule.freq, rule.fdow)
  558. }
  559. if (Array.isArray(updatedDate)) {
  560. result = result.concat(updatedDate)
  561. } else {
  562. result.push(new Date(updatedDate))
  563. }
  564. }
  565. if (!result.length) {
  566. result.push(startDate)
  567. }
  568. return result
  569. };
  570. var getDatesByCount = function(dateRules, startDate, recurrenceStartDate, rule) {
  571. var result = [];
  572. var count = rule.count;
  573. var counter = 0;
  574. var date = prepareDate(startDate, dateRules, rule.wkst);
  575. while (counter < count) {
  576. var dates = getDatesByRules(dateRules, date, rule);
  577. var checkedDates = [];
  578. for (var i = 0; i < dates.length; i++) {
  579. if (dates[i].getTime() >= recurrenceStartDate.getTime()) {
  580. checkedDates.push(dates[i])
  581. }
  582. }
  583. var length = checkedDates.length;
  584. counter += length;
  585. var delCount = counter - count;
  586. if (counter > count) {
  587. checkedDates.splice(length - delCount, delCount)
  588. }
  589. for (i = 0; i < checkedDates.length; i++) {
  590. result.push(checkedDates[i])
  591. }
  592. var interval = rule.interval;
  593. if ("days" === Object.keys(interval)[0]) {
  594. interval = {
  595. weeks: 1
  596. }
  597. }
  598. date = dateUtils.addInterval(date, interval)
  599. }
  600. return result
  601. };
  602. var prepareDate = function(startDate, dateRules, weekStartRule) {
  603. var date = new Date(startDate);
  604. var day = date.getDay();
  605. if (dateRules.length && isDefined(dateRules[0].byday)) {
  606. date.setDate(date.getDate() - day + days[weekStartRule] - (day < days[weekStartRule] ? 7 : 0))
  607. } else {
  608. date.setDate(1)
  609. }
  610. return date
  611. };
  612. var checkDateByRule = function(date, rules, weekStart) {
  613. var result = false;
  614. for (var i = 0; i < rules.length; i++) {
  615. var current = rules[i];
  616. var currentRuleResult = true;
  617. for (var field in current) {
  618. var processNegative = "bymonthday" === field && current[field] < 0;
  619. if (dateGetterMap[field] && !processNegative && current[field] !== dateGetterMap[field](date, weekStart)) {
  620. currentRuleResult = false
  621. }
  622. }
  623. result = result || currentRuleResult
  624. }
  625. return result || !rules.length
  626. };
  627. var getRecurrenceString = function(object) {
  628. if (!object || !object.freq) {
  629. return
  630. }
  631. var result = "";
  632. for (var field in object) {
  633. var value = object[field];
  634. if ("interval" === field && value < 2) {
  635. continue
  636. }
  637. if ("until" === field) {
  638. value = getAsciiStringByDate(value)
  639. }
  640. result += field + "=" + value + ";"
  641. }
  642. result = result.substring(0, result.length - 1);
  643. return result.toUpperCase()
  644. };
  645. var resultUtils = {
  646. getRecurrenceString: getRecurrenceString,
  647. getRecurrenceRule: getRecurrenceRule,
  648. getAsciiStringByDate: getAsciiStringByDate,
  649. getDatesByRecurrence: getDatesByRecurrence,
  650. dateInRecurrenceRange: dateInRecurrenceRange,
  651. getDateByAsciiString: getDateByAsciiString,
  652. daysFromByDayRule: daysFromByDayRule,
  653. getTimeZoneOffset: getTimeZoneOffset
  654. };
  655. module.exports = resultUtils;