View | Details | Raw Unified | Return to bug 37707
Collapse All | Expand All

(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/js-date-format.inc (+22 lines)
Lines 118-123 Link Here
118
            }
118
            }
119
        }
119
        }
120
120
121
        /**
122
         * Returns the configured Koha server timezone.
123
         * Useful for components that need to display dates/times in library timezone.
124
         */
125
        window.$timezone = function() {
126
            return def_tz;
127
        }
128
129
        /**
130
         * Converts a dayjs object to a Date for timeline/calendar display.
131
         * Creates a Date that displays these exact values regardless of browser timezone.
132
         * (Strips timezone info so JS interprets the values as browser-local time)
133
         *
134
         * Use this when you need to display a specific date/time from a known timezone
135
         * (e.g., library timezone) and want it to appear the same for all users.
136
         *
137
         * @param {dayjs.Dayjs} dayjsObj - A dayjs object (typically converted to a specific timezone)
138
         * @returns {Date} A Date object that will display the dayjs values in the browser
139
         */
140
        window.$toDisplayDate = function(dayjsObj) {
141
            return new Date(dayjsObj.format('YYYY-MM-DDTHH:mm:ss'));
142
        }
121
143
122
    })();
144
    })();
123
</script>
145
</script>
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/bookings/list.tt (-2 / +5 lines)
Lines 106-118 Link Here
106
                            display_cardnumber: true,
106
                            display_cardnumber: true,
107
                            url: false
107
                            url: false
108
                        });
108
                        });
109
                        // Convert to library timezone for timeline display
110
                        const startServerTz = dayjs(booking.start_date).tz($timezone());
111
                        const endServerTz = dayjs(booking.end_date).tz($timezone());
109
                        bookingsSet.add({
112
                        bookingsSet.add({
110
                            id: booking.booking_id,
113
                            id: booking.booking_id,
111
                            booking: booking.booking_id,
114
                            booking: booking.booking_id,
112
                            patron: booking.patron_id,
115
                            patron: booking.patron_id,
113
                            pickup_library: booking.pickup_library_id,
116
                            pickup_library: booking.pickup_library_id,
114
                            start: dayjs(booking.start_date).toDate(),
117
                            start: $toDisplayDate(startServerTz),
115
                            end: dayjs(booking.end_date).toDate(),
118
                            end: $toDisplayDate(endServerTz),
116
                            content: !isActive ? `<s>${patronContent}</s>` : patronContent,
119
                            content: !isActive ? `<s>${patronContent}</s>` : patronContent,
117
                            className: booking.status === "cancelled" ? "cancelled" : "",
120
                            className: booking.status === "cancelled" ? "cancelled" : "",
118
                            [% IF CAN_user_circulate_manage_bookings %]
121
                            [% IF CAN_user_circulate_manage_bookings %]
(-)a/koha-tmpl/intranet-tmpl/prog/js/modals/place_booking.js (-18 / +26 lines)
Lines 373-379 $("#placeBookingModal").on("show.bs.modal", function (e) { Link Here
373
                            end_date: checkout.due_date,
373
                            end_date: checkout.due_date,
374
                            item_id: checkout.item_id,
374
                            item_id: checkout.item_id,
375
                            patron_id: checkout.patron_id,
375
                            patron_id: checkout.patron_id,
376
                            start_date: new Date().toISOString(),
376
                            start_date: dayjs().format(),
377
                        };
377
                        };
378
                        bookings.unshift(booking);
378
                        bookings.unshift(booking);
379
                    }
379
                    }
Lines 777-791 $("#placeBookingModal").on("show.bs.modal", function (e) { Link Here
777
                            // Range set, update hidden fields and set available items
777
                            // Range set, update hidden fields and set available items
778
                            else if (selectedDates[0] && selectedDates[1]) {
778
                            else if (selectedDates[0] && selectedDates[1]) {
779
                                // set form fields from picker
779
                                // set form fields from picker
780
                                let picker_start = dayjs(selectedDates[0]);
780
                                // Extract local date and send as explicit UTC day boundaries
781
                                let picker_end = dayjs(selectedDates[1]).endOf(
781
                                // This preserves the user's selected DATE regardless of browser timezone
782
                                    "day"
782
                                // Using dayjs.utc() ensures startOf/endOf operate in UTC, not browser TZ
783
                                );
783
                                let startDate = dayjs(selectedDates[0]).format("YYYY-MM-DD");
784
                                let endDate = dayjs(selectedDates[1]).format("YYYY-MM-DD");
784
                                $("#booking_start_date").val(
785
                                $("#booking_start_date").val(
785
                                    picker_start.toISOString()
786
                                    dayjs.utc(startDate).startOf("day").toISOString()
786
                                );
787
                                );
787
                                $("#booking_end_date").val(
788
                                $("#booking_end_date").val(
788
                                    picker_end.toISOString()
789
                                    dayjs.utc(endDate).endOf("day").toISOString()
789
                                );
790
                                );
790
791
791
                                // set available items in select2
792
                                // set available items in select2
Lines 871-879 $("#placeBookingModal").on("show.bs.modal", function (e) { Link Here
871
                    // Iterate through each date within the range of start_date and end_date
872
                    // Iterate through each date within the range of start_date and end_date
872
                    let currentDate = new Date(start_date);
873
                    let currentDate = new Date(start_date);
873
                    while (currentDate <= end_date) {
874
                    while (currentDate <= end_date) {
874
                        const currentDateStr = currentDate
875
                        // Use dayjs to format date in local timezone (not UTC)
875
                            .toISOString()
876
                        const currentDateStr = dayjs(currentDate).format(
876
                            .split("T")[0];
877
                            "YYYY-MM-DD"
878
                        );
877
879
878
                        // If the date key doesn't exist in the hash, create an empty array for it
880
                        // If the date key doesn't exist in the hash, create an empty array for it
879
                        if (!bookingsByDate[currentDateStr]) {
881
                        if (!bookingsByDate[currentDateStr]) {
Lines 896-904 $("#placeBookingModal").on("show.bs.modal", function (e) { Link Here
896
                    periodPicker.config.onDayCreate.push(
898
                    periodPicker.config.onDayCreate.push(
897
                        function dayCreate(dObj, dStr, instance, dayElem) {
899
                        function dayCreate(dObj, dStr, instance, dayElem) {
898
                            const currentDate = dayElem.dateObj;
900
                            const currentDate = dayElem.dateObj;
899
                            const dateString = currentDate
901
                            // Use dayjs to format date in local timezone (not UTC)
900
                                .toISOString()
902
                            const dateString = dayjs(currentDate).format(
901
                                .split("T")[0];
903
                                "YYYY-MM-DD"
904
                            );
902
905
903
                            const isBold = boldDates.some(
906
                            const isBold = boldDates.some(
904
                                boldDate =>
907
                                boldDate =>
Lines 1409-1420 $("#placeBookingForm").on("submit", function (e) { Link Here
1409
                bookings_table.api().ajax.reload();
1412
                bookings_table.api().ajax.reload();
1410
            }
1413
            }
1411
            if (typeof timeline !== "undefined" && timeline !== null) {
1414
            if (typeof timeline !== "undefined" && timeline !== null) {
1415
                // Convert to library timezone for timeline display
1416
                const startServerTz = dayjs(data.start_date).tz($timezone());
1417
                const endServerTz = dayjs(data.end_date).tz($timezone());
1412
                timeline.itemsData.add({
1418
                timeline.itemsData.add({
1413
                    id: data.booking_id,
1419
                    id: data.booking_id,
1414
                    booking: data.booking_id,
1420
                    booking: data.booking_id,
1415
                    patron: data.patron_id,
1421
                    patron: data.patron_id,
1416
                    start: dayjs(data.start_date).toDate(),
1422
                    start: $toDisplayDate(startServerTz),
1417
                    end: dayjs(data.end_date).toDate(),
1423
                    end: $toDisplayDate(endServerTz),
1418
                    content: $patron_to_html(booking_patron, {
1424
                    content: $patron_to_html(booking_patron, {
1419
                        display_cardnumber: true,
1425
                        display_cardnumber: true,
1420
                        url: false,
1426
                        url: false,
Lines 1483-1494 $("#placeBookingForm").on("submit", function (e) { Link Here
1483
                bookings_table.api().ajax.reload();
1489
                bookings_table.api().ajax.reload();
1484
            }
1490
            }
1485
            if (typeof timeline !== "undefined" && timeline !== null) {
1491
            if (typeof timeline !== "undefined" && timeline !== null) {
1492
                // Convert to library timezone for timeline display
1493
                const startServerTz = dayjs(data.start_date).tz($timezone());
1494
                const endServerTz = dayjs(data.end_date).tz($timezone());
1486
                timeline.itemsData.update({
1495
                timeline.itemsData.update({
1487
                    id: data.booking_id,
1496
                    id: data.booking_id,
1488
                    booking: data.booking_id,
1497
                    booking: data.booking_id,
1489
                    patron: data.patron_id,
1498
                    patron: data.patron_id,
1490
                    start: dayjs(data.start_date).toDate(),
1499
                    start: $toDisplayDate(startServerTz),
1491
                    end: dayjs(data.end_date).toDate(),
1500
                    end: $toDisplayDate(endServerTz),
1492
                    content: $patron_to_html(booking_patron, {
1501
                    content: $patron_to_html(booking_patron, {
1493
                        display_cardnumber: true,
1502
                        display_cardnumber: true,
1494
                        url: false,
1503
                        url: false,
1495
- 

Return to bug 37707