When we searched around for jQuery or other JS time selection libraries we were a bit disappointed at what we found. We wanted something modern like the time selection in ClickUp, but weren’t sure we could pull off the very dynamic “enter whatever and have it converted to a time” approach that ClickUp has. We wanted something more traditional that the ClickUp approach, but not a giant list of predetermined hours and minutes either. The idea of “masking” the time entry fields was appealing, and there a lot of jQuery and non-jQuery native JS solutions for masking. We had already started using Cleave.js for the masking of currency entry fields. Unfortunately Cleave doesn’t support AM/PM time entry, it only provides masking for either 12-hour or 24-hour time entries but without an AM/PM option.
What we ended up with is a solution that handles AM/PM outside of the input field. So the input is a natively supported Cleave.js mask (defined as h:m) which enables 12-hour entries such as 01:24 or 11:39. And it has the nice feature that if the user enters the field and types “3” as the first digit, the masking realizes it must be for 3PM or 3AM entry because 3 is not a valid entry for the first digit. So rather than block entry, it enters “03” and the user only has to put in the minutes. It reduces the time entry down to 1-click and 3 digits. And then there is the AM/PM selection, which as mentioned we handle outside the input. But to make the time entry look congruent as if it’s one piece, we use absolute positioning to put the AM/PM selectors inside the input, which is wrapped in a relative positioned div element. Then we use a jQuery click event to capture the AM/PM selection, and when we parse the time we look at which selector elements has the class “selected”. We could alternatively have used a hidden input field to capture and store the selection value, but this seems like an extra step that doesn’t add any value to the process. After all the options will always be only AM/PM, and finding the element with class=”selected” is not much different from parsing the stored value from a hidden input.
Once we have the masked time entry entered in the format HH:MM plus we have the AM/PM, we then use Day.js to convert this into a dayjs object which allows for formatting or further manipulation. For time entries in the timesheet this allows us to use Day.js diff() to find the number of minutes between the entered start and end times. But there is a further complication to consider when working with AM/PM times, which is that Day.js expects 24-hours times unless we utilize an optional library for non-standardized time parsing. Rather than do that, we wrote a small function that converts our AM/PM times into 24-hour time simply by checking for values that need to be adjusted. For instance 01:00PM becomes 13:00 which is then valid 24-hour format that can be natively parsed with Day.js.