Useful Date Time Functions

Lloyd Atkinson

I recently published a library to NPM which contains a small but growing set of date and time features built mostly with date-fns. I consider this to be a “default dependency” of any TypeScript project I work on. Other ecosystems often have their popular date time library. For .NET, for example, NodaTime.

date-fns provides the most comprehensive, yet simple and consistent toolset for manipulating JavaScript dates in a browser & Node.js.

Most of the functions I’ve built so far were either extracted out and refactored from other codebases I’ve worked on. For example, this site formats dates with a specific format. It looks like January 17th, 2023. With date-fns, the format specifier for this is PPP. Hardly a memorable string!

I’ve always considered creating maintainable and readable code important. To that end, one of the first pieces of functionality I extracted from my site’s repository was the scheme I created for using memorable date time format specifiers.

/**
 * Human friendly date time format names.
 * 
 * @see {@link FormatPattern}
 */
export type FormatName =
    | 'date-month-year-and-twelve-hour-time-with-period'
    | 'date-year-month-date'
    | 'month-name-with-day-number'
    | 'month-name-with-ordinal-date'
    | 'relative'
    | 'twelve-hour-time-with-period'
    | 'twelve-hour-time'
    | 'twenty-four-hour-time';

/**
 * Format specifiers for designated format names.
 * 
 * @see {@link FormatName}
 */
export type FormatPattern =
    | 'dd/MM/yyyy - hh:mm a'
    | 'h:mm a'
    | 'h:mm'
    | 'HH:mm'
    | 'LLLL d'
    | 'PPP'
    | 'yyyy-MM-dd';

The library provides a lookup map that can be used for formatting. The user can either call specific functions or a single function that accepts a FormatName. This covers all possible use cases, except for one called relative, which is handled separately and not included in the lookup map.

Record<Exclude<FormatName, 'relative'>, FormatPattern>

This type of lookup map, especially when it has exhaustive type safety, is incredibly powerful. I wrote about how I use that with TypeScript and React over in this post

export const formatUsingName = (date: Date, formatName: FormatName): string => {
    const lookup: Record<Exclude<FormatName, 'relative'>, FormatPattern> = {
        'twelve-hour-time': 'h:mm',
        'twelve-hour-time-with-period': 'h:mm a',
        'twenty-four-hour-time': 'HH:mm',
        'month-name-with-day-number': 'LLLL d',
        'month-name-with-ordinal-date': 'PPP',
        'date-month-year-and-twelve-hour-time-with-period': 'dd/MM/yyyy - hh:mm a',
        'date-year-month-date': 'yyyy-MM-dd',
    } as const;

    if (formatName === 'relative') {
        return formatAsRelativeDate(date, new Date());
    }

    return formatUsingPattern(date, lookup[formatName]);
};

Documentation

As a believer in creating maintainable code I always document code with language specific comment formats - in this case TSDoc. This time I also used TypeDoc for the first time to turn that documentation into a static site. This was a refreshingly easy tool to setup, especially for the Node/NPM ecosystem.

Automated versioning and package releases

On the theme of first times, I also decided to research how to automatically version, generate a changelog markdown file, publish to GitHub releases, and publish to NPM. In other ecosystems, such as .NET with Nuget, this involves a moderate amount of CI/CD pipeline setup.

Many available tools with significant overlap have deterred me from setting this up for NPM in the past. Unfortunately, an enormous number of overlapping tools depend on or are alternatives to other tools. With all the tools out there like conventional-changelog, commit-lint, commitizen, semantic-release, release-it, and many others not listed, all of the features turn into this Venn diagram. I opted for changesets as it seems to work in a way you’d expect, without needing N number of listed tools combined. Merging a pull request creates a new version on NPM, provided the previous build and test steps work.

Node ecosystem for package versioning and publishing

Conclusion

As I mentioned previously, I am going through the process of extracting and refactoring complex, repeated, or confusing date time code from other repositories and formalising it in this library. The first place I started was formatting. The next time you see some code that looks like format(date, 'h:mm a LLLL d') please consider following the approach I’ve described.

Share:

Need help with your software project? Let's talk

Stay up to date

Subscribe to my newsletter to stay up to date on my articles and projects

© Lloyd Atkinson 2023

I'm avaliable for work 💡