From 0b4e2a99790347bea0ab5f7d651f2330e3054601 Mon Sep 17 00:00:00 2001 From: Jason Garber Date: Wed, 14 Jun 2023 15:58:37 -0400 Subject: [PATCH] fix: strftime getSuffix works for all dates As written, the strftime module's `getSuffix` method (responsible for returning a date's ordinal string) works correctly for all dates in a month _except_ 11, 12, and 13. The existing code would return a string based on the last digit of the date. In American English, these three dates use the `th` suffix rather than `st`, `nd`, or `rd`. This refactor/bug fix draws inspiration from Rails' ActiveSupport ordinal inflector: https://github.com/rails/rails/blob/main/activesupport/lib/active_support/locale/en.rb The Ruby code above is designed to work with any number, so the bug fix to this library is simpler. The test suite is also updated with additional examples to verify the correctness of these changes. --- src/util/strftime.spec.ts | 29 +++++++++++++++++++++++------ src/util/strftime.ts | 33 ++++++++++++++++++++++++--------- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/src/util/strftime.spec.ts b/src/util/strftime.spec.ts index d79409f124..4d60429332 100644 --- a/src/util/strftime.spec.ts +++ b/src/util/strftime.spec.ts @@ -32,13 +32,30 @@ describe('util/strftime', function () { }) }) it('should format %q as date suffix', function () { - const st = new Date('2016-03-01 03:05:03') - const nd = new Date('2016-03-02 03:05:03') - const rd = new Date('2016-03-03 03:05:03') - expect(t(st, '%q')).toBe('st') - expect(t(nd, '%q')).toBe('nd') - expect(t(rd, '%q')).toBe('rd') + const first = new Date('2016-03-01 03:05:03') + const second = new Date('2016-03-02 03:05:03') + const third = new Date('2016-03-03 03:05:03') + + const eleventh = new Date('2016-03-11 03:05:03') + const twelfth = new Date('2016-03-12 03:05:03') + const thirteenth = new Date('2016-03-13 03:05:03') + + const twentyfirst = new Date('2016-03-21 03:05:03') + const twentysecond = new Date('2016-03-22 03:05:03') + const twentythird = new Date('2016-03-23 03:05:03') + + expect(t(first, '%q')).toBe('st') + expect(t(second, '%q')).toBe('nd') + expect(t(third, '%q')).toBe('rd') expect(t(now, '%q')).toBe('th') + + expect(t(eleventh, '%q')).toBe('th') + expect(t(twelfth, '%q')).toBe('th') + expect(t(thirteenth, '%q')).toBe('th') + + expect(t(twentyfirst, '%q')).toBe('st') + expect(t(twentysecond, '%q')).toBe('nd') + expect(t(twentythird, '%q')).toBe('rd') }) }) diff --git a/src/util/strftime.ts b/src/util/strftime.ts index 007fa1c3d9..ab9bc9f026 100644 --- a/src/util/strftime.ts +++ b/src/util/strftime.ts @@ -11,12 +11,6 @@ const dayNames = [ ] const monthNamesShort = monthNames.map(abbr) const dayNamesShort = dayNames.map(abbr) -const suffixes = { - 1: 'st', - 2: 'nd', - 3: 'rd', - 'default': 'th' -} interface FormatOptions { flags: object; width?: string; @@ -52,9 +46,30 @@ function isLeapYear (d: LiquidDate) { return !!((year & 3) === 0 && (year % 100 || (year % 400 === 0 && year))) } function getSuffix (d: LiquidDate) { - const str = d.getDate().toString() - const index = parseInt(str.slice(-1)) - return suffixes[index] || suffixes['default'] + const date = d.getDate() + + let suffix = 'th' + + switch (date) { + case 11: + case 12: + case 13: + break + default: + switch (date % 10) { + case 1: + suffix = 'st' + break + case 2: + suffix = 'nd' + break + case 3: + suffix = 'rd' + break + } + } + + return suffix } function century (d: LiquidDate) { return parseInt(d.getFullYear().toString().substring(0, 2), 10)