1 import { c, msgid } from 'ttag';
3 import mod from '@proton/utils/mod';
4 import unique from '@proton/utils/unique';
6 import { format } from '../../date-fns-utc';
7 import type { WeekStartsOn } from '../../date-fns-utc/interface';
8 import { toUTCDate } from '../../date/timezone';
10 VcalDateOrDateTimeProperty,
12 VcalRrulePropertyValue,
13 } from '../../interfaces/calendar/VcalModel';
14 import { END_TYPE, FREQUENCY, MONTHLY_TYPE } from '../constants';
15 import { getPropertyTzid } from '../vcalHelper';
16 import { getIsRruleCustom, getIsRruleSimple, getPositiveSetpos } from './rrule';
17 import { getEndType, getMonthType, getUntilDate, getWeeklyDays } from './rruleProperties';
24 interface GetTimezonedFrequencyStringOptions {
27 weekStartsOn: WeekStartsOn;
29 // NOTE: due to the different grammar of different languages, to allow for a proper translation,
30 // we need to expand all possible cases so there will be quite a bit of duplicated code
32 export const getOnDayString = (date: Date, monthlyType: MONTHLY_TYPE) => {
33 const dayOfMonth = date.getUTCDate();
34 const day = date.getUTCDay();
36 if (monthlyType === MONTHLY_TYPE.ON_NTH_DAY) {
37 const setPos = getPositiveSetpos(date);
40 return c('Monthly recurring event, repeats on').t`on the first Sunday`;
43 return c('Monthly recurring event, repeats on').t`on the first Monday`;
46 return c('Monthly recurring event, repeats on').t`on the first Tuesday`;
49 return c('Monthly recurring event, repeats on').t`on the first Wednesday`;
52 return c('Monthly recurring event, repeats on').t`on the first Thursday`;
55 return c('Monthly recurring event, repeats on').t`on the first Friday`;
58 return c('Monthly recurring event, repeats on').t`on the first Saturday`;
63 return c('Monthly recurring event, repeats on').t`on the second Sunday`;
66 return c('Monthly recurring event, repeats on').t`on the second Monday`;
69 return c('Monthly recurring event, repeats on').t`on the second Tuesday`;
72 return c('Monthly recurring event, repeats on').t`on the second Wednesday`;
75 return c('Monthly recurring event, repeats on').t`on the second Thursday`;
78 return c('Monthly recurring event, repeats on').t`on the second Friday`;
81 return c('Monthly recurring event, repeats on').t`on the second Saturday`;
86 return c('Monthly recurring event, repeats on').t`on the third Sunday`;
89 return c('Monthly recurring event, repeats on').t`on the third Monday`;
92 return c('Monthly recurring event, repeats on').t`on the third Tuesday`;
95 return c('Monthly recurring event, repeats on').t`on the third Wednesday`;
98 return c('Monthly recurring event, repeats on').t`on the third Thursday`;
101 return c('Monthly recurring event, repeats on').t`on the third Friday`;
104 return c('Monthly recurring event, repeats on').t`on the third Saturday`;
109 return c('Monthly recurring event, repeats on').t`on the fourth Sunday`;
112 return c('Monthly recurring event, repeats on').t`on the fourth Monday`;
115 return c('Monthly recurring event, repeats on').t`on the fourth Tuesday`;
118 return c('Monthly recurring event, repeats on').t`on the fourth Wednesday`;
121 return c('Monthly recurring event, repeats on').t`on the fourth Thursday`;
124 return c('Monthly recurring event, repeats on').t`on the fourth Friday`;
127 return c('Monthly recurring event, repeats on').t`on the fourth Saturday`;
131 if (monthlyType === MONTHLY_TYPE.ON_MINUS_NTH_DAY) {
133 return c('Monthly recurring event, repeats on').t`on the last Sunday`;
136 return c('Monthly recurring event, repeats on').t`on the last Monday`;
139 return c('Monthly recurring event, repeats on').t`on the last Tuesday`;
142 return c('Monthly recurring event, repeats on').t`on the last Wednesday`;
145 return c('Monthly recurring event, repeats on').t`on the last Thursday`;
148 return c('Monthly recurring event, repeats on').t`on the last Friday`;
151 return c('Monthly recurring event, repeats on').t`on the last Saturday`;
154 return c('Monthly recurring event, repeats on').t`on day ${dayOfMonth}`;
157 const getTimesString = (count: number) => {
158 return c('Daily recurring event, frequency').ngettext(msgid`${count} time`, `${count} times`, count);
161 const getUntilString = (dateString: string) => {
162 return c('Daily recurring event, frequency').t`until ${dateString}`;
165 const getCustomDailyString = (
166 rruleValue: VcalRrulePropertyValue,
167 { type: endType, count = 1, until }: RruleEnd,
170 const { interval = 1 } = rruleValue;
172 if (endType === END_TYPE.NEVER) {
173 if (interval === 1) {
174 return c('Daily recurring event, frequency').t`Daily`;
176 // translator: When interval = 1 we do not use this string; we use 'daily' instead. Treat the case of interval = 1 as dummy
177 return c('Daily recurring event, frequency').ngettext(
178 msgid`Every ${interval} day`,
179 `Every ${interval} days`,
184 if (endType === END_TYPE.AFTER_N_TIMES) {
185 const timesString = getTimesString(count);
186 if (interval === 1) {
187 return c('Daily recurring event, frequency (times)').t`Daily, ${timesString}`;
189 // translator: When interval = 1 we do not use this string; we use 'daily' instead. Treat the case of interval = 1 as dummy
190 return c('Daily recurring event, frequency (times)').ngettext(
191 msgid`Every ${interval} day, ${timesString}`,
192 `Every ${interval} days, ${timesString}`,
197 if (endType === END_TYPE.UNTIL && until) {
198 const dateString = format(until, 'PP', { locale });
199 const untilString = getUntilString(dateString);
200 if (interval === 1) {
201 return c('Daily recurring event, frequency (until)').t`Daily, ${untilString}`;
203 // translator: When interval = 1 we do not use this string; we use 'daily' instead. Treat the case of interval = 1 as dummy
204 return c('Daily recurring event, frequency (until)').ngettext(
205 msgid`Every ${interval} day, ${untilString}`,
206 `Every ${interval} days, ${untilString}`,
211 const getWeekdayString = (weekday: number) => {
213 return c('Weekly recurring event, repeats on (multiple days), frequency').t`Sunday`;
216 return c('Weekly recurring event, repeats on (multiple days), frequency').t`Monday`;
219 return c('Weekly recurring event, repeats on (multiple days), frequency').t`Tuesday`;
222 return c('Weekly recurring event, repeats on (multiple days), frequency').t`Wednesday`;
225 return c('Weekly recurring event, repeats on (multiple days), frequency').t`Thursday`;
228 return c('Weekly recurring event, repeats on (multiple days), frequency').t`Friday`;
231 return c('Weekly recurring event, repeats on (multiple days), frequency').t`Saturday`;
233 throw new Error('Unknown weekday');
236 const getCustomWeeklyString = (
237 { interval = 1, byday }: VcalRrulePropertyValue,
238 { type: endType, count = 1, until }: RruleEnd,
239 weekStartsOn: WeekStartsOn,
243 const days = getWeeklyDays(byday);
244 const safeDays = unique([...days, startDate.getUTCDay()]);
245 // sort weekly days depending on the day the week starts
246 const sortedWeekDays = safeDays.slice().sort((a: number, b: number) => {
247 // shift days. Get a positive modulus
248 const A = mod(a - weekStartsOn, +7);
249 const B = mod(b - weekStartsOn, 7);
252 const multipleDaysString = sortedWeekDays.map(getWeekdayString).join(', ');
254 if (endType === END_TYPE.NEVER) {
255 if (days.length === 7) {
256 if (interval === 1) {
257 return c('Weekly recurring event, frequency').t`Weekly on all days`;
259 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
260 return c('Weekly recurring event, frequency').ngettext(
261 msgid`Every ${interval} week on all days`,
262 `Every ${interval} weeks on all days`,
266 if (days.length === 1) {
267 const startDate = days[0];
268 if (startDate === 0) {
269 if (interval === 1) {
270 return c('Weekly recurring event, frequency').t`Weekly on Sunday`;
272 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
273 return c('Weekly recurring event, frequency').ngettext(
274 msgid`Every ${interval} week on Sunday`,
275 `Every ${interval} weeks on Sunday`,
279 if (startDate === 1) {
280 if (interval === 1) {
281 return c('Weekly recurring event, frequency').t`Weekly on Monday`;
283 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
284 return c('Weekly recurring event, frequency').ngettext(
285 msgid`Every ${interval} week on Monday`,
286 `Every ${interval} weeks on Monday`,
290 if (startDate === 2) {
291 if (interval === 1) {
292 return c('Weekly recurring event, frequency').t`Weekly on Tuesday`;
294 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
295 return c('Weekly recurring event, frequency').ngettext(
296 msgid`Every ${interval} week on Tuesday`,
297 `Every ${interval} weeks on Tuesday`,
301 if (startDate === 3) {
302 if (interval === 1) {
303 return c('Weekly recurring event, frequency').t`Weekly on Wednesday`;
305 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
306 return c('Weekly recurring event, frequency').ngettext(
307 msgid`Every ${interval} week on Wednesday`,
308 `Every ${interval} weeks on Wednesday`,
312 if (startDate === 4) {
313 if (interval === 1) {
314 return c('Weekly recurring event, frequency').t`Weekly on Thursday`;
316 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
317 return c('Weekly recurring event, frequency').ngettext(
318 msgid`Every ${interval} week on Thursday`,
319 `Every ${interval} weeks on Thursday`,
323 if (startDate === 5) {
324 if (interval === 1) {
325 return c('Weekly recurring event, frequency').t`Weekly on Friday`;
327 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
328 return c('Weekly recurring event, frequency').ngettext(
329 msgid`Every ${interval} week on Friday`,
330 `Every ${interval} weeks on Friday`,
334 if (startDate === 6) {
335 if (interval === 1) {
336 return c('Weekly recurring event, frequency').t`Weekly on Saturday`;
338 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
339 return c('Weekly recurring event, frequency').ngettext(
340 msgid`Every ${interval} week on Saturday`,
341 `Every ${interval} weeks on Saturday`,
346 if (interval === 1) {
347 return c('Weekly recurring event, frequency').t`Weekly on ${multipleDaysString}`;
349 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
350 return c('Weekly recurring event, frequency').ngettext(
351 msgid`Every ${interval} week on ${multipleDaysString}`,
352 `Every ${interval} weeks on ${multipleDaysString}`,
356 if (endType === END_TYPE.AFTER_N_TIMES) {
357 const timesString = getTimesString(count);
358 if (days.length === 7) {
359 if (interval === 1) {
360 return c('Weekly recurring event, frequency (times)').t`Weekly on all days, ${timesString}`;
362 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
363 return c('Weekly recurring event, frequency (times)').ngettext(
364 msgid`Every ${interval} week on all days, ${timesString}`,
365 `Every ${interval} weeks on all days, ${timesString}`,
369 if (days.length === 1) {
370 const startDate = days[0];
371 if (startDate === 0) {
372 if (interval === 1) {
373 return c('Weekly recurring event, frequency (times)').t`Weekly on Sunday, ${timesString}`;
375 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
376 return c('Weekly recurring event, frequency').ngettext(
377 msgid`Every ${interval} week on Sunday, ${timesString}`,
378 `Every ${interval} weeks on Sunday, ${timesString}`,
382 if (startDate === 1) {
383 if (interval === 1) {
384 return c('Weekly recurring event, frequency (times)').t`Weekly on Monday, ${timesString}`;
386 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
387 return c('Weekly recurring event, frequency').ngettext(
388 msgid`Every ${interval} week on Monday, ${timesString}`,
389 `Every ${interval} weeks on Monday, ${timesString}`,
393 if (startDate === 2) {
394 if (interval === 1) {
395 return c('Weekly recurring event, frequency (times)').t`Weekly on Tuesday, ${timesString}`;
397 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
398 return c('Weekly recurring event, frequency').ngettext(
399 msgid`Every ${interval} week on Tuesday, ${timesString}`,
400 `Every ${interval} weeks on Tuesday, ${timesString}`,
404 if (startDate === 3) {
405 if (interval === 1) {
406 return c('Weekly recurring event, frequency (times)').t`Weekly on Wednesday, ${timesString}`;
408 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
409 return c('Weekly recurring event, frequency (times)').ngettext(
410 msgid`Every ${interval} week on Wednesday, ${timesString}`,
411 `Every ${interval} weeks on Wednesday, ${timesString}`,
415 if (startDate === 4) {
416 if (interval === 1) {
417 return c('Weekly recurring event, frequency (times)').t`Weekly on Thursday, ${timesString}`;
419 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
420 return c('Weekly recurring event, frequency (times)').ngettext(
421 msgid`Every ${interval} week on Thursday, ${timesString}`,
422 `Every ${interval} weeks on Thursday, ${timesString}`,
426 if (startDate === 5) {
427 if (interval === 1) {
428 return c('Weekly recurring event, frequency (times)').t`Weekly on Friday, ${timesString}`;
430 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
431 return c('Weekly recurring event, frequency (times)').ngettext(
432 msgid`Every ${interval} week on Friday, ${timesString}`,
433 `Every ${interval} weeks on Friday, ${timesString}`,
437 if (startDate === 6) {
438 if (interval === 1) {
439 return c('Weekly recurring event, frequency (times)').t`Weekly on Saturday, ${timesString}`;
441 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
442 return c('Weekly recurring event, frequency (times)').ngettext(
443 msgid`Every ${interval} week on Saturday, ${timesString}`,
444 `Every ${interval} weeks on Saturday, ${timesString}`,
449 if (interval === 1) {
450 return c('Weekly recurring event, frequency (times)').t`Weekly on ${multipleDaysString}, ${timesString}`;
452 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
453 return c('Weekly recurring event, frequency (times)').ngettext(
454 msgid`Every ${interval} week on ${multipleDaysString}, ${timesString}`,
455 `Every ${interval} weeks on ${multipleDaysString}, ${timesString}`,
459 if (endType === END_TYPE.UNTIL && until) {
460 const dateString = format(until, 'PP', { locale });
461 const untilString = getUntilString(dateString);
462 if (days.length === 7) {
463 if (interval === 1) {
464 return c('Weekly recurring event, frequency (until)').t`Weekly on all days, ${untilString}`;
466 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
467 return c('Weekly recurring event, frequency (until)').ngettext(
468 msgid`Every ${interval} week on all days, ${untilString}`,
469 `Every ${interval} weeks on all days, ${untilString}`,
473 if (days.length === 1) {
474 const startDate = days[0];
475 if (startDate === 0) {
476 if (interval === 1) {
477 return c('Weekly recurring event, frequency (until)').t`Weekly on Sunday, ${untilString}`;
479 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
480 return c('Weekly recurring event, frequency (until)').ngettext(
481 msgid`Every ${interval} week on Sunday, ${untilString}`,
482 `Every ${interval} weeks on Sunday, ${untilString}`,
486 if (startDate === 1) {
487 if (interval === 1) {
488 return c('Weekly recurring event, frequency (until)').t`Weekly on Monday, ${untilString}`;
490 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
491 return c('Weekly recurring event, frequency (until)').ngettext(
492 msgid`Every ${interval} week on Monday, ${untilString}`,
493 `Every ${interval} weeks on Monday, ${untilString}`,
497 if (startDate === 2) {
498 if (interval === 1) {
499 return c('Weekly recurring event, frequency (until)').t`Weekly on Tuesday, ${untilString}`;
501 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
502 return c('Weekly recurring event, frequency (until)').ngettext(
503 msgid`Every ${interval} week on Tuesday, ${untilString}`,
504 `Every ${interval} weeks on Tuesday, ${untilString}`,
508 if (startDate === 3) {
509 if (interval === 1) {
510 return c('Weekly recurring event, frequency (until)').t`Weekly on Wednesday, ${untilString}`;
512 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
513 return c('Weekly recurring event, frequency (until)').ngettext(
514 msgid`Every ${interval} week on Wednesday, ${untilString}`,
515 `Every ${interval} weeks on Wednesday, ${untilString}`,
519 if (startDate === 4) {
520 if (interval === 1) {
521 return c('Weekly recurring event, frequency (until)').t`Weekly on Thursday, ${untilString}`;
523 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
524 return c('Weekly recurring event, frequency (until)').ngettext(
525 msgid`Every ${interval} week on Thursday, ${untilString}`,
526 `Every ${interval} weeks on Thursday, ${untilString}`,
530 if (startDate === 5) {
531 if (interval === 1) {
532 return c('Weekly recurring event, frequency (until)').t`Weekly on Friday, ${untilString}`;
534 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
535 return c('Weekly recurring event, frequency (until)').ngettext(
536 msgid`Every ${interval} week on Friday, ${untilString}`,
537 `Every ${interval} weeks on Friday, ${untilString}`,
541 if (startDate === 6) {
542 if (interval === 1) {
543 return c('Weekly recurring event, frequency (until)').t`Weekly on Saturday, ${untilString}`;
545 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
546 return c('Weekly recurring event, frequency (until)').ngettext(
547 msgid`Every ${interval} week on Saturday, ${untilString}`,
548 `Every ${interval} weeks on Saturday, ${untilString}`,
553 if (interval === 1) {
554 return c('Weekly recurring event, frequency (until)').t`Weekly on ${multipleDaysString}, ${untilString}`;
556 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
557 return c('Weekly recurring event, frequency (until)').ngettext(
558 msgid`Every ${interval} week on ${multipleDaysString}, ${untilString}`,
559 `Every ${interval} weeks on ${multipleDaysString}, ${untilString}`,
565 const getCustomMonthlyString = (
566 rruleValue: VcalRrulePropertyValue,
567 { type: endType, count = 1, until }: RruleEnd,
568 monthlyType: MONTHLY_TYPE,
572 const { interval = 1 } = rruleValue;
573 const onDayString = date ? getOnDayString(date, monthlyType) : '';
574 if (endType === END_TYPE.NEVER) {
575 if (interval === 1) {
576 // translator: example: Monthly <on the last Thursday/on day 8>
577 return c('Monthly recurring event, frequency').t`Monthly ${onDayString}`;
579 // translator: When interval = 1 we do not use this string; we use 'monthly' instead. Treat the case of interval = 1 as dummy
580 return c('Monthly recurring event, frequency').ngettext(
581 msgid`Every ${interval} month ${onDayString}`,
582 `Every ${interval} months ${onDayString}`,
586 if (endType === END_TYPE.AFTER_N_TIMES) {
587 const timesString = getTimesString(count);
588 if (interval === 1) {
589 // translator: example: Monthly <on the last Thursday/on day 8>, <3 times>
590 return c('Monthly recurring event, frequency (times)').t`Monthly ${onDayString}, ${timesString}`;
592 // translator: When interval = 1 we do not use this string; we use 'monthly' instead. Treat the case of interval = 1 as dummy
593 return c('Monthly recurring event, frequency (times)').ngettext(
594 msgid`Every ${interval} month ${onDayString}, ${timesString}`,
595 `Every ${interval} months ${onDayString}, ${timesString}`,
599 if (endType === END_TYPE.UNTIL && until) {
600 const dateString = format(until, 'PP', { locale });
601 const untilString = getUntilString(dateString);
602 if (interval === 1) {
603 // translator: example: Monthly <on the last Thursday/on day 8>, <until Feb 29, 2024>
604 return c('Monthly recurring event, frequency (until)').t`Monthly ${onDayString}, ${untilString}`;
606 // translator: When interval = 1 we do not use this string; we use 'monthly' instead. Treat the case of interval = 1 as dummy. Example: Every <2> months <on the last Thursday/on day 8>, <until Feb 29, 2024>
607 return c('Monthly recurring event, frequency (until)').ngettext(
608 msgid`Every ${interval} month ${onDayString}, ${untilString}`,
609 `Every ${interval} months ${onDayString}, ${untilString}`,
615 const getCustomYearlyString = (
616 { interval = 1 }: VcalRrulePropertyValue,
617 { type: endType, count = 1, until }: RruleEnd,
620 if (endType === END_TYPE.NEVER) {
621 if (interval === 1) {
622 return c('Yearly recurring event, frequency').t`Yearly`;
624 // translator: When interval = 1 we do not use this string; we use 'yearly' instead. Treat the case of interval = 1 as dummy
625 return c('Yearly recurring event, frequency').ngettext(
626 msgid`Every ${interval} year`,
627 `Every ${interval} years`,
632 if (endType === END_TYPE.AFTER_N_TIMES) {
633 const timesString = getTimesString(count);
634 if (interval === 1) {
635 // translator: Example: Yearly, <3 times>
636 return c('Yearly recurring event, frequency (times)').t`Yearly, ${timesString}`;
638 // translator: When interval = 1 we do not use this string; we use 'yearly' instead. Treat the case of interval = 1 as dummy. Example: Every <2> years, <3 times>
639 return c('Yearly recurring event, frequency (times)').ngettext(
640 msgid`Every ${interval} year, ${timesString}`,
641 `Every ${interval} years, ${timesString}`,
646 if (endType === END_TYPE.UNTIL && until) {
647 const dateString = format(until, 'PP', { locale });
648 const untilString = getUntilString(dateString);
649 if (interval === 1) {
650 // translator: Example: Yearly, <until Feb 29, 2024>
651 return c('Yearly recurring event, frequency (until)').t`Yearly, ${untilString}`;
653 // translator: When interval = 1 we do not use this string; we use 'yearly' instead. Treat the case of interval = 1 as dummy. Example: Every <2> years, <until Feb 29, 2024>
654 return c('Yearly recurring event, frequency (until)').ngettext(
655 msgid`Every ${interval} year, ${untilString}`,
656 `Every ${interval} years, ${untilString}`,
662 export const getFrequencyString = (
663 rruleValue: VcalRrulePropertyValue,
664 dtstart: VcalDateOrDateTimeProperty,
665 { weekStartsOn, locale }: Pick<GetTimezonedFrequencyStringOptions, 'weekStartsOn' | 'locale'>
667 const { freq, count, until } = rruleValue;
669 const isSimple = getIsRruleSimple(rruleValue);
670 const isCustom = getIsRruleCustom(rruleValue);
671 const startFakeUtcDate = toUTCDate(dtstart.value);
672 const startDay = startFakeUtcDate.getUTCDay();
674 type: getEndType(count, until),
676 until: getUntilDate(until, getPropertyTzid(dtstart)),
681 if (freq === FREQUENCY.DAILY) {
682 return c('Info').t`Custom daily`;
684 if (freq === FREQUENCY.WEEKLY) {
685 return c('Info').t`Custom weekly`;
687 if (freq === FREQUENCY.MONTHLY) {
688 return c('Info').t`Custom monthly`;
690 if (freq === FREQUENCY.YEARLY) {
691 return c('Info').t`Custom yearly`;
693 return c('Info').t`Custom`;
695 if (freq === FREQUENCY.DAILY) {
696 return getCustomDailyString(rruleValue, end, locale);
698 if (freq === FREQUENCY.WEEKLY) {
699 return getCustomWeeklyString(rruleValue, end, weekStartsOn, startFakeUtcDate, locale);
701 if (freq === FREQUENCY.MONTHLY) {
702 const { byday, bysetpos } = rruleValue;
703 const monthType = getMonthType(byday, bysetpos);
704 return getCustomMonthlyString(rruleValue, end, monthType, startFakeUtcDate, locale);
706 if (freq === FREQUENCY.YEARLY) {
707 return getCustomYearlyString(rruleValue, end, locale);
710 if (freq === FREQUENCY.DAILY) {
711 return c('Info').t`Daily`;
713 if (freq === FREQUENCY.WEEKLY) {
714 if (startDay === 0) {
715 return c('Weekly recurring event, frequency').t`Weekly on Sunday`;
717 if (startDay === 1) {
718 return c('Weekly recurring event, frequency').t`Weekly on Monday`;
720 if (startDay === 2) {
721 return c('Weekly recurring event, frequency').t`Weekly on Tuesday`;
723 if (startDay === 3) {
724 return c('Weekly recurring event, frequency').t`Weekly on Wednesday`;
726 if (startDay === 4) {
727 return c('Weekly recurring event, frequency').t`Weekly on Thursday`;
729 if (startDay === 5) {
730 return c('Weekly recurring event, frequency').t`Weekly on Friday`;
732 if (startDay === 6) {
733 return c('Weekly recurring event, frequency').t`Weekly on Saturday`;
736 if (freq === FREQUENCY.MONTHLY) {
737 const { byday, bysetpos } = rruleValue;
738 const monthType = getMonthType(byday, bysetpos);
739 const onDayString = getOnDayString(startFakeUtcDate, monthType);
740 return c('Info').t`Monthly ${onDayString}`;
742 if (freq === FREQUENCY.YEARLY) {
743 return c('Info').t`Yearly`;
748 export const getTimezonedFrequencyString = (
749 rrule: VcalRruleProperty | undefined,
750 dtstart: VcalDateOrDateTimeProperty,
751 options: GetTimezonedFrequencyStringOptions
756 const { value: rruleValue } = rrule;
757 const startTzid = getPropertyTzid(dtstart);
758 const { currentTzid } = options;
760 if (!startTzid || startTzid === currentTzid) {
761 return getFrequencyString(rruleValue, dtstart, options);
764 const isTimezoneStringNeeded = (() => {
765 const { freq, count, until, byday } = rruleValue;
767 const isCustom = getIsRruleCustom(rruleValue);
768 const endType = getEndType(count, until);
773 if ([FREQUENCY.DAILY, FREQUENCY.YEARLY].includes(freq as FREQUENCY)) {
774 return isCustom && endType === END_TYPE.UNTIL;
776 if (freq === FREQUENCY.WEEKLY) {
777 const days = getWeeklyDays(byday);
778 const hasCustomUntil = isCustom && endType === END_TYPE.UNTIL;
779 const hasDays = days.length !== 7;
780 return hasCustomUntil || hasDays;
782 if (freq === FREQUENCY.MONTHLY) {
788 const timezoneString = isTimezoneStringNeeded ? ` (${startTzid})` : '';
789 return getFrequencyString(rruleValue, dtstart, options) + timezoneString;