Skip to content

Commit cc5726f

Browse files
committed
feat: add dateTimeFromNowFriendly localization util
1 parent 6333108 commit cc5726f

File tree

3 files changed

+125
-0
lines changed

3 files changed

+125
-0
lines changed

docs/API-Reference/utils/LocalizationUtils.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,19 @@ Returns a relative time string (e.g., "2 days ago", "in 3 hours") based on the d
4545
| [date] | <code>Date</code> | The date to compare with the current date and time. If not given, defaults to now. |
4646
| [lang] | <code>string</code> | Optional language code to use for formatting (e.g., 'en', 'fr'). If not provided, defaults to the application locale or 'en'. |
4747

48+
<a name="dateTimeFromNowFriendly"></a>
49+
50+
## dateTimeFromNowFriendly(date, [lang]) ⇒ <code>string</code>
51+
Returns an intelligent date string.
52+
- For dates within the last 30 days or the future: relative time (e.g., "2 days ago", "in 3 hours").
53+
- For dates earlier this year: formatted date (e.g., "Jan 5").
54+
- For dates not in the current year: formatted date with year (e.g., "Jan 5, 2023").
55+
56+
**Kind**: global function
57+
**Returns**: <code>string</code> - - An intelligently formatted date string.
58+
59+
| Param | Type | Description |
60+
| --- | --- | --- |
61+
| date | <code>Date</code> | The date to compare and format. |
62+
| [lang] | <code>string</code> | Optional language code to use for formatting (e.g., 'en', 'fr'). |
63+

src/utils/LocalizationUtils.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,46 @@ define(function (require, exports, module) {
102102
}
103103
}
104104

105+
/**
106+
* Returns an intelligent date string.
107+
* - For dates within the last 30 days or the future: relative time (e.g., "2 days ago", "in 3 hours").
108+
* - For dates earlier this year: formatted date (e.g., "Jan 5").
109+
* - For dates not in the current year: formatted date with year (e.g., "Jan 5, 2023").
110+
*
111+
* @param {Date} date - The date to compare and format.
112+
* @param {string} [lang] - Optional language code to use for formatting (e.g., 'en', 'fr').
113+
* @returns {string} - An intelligently formatted date string.
114+
*/
115+
function dateTimeFromNowFriendly(date, lang) {
116+
const now = new Date();
117+
const diffInMilliseconds = date - now;
118+
const diffInDays = Math.floor(diffInMilliseconds / (1000 * 60 * 60 * 24));
119+
120+
// If within the last 30 days or the future, use relative time
121+
if (Math.abs(diffInDays) <= 30) {
122+
return dateTimeFromNow(date, lang);
123+
}
124+
125+
// If in the current year, format as "MMM DD"
126+
const currentYear = now.getFullYear();
127+
const dateYear = date.getFullYear();
128+
129+
const languageOption = [lang || brackets.getLocale() || "en", "en"];
130+
131+
if (currentYear === dateYear) {
132+
return new Intl.DateTimeFormat(languageOption, { month: "short", day: "numeric" }).format(date);
133+
}
134+
135+
// For dates in previous years, format as "MMM DD, YYYY"
136+
return new Intl.DateTimeFormat(languageOption,
137+
{ month: "short", day: "numeric", year: "numeric" }).format(date);
138+
}
105139

106140
// Define public API
107141
exports.getLocalizedLabel = getLocalizedLabel;
108142
exports.getFormattedDateTime = getFormattedDateTime;
109143
exports.dateTimeFromNow = dateTimeFromNow;
144+
exports.dateTimeFromNowFriendly = dateTimeFromNowFriendly;
110145
// public constants
111146
exports.DATE_TIME_STYLE = DATE_TIME_STYLE;
112147
});

test/spec/LocalizationUtils-test.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,5 +193,79 @@ define(function (require, exports, module) {
193193
expect(result).toBe("now");
194194
});
195195
});
196+
197+
describe("dateTimeFromNowFriendly", function () {
198+
it("should use relative time for dates within the last 30 days", function () {
199+
const testDate = new Date(Date.now() - 3 * 24 * 60 * 60 * 1000); // 3 days ago
200+
const result = LocalizationUtils.dateTimeFromNowFriendly(testDate, "en");
201+
expect(result).toBe("3 days ago");
202+
});
203+
204+
it("should use relative time for dates in the future within 30 days", function () {
205+
const testDate = new Date(Date.now() + 5 * 24 * 60 * 60 * 1000); // 5 days in the future
206+
const result = LocalizationUtils.dateTimeFromNowFriendly(testDate, "en");
207+
expect(result).toBe("in 5 days");
208+
});
209+
210+
it("should use formatted date without year for dates earlier this year", function () {
211+
const now = new Date();
212+
const testDate = new Date(now.getFullYear(), 1, 15); // Feb 15 of current year
213+
const result = LocalizationUtils.dateTimeFromNowFriendly(testDate, "en");
214+
expect(result).toBe("Feb 15");
215+
});
216+
217+
it("should use formatted date with year for dates in previous years", function () {
218+
const testDate = new Date(2022, 6, 4); // July 4, 2022
219+
const result = LocalizationUtils.dateTimeFromNowFriendly(testDate, "en");
220+
expect(result).toBe("Jul 4, 2022");
221+
});
222+
223+
it("should use formatted date with year for future dates in upcoming years", function () {
224+
const testDate = new Date(new Date().getFullYear() + 2, 0, 1); // Jan 1, two years from now
225+
const result = LocalizationUtils.dateTimeFromNowFriendly(testDate, "en");
226+
expect(result).toBe("Jan 1, " + (new Date().getFullYear() + 2));
227+
});
228+
229+
it("should handle relative time for today", function () {
230+
const now = new Date();
231+
const result = LocalizationUtils.dateTimeFromNowFriendly(now, "en");
232+
expect(result).toBe("now");
233+
});
234+
235+
// Relative time tests
236+
it("should use relative time for dates within the last 30 days (de locale)", function () {
237+
const testDate = new Date(Date.now() - 3 * 24 * 60 * 60 * 1000); // 3 days ago
238+
const result = LocalizationUtils.dateTimeFromNowFriendly(testDate, "de");
239+
expect(result).toBe("vor 3 Tagen");
240+
});
241+
242+
it("should use relative time for dates in the future within 30 days (de locale)", function () {
243+
const testDate = new Date(Date.now() + 5 * 24 * 60 * 60 * 1000); // 5 days in the future
244+
const result = LocalizationUtils.dateTimeFromNowFriendly(testDate, "de");
245+
expect(result).toBe("in 5 Tagen");
246+
});
247+
248+
// Current year tests
249+
it("should use formatted date without year for dates earlier this year (de locale)", function () {
250+
const now = new Date();
251+
const testDate = new Date(now.getFullYear(), 1, 15); // Feb 15 of current year
252+
const result = LocalizationUtils.dateTimeFromNowFriendly(testDate, "de");
253+
expect(result).toBe("15. Feb.");
254+
});
255+
256+
// Non-current year tests
257+
it("should use formatted date with year for dates in previous years (de locale)", function () {
258+
const testDate = new Date(2022, 6, 4); // July 4, 2022
259+
const result = LocalizationUtils.dateTimeFromNowFriendly(testDate, "de");
260+
expect(result).toBe("4. Juli 2022");
261+
});
262+
263+
it("should use formatted date with year for future dates in upcoming years (de locale)", function () {
264+
const testDate = new Date(new Date().getFullYear() + 2, 0, 1); // Jan 1, two years from now
265+
const result = LocalizationUtils.dateTimeFromNowFriendly(testDate, "de");
266+
expect(result).toBe(`1. Jan. ${new Date().getFullYear() + 2}`);
267+
});
268+
});
269+
196270
});
197271
});

0 commit comments

Comments
 (0)