recent_topics: Standardize format of last message time.

We follow how other apps present older messages, e.g. Gmail,
Facebook Messenger, etc. display it.

Specifically, the logic we use is:

If the time is <24hr ago, show an absolute time, like "21:30" (or "9:30pm").
Otherwise, show what day it was, and not a time
  If the day was yesterday, say "Yesterday".
  Otherwise, if it was <7 days ago, say the day of week, like "Friday".
  Otherwise, if it was <1 year ago, say the month and day, like "Sep 6".
  Otherwise, say the year, month, and day, like "Sep 9, 2020".

With some tweaks from Tim Abbott to better handle the future case.

Fixes #19775
This commit is contained in:
Aman Agrawal
2022-02-09 13:59:51 +00:00
committed by Tim Abbott
parent c2a117c623
commit 27b985e868
4 changed files with 95 additions and 3 deletions

View File

@@ -115,8 +115,7 @@ mock_esm("../../static/js/stream_list", {
handle_narrow_deactivated: noop, handle_narrow_deactivated: noop,
}); });
mock_esm("../../static/js/timerender", { mock_esm("../../static/js/timerender", {
last_seen_status_from_date: () => "Just now", format_time_modern: () => "Just now",
get_full_datetime: () => "date at time", get_full_datetime: () => "date at time",
}); });
mock_esm("../../static/js/sub_store", { mock_esm("../../static/js/sub_store", {

View File

@@ -114,6 +114,78 @@ run_test("render_now_returns_month_and_day", () => {
assert.equal(actual.needs_update, expected.needs_update); assert.equal(actual.needs_update, expected.needs_update);
}); });
run_test("format_time_modern", () => {
const today = date_2021;
const few_minutes_in_future = add(today, {minutes: 30});
const weeks_in_future = add(today, {days: 20});
const less_than_24_hours_ago = add(today, {hours: -23});
const twenty_four_hours_ago = add(today, {hours: -24});
const more_than_24_hours_ago = add(today, {hours: -25});
const less_than_a_week_ago = add(today, {days: -6});
const one_week_ago = add(today, {days: -7});
const less_than_6_months_ago = add(today, {months: -3});
const more_than_6_months_ago = add(today, {months: -9});
const previous_year_but_less_than_6_months = add(today, {months: -1});
assert.equal(timerender.format_time_modern(few_minutes_in_future, today), "Jan 27, 2021");
assert.equal(timerender.format_time_modern(weeks_in_future, today), "Feb 16, 2021");
assert.equal(timerender.format_time_modern(less_than_24_hours_ago, today), "2:53 AM");
assert.equal(
timerender.format_time_modern(twenty_four_hours_ago, today),
"translated: Yesterday",
);
assert.equal(
timerender.format_time_modern(more_than_24_hours_ago, today),
"translated: Yesterday",
);
assert.equal(timerender.format_time_modern(less_than_a_week_ago, today), "Thursday");
assert.equal(timerender.format_time_modern(one_week_ago, today), "Jan 20");
assert.equal(
timerender.format_time_modern(previous_year_but_less_than_6_months, today),
"Dec 27",
);
assert.equal(timerender.format_time_modern(less_than_6_months_ago, today), "Oct 27");
assert.equal(timerender.format_time_modern(more_than_6_months_ago, today), "Apr 27, 2020");
});
run_test("format_time_modern_different_timezones", () => {
const utc_tz = process.env.TZ;
// Day is yesterday in UTC+0 but is 2 days ago in local timezone hence DOW is returned.
let today = date_2017_PM;
let yesterday = add(date_2017, {days: -1});
assert.equal(timerender.format_time_modern(yesterday, today), "translated: Yesterday");
process.env.TZ = "America/Juneau";
let expected = "translated: 5/16/2017 at 11:12:53 PM AKDT (UTC-08:00)";
assert.equal(timerender.get_full_datetime(yesterday), expected);
assert.equal(timerender.format_time_modern(yesterday, today), "Tuesday");
process.env.TZ = utc_tz;
// Day is 2 days ago in UTC+0 but is yesterday in local timezone.
today = date_2017;
yesterday = add(date_2017_PM, {days: -2});
assert.equal(timerender.format_time_modern(yesterday, today), "Tuesday");
process.env.TZ = "Asia/Brunei";
expected = "translated: 5/17/2017 at 5:12:53 AM (UTC+08:00)";
assert.equal(timerender.get_full_datetime(yesterday), expected);
assert.equal(timerender.format_time_modern(yesterday, today), "translated: Yesterday");
process.env.TZ = utc_tz;
// Day is 6 days ago in UTC+0 but a week ago in local timezone hence difference in returned strings.
today = date_2017_PM;
yesterday = add(date_2017, {days: -6});
assert.equal(timerender.format_time_modern(yesterday, today), "Friday");
process.env.TZ = "America/Juneau";
expected = "translated: 5/11/2017 at 11:12:53 PM AKDT (UTC-08:00)";
assert.equal(timerender.get_full_datetime(yesterday), expected);
assert.equal(timerender.format_time_modern(yesterday, today), "May 11");
process.env.TZ = utc_tz;
});
run_test("render_now_returns_year_with_year_boundary", () => { run_test("render_now_returns_year_with_year_boundary", () => {
const today = date_2019; const today = date_2019;

View File

@@ -253,7 +253,7 @@ function format_topic(topic_data) {
} }
const topic = last_msg.topic; const topic = last_msg.topic;
const time = new Date(last_msg.timestamp * 1000); const time = new Date(last_msg.timestamp * 1000);
const last_msg_time = timerender.last_seen_status_from_date(time); const last_msg_time = timerender.format_time_modern(time);
const full_datetime = timerender.get_full_datetime(time); const full_datetime = timerender.get_full_datetime(time);
// We hide the row according to filters or if it's muted. // We hide the row according to filters or if it's muted.

View File

@@ -1,5 +1,6 @@
import { import {
differenceInCalendarDays, differenceInCalendarDays,
differenceInHours,
differenceInMinutes, differenceInMinutes,
format, format,
formatISO, formatISO,
@@ -304,6 +305,26 @@ export function stringify_time(time: number | Date): string {
return format(time, "h:mm a"); return format(time, "h:mm a");
} }
export function format_time_modern(time: number | Date, today = new Date()): String {
const hours = differenceInHours(today, time);
const days_old = differenceInCalendarDays(today, time);
if (time > today) {
/* For timestamps in the future, we always show the year*/
return format(time, "MMM\u00A0dd,\u00A0yyyy");
} else if (hours < 24) {
return stringify_time(time);
} else if (days_old === 1) {
return $t({defaultMessage: "Yesterday"});
} else if (days_old < 7) {
return format(time, "EEEE");
} else if (days_old <= 180) {
return format(time, "MMM\u00A0dd");
}
return format(time, "MMM\u00A0dd,\u00A0yyyy");
}
// this is for rendering absolute time based off the preferences for twenty-four // this is for rendering absolute time based off the preferences for twenty-four
// hour time in the format of "%mmm %d, %h:%m %p". // hour time in the format of "%mmm %d, %h:%m %p".
export function absolute_time(timestamp: number, today = new Date()): string { export function absolute_time(timestamp: number, today = new Date()): string {