-
Notifications
You must be signed in to change notification settings - Fork 72
[LG-5532] feat(time-input): Segment state utils #3385
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: LG-5532/segments-display-values
Are you sure you want to change the base?
[LG-5532] feat(time-input): Segment state utils #3385
Conversation
… from specified time zones
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR introduces utility functions for managing time input segment state, including validation, formatting, and conversion between 12-hour and 24-hour formats. The implementation handles timezone conversions, segment validation, and provides comprehensive test coverage for UTC date creation from time segments.
- Adds utilities for validating and formatting time segments (hour, minute, second)
- Implements conversion logic between 12-hour and 24-hour time formats
- Introduces timezone-aware UTC date creation from time segments
Reviewed changes
Copilot reviewed 27 out of 27 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
packages/time-input/src/utils/shouldSetValue/shouldSetValue.ts |
Core logic determining when to update time input value based on segment state |
packages/time-input/src/utils/isSameUTCDayAndTime/isSameUTCDayAndTime.ts |
Utility for comparing two dates for equality in UTC |
packages/time-input/src/utils/isEverySegmentValueExplicit/isEverySegmentValueExplicit.ts |
Validates that all time segments have unambiguous values |
packages/time-input/src/utils/isEverySegmentValid/isEverySegmentValid.ts |
Checks if all time segments are within valid ranges |
packages/time-input/src/utils/isEverySegmentFilled/isEverySegmentFilled.ts |
Verifies that all time segments contain values |
packages/time-input/src/utils/getNewUTCDateFromSegments/getNewUTCDateFromSegments.ts |
Creates UTC date from time segments with timezone support |
packages/time-input/src/utils/getFormattedTimeSegmentsFromDate/getFormattedTimeSegmentsFromDate.ts |
Extracts and formats time segments from a date object |
packages/time-input/src/utils/getFormattedTimeSegments/getFormattedTimeSegments.ts |
Pads time segments to 2-digit format |
packages/time-input/src/utils/findUnitOptionByDayPeriod/findUnitOptionByDayPeriod.ts |
Locates the AM/PM unit option matching a day period |
packages/time-input/src/utils/doesSomeSegmentExist/doesSomeSegmentExist.ts |
Checks if any time segment has a value |
packages/time-input/src/utils/convert12hTo24h/convert12hTo24h.ts |
Converts 12-hour format to 24-hour format |
packages/date-utils/src/newUTCFromTimeZone/newUTCFromTimeZone.ts |
Creates UTC date from local time in specified timezone |
packages/time-input/src/utils/index.ts |
Exports all new time input utility functions |
packages/date-utils/src/index.ts |
Exports new UTC timezone conversion utility |
| Test files (*.spec.ts) | Comprehensive test coverage for all new utilities |
packages/time-input/src/utils/isEverySegmentFilled/isEverySegmentFilled.spec.ts
Outdated
Show resolved
Hide resolved
packages/time-input/src/utils/doesSomeSegmentExist/doesSomeSegmentExist.spec.ts
Outdated
Show resolved
Hide resolved
|
Size Change: +255 B (+0.01%) Total Size: 1.83 MB
ℹ️ View Unchanged
|
…orrect input validation
| * // returns new Date('2026-02-21T04:00:00Z') | ||
| * ``` | ||
| */ | ||
| export const newUTCFromTimeZone = ({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like this is doing the same thing as newTZDate
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you're correct. I initially looked at this, and I thought it was doing something else.
You have this comment innewTZDate, // This API is less than perfect. Do you remember why you wrote that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function signature of newUTC matches the signature of new Date() (i.e. both support an array of numbers, newUTC(2025, 12, 19) etc)
Needing to prefix newTZDate with the offset feels a bit clunky to me
i.e. newTZDate(-5, 2025, 12, 19) just looks kinda wrong. Nothing inherently bad about it though
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your implementation supports IANA strings, so we could extend newTZDate to support that: newTZDate('America/New_York', 2025, 12, 19)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer not to pass the offset number and would rather use a string. I agree that it looks kinda wrong. I can update this to either:
With the IANA string and date, I can find the offset using getTimezoneOffset from date-fns, and continue to use what you have.
OR
With the IANA string and date, use the zonedTimeToUtc util from date-fns.
Regardless of the approach, we still need to depend on date-fns. I think that 2 would be easier because we don't need to calculate the offset manually.
| // if dayPeriod is AM and hour is 12, return 0 since 12 AM is 00:00 | ||
| if (dayPeriod === 'AM') { | ||
| if (hour === '12') { | ||
| return '0'; | ||
| } | ||
|
|
||
| // else return hour as-is | ||
| return hour; | ||
| } | ||
|
|
||
| // if dayPeriod is PM and hour is 12, return 12 since 12 PM is 12:00 | ||
| if (hour === '12') { | ||
| return '12'; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| // if dayPeriod is AM and hour is 12, return 0 since 12 AM is 00:00 | |
| if (dayPeriod === 'AM') { | |
| if (hour === '12') { | |
| return '0'; | |
| } | |
| // else return hour as-is | |
| return hour; | |
| } | |
| // if dayPeriod is PM and hour is 12, return 12 since 12 PM is 12:00 | |
| if (hour === '12') { | |
| return '12'; | |
| } | |
| if(hour === '12') { | |
| // 12AM -> 0:00 | |
| // 12PM -> 12:00 | |
| return dayPeriod === 'AM' ? '0' : '12' | |
| } |
| * @param dayPeriod - The day period to use for the conversion (AM or PM) | ||
| * @returns The converted hour | ||
| */ | ||
| export const convert12hTo24h = (hour: string, dayPeriod: string) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is hour: string , and not number?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, should dayPeriod be an enum or string literal?
| export const convert12hTo24h = (hour: string, dayPeriod: string) => { | |
| export const convert12hTo24h = (hour: string, dayPeriod: 'AM' | 'PM') => { |
| } | ||
|
|
||
| // else return hour + 12 | ||
| return `${parseInt(hour) + 12}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if hour is
<= 0?> 12?- any other string?
| * @returns The select unit option. | ||
| */ | ||
| export const findUnitOptionByDayPeriod = ( | ||
| dayPeriod: string, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like dayPeriod should be more explicitly typed (everywhere)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agree, will update
| ): UnitOption => { | ||
| const selectUnitOption = unitOptions.find( | ||
| option => option.displayName === dayPeriod, | ||
| ) as UnitOption; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unitOptions.find(...) might return undefined. We should handle that case here
| * // returns: { hour: '02', minute: '30', second: '00' } | ||
| * ``` | ||
| */ | ||
| export const getFormattedTimeSegments = (segments: TimeSegmentsState) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
using the word formatted here makes me think it's supposed to be using a the formatter
| const { day, month, year } = dateValues; | ||
| const { hour, minute, second } = segments; | ||
|
|
||
| const convertedHour = is12HourFormat |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: maybe be more explicit with the var name? converted to what?
| day1.getUTCHours() === day2.getUTCHours() && | ||
| day1.getUTCMinutes() === day2.getUTCMinutes() && | ||
| day1.getUTCSeconds() === day2.getUTCSeconds() && | ||
| day1.getUTCMilliseconds() === day2.getUTCMilliseconds() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: do we need to check milliseconds?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prob not
| * @param day2 - The second date to check | ||
| * @returns Whether the two dates are the same day and time in UTC | ||
| */ | ||
| export const isSameUTCDayAndTime = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can probably use isEqual from date-fns
https://date-fns.org/v4.1.0/docs/isEqual
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we need to explicitly check UTC, can we rename this isEqualUTC and move to date-utils?
| import { isEverySegmentValueExplicit } from '../isEverySegmentValueExplicit/isEverySegmentValueExplicit'; | ||
|
|
||
| /** | ||
| * Checks if the new date should be set. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we summarize the logic here?
…leafygreen-ui into LG-5532/segments-state-utils
…corresponding tests
…es for time conversion and selection
…ests for date comparison in UTC
…om time-input exports
…ove clarity and consistency
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ignore this hook, it's being replaced in the next PR
…ty for improved clarity
|
Coverage after merging LG-5532/segments-state-utils into LG-5532/segments-display-values will be
Coverage Report for Changed Files
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
✍️ Proposed changes
🎟 Jira ticket: Name of ticket
This PR introduces utility functions for managing time input segment state, including validation, formatting, and conversion between 12-hour and 24-hour formats. The implementation handles timezone conversions, segment validation, and provides comprehensive test coverage.
This PR is the third PR in a chain of PRs
🧪 How to test changes