Skip to content

Commit

Permalink
fix: force current input to standard format (local time) when opening…
Browse files Browse the repository at this point in the history
… date picker
  • Loading branch information
cpulvermacher committed Feb 13, 2025
1 parent 42fde1c commit 650fcf5
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 13 deletions.
13 changes: 7 additions & 6 deletions src/popup/Datepicker.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import { DatePicker } from '@svelte-plugins/datepicker'
import { tick } from 'svelte'
import { formatLocalTime, overwriteYYYYMMDD } from '../util/common'
import { formatLocalTime, overwriteDatePart } from '../util/common'
interface Props {
fakeDate: string
Expand All @@ -26,13 +26,15 @@
async function toggleDatePicker() {
isOpen = !isOpen
if (isOpen) {
// when opening the date picker, allow editing the current date entered
// when opening the date picker, force to standard format and allow editing the current date entered
let inputDateTimestamp = new Date(fakeDate).getTime()
if (isNaN(inputDateTimestamp)) {
// if date in input field is invalid, reset
const newDate = new Date()
fakeDate = formatLocalTime(newDate)
inputDateTimestamp = newDate.getTime()
} else {
fakeDate = formatLocalTime(new Date(fakeDate), { fullPrecision: true })
}
pickerDate = inputDateTimestamp
Expand All @@ -43,12 +45,11 @@
}
async function onDateChange() {
const newDate = new Date(pickerDate)
fakeDate = overwriteYYYYMMDD(fakeDate, newDate)
fakeDate = overwriteDatePart(fakeDate, newDate)
//focus time part
inputRef.focus()
await tick() // wait for next DOM update
inputRef.setSelectionRange(11, 16)
inputRef.setSelectionRange(11, -1) // select hh:mm (and everything afterwards)
}
</script>

Expand All @@ -63,7 +64,7 @@
maxlength="28"
spellcheck="false"
/>
<button onclick={toggleDatePicker} aria-label="Choose date" class="calendar-icon"></button>
<button onclick={toggleDatePicker} title="Choose date" aria-label="Choose date" class="calendar-icon"></button>
</DatePicker>

<style>
Expand Down
48 changes: 48 additions & 0 deletions src/test/util/common.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { describe, expect, it } from 'vitest'
import { formatLocalTime, overwriteDatePart } from '../../util/common'

describe('overwriteDatePart', () => {
it('updates date part while preserving format if possible', () => {
const date = new Date('2033-01-22')
expect(overwriteDatePart('2025-02-10', date)).toBe('2033-01-22')
expect(overwriteDatePart('2025-02-10 12:34', date)).toBe('2033-01-22 12:34')
expect(overwriteDatePart('2025-02-10 12:34', date)).toBe('2033-01-22 12:34')
expect(overwriteDatePart('2025-02-10 12:34Z', date)).toBe('2033-01-22 12:34Z')
expect(overwriteDatePart('2025-02-10 12:40+1130', date)).toBe('2033-01-22 12:40+1130')
expect(overwriteDatePart('2023-03-25 12:40:00.120', date)).toBe('2033-01-22 12:40:00.120')
})

it('ignores time part of new date', () => {
const date = new Date('2033-01-22 12:34:56.789')
expect(overwriteDatePart('2025-02-10 12:34', date)).toBe('2033-01-22 12:34')
})
})

describe('formatLocalTime', () => {
it('formats in correct format', () => {
expect(formatLocalTime(new Date('2025-02-10 12:34'))).toBe('2025-02-10 12:34')
expect(formatLocalTime(new Date('2025-02-10 12:34:55'))).toBe('2025-02-10 12:34')
expect(formatLocalTime(new Date('2025-02-10 12:34:55.123'))).toBe('2025-02-10 12:34')
expect(formatLocalTime(new Date('2025-02-10 00:00'))).toBe('2025-02-10 00:00')
expect(formatLocalTime(new Date('2025-01-01 00:00'))).toBe('2025-01-01 00:00')
})

it('handles invalid dates', () => {
expect(formatLocalTime(new Date(''))).toBe('Invalid Date')
expect(formatLocalTime(new Date('abcdefgh'))).toBe('Invalid Date')
expect(formatLocalTime(new Date('2025-01-32'))).toBe('Invalid Date')
})

it('uses local time', () => {
const date = new Date('2025-02-13T12:00Z')
const formattedDate = formatLocalTime(date)
expect(new Date(formattedDate).getTime()).toBe(date.getTime())
})
it('fullPrecision: outputs as much precision as required', () => {
const options = { fullPrecision: true }
expect(formatLocalTime(new Date('2025-02-10 12:34:55.123'), options)).toBe('2025-02-10 12:34:55.123')
expect(formatLocalTime(new Date('2025-02-10 12:34:55'), options)).toBe('2025-02-10 12:34:55')
expect(formatLocalTime(new Date('2025-02-10 12:34'), options)).toBe('2025-02-10 12:34')
expect(formatLocalTime(new Date('2025-02-10 00:00'), options)).toBe('2025-02-10 00:00')
})
})
33 changes: 26 additions & 7 deletions src/util/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,39 @@ export type ActivationMessage = {
isClockTicking: boolean
}

/** Returns date in format "YYYY-MM-DD hh:mm" in local time, or "Invalid Date" if invalid */
export function formatLocalTime(date: Date): string {
export type FormatOptions = {
fullPrecision: boolean
}

/** Returns date in format "YYYY-MM-DD hh:mm" in local time, or "Invalid Date" if invalid
*
* If options.fullPrecision is true, returns seconds and milliseconds if they are non-zero
*/
export function formatLocalTime(date: Date, options?: FormatOptions): string {
if (isNaN(date.getTime())) {
return 'Invalid Date'
}

const d = new Date(date.getTime() - date.getTimezoneOffset() * 60000)
return d.toISOString().slice(0, 16).replace('T', ' ')
const isoString = d.toISOString().replace('T', ' ')
if (options?.fullPrecision) {
if (d.getMilliseconds() !== 0) {
return isoString.slice(0, 23)
}
if (d.getSeconds() !== 0) {
return isoString.slice(0, 19)
}
}
return isoString.slice(0, 16)
}

/** For a date in format "YYYY-MM-DD hh:mm", returns a new date string in the same format, with the date part replaced by the given date */
export function overwriteYYYYMMDD(dateString: string, newDate: Date): string {
const newDateString = formatLocalTime(newDate)
return newDateString.slice(0, 11) + dateString.slice(11)
/** For a date in format "YYYY-MM-DD hh:mm...", returns a new date string in the same format, with the date part replaced by the given date */
export function overwriteDatePart(dateString: string, newDate: Date): string {
const newDateString = formatLocalTime(newDate).slice(0, 10)
if (dateString.length <= 11) {
return newDateString
}
return newDateString + dateString.slice(10)
}

export async function setBadgeAndTitle(tabId: number, state: ContentScriptState) {
Expand Down

0 comments on commit 650fcf5

Please sign in to comment.