formkit / tempo

📆 Parse, format, manipulate, and internationalize dates and times in JavaScript and TypeScript.
https://tempo.formkit.com
MIT License
2.37k stars 33 forks source link

feat: Add versatile duration formatting and parsing function #66

Open SeanLuis opened 3 months ago

SeanLuis commented 3 months ago

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

Tasks Completed

Implementation Details

Function: formatOrParseDuration

Tests

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

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.
}
vercel[bot] commented 3 months ago

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

A member of the Team first needs to authorize it.

SeanLuis commented 3 months ago

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.

justin-schroeder commented 3 months ago

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 commented 3 months ago

@justin-schroeder

Add Millisecond Support and Improve Date Handling Functions

Description

This commit 4e94ac2b1999824ae722484d5758dd896a49052c 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)

2. Parse Function (parse.ts)

3. Format Function (format.ts)

4. Parts Function (parts.ts)

5. Date Normalization (date.ts)

6. Offset Handling (offset.ts)

7. ISO 8601 Validation (iso8601.ts)

8. Common Utilities (common.ts)

9. Test Files

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 commented 3 months ago

@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 commented 3 months ago

@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 commented 3 months ago

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 commented 3 months ago

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.

Serator commented 1 month ago

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.

[Intl.DurationFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DurationFormat()) is supported by more and more browsers. It makes sense to use it as the main tool for working with duration and foulback in case Intl.DurationFormat() is not supported.