Skip to content
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

feat: Add versatile duration formatting and parsing function #66

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

SeanLuis
Copy link

@SeanLuis SeanLuis commented Jul 30, 2024

Overview

This pull request introduces a new feature: the formatOrParseDuration function. This function provides the ability to both format durations given in milliseconds to a specified string format and parse duration strings back into milliseconds. This enhancement aims to offer a robust and scalable solution for duration handling within the library.

Features

  • Formatting Durations: Converts durations from milliseconds to formatted strings in various styles.
    • Supported formats include:
      • hh:mm:ss
      • mm:ss
      • DD:hh:mm:ss
      • hh:mm:ss,SSS
  • Parsing Durations: Converts duration strings in specified formats back into milliseconds.
    • Supported formats include:
      • hh:mm:ss
      • mm:ss
      • DD:hh:mm:ss
      • hh:mm:ss,SSS

Tasks Completed

  • Implemented formatOrParseDuration function
    • Added support for formatting durations from milliseconds to various string formats
    • Added support for parsing duration strings back into milliseconds
  • Enhanced duration formatting options to include milliseconds (hh:mm:ss,SSS)
  • Updated types and interfaces to support the new duration formatting and parsing
  • Wrote comprehensive unit tests for:
    • Formatting durations (hh:mm:ss, mm:ss, DD:hh:mm:ss, hh:mm:ss,SSS)
    • Parsing durations (hh:mm:ss, mm:ss, DD:hh:mm:ss, hh:mm:ss,SSS)
    • Handling invalid inputs and duration strings

Implementation Details

Function: formatOrParseDuration

  • Parameters:

    • input (number | string): Duration in milliseconds to format or a string to parse.
    • options (DurationOptions): Options to define formatting or parsing behavior, including:
      • format: Desired format for the duration string or parsing pattern.
      • parse: Boolean indicating whether to parse a string or format a duration.
      • locale: Locale string for future compatibility.
  • Behavior:

    • When parse is true, it parses the provided duration string into milliseconds based on the specified format.
    • When parse is false, it formats the given duration in milliseconds to a string based on the specified format.
    • Throws an error for invalid inputs or options.

Tests

Comprehensive tests have been added to cover various scenarios for both formatting and parsing:

  • Formatting durations to hh:mm:ss, mm:ss, DD:hh:mm:ss, hh:mm:ss,SSS.
  • Parsing duration strings in the above formats back into milliseconds.
  • Handling invalid inputs and duration strings with appropriate error messages.

Example Usage

import { formatOrParseDuration } from "@formkit/tempo";

// Formatting durations
formatOrParseDuration(3661000); // Output: "01:01:01"
formatOrParseDuration(61000, { format: "mm:ss" }); // Output: "01:01"
formatOrParseDuration(3660000, { format: "hh:mm" }); // Output: "01:01"
formatOrParseDuration(90061000, { format: "DD:hh:mm:ss" }); // Output: "01:01:01:01"
formatOrParseDuration(90061001, { format: "DD:hh:mm:ss,SSS" }); // Output: "01:01:01:01,001"
formatOrParseDuration(3661001, { format: "hh:mm:ss,SSS" }); // Output: "01:01:01,001"

// Parsing durations
formatOrParseDuration("01:01:01", { format: "hh:mm:ss", parse: true }); // Output: 3661000
formatOrParseDuration("01:01", { format: "mm:ss", parse: true }); // Output: 61000
formatOrParseDuration("01:01", { format: "hh:mm", parse: true }); // Output: 3660000
formatOrParseDuration("01:01:01:01", { format: "DD:hh:mm:ss", parse: true }); // Output: 90061000
formatOrParseDuration("01:01:01,001", { format: "hh:mm:ss,SSS", parse: true }); // Output: 3661001

// Error handling
try {
  formatOrParseDuration("invalid input");
} catch (error) {
  console.error(error); // Output: Error: Invalid input or options.
}

try {
  formatOrParseDuration("invalid input", { format: "hh:mm:ss", parse: true });
} catch (error) {
  console.error(error); // Output: Error: Invalid duration string.
}

- Introduced `formatOrParseDuration` function to format and parse durations.
- Supports various formats including `hh:mm:ss`, `mm:ss`, `DD:hh:mm:ss`, `hh:mm:ss,SSS`.
- Updated related types and interfaces to accommodate the new function.
- Added comprehensive tests for different duration formats and parsing.
Copy link

vercel bot commented Jul 30, 2024

@SeanLuis is attempting to deploy a commit to the Formkit Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Member

@justin-schroeder justin-schroeder left a comment

Choose a reason for hiding this comment

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

Thank you for your work on this PR. it looks well put together and I appreciate the effort. What I like about this:

  • Simple to implement with today’s browsers
  • You include tests
  • Bi-directional — this follows the tempo mantra that anything we can format we should be able to parse.

Those are great positives, and in that regard I would consider merging this. However, I think it may warrant further discussion as for how we should actually handle things like duration formats.

Unlike the rest of Tempo, this feature does not take into account locales. The colon, for example, is not a global standard. Some countries use a dot . some use h, some use a comma ,. This is why it would be so great to have names like short or long.

Now, of course, the rub is how could we take these things into account since the Intl.DurationFormat() is barely supported anywhere at this point. The DurationFormat is a stage 3 proposal, so perhaps it will be more broadly available in a year or two and this API could be more complete...

I’m not going to immediately merge this or close this — I’d love to hear more feedback from other users.

Again — thanks so much for the effort, great work.

src/index.ts Outdated Show resolved Hide resolved
src/types.ts Show resolved Hide resolved
src/duration.ts Outdated Show resolved Hide resolved
@SeanLuis
Copy link
Author

Thank you for your work on this PR. it looks well put together and I appreciate the effort. What I like about this:

  • Simple to implement with today’s browsers
  • You include tests
  • Bi-directional — this follows the tempo mantra that anything we can format we should be able to parse.

Those are great positives, and in that regard I would consider merging this. However, I think it may warrant further discussion as for how we should actually handle things like duration formats.

Unlike the rest of Tempo, this feature does not take into account locales. The colon, for example, is not a global standard. Some countries use a dot . some use h, some use a comma ,. This is why it would be so great to have names like short or long.

Now, of course, the rub is how could we take these things into account since the Intl.DurationFormat() is barely supported anywhere at this point. The DurationFormat is a stage 3 proposal, so perhaps it will be more broadly available in a year or two and this API could be more complete...

I’m not going to immediately merge this or close this — I’d love to hear more feedback from other users.

Again — thanks so much for the effort, great work.

Thank you, Justin, for your detailed feedback and kind words. I appreciate your points regarding the implementation and the thoughtful consideration about locale-specific formats. I completely agree that having options like short and long would make the feature more robust and internationally friendly.

I'll explore how we can incorporate locale variations and ensure the feature aligns better with global standards. As for the Intl.DurationFormat(), it’s indeed in its early stages, and we'll keep an eye on its progress to enhance our implementation when it becomes more widely supported.

I'll also await further feedback from the community to gather more insights. Thank you again for your guidance and support.

SeanLuis and others added 2 commits July 30, 2024 12:14
- Extended DateTimeFormatPartTypes to include "millisecond".
- Added support for parsing and formatting milliseconds in `parse.ts`.
- Updated `format.ts` to handle new format tokens for milliseconds.
- Enhanced `parts.ts` with millisecond support in guessPattern and createPart functions.
- Modified `date.ts` to correctly normalize and handle milliseconds.
- Updated `offset.ts` to apply offsets including milliseconds.
- Improved ISO 8601 validation in `iso8601.ts` to support milliseconds.
- Updated common utilities in `common.ts` to handle `SSS` format pattern and ensure millisecond support.
- Added new test cases for milliseconds in `sameMillisecond.test.ts`.
- Expanded ISO 8601 validation tests in `iso8601.test.ts` to include milliseconds.

These changes enhance the precision of date handling functions, ensuring compliance with ISO 8601 and extending support to milliseconds.
@justin-schroeder
Copy link
Member

I took a quick glance over the work @SeanLuis — it looks really promising. I’ll try to carve out some time to fully review it and test it soon!

@SeanLuis
Copy link
Author

@justin-schroeder

Add Millisecond Support and Improve Date Handling Functions

Description

This commit 4e94ac2 introduces support for milliseconds across various date handling functions within the codebase. The following changes have been made:

Detailed Changes

1. Types and Interfaces Update (types.ts)

  • Extended DateTimeFormatPartTypes:
    • Added millisecond to Intl.DateTimeFormatPartTypes.
  • Extended DateTimeFormatPartTypesRegistry:
    • Added millisecond to Intl.DateTimeFormatPartTypesRegistry.

2. Parse Function (parse.ts)

  • Added support for parsing milliseconds.
  • Added comments to clarify the functionality.
  • Updated parseParts to handle milliseconds.

3. Format Function (format.ts)

  • Added support for formatting dates with milliseconds.
  • Included new format tokens for milliseconds.
  • Ensured ISO 8601 compliance when formatting dates.

4. Parts Function (parts.ts)

  • Updated the guessPattern function to include a case for millisecond.
  • Added comments to clarify the functionality.
  • Updated createPart function to handle millisecond.

5. Date Normalization (date.ts)

  • Updated the normalize function to handle milliseconds correctly.
  • Included detailed comments explaining each change.

6. Offset Handling (offset.ts)

  • Updated the applyOffset function to support milliseconds.
  • Added comments to clarify the functionality.
  • Included milliseconds in the relativeTime function.

7. ISO 8601 Validation (iso8601.ts)

  • Modified the regex pattern in iso8601Match to allow validation of milliseconds.
  • Included tests for various ISO 8601 date formats including those with milliseconds.

8. Common Utilities (common.ts)

  • Added SSS to clockAgnostic format patterns.
  • Updated createPartMap and other relevant functions to handle millisecond.
  • Added comments to clarify the functionality.

9. Test Files

  • New Tests for Milliseconds (sameMillisecond.test.ts):
    • Created a test suite for the sameMillisecond function.
    • Verified the functionality for different cases, including string dates and exact millisecond matching.
  • Updated Tests for ISO 8601 (iso8601.test.ts):
    • Added tests to validate ISO 8601 dates with milliseconds.

Impact

These changes improve the precision of date handling functions and extend support to milliseconds, ensuring better accuracy and compliance with various date standards, including ISO 8601.

@WilcoSp
Copy link
Contributor

WilcoSp commented Aug 1, 2024

@SeanLuis & @justin-schroeder maybe best to split the formatOrParseDuration function up into multiple functions to allow for better bundle sizes & also allows developers to decide on their own how

I would split it up into the following (names are only suggestions)

type DurationObj = {years: number, months: number .........}

function formatDuration(duration: DurationObj, options: {format: string, style: string, .....}): string

function parseDuration(durationString: string, options: { style: string, locale: string, ...}): DurationObj

function msToDurationObj(ms: number): DurationObj

function durationObjToMs(duration: DurationObj): number

Also to add to this, I'm also planning to create a diff function which will return DurationObj & also an add function which will take DurationObj to modify/create a new date

@SeanLuis
Copy link
Author

SeanLuis commented Aug 1, 2024

@SeanLuis & @justin-schroeder maybe best to split the formatOrParseDuration function up into multiple functions to allow for better bundle sizes & also allows developers to decide on their own how

I would split it up into the following (names are only suggestions)

type DurationObj = {years: number, months: number .........}

function formatDuration(duration: DurationObj, options: {format: string, style: string, .....}): string

function parseDuration(durationString: string, options: { style: string, locale: string, ...}): DurationObj

function msToDurationObj(ms: number): DurationObj

function durationObjToMs(duration: DurationObj): number

Also to add to this, I'm also planning to create a diff function which will return DurationObj & also an add function which will take DurationObj to modify/create a new date

It seems good to me, it is in line with the structure of the package. As for the names of the functions, they seem fine to me. 👌🏼

@peter-emad99
Copy link

hey @SeanLuis i think we could add more formats like 1 day, 2hours, 30 minutes by levaraging

Intl.NumberFormat('en',{
    unit:'hour'  // hour ,day, minute,..etc
    style:'unit'
    unitDisplay:'long' // long short narrow ( hours hr h ,  minutes min m ,...etc)

})

list of unit indentifier supported

@justin-schroeder
Copy link
Member

This is really great work @SeanLuis — Ive got some stuff to ship beginning of the week and then ill turn my attention to this PR. thanks for keeping the quality high.

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.

4 participants