2023-07-10 09:53:00 +00:00
|
|
|
import * as System from 'system';
|
|
|
|
import * as Gettext from 'gettext';
|
|
|
|
import GLib from 'gi://GLib';
|
|
|
|
import Gio from 'gi://Gio';
|
|
|
|
import Shell from 'gi://Shell';
|
2023-07-08 00:58:11 +00:00
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
import * as Params from './params.js';
|
2023-07-08 00:58:11 +00:00
|
|
|
|
|
|
|
let _desktopSettings = null;
|
|
|
|
let _localTimeZone = null;
|
|
|
|
|
2023-07-08 01:20:26 +00:00
|
|
|
/**
|
|
|
|
* @private
|
|
|
|
*
|
|
|
|
* @param {Date} date a Date object
|
|
|
|
* @returns {GLib.DateTime | null}
|
|
|
|
*/
|
|
|
|
function _convertJSDateToGLibDateTime(date) {
|
2023-07-08 00:58:11 +00:00
|
|
|
if (_localTimeZone === null)
|
|
|
|
_localTimeZone = GLib.TimeZone.new_local();
|
|
|
|
|
2023-07-08 01:20:26 +00:00
|
|
|
const dt = GLib.DateTime.new(_localTimeZone,
|
2023-07-08 00:58:11 +00:00
|
|
|
date.getFullYear(),
|
|
|
|
date.getMonth() + 1,
|
|
|
|
date.getDate(),
|
|
|
|
date.getHours(),
|
|
|
|
date.getMinutes(),
|
|
|
|
date.getSeconds());
|
2023-07-08 01:20:26 +00:00
|
|
|
|
|
|
|
return dt;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Formats a Date object according to a C sprintf-style string using
|
|
|
|
* the cached local timezone.
|
|
|
|
*
|
|
|
|
* @param {Date} date a Date object
|
|
|
|
* @param {string} format a format String for the date
|
|
|
|
* @returns {string}
|
|
|
|
*/
|
2023-07-10 09:53:00 +00:00
|
|
|
export function formatDateWithCFormatString(date, format) {
|
2023-07-08 01:20:26 +00:00
|
|
|
const dt = _convertJSDateToGLibDateTime(date);
|
|
|
|
|
2023-07-08 00:58:11 +00:00
|
|
|
return dt?.format(format) ?? '';
|
|
|
|
}
|
|
|
|
|
2023-07-08 01:20:26 +00:00
|
|
|
/**
|
|
|
|
* Formats a time span string representing the
|
|
|
|
* date passed in to the current time.
|
|
|
|
*
|
|
|
|
* @param {Date} date the start of the time span
|
|
|
|
* @returns {string}
|
|
|
|
*/
|
2023-07-10 09:53:00 +00:00
|
|
|
export function formatTimeSpan(date) {
|
2023-07-08 01:20:26 +00:00
|
|
|
if (_localTimeZone === null)
|
|
|
|
_localTimeZone = GLib.TimeZone.new_local();
|
2023-07-08 00:58:11 +00:00
|
|
|
|
2023-07-08 01:20:26 +00:00
|
|
|
const now = GLib.DateTime.new_now(_localTimeZone);
|
2023-07-08 00:58:11 +00:00
|
|
|
const timespan = now.difference(date);
|
|
|
|
|
|
|
|
const minutesAgo = timespan / GLib.TIME_SPAN_MINUTE;
|
|
|
|
const hoursAgo = timespan / GLib.TIME_SPAN_HOUR;
|
|
|
|
const daysAgo = timespan / GLib.TIME_SPAN_DAY;
|
|
|
|
const weeksAgo = daysAgo / 7;
|
|
|
|
const monthsAgo = daysAgo / 30;
|
|
|
|
const yearsAgo = weeksAgo / 52;
|
|
|
|
|
|
|
|
if (minutesAgo < 5)
|
|
|
|
return _('Just now');
|
|
|
|
if (hoursAgo < 1) {
|
|
|
|
return Gettext.ngettext(
|
|
|
|
'%d minute ago',
|
|
|
|
'%d minutes ago',
|
|
|
|
minutesAgo
|
|
|
|
).format(minutesAgo);
|
|
|
|
}
|
|
|
|
if (daysAgo < 1) {
|
|
|
|
return Gettext.ngettext(
|
|
|
|
'%d hour ago',
|
|
|
|
'%d hours ago',
|
|
|
|
hoursAgo
|
|
|
|
).format(hoursAgo);
|
|
|
|
}
|
|
|
|
if (daysAgo < 2)
|
|
|
|
return _('Yesterday');
|
|
|
|
if (daysAgo < 15) {
|
|
|
|
return Gettext.ngettext(
|
|
|
|
'%d day ago',
|
|
|
|
'%d days ago',
|
|
|
|
daysAgo
|
|
|
|
).format(daysAgo);
|
|
|
|
}
|
|
|
|
if (weeksAgo < 8) {
|
|
|
|
return Gettext.ngettext(
|
|
|
|
'%d week ago',
|
|
|
|
'%d weeks ago',
|
|
|
|
weeksAgo
|
|
|
|
).format(weeksAgo);
|
|
|
|
}
|
|
|
|
if (yearsAgo < 1) {
|
|
|
|
return Gettext.ngettext(
|
|
|
|
'%d month ago',
|
|
|
|
'%d months ago',
|
|
|
|
monthsAgo
|
|
|
|
).format(monthsAgo);
|
|
|
|
}
|
|
|
|
return Gettext.ngettext(
|
|
|
|
'%d year ago',
|
|
|
|
'%d years ago',
|
|
|
|
yearsAgo
|
|
|
|
).format(yearsAgo);
|
|
|
|
}
|
|
|
|
|
2023-07-08 01:20:26 +00:00
|
|
|
/**
|
|
|
|
* Formats a date time string based on style parameters
|
|
|
|
*
|
|
|
|
* @param {GLib.DateTime | Date} time a Date object
|
|
|
|
* @param {object} [params] style parameters for the output string
|
|
|
|
* @param {boolean=} params.timeOnly whether the string should only contain the time (no date)
|
|
|
|
* @param {boolean=} params.ampm whether to include the "am" or "pm" in the string
|
|
|
|
* @returns {string}
|
|
|
|
*/
|
2023-07-10 09:53:00 +00:00
|
|
|
export function formatTime(time, params) {
|
2023-07-08 00:58:11 +00:00
|
|
|
let date;
|
|
|
|
// HACK: The built-in Date type sucks at timezones, which we need for the
|
|
|
|
// world clock; it's often more convenient though, so allow either
|
|
|
|
// Date or GLib.DateTime as parameter
|
|
|
|
if (time instanceof Date)
|
2023-07-08 01:20:26 +00:00
|
|
|
date = _convertJSDateToGLibDateTime(time);
|
2023-07-08 00:58:11 +00:00
|
|
|
else
|
|
|
|
date = time;
|
|
|
|
|
2023-07-08 01:20:26 +00:00
|
|
|
if (!date)
|
|
|
|
return '';
|
2023-07-08 00:58:11 +00:00
|
|
|
|
2023-07-08 01:20:26 +00:00
|
|
|
// _localTimeZone is defined in _convertJSDateToGLibDateTime
|
|
|
|
const now = GLib.DateTime.new_now(_localTimeZone);
|
2023-07-08 00:58:11 +00:00
|
|
|
const daysAgo = now.difference(date) / (24 * 60 * 60 * 1000 * 1000);
|
|
|
|
|
|
|
|
let format;
|
|
|
|
|
|
|
|
if (_desktopSettings == null)
|
|
|
|
_desktopSettings = new Gio.Settings({schema_id: 'org.gnome.desktop.interface'});
|
|
|
|
const clockFormat = _desktopSettings.get_string('clock-format');
|
|
|
|
|
|
|
|
params = Params.parse(params, {
|
|
|
|
timeOnly: false,
|
|
|
|
ampm: true,
|
|
|
|
});
|
|
|
|
|
|
|
|
if (clockFormat === '24h') {
|
|
|
|
// Show only the time if date is on today
|
|
|
|
if (daysAgo < 1 || params.timeOnly)
|
|
|
|
/* Translators: Time in 24h format */
|
|
|
|
format = N_('%H\u2236%M');
|
|
|
|
// Show the word "Yesterday" and time if date is on yesterday
|
|
|
|
else if (daysAgo < 2)
|
|
|
|
/* Translators: this is the word "Yesterday" followed by a
|
|
|
|
time string in 24h format. i.e. "Yesterday, 14:30" */
|
|
|
|
// xgettext:no-c-format
|
|
|
|
format = N_('Yesterday, %H\u2236%M');
|
|
|
|
// Show a week day and time if date is in the last week
|
|
|
|
else if (daysAgo < 7)
|
|
|
|
/* Translators: this is the week day name followed by a time
|
|
|
|
string in 24h format. i.e. "Monday, 14:30" */
|
|
|
|
// xgettext:no-c-format
|
|
|
|
format = N_('%A, %H\u2236%M');
|
|
|
|
else if (date.get_year() === now.get_year())
|
|
|
|
/* Translators: this is the month name and day number
|
|
|
|
followed by a time string in 24h format.
|
|
|
|
i.e. "May 25, 14:30" */
|
|
|
|
// xgettext:no-c-format
|
|
|
|
format = N_('%B %-d, %H\u2236%M');
|
|
|
|
else
|
|
|
|
/* Translators: this is the month name, day number, year
|
|
|
|
number followed by a time string in 24h format.
|
|
|
|
i.e. "May 25 2012, 14:30" */
|
|
|
|
// xgettext:no-c-format
|
|
|
|
format = N_('%B %-d %Y, %H\u2236%M');
|
|
|
|
} else {
|
|
|
|
// Show only the time if date is on today
|
|
|
|
if (daysAgo < 1 || params.timeOnly) // eslint-disable-line no-lonely-if
|
|
|
|
/* Translators: Time in 12h format */
|
|
|
|
format = N_('%l\u2236%M %p');
|
|
|
|
// Show the word "Yesterday" and time if date is on yesterday
|
|
|
|
else if (daysAgo < 2)
|
|
|
|
/* Translators: this is the word "Yesterday" followed by a
|
|
|
|
time string in 12h format. i.e. "Yesterday, 2:30 pm" */
|
|
|
|
// xgettext:no-c-format
|
|
|
|
format = N_('Yesterday, %l\u2236%M %p');
|
|
|
|
// Show a week day and time if date is in the last week
|
|
|
|
else if (daysAgo < 7)
|
|
|
|
/* Translators: this is the week day name followed by a time
|
|
|
|
string in 12h format. i.e. "Monday, 2:30 pm" */
|
|
|
|
// xgettext:no-c-format
|
|
|
|
format = N_('%A, %l\u2236%M %p');
|
|
|
|
else if (date.get_year() === now.get_year())
|
|
|
|
/* Translators: this is the month name and day number
|
|
|
|
followed by a time string in 12h format.
|
|
|
|
i.e. "May 25, 2:30 pm" */
|
|
|
|
// xgettext:no-c-format
|
|
|
|
format = N_('%B %-d, %l\u2236%M %p');
|
|
|
|
else
|
|
|
|
/* Translators: this is the month name, day number, year
|
|
|
|
number followed by a time string in 12h format.
|
|
|
|
i.e. "May 25 2012, 2:30 pm"*/
|
|
|
|
// xgettext:no-c-format
|
|
|
|
format = N_('%B %-d %Y, %l\u2236%M %p');
|
|
|
|
}
|
|
|
|
|
|
|
|
// Time in short 12h format, without the equivalent of "AM" or "PM"; used
|
|
|
|
// when it is clear from the context
|
|
|
|
if (!params.ampm)
|
|
|
|
format = format.replace(/\s*%p/g, '');
|
|
|
|
|
|
|
|
let formattedTime = date.format(Shell.util_translate_time_string(format));
|
|
|
|
// prepend LTR-mark to colon/ratio to force a text direction on times
|
|
|
|
return formattedTime.replace(/([:\u2236])/g, '\u200e$1');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update the timezone used by JavaScript Date objects and other
|
|
|
|
* date utilities
|
|
|
|
*/
|
2023-07-10 09:53:00 +00:00
|
|
|
export function clearCachedLocalTimeZone() {
|
2023-07-08 00:58:11 +00:00
|
|
|
// SpiderMonkey caches the time zone so we must explicitly clear it
|
|
|
|
// before we can update the calendar, see
|
|
|
|
// https://bugzilla.gnome.org/show_bug.cgi?id=678507
|
|
|
|
System.clearDateCaches();
|
|
|
|
|
|
|
|
_localTimeZone = GLib.TimeZone.new_local();
|
|
|
|
}
|