add-to-calendar-button 2.12.9 → 2.12.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Style: 3D
7
7
  *
8
- * Version: 2.12.9
8
+ * Version: 2.12.11
9
9
  * Creator: Jens Kuerschner (https://jekuer.com)
10
10
  * Project: https://github.com/add2cal/add-to-calendar-button
11
11
  * License: Elastic License 2.0 (ELv2) (https://github.com/add2cal/add-to-calendar-button/blob/main/LICENSE.txt)
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Style: Date
7
7
  *
8
- * Version: 2.12.9
8
+ * Version: 2.12.11
9
9
  * Creator: Jens Kuerschner (https://jekuer.com)
10
10
  * Project: https://github.com/add2cal/add-to-calendar-button
11
11
  * License: Elastic License 2.0 (ELv2) (https://github.com/add2cal/add-to-calendar-button/blob/main/LICENSE.txt)
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Style: Flat
7
7
  *
8
- * Version: 2.12.9
8
+ * Version: 2.12.11
9
9
  * Creator: Jens Kuerschner (https://jekuer.com)
10
10
  * Project: https://github.com/add2cal/add-to-calendar-button
11
11
  * License: Elastic License 2.0 (ELv2) (https://github.com/add2cal/add-to-calendar-button/blob/main/LICENSE.txt)
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Style: Neumorphism
7
7
  *
8
- * Version: 2.12.9
8
+ * Version: 2.12.11
9
9
  * Creator: Jens Kuerschner (https://jekuer.com)
10
10
  * Project: https://github.com/add2cal/add-to-calendar-button
11
11
  * License: Elastic License 2.0 (ELv2) (https://github.com/add2cal/add-to-calendar-button/blob/main/LICENSE.txt)
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Style: Round
7
7
  *
8
- * Version: 2.12.9
8
+ * Version: 2.12.11
9
9
  * Creator: Jens Kuerschner (https://jekuer.com)
10
10
  * Project: https://github.com/add2cal/add-to-calendar-button
11
11
  * License: Elastic License 2.0 (ELv2) (https://github.com/add2cal/add-to-calendar-button/blob/main/LICENSE.txt)
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Style: Simple
7
7
  *
8
- * Version: 2.12.9
8
+ * Version: 2.12.11
9
9
  * Creator: Jens Kuerschner (https://jekuer.com)
10
10
  * Project: https://github.com/add2cal/add-to-calendar-button
11
11
  * License: Elastic License 2.0 (ELv2) (https://github.com/add2cal/add-to-calendar-button/blob/main/LICENSE.txt)
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Style: Text
7
7
  *
8
- * Version: 2.12.9
8
+ * Version: 2.12.11
9
9
  * Creator: Jens Kuerschner (https://jekuer.com)
10
10
  * Project: https://github.com/add2cal/add-to-calendar-button
11
11
  * License: Elastic License 2.0 (ELv2) (https://github.com/add2cal/add-to-calendar-button/blob/main/LICENSE.txt)
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Style: Default
7
7
  *
8
- * Version: 2.12.9
8
+ * Version: 2.12.11
9
9
  * Creator: Jens Kuerschner (https://jekuer.com)
10
10
  * Project: https://github.com/add2cal/add-to-calendar-button
11
11
  * License: Elastic License 2.0 (ELv2) (https://github.com/add2cal/add-to-calendar-button/blob/main/LICENSE.txt)
@@ -223,14 +223,14 @@ function tzlib_get_timezones(jsonType = false) {
223
223
  * Add to Calendar Button
224
224
  * ++++++++++++++++++++++
225
225
  *
226
- * Version: 2.12.9
226
+ * Version: 2.12.11
227
227
  * Creator: Jens Kuerschner (https://jekuer.com)
228
228
  * Project: https://github.com/add2cal/add-to-calendar-button
229
229
  * License: Elastic License 2.0 (ELv2) (https://github.com/add2cal/add-to-calendar-button/blob/main/LICENSE.txt)
230
230
  * Note: DO NOT REMOVE THE COPYRIGHT NOTICE ABOVE!
231
231
  *
232
232
  */
233
- const atcbVersion = '2.12.9';
233
+ const atcbVersion = '2.12.11';
234
234
  const atcbCssTemplate = {};
235
235
  const atcbIsBrowser = () => {
236
236
  if (typeof window === 'undefined') {
@@ -595,43 +595,52 @@ function atcb_decorate_data_rrule(data) {
595
595
  function atcb_decorate_data_recurring_events(data) {
596
596
  const startDate = data.dates?.[0].startDate || data.startDate;
597
597
  const startTime = data.dates?.[0].startTime || data.startTime;
598
+ const tzid = data.dates?.[0].timeZone || data.timeZone || 'UTC';
599
+ const offset = startTime && startTime !== '' ? tzlib_get_offset(tzid, startDate, startTime) : '';
598
600
  const startDateTime = (function () {
599
601
  if (startTime && startTime !== '') {
600
- const offset = tzlib_get_offset(data.dates?.[0].timeZone || data.timeZone, startDate, startTime);
601
602
  return new Date(startDate + ' ' + startTime + ':00 GMT' + offset);
602
603
  }
603
- return new Date(startDate + 'T00:00:00Z');
604
+ const localMidnightOffset = tzlib_get_offset(tzid, startDate, '00:00');
605
+ return new Date(`${startDate} 00:00:00 GMT${localMidnightOffset}`);
604
606
  })();
605
607
  const isAllDay = !(startTime && startTime !== '');
606
608
  const occurenceData = atcb_getNextOccurrence(data.recurrence, startDateTime, isAllDay);
607
609
  if (!occurenceData || !occurenceData.nextOccurrence) {
608
610
  return data;
609
611
  }
610
- const nextOccurrence =
611
- String(occurenceData.nextOccurrence.getFullYear()) +
612
- '-' +
613
- String(occurenceData.nextOccurrence.getMonth() + 1).padStart(2, '0') +
614
- '-' +
615
- String(occurenceData.nextOccurrence.getDate()).padStart(2, '0') +
616
- (startTime && startTime !== '' ? 'T' + String(occurenceData.nextOccurrence.getHours()).padStart(2, '0') + ':' + String(occurenceData.nextOccurrence.getMinutes()).padStart(2, '0') : '');
617
- data.startDate = nextOccurrence.slice(0, 10);
612
+ function formatInTz(dateObj, timeZone, includeTime) {
613
+ const opts = includeTime ? { timeZone, hour12: false, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' } : { timeZone, hour12: false, year: 'numeric', month: '2-digit', day: '2-digit' };
614
+ const parts = new Intl.DateTimeFormat('en-CA', opts).formatToParts(dateObj);
615
+ const get = (t) => parts.find((p) => p.type === t)?.value || '';
616
+ return {
617
+ date: `${get('year')}-${get('month')}-${get('day')}`,
618
+ time: includeTime ? `${get('hour')}:${get('minute')}` : '',
619
+ };
620
+ }
621
+ const hasExplicitTimeRule = /;BYHOUR=/i.test(data.recurrence);
622
+ const nextLocalDate = formatInTz(occurenceData.nextOccurrence, tzid, false).date;
623
+ let nextStartTime = '';
618
624
  if (startTime && startTime !== '') {
619
- data.startTime = nextOccurrence.slice(11, 16);
625
+ nextStartTime = hasExplicitTimeRule
626
+ ? formatInTz(occurenceData.nextOccurrence, tzid, true).time // RRULE drives time
627
+ : startTime;
628
+ }
629
+ data.startDate = nextLocalDate;
630
+ if (nextStartTime) {
631
+ data.startTime = nextStartTime;
620
632
  }
621
633
  const endDate = data.dates?.[0].endDate || data.endDate || startDate;
622
634
  const endTime = data.dates?.[0].endTime || data.endTime || '';
623
- const diff = new Date(endDate + (endTime && endTime !== '' ? 'T' + endTime : '')).getTime() - new Date(startDate + (startTime && startTime !== '' ? 'T' + startTime : '')).getTime();
624
- const newEndDateTime = new Date(occurenceData.nextOccurrence.getTime() + diff);
625
- const newEndDateTimeString =
626
- String(newEndDateTime.getFullYear()) +
627
- '-' +
628
- String(newEndDateTime.getMonth() + 1).padStart(2, '0') +
629
- '-' +
630
- String(newEndDateTime.getDate()).padStart(2, '0') +
631
- (endTime && endTime !== '' ? 'T' + String(newEndDateTime.getHours()).padStart(2, '0') + ':' + String(newEndDateTime.getMinutes()).padStart(2, '0') : '');
632
- data.endDate = newEndDateTimeString.slice(0, 10);
635
+ const origStart = startTime && startTime !== '' ? new Date(`${startDate} ${startTime}:00 GMT${tzlib_get_offset(tzid, startDate, startTime)}`) : new Date(`${startDate} 00:00:00 GMT${tzlib_get_offset(tzid, startDate, '00:00')}`);
636
+ const origEnd = endTime && endTime !== '' ? new Date(`${endDate} ${endTime}:00 GMT${tzlib_get_offset(tzid, endDate, endTime)}`) : new Date(`${endDate} 00:00:00 GMT${tzlib_get_offset(tzid, endDate, '00:00')}`);
637
+ const diff = origEnd.getTime() - origStart.getTime();
638
+ const newStartInstant = nextStartTime ? new Date(`${data.startDate} ${nextStartTime}:00 GMT${tzlib_get_offset(tzid, data.startDate, nextStartTime)}`) : new Date(`${data.startDate} 00:00:00 GMT${tzlib_get_offset(tzid, data.startDate, '00:00')}`);
639
+ const newEndDateTime = new Date(newStartInstant.getTime() + diff);
640
+ const nextEndLocal = formatInTz(newEndDateTime, tzid, !!(endTime && endTime !== ''));
641
+ data.endDate = nextEndLocal.date;
633
642
  if (endTime && endTime !== '') {
634
- data.endTime = newEndDateTimeString.slice(11, 16);
643
+ data.endTime = nextEndLocal.time;
635
644
  }
636
645
  if ((data.recurrence_count && data.recurrence_count !== '') || (data.recurrence_until && data.recurrence_until !== '')) {
637
646
  if (occurenceData.adjustedCount < 2) {
@@ -3546,14 +3555,7 @@ function atcb_parseByWeekdayTokens(rawByDay) {
3546
3555
  digits = digits.slice(1);
3547
3556
  }
3548
3557
  if (!digits || digits.length > 2) continue;
3549
- let validDigits = true;
3550
- for (let i = 0; i < digits.length; i++) {
3551
- const code = digits.charCodeAt(i);
3552
- if (code < 48 || code > 57) {
3553
- validDigits = false;
3554
- break;
3555
- }
3556
- }
3558
+ const validDigits = typeof digits === 'string' && /^\d+$/.test(digits);
3557
3559
  if (!validDigits) continue;
3558
3560
  const abs = parseInt(digits, 10);
3559
3561
  if (abs < 1 || abs > 53) continue;
@@ -223,14 +223,14 @@ function tzlib_get_timezones(jsonType = false) {
223
223
  * Add to Calendar Button
224
224
  * ++++++++++++++++++++++
225
225
  *
226
- * Version: 2.12.9
226
+ * Version: 2.12.11
227
227
  * Creator: Jens Kuerschner (https://jekuer.com)
228
228
  * Project: https://github.com/add2cal/add-to-calendar-button
229
229
  * License: Elastic License 2.0 (ELv2) (https://github.com/add2cal/add-to-calendar-button/blob/main/LICENSE.txt)
230
230
  * Note: DO NOT REMOVE THE COPYRIGHT NOTICE ABOVE!
231
231
  *
232
232
  */
233
- const atcbVersion = '2.12.9';
233
+ const atcbVersion = '2.12.11';
234
234
  const atcbCssTemplate = {
235
235
  if (typeof window === 'undefined') {
236
236
  return false;
@@ -594,43 +594,52 @@ function atcb_decorate_data_rrule(data) {
594
594
  function atcb_decorate_data_recurring_events(data) {
595
595
  const startDate = data.dates?.[0].startDate || data.startDate;
596
596
  const startTime = data.dates?.[0].startTime || data.startTime;
597
+ const tzid = data.dates?.[0].timeZone || data.timeZone || 'UTC';
598
+ const offset = startTime && startTime !== '' ? tzlib_get_offset(tzid, startDate, startTime) : '';
597
599
  const startDateTime = (function () {
598
600
  if (startTime && startTime !== '') {
599
- const offset = tzlib_get_offset(data.dates?.[0].timeZone || data.timeZone, startDate, startTime);
600
601
  return new Date(startDate + ' ' + startTime + ':00 GMT' + offset);
601
602
  }
602
- return new Date(startDate + 'T00:00:00Z');
603
+ const localMidnightOffset = tzlib_get_offset(tzid, startDate, '00:00');
604
+ return new Date(`${startDate} 00:00:00 GMT${localMidnightOffset}`);
603
605
  })();
604
606
  const isAllDay = !(startTime && startTime !== '');
605
607
  const occurenceData = atcb_getNextOccurrence(data.recurrence, startDateTime, isAllDay);
606
608
  if (!occurenceData || !occurenceData.nextOccurrence) {
607
609
  return data;
608
610
  }
609
- const nextOccurrence =
610
- String(occurenceData.nextOccurrence.getFullYear()) +
611
- '-' +
612
- String(occurenceData.nextOccurrence.getMonth() + 1).padStart(2, '0') +
613
- '-' +
614
- String(occurenceData.nextOccurrence.getDate()).padStart(2, '0') +
615
- (startTime && startTime !== '' ? 'T' + String(occurenceData.nextOccurrence.getHours()).padStart(2, '0') + ':' + String(occurenceData.nextOccurrence.getMinutes()).padStart(2, '0') : '');
616
- data.startDate = nextOccurrence.slice(0, 10);
611
+ function formatInTz(dateObj, timeZone, includeTime) {
612
+ const opts = includeTime ? { timeZone, hour12: false, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' } : { timeZone, hour12: false, year: 'numeric', month: '2-digit', day: '2-digit' };
613
+ const parts = new Intl.DateTimeFormat('en-CA', opts).formatToParts(dateObj);
614
+ const get = (t) => parts.find((p) => p.type === t)?.value || '';
615
+ return {
616
+ date: `${get('year')}-${get('month')}-${get('day')}`,
617
+ time: includeTime ? `${get('hour')}:${get('minute')}` : '',
618
+ };
619
+ }
620
+ const hasExplicitTimeRule = /;BYHOUR=/i.test(data.recurrence);
621
+ const nextLocalDate = formatInTz(occurenceData.nextOccurrence, tzid, false).date;
622
+ let nextStartTime = '';
617
623
  if (startTime && startTime !== '') {
618
- data.startTime = nextOccurrence.slice(11, 16);
624
+ nextStartTime = hasExplicitTimeRule
625
+ ? formatInTz(occurenceData.nextOccurrence, tzid, true).time // RRULE drives time
626
+ : startTime;
627
+ }
628
+ data.startDate = nextLocalDate;
629
+ if (nextStartTime) {
630
+ data.startTime = nextStartTime;
619
631
  }
620
632
  const endDate = data.dates?.[0].endDate || data.endDate || startDate;
621
633
  const endTime = data.dates?.[0].endTime || data.endTime || '';
622
- const diff = new Date(endDate + (endTime && endTime !== '' ? 'T' + endTime : '')).getTime() - new Date(startDate + (startTime && startTime !== '' ? 'T' + startTime : '')).getTime();
623
- const newEndDateTime = new Date(occurenceData.nextOccurrence.getTime() + diff);
624
- const newEndDateTimeString =
625
- String(newEndDateTime.getFullYear()) +
626
- '-' +
627
- String(newEndDateTime.getMonth() + 1).padStart(2, '0') +
628
- '-' +
629
- String(newEndDateTime.getDate()).padStart(2, '0') +
630
- (endTime && endTime !== '' ? 'T' + String(newEndDateTime.getHours()).padStart(2, '0') + ':' + String(newEndDateTime.getMinutes()).padStart(2, '0') : '');
631
- data.endDate = newEndDateTimeString.slice(0, 10);
634
+ const origStart = startTime && startTime !== '' ? new Date(`${startDate} ${startTime}:00 GMT${tzlib_get_offset(tzid, startDate, startTime)}`) : new Date(`${startDate} 00:00:00 GMT${tzlib_get_offset(tzid, startDate, '00:00')}`);
635
+ const origEnd = endTime && endTime !== '' ? new Date(`${endDate} ${endTime}:00 GMT${tzlib_get_offset(tzid, endDate, endTime)}`) : new Date(`${endDate} 00:00:00 GMT${tzlib_get_offset(tzid, endDate, '00:00')}`);
636
+ const diff = origEnd.getTime() - origStart.getTime();
637
+ const newStartInstant = nextStartTime ? new Date(`${data.startDate} ${nextStartTime}:00 GMT${tzlib_get_offset(tzid, data.startDate, nextStartTime)}`) : new Date(`${data.startDate} 00:00:00 GMT${tzlib_get_offset(tzid, data.startDate, '00:00')}`);
638
+ const newEndDateTime = new Date(newStartInstant.getTime() + diff);
639
+ const nextEndLocal = formatInTz(newEndDateTime, tzid, !!(endTime && endTime !== ''));
640
+ data.endDate = nextEndLocal.date;
632
641
  if (endTime && endTime !== '') {
633
- data.endTime = newEndDateTimeString.slice(11, 16);
642
+ data.endTime = nextEndLocal.time;
634
643
  }
635
644
  if ((data.recurrence_count && data.recurrence_count !== '') || (data.recurrence_until && data.recurrence_until !== '')) {
636
645
  if (occurenceData.adjustedCount < 2) {
@@ -3545,14 +3554,7 @@ function atcb_parseByWeekdayTokens(rawByDay) {
3545
3554
  digits = digits.slice(1);
3546
3555
  }
3547
3556
  if (!digits || digits.length > 2) continue;
3548
- let validDigits = true;
3549
- for (let i = 0; i < digits.length; i++) {
3550
- const code = digits.charCodeAt(i);
3551
- if (code < 48 || code > 57) {
3552
- validDigits = false;
3553
- break;
3554
- }
3555
- }
3557
+ const validDigits = typeof digits === 'string' && /^\d+$/.test(digits);
3556
3558
  if (!validDigits) continue;
3557
3559
  const abs = parseInt(digits, 10);
3558
3560
  if (abs < 1 || abs > 53) continue;
@@ -223,14 +223,14 @@ function tzlib_get_timezones(jsonType = false) {
223
223
  * Add to Calendar Button
224
224
  * ++++++++++++++++++++++
225
225
  *
226
- * Version: 2.12.9
226
+ * Version: 2.12.11
227
227
  * Creator: Jens Kuerschner (https://jekuer.com)
228
228
  * Project: https://github.com/add2cal/add-to-calendar-button
229
229
  * License: Elastic License 2.0 (ELv2) (https://github.com/add2cal/add-to-calendar-button/blob/main/LICENSE.txt)
230
230
  * Note: DO NOT REMOVE THE COPYRIGHT NOTICE ABOVE!
231
231
  *
232
232
  */
233
- const atcbVersion = '2.12.9';
233
+ const atcbVersion = '2.12.11';
234
234
  const atcbCssTemplate = {};
235
235
  const atcbIsBrowser = () => {
236
236
  if (typeof window === 'undefined') {
@@ -595,43 +595,52 @@ function atcb_decorate_data_rrule(data) {
595
595
  function atcb_decorate_data_recurring_events(data) {
596
596
  const startDate = data.dates?.[0].startDate || data.startDate;
597
597
  const startTime = data.dates?.[0].startTime || data.startTime;
598
+ const tzid = data.dates?.[0].timeZone || data.timeZone || 'UTC';
599
+ const offset = startTime && startTime !== '' ? tzlib_get_offset(tzid, startDate, startTime) : '';
598
600
  const startDateTime = (function () {
599
601
  if (startTime && startTime !== '') {
600
- const offset = tzlib_get_offset(data.dates?.[0].timeZone || data.timeZone, startDate, startTime);
601
602
  return new Date(startDate + ' ' + startTime + ':00 GMT' + offset);
602
603
  }
603
- return new Date(startDate + 'T00:00:00Z');
604
+ const localMidnightOffset = tzlib_get_offset(tzid, startDate, '00:00');
605
+ return new Date(`${startDate} 00:00:00 GMT${localMidnightOffset}`);
604
606
  })();
605
607
  const isAllDay = !(startTime && startTime !== '');
606
608
  const occurenceData = atcb_getNextOccurrence(data.recurrence, startDateTime, isAllDay);
607
609
  if (!occurenceData || !occurenceData.nextOccurrence) {
608
610
  return data;
609
611
  }
610
- const nextOccurrence =
611
- String(occurenceData.nextOccurrence.getFullYear()) +
612
- '-' +
613
- String(occurenceData.nextOccurrence.getMonth() + 1).padStart(2, '0') +
614
- '-' +
615
- String(occurenceData.nextOccurrence.getDate()).padStart(2, '0') +
616
- (startTime && startTime !== '' ? 'T' + String(occurenceData.nextOccurrence.getHours()).padStart(2, '0') + ':' + String(occurenceData.nextOccurrence.getMinutes()).padStart(2, '0') : '');
617
- data.startDate = nextOccurrence.slice(0, 10);
612
+ function formatInTz(dateObj, timeZone, includeTime) {
613
+ const opts = includeTime ? { timeZone, hour12: false, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' } : { timeZone, hour12: false, year: 'numeric', month: '2-digit', day: '2-digit' };
614
+ const parts = new Intl.DateTimeFormat('en-CA', opts).formatToParts(dateObj);
615
+ const get = (t) => parts.find((p) => p.type === t)?.value || '';
616
+ return {
617
+ date: `${get('year')}-${get('month')}-${get('day')}`,
618
+ time: includeTime ? `${get('hour')}:${get('minute')}` : '',
619
+ };
620
+ }
621
+ const hasExplicitTimeRule = /;BYHOUR=/i.test(data.recurrence);
622
+ const nextLocalDate = formatInTz(occurenceData.nextOccurrence, tzid, false).date;
623
+ let nextStartTime = '';
618
624
  if (startTime && startTime !== '') {
619
- data.startTime = nextOccurrence.slice(11, 16);
625
+ nextStartTime = hasExplicitTimeRule
626
+ ? formatInTz(occurenceData.nextOccurrence, tzid, true).time // RRULE drives time
627
+ : startTime;
628
+ }
629
+ data.startDate = nextLocalDate;
630
+ if (nextStartTime) {
631
+ data.startTime = nextStartTime;
620
632
  }
621
633
  const endDate = data.dates?.[0].endDate || data.endDate || startDate;
622
634
  const endTime = data.dates?.[0].endTime || data.endTime || '';
623
- const diff = new Date(endDate + (endTime && endTime !== '' ? 'T' + endTime : '')).getTime() - new Date(startDate + (startTime && startTime !== '' ? 'T' + startTime : '')).getTime();
624
- const newEndDateTime = new Date(occurenceData.nextOccurrence.getTime() + diff);
625
- const newEndDateTimeString =
626
- String(newEndDateTime.getFullYear()) +
627
- '-' +
628
- String(newEndDateTime.getMonth() + 1).padStart(2, '0') +
629
- '-' +
630
- String(newEndDateTime.getDate()).padStart(2, '0') +
631
- (endTime && endTime !== '' ? 'T' + String(newEndDateTime.getHours()).padStart(2, '0') + ':' + String(newEndDateTime.getMinutes()).padStart(2, '0') : '');
632
- data.endDate = newEndDateTimeString.slice(0, 10);
635
+ const origStart = startTime && startTime !== '' ? new Date(`${startDate} ${startTime}:00 GMT${tzlib_get_offset(tzid, startDate, startTime)}`) : new Date(`${startDate} 00:00:00 GMT${tzlib_get_offset(tzid, startDate, '00:00')}`);
636
+ const origEnd = endTime && endTime !== '' ? new Date(`${endDate} ${endTime}:00 GMT${tzlib_get_offset(tzid, endDate, endTime)}`) : new Date(`${endDate} 00:00:00 GMT${tzlib_get_offset(tzid, endDate, '00:00')}`);
637
+ const diff = origEnd.getTime() - origStart.getTime();
638
+ const newStartInstant = nextStartTime ? new Date(`${data.startDate} ${nextStartTime}:00 GMT${tzlib_get_offset(tzid, data.startDate, nextStartTime)}`) : new Date(`${data.startDate} 00:00:00 GMT${tzlib_get_offset(tzid, data.startDate, '00:00')}`);
639
+ const newEndDateTime = new Date(newStartInstant.getTime() + diff);
640
+ const nextEndLocal = formatInTz(newEndDateTime, tzid, !!(endTime && endTime !== ''));
641
+ data.endDate = nextEndLocal.date;
633
642
  if (endTime && endTime !== '') {
634
- data.endTime = newEndDateTimeString.slice(11, 16);
643
+ data.endTime = nextEndLocal.time;
635
644
  }
636
645
  if ((data.recurrence_count && data.recurrence_count !== '') || (data.recurrence_until && data.recurrence_until !== '')) {
637
646
  if (occurenceData.adjustedCount < 2) {
@@ -4266,14 +4275,7 @@ function atcb_parseByWeekdayTokens(rawByDay) {
4266
4275
  digits = digits.slice(1);
4267
4276
  }
4268
4277
  if (!digits || digits.length > 2) continue;
4269
- let validDigits = true;
4270
- for (let i = 0; i < digits.length; i++) {
4271
- const code = digits.charCodeAt(i);
4272
- if (code < 48 || code > 57) {
4273
- validDigits = false;
4274
- break;
4275
- }
4276
- }
4278
+ const validDigits = typeof digits === 'string' && /^\d+$/.test(digits);
4277
4279
  if (!validDigits) continue;
4278
4280
  const abs = parseInt(digits, 10);
4279
4281
  if (abs < 1 || abs > 53) continue;
package/dist/atcb.js CHANGED
@@ -223,14 +223,14 @@ function tzlib_get_timezones(jsonType = false) {
223
223
  * Add to Calendar Button
224
224
  * ++++++++++++++++++++++
225
225
  *
226
- * Version: 2.12.9
226
+ * Version: 2.12.11
227
227
  * Creator: Jens Kuerschner (https://jekuer.com)
228
228
  * Project: https://github.com/add2cal/add-to-calendar-button
229
229
  * License: Elastic License 2.0 (ELv2) (https://github.com/add2cal/add-to-calendar-button/blob/main/LICENSE.txt)
230
230
  * Note: DO NOT REMOVE THE COPYRIGHT NOTICE ABOVE!
231
231
  *
232
232
  */
233
- const atcbVersion = '2.12.9';
233
+ const atcbVersion = '2.12.11';
234
234
  const atcbCssTemplate = {
235
235
  if (typeof window === 'undefined') {
236
236
  return false;
@@ -594,43 +594,52 @@ function atcb_decorate_data_rrule(data) {
594
594
  function atcb_decorate_data_recurring_events(data) {
595
595
  const startDate = data.dates?.[0].startDate || data.startDate;
596
596
  const startTime = data.dates?.[0].startTime || data.startTime;
597
+ const tzid = data.dates?.[0].timeZone || data.timeZone || 'UTC';
598
+ const offset = startTime && startTime !== '' ? tzlib_get_offset(tzid, startDate, startTime) : '';
597
599
  const startDateTime = (function () {
598
600
  if (startTime && startTime !== '') {
599
- const offset = tzlib_get_offset(data.dates?.[0].timeZone || data.timeZone, startDate, startTime);
600
601
  return new Date(startDate + ' ' + startTime + ':00 GMT' + offset);
601
602
  }
602
- return new Date(startDate + 'T00:00:00Z');
603
+ const localMidnightOffset = tzlib_get_offset(tzid, startDate, '00:00');
604
+ return new Date(`${startDate} 00:00:00 GMT${localMidnightOffset}`);
603
605
  })();
604
606
  const isAllDay = !(startTime && startTime !== '');
605
607
  const occurenceData = atcb_getNextOccurrence(data.recurrence, startDateTime, isAllDay);
606
608
  if (!occurenceData || !occurenceData.nextOccurrence) {
607
609
  return data;
608
610
  }
609
- const nextOccurrence =
610
- String(occurenceData.nextOccurrence.getFullYear()) +
611
- '-' +
612
- String(occurenceData.nextOccurrence.getMonth() + 1).padStart(2, '0') +
613
- '-' +
614
- String(occurenceData.nextOccurrence.getDate()).padStart(2, '0') +
615
- (startTime && startTime !== '' ? 'T' + String(occurenceData.nextOccurrence.getHours()).padStart(2, '0') + ':' + String(occurenceData.nextOccurrence.getMinutes()).padStart(2, '0') : '');
616
- data.startDate = nextOccurrence.slice(0, 10);
611
+ function formatInTz(dateObj, timeZone, includeTime) {
612
+ const opts = includeTime ? { timeZone, hour12: false, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' } : { timeZone, hour12: false, year: 'numeric', month: '2-digit', day: '2-digit' };
613
+ const parts = new Intl.DateTimeFormat('en-CA', opts).formatToParts(dateObj);
614
+ const get = (t) => parts.find((p) => p.type === t)?.value || '';
615
+ return {
616
+ date: `${get('year')}-${get('month')}-${get('day')}`,
617
+ time: includeTime ? `${get('hour')}:${get('minute')}` : '',
618
+ };
619
+ }
620
+ const hasExplicitTimeRule = /;BYHOUR=/i.test(data.recurrence);
621
+ const nextLocalDate = formatInTz(occurenceData.nextOccurrence, tzid, false).date;
622
+ let nextStartTime = '';
617
623
  if (startTime && startTime !== '') {
618
- data.startTime = nextOccurrence.slice(11, 16);
624
+ nextStartTime = hasExplicitTimeRule
625
+ ? formatInTz(occurenceData.nextOccurrence, tzid, true).time // RRULE drives time
626
+ : startTime;
627
+ }
628
+ data.startDate = nextLocalDate;
629
+ if (nextStartTime) {
630
+ data.startTime = nextStartTime;
619
631
  }
620
632
  const endDate = data.dates?.[0].endDate || data.endDate || startDate;
621
633
  const endTime = data.dates?.[0].endTime || data.endTime || '';
622
- const diff = new Date(endDate + (endTime && endTime !== '' ? 'T' + endTime : '')).getTime() - new Date(startDate + (startTime && startTime !== '' ? 'T' + startTime : '')).getTime();
623
- const newEndDateTime = new Date(occurenceData.nextOccurrence.getTime() + diff);
624
- const newEndDateTimeString =
625
- String(newEndDateTime.getFullYear()) +
626
- '-' +
627
- String(newEndDateTime.getMonth() + 1).padStart(2, '0') +
628
- '-' +
629
- String(newEndDateTime.getDate()).padStart(2, '0') +
630
- (endTime && endTime !== '' ? 'T' + String(newEndDateTime.getHours()).padStart(2, '0') + ':' + String(newEndDateTime.getMinutes()).padStart(2, '0') : '');
631
- data.endDate = newEndDateTimeString.slice(0, 10);
634
+ const origStart = startTime && startTime !== '' ? new Date(`${startDate} ${startTime}:00 GMT${tzlib_get_offset(tzid, startDate, startTime)}`) : new Date(`${startDate} 00:00:00 GMT${tzlib_get_offset(tzid, startDate, '00:00')}`);
635
+ const origEnd = endTime && endTime !== '' ? new Date(`${endDate} ${endTime}:00 GMT${tzlib_get_offset(tzid, endDate, endTime)}`) : new Date(`${endDate} 00:00:00 GMT${tzlib_get_offset(tzid, endDate, '00:00')}`);
636
+ const diff = origEnd.getTime() - origStart.getTime();
637
+ const newStartInstant = nextStartTime ? new Date(`${data.startDate} ${nextStartTime}:00 GMT${tzlib_get_offset(tzid, data.startDate, nextStartTime)}`) : new Date(`${data.startDate} 00:00:00 GMT${tzlib_get_offset(tzid, data.startDate, '00:00')}`);
638
+ const newEndDateTime = new Date(newStartInstant.getTime() + diff);
639
+ const nextEndLocal = formatInTz(newEndDateTime, tzid, !!(endTime && endTime !== ''));
640
+ data.endDate = nextEndLocal.date;
632
641
  if (endTime && endTime !== '') {
633
- data.endTime = newEndDateTimeString.slice(11, 16);
642
+ data.endTime = nextEndLocal.time;
634
643
  }
635
644
  if ((data.recurrence_count && data.recurrence_count !== '') || (data.recurrence_until && data.recurrence_until !== '')) {
636
645
  if (occurenceData.adjustedCount < 2) {
@@ -4265,14 +4274,7 @@ function atcb_parseByWeekdayTokens(rawByDay) {
4265
4274
  digits = digits.slice(1);
4266
4275
  }
4267
4276
  if (!digits || digits.length > 2) continue;
4268
- let validDigits = true;
4269
- for (let i = 0; i < digits.length; i++) {
4270
- const code = digits.charCodeAt(i);
4271
- if (code < 48 || code > 57) {
4272
- validDigits = false;
4273
- break;
4274
- }
4275
- }
4277
+ const validDigits = typeof digits === 'string' && /^\d+$/.test(digits);
4276
4278
  if (!validDigits) continue;
4277
4279
  const abs = parseInt(digits, 10);
4278
4280
  if (abs < 1 || abs > 53) continue;