Time ago

Journal 08

Now that cost per wear is showing up; the next feature on my list is to capture and display when the garment was last worn.

This means jumping into how Ruby can show dates. Especially as I wanted a more approachable and contextual way to display how much time had passed. This lead me to time ago in words.

Last Worn

The two main reasons to include this:

  • Showing which clothes aren’t worn. If it’s been a substantial time – say, two or more years – it could be a good prompt to either wear the garment again or perhaps to donate or sell it on. Either way, it probably doesn’t need to stay sitting, forgotten, at the bottom of a drawer.
  • Ensuring a good rotation of clothes. With the idea of owning less, wearing more; this becomes really helpful!

Implementation

First was to capture some key dates. I ran a migration to add two :date columns to my database for:

  • last_worn
  • purchase_date

Added to the params, and brought into the show view and form partial view. Not too hard to get up and running! However…

American date formatting. Not exactly my style – so I wanted to change it. I could hard code the date formatting in, but a more elegant solution would be to localise the formatting. This means jumping into internationalisation in Ruby… and while this is only going to touch on it briefly, it will mean it’s set up for for proper localisation later. Say if I wanted to later translate to French!

Localisation

For now I’m just going to edit the default English module in the locales folder (config > locales).

I like that this will allow flexibility for translations later, while also giving a customised global formatting for the dates at the moment.

To the en.yml file I added:

date:
    formats:
      default: "%-d %B %Y"

A date format (strftime) cheat sheet is really helpful here!

This places the default to show in my preferred format and with today’s date as an example: 23 September 2021.

Also note, there’s a subtle difference between %e and %-d – something I definitely do obsess over, with my love for typography.

%e: Day of the month without leading 0; blank-padded. ( 1… 31)

%-d: Day of the month without leading 0. (1… 31)

Both very useful for different situations, and I love this level of detailing!

This displays the dates perfectly throughout the app, everywhere except in the form, which uses the date picker and shows in the dd/mm/yyyy format – as determined by the user’s browser locale.

Here is the result:

Time in Words

The copy isn’t as intuitive and clear as I would like though. It could be more user friendly and helpful by showing the amount of time that has passed in approximate words. ie. 2 weeks ago instead of giving an exact date and putting the onus on the user to calculate what this means.

Excitingly, time_ago_in_words exists as a view helper function, which practically automates it.

It still needed some customising though, to get the grammar working perfectly with pluralisation and to sit with that ‘ago’.

This meant creating a helper with a case statement that changes the phrasing based on how much time had passed. The aim is to align the copy more closely to how we would roughly group the passing of time.

case days
when 0
	"Today"
when 1...7
	pluralize(days, 'day')
when 7...52
	weeks = (days/7).round
	pluralize(weeks, 'week')
end
= Half open range which includes the first number, but excludes the last number.

This means that:

  • ‘Today’ will be shown if the date range is under 1 day ie. worn today.
  • Days 1–6 will be counted in days and pluralised as necessary. ie. 1 day, 6 days
  • For the 7th day and up until 52 days (starting to approach 2 months) the value will be shown in weeks. Also pluralised. ie. 1 week, 5 weeks

It continues on to months and years. It took a little tweaking to make ‘breaks’ feel natural.

Days up until it becomes a week feels pretty good. 5 weeks makes sense, but then it feels like becoming a fuzzy rounding to months works quite well. As time passes, the rounding can become softer – this matches with how we tend to relate to time passed.

Ago or otherwise

I initially hard coded in the ‘ago’ but I wanted to use the same helper for the purchase_date column and it just didn’t make sense to say ‘ago’ at the end. So I made a named argument for with_ago and added it in as a ternary expression so it could be triggered on when desired.

I set the default to be false, so ‘ago’ would only appear when requested.

def time_in_words(date, with_ago: false)

and then added the expression to each of the when conditions (other than the ‘Today’ one) so it would appear when called.

when 1...7
	pluralize(days, 'day') + (with_ago ? " ago" : "")

It’s in the view we set this to true:

Last worn:
	<%= time_in_words(@article.last_worn, with_ago: true) %>

I also added in a little basic formatting, and decided to add the date as well, for those who would like an exact date.

I’m pretty happy with how it comes out, as a start!