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'; import * as Params from './params.js'; let _desktopSettings = null; let _localTimeZone = null; /** * @private * * @param {Date} date a Date object * @returns {GLib.DateTime | null} */ function _convertJSDateToGLibDateTime(date) { if (_localTimeZone === null) _localTimeZone = GLib.TimeZone.new_local(); const dt = GLib.DateTime.new(_localTimeZone, date.getFullYear(), date.getMonth() + 1, date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds()); 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} */ export function formatDateWithCFormatString(date, format) { const dt = _convertJSDateToGLibDateTime(date); return dt?.format(format) ?? ''; } /** * 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} */ export function formatTimeSpan(date) { if (_localTimeZone === null) _localTimeZone = GLib.TimeZone.new_local(); const now = GLib.DateTime.new_now(_localTimeZone); 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); } /** * 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} */ export function formatTime(time, params) { 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) date = _convertJSDateToGLibDateTime(time); else date = time; if (!date) return ''; // _localTimeZone is defined in _convertJSDateToGLibDateTime const now = GLib.DateTime.new_now(_localTimeZone); 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 */ export function clearCachedLocalTimeZone() { // 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(); }