fix(settings): in absence form avoid shifting dates to browsers timezone

Fixes #61584

Signed-off-by: Oleksandr Dzhychko <hey@oleks.dev>
pull/61638/head
Oleksandr Dzhychko 2 months ago
parent 4f774bbae1
commit 0c82f741fa
  1. 67
      apps/dav/src/components/AbsenceForm.spec.js
  2. 23
      apps/dav/src/components/AbsenceForm.vue
  3. 22
      dist/dav-settings-personal-availability.mjs
  4. 2
      dist/dav-settings-personal-availability.mjs.map

@ -0,0 +1,67 @@
/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { mount } from '@vue/test-utils'
import { afterEach, describe, expect, it, vi } from 'vitest'
import AbsenceForm from './AbsenceForm.vue'
let davAbsence
vi.mock('@nextcloud/initial-state', () => ({
loadState(app, key, fallback) {
if (app === 'dav' && key === 'absence' && davAbsence !== undefined) {
return davAbsence
}
if (fallback !== undefined) {
return fallback
}
console.error('Unexpected loadState call without fallback', { app, key })
throw new Error()
},
}))
afterEach(() => {
vi.unstubAllEnvs()
davAbsence = undefined
vi.resetModules()
})
function getInputs(wrapper) {
const lables = wrapper.findAll('label')
const firstDayLabel = lables.find((l) => l.text() === 'First day')
const firstDayInput = wrapper.get(`#${firstDayLabel.attributes('for')}`)
const lastDayLabel = lables.find((l) => l.text() === 'Last day (inclusive)')
const lastDayInput = wrapper.get(`#${lastDayLabel.attributes('for')}`)
return { firstDayInput, lastDayInput }
}
describe('AbsenceForm', () => {
it('displays default state when browser timezone is set', async () => {
vi.setSystemTime(new Date(2026, 5, 29, 5, 0))
vi.stubEnv('TZ', 'US/Pacific')
const wrapper = mount(AbsenceForm)
const { firstDayInput } = getInputs(wrapper)
expect(firstDayInput.element.value).toBe('2026-06-29')
})
it('displays state when browser timezone is set', async () => {
vi.stubEnv('TZ', 'US/Pacific')
davAbsence = {
firstDay: '2026-06-29',
lastDay: '2026-06-30',
}
const wrapper = mount(AbsenceForm)
const { firstDayInput, lastDayInput } = getInputs(wrapper)
expect(firstDayInput.element.value).toBe('2026-06-29')
expect(lastDayInput.element.value).toBe('2026-06-30')
})
})

@ -72,6 +72,22 @@ import NcTextField from '@nextcloud/vue/components/NcTextField'
import { logger } from '../service/logger.ts'
import { formatDateAsYMD } from '../utils/date.ts'
/**
* Adjusts a date so `NcDateTimePickerNative` shows the same date
* instead of shifting it to the browsers timezone.
*
* @param {Date} date - e.g., new Date("1987-12-01")
* @return {Date}
*/
function inputAdjustDate(date) {
// e.g., date === Mon Nov 30 1987 16:00:00 GMT-0800 (Pacific Standard Time)
const timezoneOffsetMilliseconds = date.getTimezoneOffset() * 60 * 1000
// e.g., Tue Dec 01 1987 00:00:00 GMT-0800 (Pacific Standard Time)
const adjustedDate = new Date(date.getTime() + timezoneOffsetMilliseconds)
// `NcDateTimePickerNative` will display this as 12/01/1987
return adjustedDate
}
export default {
name: 'AbsenceForm',
components: {
@ -88,12 +104,15 @@ export default {
data() {
const { firstDay, lastDay, status, message, replacementUserId, replacementUserDisplayName } = loadState('dav', 'absence', {})
const firstDayDate = firstDay ? new Date(firstDay) : new Date()
const firstDayInputAdjusted = inputAdjustDate(firstDayDate)
const lastDayInputAdjusted = lastDay ? inputAdjustDate(new Date(lastDay)) : null
return {
loading: false,
status: status ?? '',
message: message ?? '',
firstDay: firstDay ? new Date(firstDay) : new Date(),
lastDay: lastDay ? new Date(lastDay) : null,
firstDay: firstDayInputAdjusted,
lastDay: lastDayInputAdjusted,
replacementUserId,
replacementUser: replacementUserId ? { user: replacementUserId, displayName: replacementUserDisplayName } : null,
searchLoading: false,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save