Relative Time Formatter - My First Attempt - Part 1
In relative time formatter assignment I laid out a simple learning assignment, and some potential design decisions. Later, I'll share some existing time formatters from the JS ecosystem and others. First though, I thought it would be interesting to work through the problem myself.
In this post, I'll provide some quick answers to my initial design questions, and then I'll plan some next steps.
First, a pragmatic tip
The point of this series is to work through a realistic problem as a way to practice your coding skills. But, this is also a good example of something you shouldn't build yourself. If you're working on a 'real project' that needs relative time formatting - but relative time formatting is just a detail, not the main unique value proposition of your project - then you should simply reach for one of the existing solutions. They are likely more than good enough for your purpose, and will save you quite a bit of time.
Don't build a utility like this yourself without first seriously exploring existing solutions. Unless you're just trying to learn. Which we are. So let's build something.
Answering the Design Questions
Which programming language will you use?
I'm going to start with JavaScript, since it's such a widely used language, especially for new developers. I'll use vanilla JS to keep it simple. If I was going to publish this for others to use, I'd also provide TypeScript declarations.
And which language features or libraries?
I'll explore the Temporal API and Internationalization API first, as they might cover much of what I need.
How will your algorithm decide what to return?
I think there are basically two logical chunks to this algorithm.
First, we need to decide which units are significant. Depending on our desired level of precision, this could be either a heirarchical selection, or a combination. In either case, we could start with our biggest unit - like years - and work our way down to our smallest unit.
For a single, heirarchical unit, we just return the first non zero value. If we have at least a year - or almost a year - then we return years, rounded, and discard the remainder. If not, we work our way down the heirarchy to return months, weeks, days, or hours. With this approach, we'd return something like 1 year, or 3 weeks.
For a combination of units, we can still work down from the largest unit to the smallest. But, instead of rounding and discarding the remainder, we keep iterating on the remainder. For example, we might return something like 1 day, 2 hours, and 30 minutes.
Finally, once we've decided on the significant units, we need to format a string based on our style preferences and supported languages. Like 1 week ago, 1W, one week ago, or last week. Or 1Y 3W 2D 4H.
What level of granularity will you return? and How will you handle precision and rounding?
I'll start by returning a single unit, rounded, and discard the remainder, rather than trying to enumerate every descending increment. But, maybe I'll leave the option open to provide both formats.
Which human language(s) will you support?
I'll start with English. If I use the Intl API then I might be able to support other languages very easily. If not, maybe i'll leave room for a internationalization hook, but not directly handle internationalization yet.
How will you handle pluralization?
I'll start by seeing what Intl provides. Otherwise, if I have to handle formatting and translation more manually, I'll use an internationalization library that supports things like plurals and ordinals out of the box.
What types of time units will you support?
It would be fun to support custom units. For example, maybe you're working on a game or story that uses unique time units. Or, maybe you work in a technical domain that requires unique or extended time units.
But, this would also change the scope, or change the possible implementation patterns. So, I'll explore it but not commit to it yet.
What level of customization will you support, and how?
TBD based on my initial exploration
What types of inputs will you support?
TBD based on my initial exploration
How will you handle errors, like invalid inputs or configurations?
Assert and throw. Don't try to provide 'user friendly' error string like a validation library might, but make sure the errors are clear for developers.
Will you create a standalone utility, or part of a broader utility library?
Standalone for now.
Will you create a UI component, or only a utility?
Just a utility function for now.
How will you test?
I think unit tests are a must for something like this. I'll keep it simple and use Node's built in test runner and assertions.
How will you document?
TBD.
What performance optimizations might you consider?
TBD. I don't think performance will be a major concern for a library like this. But, if it's used in a large loop we don't want to introduce any bottlenecks or memory issues.
Prototyping
I think my next step will be to explore Temporal and Intl since I've barely used either yet, and then write one or more simple prototypes. Stay tuned.