Skip to content

Commit

Permalink
feat: add dateTimeFromNow localization util
Browse files Browse the repository at this point in the history
  • Loading branch information
abose committed Dec 25, 2024
1 parent 26fb238 commit 06d20ac
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 1 deletion.
13 changes: 13 additions & 0 deletions docs/API-Reference/utils/LocalizationUtils.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,16 @@ Formats a given date object into a locale-aware date and time string.
| [dateTimeFormat.dateStyle] | <code>string</code> | Specifies the date format style. One of: DATE_TIME_STYLE.* |
| [dateTimeFormat.timeStyle] | <code>string</code> | Specifies the time format style. One of: DATE_TIME_STYLE.* |

<a name="dateTimeFromNow"></a>

## dateTimeFromNow([date], [lang]) ⇒ <code>string</code>
Returns a relative time string (e.g., "2 days ago", "in 3 hours") based on the difference between the given date and now.

**Kind**: global function
**Returns**: <code>string</code> - - A human-readable relative time string (e.g., "2 days ago", "in 3 hours").

| Param | Type | Description |
| --- | --- | --- |
| [date] | <code>Date</code> | The date to compare with the current date and time. If not given, defaults to now. |
| [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'. |

33 changes: 33 additions & 0 deletions src/utils/LocalizationUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,42 @@ define(function (require, exports, module) {
return Intl.DateTimeFormat([lang || brackets.getLocale() || "en", "en"], dateTimeFormat).format(date);
}

/**
* Returns a relative time string (e.g., "2 days ago", "in 3 hours") based on the difference between the given date and now.
*
* @param {Date} [date] - The date to compare with the current date and time. If not given, defaults to now.
* @param {string} [lang] - Optional language code to use for formatting (e.g., 'en', 'fr').
* If not provided, defaults to the application locale or 'en'.
* @returns {string} - A human-readable relative time string (e.g., "2 days ago", "in 3 hours").
*/
function dateTimeFromNow(date, lang) {
date = date || new Date();
const now = new Date();
const diffInSeconds = Math.floor((date - now) / 1000);

const rtf = new Intl.RelativeTimeFormat([lang || brackets.getLocale() || "en", "en"],
{ numeric: 'auto' });

if (Math.abs(diffInSeconds) < 60) {
return rtf.format(diffInSeconds, 'second');
} else if (Math.abs(diffInSeconds) < 3600) {
return rtf.format(Math.floor(diffInSeconds / 60), 'minute');
} else if (Math.abs(diffInSeconds) < 86400) {
return rtf.format(Math.floor(diffInSeconds / 3600), 'hour');
} else if (Math.abs(diffInSeconds) < 2592000) {
return rtf.format(Math.floor(diffInSeconds / 86400), 'day');
} else if (Math.abs(diffInSeconds) < 31536000) {
return rtf.format(Math.floor(diffInSeconds / 2592000), 'month');
} else {
return rtf.format(Math.floor(diffInSeconds / 31536000), 'year');
}
}


// Define public API
exports.getLocalizedLabel = getLocalizedLabel;
exports.getFormattedDateTime = getFormattedDateTime;
exports.dateTimeFromNow = dateTimeFromNow;
// public constants
exports.DATE_TIME_STYLE = DATE_TIME_STYLE;
});
66 changes: 65 additions & 1 deletion test/spec/LocalizationUtils-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ define(function (require, exports, module) {
};
const formatted = LocalizationUtils.getFormattedDateTime(testDate, "fr", customFormat);
// Example format: "1 janvier 2024 à 13:30"
expect(formatted).toBe("1 janvier 2024 à 13:30");
expect(formatted.includes("1 janvier 2024")).toBeTrue();
expect(formatted.includes("13:30")).toBeTrue();
});

it("should default to current date with custom dateTimeFormat", function () {
Expand All @@ -129,5 +130,68 @@ define(function (require, exports, module) {
expect(formattedNow).toBe(expected);
});
});

describe("dateTimeFromNow", function () {
it("should return 'now' for current time", function () {
const now = new Date();
let result = LocalizationUtils.dateTimeFromNow(now, "en");
expect(result).toBe("now");
result = LocalizationUtils.dateTimeFromNow(now, "de");
expect(result).toBe("jetzt");
});

it("should handle future dates within seconds", function () {
const futureDate = new Date(Date.now() + 30 * 1000); // 30 seconds in the future
const result = LocalizationUtils.dateTimeFromNow(futureDate, "en");
expect(result).toBe("in 30 seconds");
});

it("should handle past dates within minutes", function () {
const pastDate = new Date(Date.now() - 90 * 1000); // 90 seconds in the past
const result = LocalizationUtils.dateTimeFromNow(pastDate, "en");
expect(result).toBe("2 minutes ago");
});

it("should handle future dates within hours", function () {
const futureDate = new Date(Date.now() + 2 * 60 * 60 * 1000); // 2 hours in the future
const result = LocalizationUtils.dateTimeFromNow(futureDate, "en");
expect(result).toBe("in 2 hours");
});

it("should handle past dates within days", function () {
const pastDate = new Date(Date.now() - 3 * 24 * 60 * 60 * 1000); // 3 days ago
const result = LocalizationUtils.dateTimeFromNow(pastDate, "en");
expect(result).toBe("3 days ago");
});

it("should handle future dates within months", function () {
const futureDate = new Date(Date.now() + 45 * 24 * 60 * 60 * 1000); // 45 days in the future
const result = LocalizationUtils.dateTimeFromNow(futureDate, "en");
expect(result).toBe("next month");
});

it("should handle past dates within years", function () {
const pastDate = new Date(Date.now() - 2 * 365 * 24 * 60 * 60 * 1000); // 2 years ago
const result = LocalizationUtils.dateTimeFromNow(pastDate, "en");
expect(result).toBe("2 years ago");
});

it("should return relative time in French locale", function () {
const pastDate = new Date(Date.now() - 3 * 24 * 60 * 60 * 1000); // 3 days ago
const result = LocalizationUtils.dateTimeFromNow(pastDate, "fr");
expect(result).toBe("il y a 3 jours");
});

it("should fallback to default locale if an invalid locale is specified", function () {
const futureDate = new Date(Date.now() + 2 * 60 * 60 * 1000); // 2 hours in the future
const result = LocalizationUtils.dateTimeFromNow(futureDate, "invalid-locale");
expect(result).toBe("in 2 hours");
});

it("should handle default date input (now) gracefully", function () {
const result = LocalizationUtils.dateTimeFromNow(undefined, "en");
expect(result).toBe("now");
});
});
});
});

0 comments on commit 06d20ac

Please sign in to comment.