mirror of https://github.com/langgenius/dify.git
fix(timezone): support half-hour and 45-minute timezone offsets
Critical regression fix for convertTimezoneToOffsetStr:
Issues Fixed:
- Previous regex /^([+-]?\d{1,2}):00/ only matched :00 offsets
- This caused half-hour offsets (e.g., India +05:30) to return UTC+0
- Even if matched, parseInt only parsed hours, losing minute info
Changes:
- Update regex to /^([+-]?\d{1,2}):(\d{2})/ to match all offset formats
- Parse both hours and minutes separately
- Output format: "UTC+5:30" for non-zero minutes, "UTC+8" for whole hours
- Preserve leading zeros in minute part (e.g., "UTC+5:30" not "UTC+5:3")
Test Coverage:
- Added 8 comprehensive tests covering:
* Default/invalid timezone handling
* Whole hour offsets (positive/negative)
* Zero offset (UTC)
* Half-hour offsets (India +5:30, Australia +9:30)
* 45-minute offset (Chatham +12:45)
* Leading zero preservation in minutes
All 14 tests passing. Verified with timezone.json entries at lines 967, 1135, 1251.
This commit is contained in:
parent
920a608e5d
commit
7560e2427d
|
|
@ -1,5 +1,6 @@
|
|||
import dayjs from './dayjs'
|
||||
import {
|
||||
convertTimezoneToOffsetStr,
|
||||
getDateWithTimezone,
|
||||
isDayjsObject,
|
||||
toDayjs,
|
||||
|
|
@ -65,3 +66,50 @@ describe('dayjs utilities', () => {
|
|||
expect(result?.minute()).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('convertTimezoneToOffsetStr', () => {
|
||||
test('should return default UTC+0 for undefined timezone', () => {
|
||||
expect(convertTimezoneToOffsetStr(undefined)).toBe('UTC+0')
|
||||
})
|
||||
|
||||
test('should return default UTC+0 for invalid timezone', () => {
|
||||
expect(convertTimezoneToOffsetStr('Invalid/Timezone')).toBe('UTC+0')
|
||||
})
|
||||
|
||||
test('should handle whole hour positive offsets without leading zeros', () => {
|
||||
expect(convertTimezoneToOffsetStr('Asia/Shanghai')).toBe('UTC+8')
|
||||
expect(convertTimezoneToOffsetStr('Pacific/Auckland')).toBe('UTC+12')
|
||||
expect(convertTimezoneToOffsetStr('Pacific/Apia')).toBe('UTC+13')
|
||||
})
|
||||
|
||||
test('should handle whole hour negative offsets without leading zeros', () => {
|
||||
expect(convertTimezoneToOffsetStr('Pacific/Niue')).toBe('UTC-11')
|
||||
expect(convertTimezoneToOffsetStr('Pacific/Honolulu')).toBe('UTC-10')
|
||||
expect(convertTimezoneToOffsetStr('America/New_York')).toBe('UTC-5')
|
||||
})
|
||||
|
||||
test('should handle zero offset', () => {
|
||||
expect(convertTimezoneToOffsetStr('Europe/London')).toBe('UTC+0')
|
||||
expect(convertTimezoneToOffsetStr('UTC')).toBe('UTC+0')
|
||||
})
|
||||
|
||||
test('should handle half-hour offsets (30 minutes)', () => {
|
||||
// India Standard Time: UTC+5:30
|
||||
expect(convertTimezoneToOffsetStr('Asia/Kolkata')).toBe('UTC+5:30')
|
||||
// Australian Central Time: UTC+9:30
|
||||
expect(convertTimezoneToOffsetStr('Australia/Adelaide')).toBe('UTC+9:30')
|
||||
expect(convertTimezoneToOffsetStr('Australia/Darwin')).toBe('UTC+9:30')
|
||||
})
|
||||
|
||||
test('should handle 45-minute offsets', () => {
|
||||
// Chatham Time: UTC+12:45
|
||||
expect(convertTimezoneToOffsetStr('Pacific/Chatham')).toBe('UTC+12:45')
|
||||
})
|
||||
|
||||
test('should preserve leading zeros in minute part for non-zero minutes', () => {
|
||||
// Ensure +05:30 is displayed as "UTC+5:30", not "UTC+5:3"
|
||||
const result = convertTimezoneToOffsetStr('Asia/Kolkata')
|
||||
expect(result).toMatch(/UTC[+-]\d+:30/)
|
||||
expect(result).not.toMatch(/UTC[+-]\d+:3[^0]/)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -107,15 +107,18 @@ export const convertTimezoneToOffsetStr = (timezone?: string) => {
|
|||
const tzItem = tz.find(item => item.value === timezone)
|
||||
if (!tzItem)
|
||||
return DEFAULT_OFFSET_STR
|
||||
// Extract offset from name format like "-11:00 Niue Time - Alofi"
|
||||
// Name format is always "{offset}:00 {timezone name}"
|
||||
const offsetMatch = tzItem.name.match(/^([+-]?\d{1,2}):00/)
|
||||
// Extract offset from name format like "-11:00 Niue Time" or "+05:30 India Time"
|
||||
// Name format is always "{offset}:{minutes} {timezone name}"
|
||||
const offsetMatch = tzItem.name.match(/^([+-]?\d{1,2}):(\d{2})/)
|
||||
if (!offsetMatch)
|
||||
return DEFAULT_OFFSET_STR
|
||||
// Parse offset as integer to remove leading zeros (e.g., "-05" → "-5")
|
||||
const offsetNum = Number.parseInt(offsetMatch[1], 10)
|
||||
const sign = offsetNum >= 0 ? '+' : ''
|
||||
return `UTC${sign}${offsetNum}`
|
||||
// Parse hours and minutes separately
|
||||
const hours = Number.parseInt(offsetMatch[1], 10)
|
||||
const minutes = Number.parseInt(offsetMatch[2], 10)
|
||||
const sign = hours >= 0 ? '+' : ''
|
||||
// If minutes are non-zero, include them in the output (e.g., "UTC+5:30")
|
||||
// Otherwise, only show hours (e.g., "UTC+8")
|
||||
return minutes !== 0 ? `UTC${sign}${hours}:${offsetMatch[2]}` : `UTC${sign}${hours}`
|
||||
}
|
||||
|
||||
export const isDayjsObject = (value: unknown): value is Dayjs => dayjs.isDayjs(value)
|
||||
|
|
|
|||
Loading…
Reference in New Issue