@happychef/algorithm 1.2.24 → 1.2.26

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.
@@ -3,6 +3,42 @@
3
3
  const { timeblocksAvailable } = require('./processing/timeblocksAvailable');
4
4
  const { parseTime, getMealTypeByTime } = require('./tableHelpers');
5
5
 
6
+ /**
7
+ * Gets the current date/time components in the specified timezone.
8
+ * Uses Intl.DateTimeFormat for reliable cross-browser timezone conversion.
9
+ * @param {Date} date - The date to convert.
10
+ * @param {string} timeZone - The IANA timezone identifier.
11
+ * @returns {Object} Object with year, month, day, hour, minute, second components.
12
+ */
13
+ function getDatePartsInTimeZone(date, timeZone) {
14
+ const formatter = new Intl.DateTimeFormat('en-US', {
15
+ timeZone,
16
+ year: 'numeric',
17
+ month: '2-digit',
18
+ day: '2-digit',
19
+ hour: '2-digit',
20
+ minute: '2-digit',
21
+ second: '2-digit',
22
+ hourCycle: 'h23', // Use 'h23' instead of hour12:false for consistent 0-23 range
23
+ });
24
+ const parts = formatter.formatToParts(date);
25
+ const values = {};
26
+ for (const part of parts) {
27
+ values[part.type] = part.value;
28
+ }
29
+ // Handle edge case: Chrome may return "24" for midnight with some hourCycle settings
30
+ let hour = parseInt(values.hour, 10);
31
+ if (hour === 24) hour = 0;
32
+ return {
33
+ year: parseInt(values.year, 10),
34
+ month: parseInt(values.month, 10),
35
+ day: parseInt(values.day, 10),
36
+ hour: hour,
37
+ minute: parseInt(values.minute, 10),
38
+ second: parseInt(values.second, 10),
39
+ };
40
+ }
41
+
6
42
  /**
7
43
  * Parses a time string in "HH:MM" format into a Date object on a specific date.
8
44
  * @param {string} dateStr - The date string in "YYYY-MM-DD" format.
@@ -54,10 +90,13 @@ function getAvailableTimeblocks(data, dateStr, reservations, guests, blockedSlot
54
90
  // Time zone for CEST/CET (Europe/Amsterdam)
55
91
  const timeZone = 'Europe/Amsterdam';
56
92
 
57
- // Current date/time in local timezone (system time)
58
- // Note: We assume the server is running in the correct timezone
93
+ // Get current time in target timezone using reliable cross-browser method
59
94
  const now = new Date();
60
- const currentTimeInTimeZone = now;
95
+ const nowParts = getDatePartsInTimeZone(now, timeZone);
96
+ const currentTimeInTimeZone = new Date(
97
+ nowParts.year, nowParts.month - 1, nowParts.day,
98
+ nowParts.hour, nowParts.minute, nowParts.second
99
+ );
61
100
 
62
101
  // Calculate the maximum allowed date
63
102
  const maxAllowedDate = new Date(currentTimeInTimeZone.getTime());
@@ -75,8 +114,9 @@ function getAvailableTimeblocks(data, dateStr, reservations, guests, blockedSlot
75
114
  }
76
115
 
77
116
  // Check if the target date is today in the specified time zone
117
+ // Compare year, month, day components to determine if it's today
78
118
  const isToday =
79
- currentTimeInTimeZone.toDateString() === targetDateInTimeZone.toDateString();
119
+ nowParts.year === year && nowParts.month === month && nowParts.day === day;
80
120
 
81
121
  // Get available time blocks or shifts
82
122
  const availableTimeblocks = timeblocksAvailable(data, dateStr, reservations, guests, blockedSlots, giftcard, isAdmin, duration);
@@ -115,11 +155,14 @@ function getAvailableTimeblocks(data, dateStr, reservations, guests, blockedSlot
115
155
  timeZone: 'Europe/Brussels',
116
156
  hour: '2-digit',
117
157
  minute: '2-digit',
118
- hour12: false,
158
+ hourCycle: 'h23', // Use 'h23' for consistent 0-23 hour range across browsers
119
159
  });
120
160
  const parts = formatter.formatToParts(now);
121
161
  const timeParts = Object.fromEntries(parts.map(p => [p.type, p.value]));
122
- const currentTimeMinutes = parseInt(timeParts.hour, 10) * 60 + parseInt(timeParts.minute, 10);
162
+ // Handle edge case: some browsers may return "24" for midnight
163
+ let hourValue = parseInt(timeParts.hour, 10);
164
+ if (hourValue === 24) hourValue = 0;
165
+ const currentTimeMinutes = hourValue * 60 + parseInt(timeParts.minute, 10);
123
166
 
124
167
  // Check if current time has passed any stop times
125
168
  const breakfastStopMinutes = breakfastStop ? parseTime(breakfastStop) : null;
@@ -12,6 +12,42 @@ function parseTime(timeStr) {
12
12
  return hours * 60 + minutes;
13
13
  }
14
14
 
15
+ /**
16
+ * Gets the current date/time components in the specified timezone.
17
+ * Uses Intl.DateTimeFormat for reliable cross-browser timezone conversion.
18
+ * @param {Date} date - The date to convert.
19
+ * @param {string} timeZone - The IANA timezone identifier.
20
+ * @returns {Object} Object with year, month, day, hour, minute, second components.
21
+ */
22
+ function getDatePartsInTimeZone(date, timeZone) {
23
+ const formatter = new Intl.DateTimeFormat('en-US', {
24
+ timeZone,
25
+ year: 'numeric',
26
+ month: '2-digit',
27
+ day: '2-digit',
28
+ hour: '2-digit',
29
+ minute: '2-digit',
30
+ second: '2-digit',
31
+ hourCycle: 'h23', // Use 'h23' instead of hour12:false for consistent 0-23 range
32
+ });
33
+ const parts = formatter.formatToParts(date);
34
+ const values = {};
35
+ for (const part of parts) {
36
+ values[part.type] = part.value;
37
+ }
38
+ // Handle edge case: Chrome may return "24" for midnight with some hourCycle settings
39
+ let hour = parseInt(values.hour, 10);
40
+ if (hour === 24) hour = 0;
41
+ return {
42
+ year: parseInt(values.year, 10),
43
+ month: parseInt(values.month, 10),
44
+ day: parseInt(values.day, 10),
45
+ hour: hour,
46
+ minute: parseInt(values.minute, 10),
47
+ second: parseInt(values.second, 10),
48
+ };
49
+ }
50
+
15
51
  /**
16
52
  * Checks if a date is within the allowed future range defined by dagenInToekomst.
17
53
  * @param {Object} data - The main data object (to access general settings).
@@ -31,20 +67,21 @@ function isDateWithinAllowedRange(data, dateStr) {
31
67
 
32
68
  const timeZone = 'Europe/Amsterdam';
33
69
 
70
+ // Get current time in the target timezone using reliable cross-browser method
34
71
  const now = new Date();
72
+ const nowParts = getDatePartsInTimeZone(now, timeZone);
35
73
  const currentTimeInTimeZone = new Date(
36
- now.toLocaleString('en-US', { timeZone: timeZone })
74
+ nowParts.year, nowParts.month - 1, nowParts.day,
75
+ nowParts.hour, nowParts.minute, nowParts.second
37
76
  );
38
77
 
39
78
  const maxAllowedDate = new Date(currentTimeInTimeZone.getTime());
40
79
  maxAllowedDate.setDate(maxAllowedDate.getDate() + dagenInToekomst);
41
80
  maxAllowedDate.setHours(23, 59, 59, 999);
42
81
 
82
+ // Parse target date - already in local date format, no timezone conversion needed
43
83
  const [year, month, day] = dateStr.split('-').map(Number);
44
- const targetDate = new Date(Date.UTC(year, month - 1, day));
45
- const targetDateInTimeZone = new Date(
46
- targetDate.toLocaleString('en-US', { timeZone: timeZone })
47
- );
84
+ const targetDateInTimeZone = new Date(year, month - 1, day);
48
85
 
49
86
  return targetDateInTimeZone <= maxAllowedDate;
50
87
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happychef/algorithm",
3
- "version": "1.2.24",
3
+ "version": "1.2.26",
4
4
  "description": "Restaurant and reservation algorithm utilities",
5
5
  "main": "index.js",
6
6
  "scripts": {