Skip to content

[PER-10632] Add EDTF datepicker, timepicker, and timezone-dropdown components#1029

Open
aasandei-vsp wants to merge 4 commits into
mainfrom
PER-10632-add-components-edtf
Open

[PER-10632] Add EDTF datepicker, timepicker, and timezone-dropdown components#1029
aasandei-vsp wants to merge 4 commits into
mainfrom
PER-10632-add-components-edtf

Conversation

@aasandei-vsp
Copy link
Copy Markdown
Contributor

@aasandei-vsp aasandei-vsp commented May 27, 2026

These changes do not reflect in the UI at all, so no need for QA. Maybe just to check the app is loading.

The commits about 24H and unspecified digits should be checked thouroughly, the first main commit has already been checked on PR #924

Issue: PER-10632

@aasandei-vsp aasandei-vsp self-assigned this May 27, 2026
@aasandei-vsp aasandei-vsp marked this pull request as draft May 27, 2026 17:00
@codecov
Copy link
Copy Markdown

codecov Bot commented May 27, 2026

Codecov Report

❌ Patch coverage is 77.31959% with 66 lines in your changes missing coverage. Please review.
✅ Project coverage is 50.36%. Comparing base (e929778) to head (05e98b2).
⚠️ Report is 6 commits behind head on main.

Files with missing lines Patch % Lines
...c/app/shared/services/edtf-service/edtf.service.ts 82.67% 14 Missing and 8 partials ⚠️
...nts/timepicker-input/timepicker-input.component.ts 69.11% 18 Missing and 3 partials ⚠️
...nts/datepicker-input/datepicker-input.component.ts 73.01% 14 Missing and 3 partials ⚠️
...s/timezone-dropdown/timezone-dropdown.component.ts 81.81% 4 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1029      +/-   ##
==========================================
+ Coverage   50.01%   50.36%   +0.34%     
==========================================
  Files         348      351       +3     
  Lines       11502    11712     +210     
  Branches     1975     2057      +82     
==========================================
+ Hits         5753     5899     +146     
- Misses       5564     5608      +44     
- Partials      185      205      +20     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@aasandei-vsp aasandei-vsp force-pushed the PER-10632-add-components-edtf branch from 89f773d to 6fc773a Compare May 28, 2026 07:59
@aasandei-vsp aasandei-vsp marked this pull request as ready for review May 28, 2026 12:02
Copy link
Copy Markdown
Member

@cecilia-donnelly cecilia-donnelly left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I asked Claude to review so I could get you something today! It found a few things in the EDTF service (as you suspected). My comments in italics where I've quoted from the results.

  1. It had a concern about existing data like 19XX being transformed into 0019. I don't think we have any existing data in an EDTF format in the database, since we haven't exposed that in the UI, so I'm not overly worried, but might be worth double checking that a date with a partially unknown year is saving / updating correctly.
  2. "Clearing the year field while a month is already set silently produces '0000-MM' instead of treating the date as invalid." -- but I think this is the expected behavior. I would expect the user to need to clear fields independently.
  3. "isValidHour('0') returns true (0 ≤ 1 single-digit guard), so typing '0' in the hours field passes validation, but buildTimeString later throws when serializing it.", and more detail:
"User types '0' → isValidHour('0') = true → timeChange emits {hours:'0'}; when the form saves, buildTimeString is called with hours:'0' (truthy, no early-exit); parseTimeAs24Hour feeds '0' into date-fns parse('0:m:s a', 'h:m:s a') where the h token 
  requires 1–12 → isValid returns false → parseTimeAs24Hour returns null → buildTimeString throws 'Invalid time' → save fails."
  1. "When showSeconds is false, onTimeSelect emits the stale this.time.seconds value instead of clearing it, causing stale seconds to persist silently in the EDTF output."
  2. "normalizeEdtfString appends EDTF qualifiers after the time component (producing '1985-05-20T14:30:45~'), which is not a valid EDTF production; the edtf() validation call in toEdtfDate would then throw, making it impossible to save any record that has both a time
    and an approximate/uncertain qualifier." I don't think we'd ever get here, since we don't let users indicate time to the second and approximate/uncertain
  3. And one non-EDTF service possible bug
 "summary": "toggleAmPm cross-assigns am and pm by value, so if either optional field is absent the emitted model has undefined for one meridian, permanently corrupting am/pm on the next toggle.",
      "failure_scenario": "A consumer passes TimeModel {hours:'10', minutes:'30', pm:true} omitting the optional am field → toggleAmPm emits {am:true, pm:undefined}; a subsequent toggle emits {am:undefined, pm:true}; any code that reads time.am (e.g. parseTimeAs24Hour, which 
  defaults to AM when pm!==true) will permanently misread midnight/noon for this record."

I can definitely validate these, but it's the end of the day here and I wanted to give you some feedback for your Wednesday! I hope they aren't rabbit holes / definitely treat them with suspicion.

@aasandei-vsp
Copy link
Copy Markdown
Contributor Author

I asked Claude to review so I could get you something today! It found a few things in the EDTF service (as you suspected). My comments in italics where I've quoted from the results.

  1. It had a concern about existing data like 19XX being transformed into 0019. I don't think we have any existing data in an EDTF format in the database, since we haven't exposed that in the UI, so I'm not overly worried, but might be worth double checking that a date with a partially unknown year is saving / updating correctly.
  2. "Clearing the year field while a month is already set silently produces '0000-MM' instead of treating the date as invalid." -- but I think this is the expected behavior. I would expect the user to need to clear fields independently.
  3. "isValidHour('0') returns true (0 ≤ 1 single-digit guard), so typing '0' in the hours field passes validation, but buildTimeString later throws when serializing it.", and more detail:
"User types '0' → isValidHour('0') = true → timeChange emits {hours:'0'}; when the form saves, buildTimeString is called with hours:'0' (truthy, no early-exit); parseTimeAs24Hour feeds '0' into date-fns parse('0:m:s a', 'h:m:s a') where the h token 
  requires 1–12 → isValid returns false → parseTimeAs24Hour returns null → buildTimeString throws 'Invalid time' → save fails."
  1. "When showSeconds is false, onTimeSelect emits the stale this.time.seconds value instead of clearing it, causing stale seconds to persist silently in the EDTF output."
  2. "normalizeEdtfString appends EDTF qualifiers after the time component (producing '1985-05-20T14:30:45~'), which is not a valid EDTF production; the edtf() validation call in toEdtfDate would then throw, making it impossible to save any record that has both a time
    and an approximate/uncertain qualifier." I don't think we'd ever get here, since we don't let users indicate time to the second and approximate/uncertain
  3. And one non-EDTF service possible bug
 "summary": "toggleAmPm cross-assigns am and pm by value, so if either optional field is absent the emitted model has undefined for one meridian, permanently corrupting am/pm on the next toggle.",
      "failure_scenario": "A consumer passes TimeModel {hours:'10', minutes:'30', pm:true} omitting the optional am field → toggleAmPm emits {am:true, pm:undefined}; a subsequent toggle emits {am:undefined, pm:true}; any code that reads time.am (e.g. parseTimeAs24Hour, which 
  defaults to AM when pm!==true) will permanently misread midnight/noon for this record."

I can definitely validate these, but it's the end of the day here and I wanted to give you some feedback for your Wednesday! I hope they aren't rabbit holes / definitely treat them with suspicion.

  1. In the document "2026-01-21 Dates Times and Locations Metadata", we have a paragraph specifying that we only validate standard dates and times. From that, I understood that we do not support any X-es in the date or time: "USE STANDARD DATE/TIME VALIDATION, e.g. 4 digit year positive, 1-12 months, 1-31 days, 12/24 hours, etc.". So this is not a concern atm.

  2. It is expected behavior, we can change it upon request.

  3. It is expected behavior, I chose to implement it this way, because atm we do not actually "validate" the date and time, we just filter which characters we are allowed to fill in. Starting an hour with 0 is allowed, because 01 should be valid, so the user will first input 0. The general validation does fail because as a whole, the edtf date is invalid, but up until the user figures out what hour it needs to fill in.

  4. The default value of that is true and we do not actually set it anywhere. We made it into an input because ngb-timepicker gets it as an input property. I'll just remove it and add it if we ever need to hide seconds.

  5. Nowhere in the "Extended Date/Time Format (EDTF) Specification" document is mentioned that a date and time can have a qualifier. In all places where qualifiers are mentioned(level 1 and level 2), it is always only about date. Maybe I am missing something, so if there is a different interpretation of the document, please let me know.

  6. The scenario is not correct, there is no bug, am and pm will be set correctly even if we send undefined. But indeed, the code is inconsistent, so we're going to coerce to true/false, so no undefined is sent.

I think I have answered all the questions, let me know if there is anything else that I need to address.

@aasandei-vsp aasandei-vsp force-pushed the PER-10632-add-components-edtf branch 2 times, most recently from f9d8508 to 173fba1 Compare June 3, 2026 10:44
@cecilia-donnelly
Copy link
Copy Markdown
Member

Thank you for addressing all of these! I see that Robert added the note about only validating standard dates in that document (for #1) but that's very surprising to me since right along we've been talking about the X's as a very useful part of the EDTF standard. Let me clarify that with him.

@aasandei-vsp
Copy link
Copy Markdown
Contributor Author

Thank you for addressing all of these! I see that Robert added the note about only validating standard dates in that document (for #1) but that's very surprising to me since right along we've been talking about the X's as a very useful part of the EDTF standard. Let me clarify that with him.

@cecilia-donnelly I know there has been a lot of talk about what we keep and what we don't, but what was put in writing, for me, it meant we are not supporting unspecified digits. The only place where unspecified digits are mentioned are actually in the unknown date description.

Copy link
Copy Markdown

Copilot AI left a 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 adds shared date/time input building blocks (datepicker, timepicker, and a timezone dropdown scaffold) and significantly expands the EDTF parsing/formatting service to support qualifiers, 24-hour mode, and safer handling of wall-clock times.

Changes:

  • Added standalone Angular components for date picking, time picking (AM/PM/24H), and a timezone dropdown (IANA-based).
  • Reworked EdtfService parsing/serialization (unknown value handling, interval support, wall-clock time preservation, validation helpers, human-readable errors) with expanded unit tests.
  • Added new SCSS mixins and additional PR-blue palette variants to style the new inputs.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/styles/_mixins.scss Adds reusable mixins for date/time input styling.
src/styles/_colors.scss Adds PR blue palette variants used by the new components.
src/app/shared/services/edtf-service/edtf.service.ts Major EDTF parsing/formatting + validation updates (incl. 24H mode).
src/app/shared/services/edtf-service/edtf.service.spec.ts Updates/adds extensive coverage for the new EDTF behaviors.
src/app/shared/components/timezone-dropdown/timezone-dropdown.component.ts New timezone dropdown scaffold using IANA zones and signals.
src/app/shared/components/timezone-dropdown/timezone-dropdown.component.html Dropdown template with filtering and selection UI.
src/app/shared/components/timezone-dropdown/timezone-dropdown.component.scss Styles for the timezone dropdown.
src/app/shared/components/timezone-dropdown/timezone-dropdown.component.spec.ts Basic component tests and filtering/selection tests.
src/app/shared/components/timepicker-input/timepicker-input.component.ts New timepicker input with manual entry, NgbTimepicker sync, and AM/PM/24H cycling.
src/app/shared/components/timepicker-input/timepicker-input.component.html Timepicker input template (segments + dropdown timepicker).
src/app/shared/components/timepicker-input/timepicker-input.component.scss Styles for the timepicker input, incl. ng-bootstrap overrides.
src/app/shared/components/timepicker-input/timepicker-input.component.spec.ts Extensive tests for manual entry, validation, format cycling, and control sync.
src/app/shared/components/datepicker-input/datepicker-input.component.ts New datepicker input with manual entry, validation, and NgbDatepicker sync.
src/app/shared/components/datepicker-input/datepicker-input.component.html Datepicker input template (segments + dropdown datepicker).
src/app/shared/components/datepicker-input/datepicker-input.component.scss Styles for the datepicker input, incl. ng-bootstrap overrides.
src/app/shared/components/datepicker-input/datepicker-input.component.spec.ts Tests for manual entry validation, toggling, and date selection behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/app/shared/services/edtf-service/edtf.service.ts Outdated
Introduce three shared components for EDTF date/time editing:
datepicker-input, timepicker-input, and timezone-dropdown.

Includes related updates to EdtfService and to the shared styles
(_colors.scss and _mixins.scss) to support the new components.

Issue: PER-10632
The time picker only supported Meridian times, but now we can
also have a 24h valid time input.

In order to enable the 24h time, click the AM/PM/24H format cycle button.

Issue: PER-10632
When parsing the input for date, if there are missing digits,
we will fill those with X values.

Issue: PER-10632
@aasandei-vsp aasandei-vsp force-pushed the PER-10632-add-components-edtf branch from 3788f36 to f133a8c Compare June 4, 2026 07:45
Made the elements accessible by keyboard

Issue: PER-10632
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants