Skip to content

Commit d3c8cf2

Browse files
committed
feat(locale): add setTimezone & getTimezone functions
Signed-off-by: Sector6759 <149817326+Sector6759@users.noreply.github.com>
1 parent fcffb82 commit d3c8cf2

5 files changed

Lines changed: 101 additions & 4 deletions

File tree

lib/globals.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type { PluralFunction, Translations } from './registry.ts'
1010
declare global {
1111
var _nc_l10n_locale: string
1212
var _nc_l10n_language: string
13+
var _nc_l10n_timezone: string
1314

1415
var _oc_l10n_registry_translations: Record<string, Translations>
1516
var _oc_l10n_registry_plural_functions: Record<string, PluralFunction>

lib/locale.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
* SPDX-License-Identifier: GPL-3.0-or-later
44
*/
55

6+
import { getCapabilities } from '@nextcloud/capabilities'
7+
68
/**
79
* Returns the user's locale
810
*/
@@ -57,6 +59,42 @@ export function setLanguage(lang: string): void {
5759
}
5860
}
5961

62+
/**
63+
* Returns the user's timezone
64+
*
65+
* @since 3.5.0
66+
*/
67+
export function getTimezone(): string {
68+
if (!globalThis._nc_l10n_timezone) {
69+
let capabilities: undefined | { core?: { user?: { timezone?: string } } }
70+
try {
71+
capabilities = getCapabilities()
72+
} catch {
73+
// An error indicates getCapabilities threw due to no window object being present,
74+
// which is the case when called outside of a browser runtime
75+
76+
// TODO: this try/catch block should be removed as soon as the @nextcloud/capabilities
77+
// package has been updated to check for presence of the window object.
78+
// Furthermore, everything except the return statement should be moved to the
79+
// end of this file where the remaining global state is initialized.
80+
}
81+
globalThis._nc_l10n_timezone ??= capabilities?.core?.user?.timezone
82+
|| Intl.DateTimeFormat().resolvedOptions().timeZone
83+
}
84+
return globalThis._nc_l10n_timezone
85+
}
86+
87+
/**
88+
* Set the current user's timezone (locally).
89+
* This is to be used only for e.g. usage within web workers etc.
90+
*
91+
* @param timezone - The new timezone
92+
* @since 3.5.0
93+
*/
94+
export function setTimezone(timezone: string): void {
95+
globalThis._nc_l10n_timezone = timezone
96+
}
97+
6098
/**
6199
* Check whether the current, or a given, language is read right-to-left
62100
*

package-lock.json

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,16 @@
4343
"lint:fix": "eslint --fix lib tests",
4444
"prerelease:format-changelog": "node build/format-changelog.mjs",
4545
"test": "npm run test:dom && npm run test:node",
46-
"test:coverage": "LANG=en-US vitest run --coverage",
47-
"test:dom": "LANG=en-US vitest run",
48-
"test:node": "LANG=en-US vitest run --environment node",
49-
"test:watch": "LANG=en-US vitest watch"
46+
"test:coverage": "LANG=en-US TZ=America/Chicago vitest run --coverage",
47+
"test:dom": "LANG=en-US TZ=America/Chicago vitest run",
48+
"test:node": "LANG=en-US TZ=America/Chicago vitest run --environment node",
49+
"test:watch": "LANG=en-US TZ=America/Chicago vitest watch"
5050
},
5151
"browserslist": [
5252
"extends @nextcloud/browserslist-config"
5353
],
5454
"dependencies": {
55+
"@nextcloud/capabilities": "^1.2.1",
5556
"@nextcloud/router": "^3.1.0",
5657
"@nextcloud/typings": "^1.10.0",
5758
"@types/escape-html": "^1.0.4",

tests/locale.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,27 @@ describe('getCanonicalLocale', () => {
8282
})
8383
})
8484

85+
describe('getTimezone', () => {
86+
vi.spyOn(console, 'debug').mockImplementation(() => {})
87+
it('returns the set timezone as it is', async () => {
88+
mockTimezone('Europe/Berlin')
89+
expect(await getTimezone()).toEqual('Europe/Berlin')
90+
})
91+
92+
it('returns the environment timezone if no timezone is set', async () => {
93+
mockTimezone(undefined)
94+
expect(await getTimezone()).toEqual(process.env.TZ)
95+
})
96+
})
97+
98+
describe('setTimezone', () => {
99+
it('sets the global state', async () => {
100+
mockTimezone('Europe/Berlin')
101+
await setTimezone('America/New_York')
102+
expect(globalThis._nc_l10n_timezone).toBe('America/New_York')
103+
})
104+
})
105+
85106
async function getCanonicalLocale() {
86107
const { getCanonicalLocale } = await import('../lib/locale.ts')
87108
return getCanonicalLocale()
@@ -97,6 +118,11 @@ async function getLanguage() {
97118
return getLanguage()
98119
}
99120

121+
async function getTimezone() {
122+
const { getTimezone } = await import('../lib/locale.ts')
123+
return getTimezone()
124+
}
125+
100126
async function setLocale(locale: string) {
101127
const { setLocale } = await import('../lib/locale.ts')
102128
return setLocale(locale)
@@ -107,6 +133,11 @@ async function setLanguage(language: string) {
107133
return setLanguage(language)
108134
}
109135

136+
async function setTimezone(timezone: string) {
137+
const { setTimezone } = await import('../lib/locale.ts')
138+
return setTimezone(timezone)
139+
}
140+
110141
function mockLanguage(lang?: string) {
111142
// @ts-expect-error - Mocking global state
112143
globalThis._nc_l10n_language = lang
@@ -118,3 +149,7 @@ function mockLocale(locale?: string) {
118149
delete globalThis.document.documentElement.dataset.locale
119150
}
120151
}
152+
function mockTimezone(timezone?: string) {
153+
// @ts-expect-error - Mocking global state
154+
globalThis._nc_l10n_timezone = timezone
155+
}

0 commit comments

Comments
 (0)