Skip to content

Commit ba0702d

Browse files
Merge pull request #61711 from nextcloud/backport/61638/stable33
[stable33] fix(settings): in absence form avoid shifting dates to browsers timezone
2 parents 921bc63 + cded30a commit ba0702d

4 files changed

Lines changed: 100 additions & 14 deletions

File tree

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import { mount } from '@vue/test-utils'
7+
import { afterEach, describe, expect, it, vi } from 'vitest'
8+
import AbsenceForm from './AbsenceForm.vue'
9+
10+
let davAbsence
11+
vi.mock('@nextcloud/initial-state', () => ({
12+
loadState(app, key, fallback) {
13+
if (app === 'dav' && key === 'absence' && davAbsence !== undefined) {
14+
return davAbsence
15+
}
16+
if (fallback !== undefined) {
17+
return fallback
18+
}
19+
20+
console.error('Unexpected loadState call without fallback', { app, key })
21+
throw new Error()
22+
},
23+
}))
24+
25+
afterEach(() => {
26+
vi.unstubAllEnvs()
27+
davAbsence = undefined
28+
vi.resetModules()
29+
})
30+
31+
function getInputs(wrapper) {
32+
const lables = wrapper.findAll('label')
33+
34+
const firstDayLabel = lables.find((l) => l.text() === 'First day')
35+
const firstDayInput = wrapper.get(`#${firstDayLabel.attributes('for')}`)
36+
37+
const lastDayLabel = lables.find((l) => l.text() === 'Last day (inclusive)')
38+
const lastDayInput = wrapper.get(`#${lastDayLabel.attributes('for')}`)
39+
40+
return { firstDayInput, lastDayInput }
41+
}
42+
43+
describe('AbsenceForm', () => {
44+
it('displays default state when browser timezone is set', async () => {
45+
vi.setSystemTime(new Date(2026, 5, 29, 5, 0))
46+
vi.stubEnv('TZ', 'US/Pacific')
47+
48+
const wrapper = mount(AbsenceForm)
49+
50+
const { firstDayInput } = getInputs(wrapper)
51+
expect(firstDayInput.element.value).toBe('2026-06-29')
52+
})
53+
54+
it('displays state when browser timezone is set', async () => {
55+
vi.stubEnv('TZ', 'US/Pacific')
56+
davAbsence = {
57+
firstDay: '2026-06-29',
58+
lastDay: '2026-06-30',
59+
}
60+
61+
const wrapper = mount(AbsenceForm)
62+
63+
const { firstDayInput, lastDayInput } = getInputs(wrapper)
64+
expect(firstDayInput.element.value).toBe('2026-06-29')
65+
expect(lastDayInput.element.value).toBe('2026-06-30')
66+
})
67+
})

apps/dav/src/components/AbsenceForm.vue

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,22 @@ import NcTextField from '@nextcloud/vue/components/NcTextField'
7272
import { logger } from '../service/logger.ts'
7373
import { formatDateAsYMD } from '../utils/date.ts'
7474
75+
/**
76+
* Adjusts a date so `NcDateTimePickerNative` shows the same date
77+
* instead of shifting it to the browsers timezone.
78+
*
79+
* @param {Date} date - e.g., new Date("1987-12-01")
80+
* @return {Date}
81+
*/
82+
function inputAdjustDate(date) {
83+
// e.g., date === Mon Nov 30 1987 16:00:00 GMT-0800 (Pacific Standard Time)
84+
const timezoneOffsetMilliseconds = date.getTimezoneOffset() * 60 * 1000
85+
// e.g., Tue Dec 01 1987 00:00:00 GMT-0800 (Pacific Standard Time)
86+
const adjustedDate = new Date(date.getTime() + timezoneOffsetMilliseconds)
87+
// `NcDateTimePickerNative` will display this as 12/01/1987
88+
return adjustedDate
89+
}
90+
7591
export default {
7692
name: 'AbsenceForm',
7793
components: {
@@ -88,12 +104,15 @@ export default {
88104
89105
data() {
90106
const { firstDay, lastDay, status, message, replacementUserId, replacementUserDisplayName } = loadState('dav', 'absence', {})
107+
const firstDayDate = firstDay ? new Date(firstDay) : new Date()
108+
const firstDayInputAdjusted = inputAdjustDate(firstDayDate)
109+
const lastDayInputAdjusted = lastDay ? inputAdjustDate(new Date(lastDay)) : null
91110
return {
92111
loading: false,
93112
status: status ?? '',
94113
message: message ?? '',
95-
firstDay: firstDay ? new Date(firstDay) : new Date(),
96-
lastDay: lastDay ? new Date(lastDay) : null,
114+
firstDay: firstDayInputAdjusted,
115+
lastDay: lastDayInputAdjusted,
97116
replacementUserId,
98117
replacementUser: replacementUserId ? { user: replacementUserId, displayName: replacementUserDisplayName } : null,
99118
searchLoading: false,

dist/dav-settings-personal-availability.mjs

Lines changed: 11 additions & 11 deletions
Large diffs are not rendered by default.

dist/dav-settings-personal-availability.mjs.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)