diff --git a/src/org/labkey/targetedms/view/instrumentCalendar.jsp b/src/org/labkey/targetedms/view/instrumentCalendar.jsp index 2e6c35d1a..6fe5b5e73 100644 --- a/src/org/labkey/targetedms/view/instrumentCalendar.jsp +++ b/src/org/labkey/targetedms/view/instrumentCalendar.jsp @@ -23,6 +23,7 @@ { dependencies.add("internal/jQuery"); dependencies.add("targetedms/yearCalendar"); + dependencies.add("TargetedMS/js/scheduleUtils.js"); } %> @@ -51,8 +52,8 @@ $('#delete-event').css('display', event.annotation ? '' : 'none'); $('#event-modal input[name="event-description"]').val(event.annotation ? event.annotation.description : ''); - $('#event-modal input[name="event-start-date"]').val(startDate.getFullYear() + '-' + (startDate.getMonth() + 1 < 10 ? '0' : '') + (startDate.getMonth() + 1) + '-' + (startDate.getDate() < 10 ? '0' : '') + startDate.getDate()); - $('#event-modal input[name="event-end-date"]').val(endDate.getFullYear() + '-' + (endDate.getMonth() + 1 < 10 ? '0' : '') + (endDate.getMonth() + 1) + '-' + (endDate.getDate() < 10 ? '0' : '') + endDate.getDate()); + $('#event-modal input[name="event-start-date"]').val(ScheduleUtils.toDateValue(startDate)); + $('#event-modal input[name="event-end-date"]').val(ScheduleUtils.toDateValue(endDate)); $('#annotation-save-error').text(''); $('#event-modal').modal(); } diff --git a/test/src/org/labkey/test/tests/targetedms/InstrumentSchedulingTest.java b/test/src/org/labkey/test/tests/targetedms/InstrumentSchedulingTest.java index 1ce61bf63..ea9905ba9 100644 --- a/test/src/org/labkey/test/tests/targetedms/InstrumentSchedulingTest.java +++ b/test/src/org/labkey/test/tests/targetedms/InstrumentSchedulingTest.java @@ -183,6 +183,9 @@ public void testSchedule() throws IOException, CommandException { String originalStart = getFormElement(START_DATE_TIME_FIELD.findElement(getDriver())); String originalEnd = getFormElement(END_DATE_TIME_FIELD.findElement(getDriver())); + // Pin the 8AM/5PM defaults so a regression that zeroes or shifts the time is caught even on agents whose timezone would otherwise hide it. + assertTrue("Start field should default to 8AM, was: " + originalStart, originalStart.endsWith("T08:00")); + assertTrue("End field should default to 5PM, was: " + originalEnd, originalEnd.endsWith("T17:00")); // Try scheduling over the first reservation and verify it is blocked setFormElement(START_DATE_TIME_FIELD.findElement(getDriver()), originalStart.replace("-03T", "-02T")); setFormElement(END_DATE_TIME_FIELD.findElement(getDriver()), originalEnd.replace("-03T", "-02T")); @@ -197,6 +200,9 @@ public void testSchedule() throws IOException, CommandException assertProjectEventCounts(2, 0); + // The event chip time range is rendered via DateFormat (the patched parseTime path); verify it shows the real 8AM-5PM range, not a zeroed 00:00 - 00:00 as happened in colon-labelled timezones. + assertEquals("Event chip should show the 8AM-5PM time range", "08:00 - 17:00", getText(Locator.css(".activeProjectEvent .event-date"))); + doAndWaitForPageToLoad(() -> selectOptionByText(PROJECT_DROP_DOWN, PROJECT_2)); scheduleInstrument(yearMonth + "-04", false); diff --git a/webapp/TargetedMS/js/scheduleUtils.js b/webapp/TargetedMS/js/scheduleUtils.js index 02588526f..7b4199c36 100644 --- a/webapp/TargetedMS/js/scheduleUtils.js +++ b/webapp/TargetedMS/js/scheduleUtils.js @@ -8,6 +8,18 @@ (function(window) { const utils = {}; + const pad = function(n) { return (n < 10 ? '0' : '') + n; }; + + // Date-only 'yyyy-MM-dd' wire form from local fields. + utils.toDateValue = function(date) { + return date.getFullYear() + '-' + pad(date.getMonth() + 1) + '-' + pad(date.getDate()); + }; + + // Format a Date as datetime-local's fixed 'yyyy-MM-ddTHH:mm' wire form from local fields; avoids DateFormat, which zeroes the time in timezones whose label contains a colon (e.g. Honolulu). + utils.toDateTimeLocalValue = function(date) { + return utils.toDateValue(date) + 'T' + pad(date.getHours()) + ':' + pad(date.getMinutes()); + }; + // Convert a CSS color string (named, rgb, hex) to standard 6-digit HEX color (#RRGGBB) utils.stringToColor = function(color) { if (!color) return '#888888'; diff --git a/webapp/TargetedMS/js/scheduler.js b/webapp/TargetedMS/js/scheduler.js index f289542e2..622221018 100644 --- a/webapp/TargetedMS/js/scheduler.js +++ b/webapp/TargetedMS/js/scheduler.js @@ -88,10 +88,7 @@ $(function() { let bgColor = e.event.extendedProps.project === project ? e.backgroundColor : 'gray'; const cl = e.event.extendedProps.project === project ? 'activeProjectEvent' : 'otherProjectEvent'; let textColor = ScheduleUtils.getContrastTextColor(ScheduleUtils.stringToColor(bgColor)); - let timeFormatString = LABKEY.container.formats.timeFormat; - // Strip seconds and milliseconds - timeFormatString = timeFormatString.replace(':ss', '').replace('.SSS', ''); - let dateStr = DateFormat.format.date(e.event.start, timeFormatString) + ' - ' + DateFormat.format.date(e.event.end, timeFormatString); + let dateStr = ScheduleUtils.formatTimeRange(e.event.start, e.event.end); let style = 'background-color: ' + LABKEY.Utils.encodeHtml(bgColor) + '; color: ' + LABKEY.Utils.encodeHtml(textColor) + ';' + 'width: 100%'; content += '