mirror of
https://github.com/qbittorrent/qBittorrent
synced 2025-08-20 13:23:34 -07:00
WebUI: Support modifying default date format
This commit is contained in:
parent
afb1b51566
commit
2b6f858307
8 changed files with 223 additions and 19 deletions
|
@ -275,7 +275,7 @@ window.qBittorrent.AddTorrent ??= (() => {
|
|||
if (metadata.info?.length !== undefined)
|
||||
document.getElementById("size").textContent = window.qBittorrent.Misc.friendlyUnit(metadata.info.length, false);
|
||||
if ((metadata.creation_date !== undefined) && (metadata.creation_date > 1))
|
||||
document.getElementById("createdDate").textContent = new Date(metadata.creation_date * 1000).toLocaleString();
|
||||
document.getElementById("createdDate").textContent = window.qBittorrent.Misc.formatDate(new Date(metadata.creation_date * 1000));
|
||||
if (metadata.comment !== undefined)
|
||||
document.getElementById("comment").textContent = metadata.comment;
|
||||
|
||||
|
|
|
@ -69,6 +69,7 @@ window.qBittorrent.Client ??= (() => {
|
|||
"show_status_bar",
|
||||
"show_filters_sidebar",
|
||||
"hide_zero_status_filters",
|
||||
"date_format",
|
||||
"color_scheme",
|
||||
"full_url_tracker_column",
|
||||
"use_alt_row_colors",
|
||||
|
|
|
@ -1426,7 +1426,7 @@ window.qBittorrent.DynamicTable ??= (() => {
|
|||
|
||||
// added on
|
||||
this.columns["added_on"].updateTd = function(td, row) {
|
||||
const date = new Date(this.getRowValue(row) * 1000).toLocaleString();
|
||||
const date = window.qBittorrent.Misc.formatDate(new Date(this.getRowValue(row) * 1000));
|
||||
td.textContent = date;
|
||||
td.title = date;
|
||||
};
|
||||
|
@ -1439,7 +1439,7 @@ window.qBittorrent.DynamicTable ??= (() => {
|
|||
td.title = "";
|
||||
}
|
||||
else {
|
||||
const date = new Date(this.getRowValue(row) * 1000).toLocaleString();
|
||||
const date = window.qBittorrent.Misc.formatDate(new Date(this.getRowValue(row) * 1000));
|
||||
td.textContent = date;
|
||||
td.title = date;
|
||||
}
|
||||
|
@ -1964,7 +1964,7 @@ window.qBittorrent.DynamicTable ??= (() => {
|
|||
};
|
||||
const displayDate = function(td, row) {
|
||||
const value = this.getRowValue(row) * 1000;
|
||||
const formattedValue = (Number.isNaN(value) || (value <= 0)) ? "" : (new Date(value).toLocaleString());
|
||||
const formattedValue = (Number.isNaN(value) || (value <= 0)) ? "" : window.qBittorrent.Misc.formatDate(new Date(value));
|
||||
td.textContent = formattedValue;
|
||||
td.title = formattedValue;
|
||||
};
|
||||
|
@ -3734,7 +3734,7 @@ window.qBittorrent.DynamicTable ??= (() => {
|
|||
|
||||
initColumnsFunctions() {
|
||||
this.columns["timestamp"].updateTd = function(td, row) {
|
||||
const date = new Date(this.getRowValue(row) * 1000).toLocaleString();
|
||||
const date = window.qBittorrent.Misc.formatDate(new Date(this.getRowValue(row) * 1000));
|
||||
td.textContent = date;
|
||||
td.title = date;
|
||||
};
|
||||
|
@ -3812,7 +3812,7 @@ window.qBittorrent.DynamicTable ??= (() => {
|
|||
this.newColumn("reason", "", "QBT_TR(Reason)QBT_TR[CONTEXT=ExecutionLogWidget]", 150, true);
|
||||
|
||||
this.columns["timestamp"].updateTd = function(td, row) {
|
||||
const date = new Date(this.getRowValue(row) * 1000).toLocaleString();
|
||||
const date = window.qBittorrent.Misc.formatDate(new Date(this.getRowValue(row) * 1000));
|
||||
td.textContent = date;
|
||||
td.title = date;
|
||||
};
|
||||
|
@ -4036,7 +4036,7 @@ window.qBittorrent.DynamicTable ??= (() => {
|
|||
td.title = "";
|
||||
}
|
||||
else {
|
||||
const date = new Date(val).toLocaleString();
|
||||
const date = window.qBittorrent.Misc.formatDate(new Date(val));
|
||||
td.textContent = date;
|
||||
td.title = date;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,108 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* @type Record<string, {locale: string, options: {}}>
|
||||
*/
|
||||
const DateFormatOptions = {
|
||||
"MM/dd/yyyy, h:mm:ss AM/PM": {
|
||||
locale: "en-US",
|
||||
options: {
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
hour: "numeric",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
hour12: true
|
||||
}
|
||||
},
|
||||
"MM/dd/yyyy, HH:mm:ss": {
|
||||
locale: "en-US",
|
||||
options: {
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
hour12: false
|
||||
}
|
||||
},
|
||||
"dd/MM/yyyy, HH:mm:ss": {
|
||||
locale: "en-GB",
|
||||
options: {
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
hour12: false
|
||||
}
|
||||
},
|
||||
"yyyy-MM-dd HH:mm:ss": {
|
||||
locale: "sv-SE",
|
||||
options: {
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
hour12: false
|
||||
}
|
||||
},
|
||||
"yyyy/MM/dd HH:mm:ss": {
|
||||
locale: "ja-JP",
|
||||
options: {
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
hour12: false
|
||||
}
|
||||
},
|
||||
"dd.MM.yyyy, HH:mm:ss": {
|
||||
locale: "en-GB",
|
||||
options: {
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
hour12: false
|
||||
}
|
||||
},
|
||||
"MMM dd, yyyy, h:mm:ss AM/PM": {
|
||||
locale: "en-US",
|
||||
options: {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "2-digit",
|
||||
hour: "numeric",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
hour12: true
|
||||
}
|
||||
},
|
||||
"dd MMM yyyy, HH:mm:ss": {
|
||||
locale: "en-GB",
|
||||
options: {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
hour12: false
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
window.qBittorrent ??= {};
|
||||
window.qBittorrent.Misc ??= (() => {
|
||||
const exports = () => {
|
||||
|
@ -46,6 +148,7 @@ window.qBittorrent.Misc ??= (() => {
|
|||
containsAllTerms: containsAllTerms,
|
||||
sleep: sleep,
|
||||
downloadFile: downloadFile,
|
||||
formatDate: formatDate,
|
||||
// variables
|
||||
FILTER_INPUT_DELAY: 400,
|
||||
MAX_ETA: 8640000
|
||||
|
@ -302,6 +405,21 @@ window.qBittorrent.Misc ??= (() => {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Date} date
|
||||
* @param {string} format
|
||||
* @returns {string}
|
||||
*/
|
||||
const formatDate = (date, format = window.parent.qBittorrent.ClientData.getCached("date_format")) => {
|
||||
if ((format === "default") || !Object.keys(DateFormatOptions).includes(format))
|
||||
return date.toLocaleString();
|
||||
|
||||
const { locale, options } = DateFormatOptions[format];
|
||||
const formatter = new Intl.DateTimeFormat(locale, options);
|
||||
const formatted = formatter.format(date).replace(" at ", ", ");
|
||||
return format.includes(".") ? formatted.replaceAll("/", ".") : formatted;
|
||||
};
|
||||
|
||||
return exports();
|
||||
})();
|
||||
Object.freeze(window.qBittorrent.Misc);
|
||||
|
|
|
@ -177,7 +177,7 @@ window.qBittorrent.PropGeneral ??= (() => {
|
|||
document.getElementById("reannounce").textContent = window.qBittorrent.Misc.friendlyDuration(data.reannounce);
|
||||
|
||||
const lastSeen = (data.last_seen >= 0)
|
||||
? new Date(data.last_seen * 1000).toLocaleString()
|
||||
? window.qBittorrent.Misc.formatDate(new Date(data.last_seen * 1000))
|
||||
: "QBT_TR(Never)QBT_TR[CONTEXT=PropertiesWidget]";
|
||||
document.getElementById("last_seen").textContent = lastSeen;
|
||||
|
||||
|
@ -195,17 +195,17 @@ window.qBittorrent.PropGeneral ??= (() => {
|
|||
document.getElementById("created_by").textContent = data.created_by;
|
||||
|
||||
const additionDate = (data.addition_date >= 0)
|
||||
? new Date(data.addition_date * 1000).toLocaleString()
|
||||
? window.qBittorrent.Misc.formatDate(new Date(data.addition_date * 1000))
|
||||
: "QBT_TR(Unknown)QBT_TR[CONTEXT=HttpServer]";
|
||||
document.getElementById("addition_date").textContent = additionDate;
|
||||
|
||||
const completionDate = (data.completion_date >= 0)
|
||||
? new Date(data.completion_date * 1000).toLocaleString()
|
||||
? window.qBittorrent.Misc.formatDate(new Date(data.completion_date * 1000))
|
||||
: "";
|
||||
document.getElementById("completion_date").textContent = completionDate;
|
||||
|
||||
const creationDate = (data.creation_date >= 0)
|
||||
? new Date(data.creation_date * 1000).toLocaleString()
|
||||
? window.qBittorrent.Misc.formatDate(new Date(data.creation_date * 1000))
|
||||
: "";
|
||||
document.getElementById("creation_date").textContent = creationDate;
|
||||
|
||||
|
|
|
@ -1,10 +1,34 @@
|
|||
<div id="BehaviorTab" class="PrefTab">
|
||||
<fieldset class="settings">
|
||||
<legend>QBT_TR(Language)QBT_TR[CONTEXT=OptionsDialog]</legend>
|
||||
<label for="locale_select">QBT_TR(User interface language:)QBT_TR[CONTEXT=OptionsDialog]</label>
|
||||
<select id="locale_select">
|
||||
${LANGUAGE_OPTIONS}
|
||||
</select>
|
||||
<legend>QBT_TR(Localization)QBT_TR[CONTEXT=OptionsDialog]</legend>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><label for="locale_select">QBT_TR(User interface language:)QBT_TR[CONTEXT=OptionsDialog]</label></td>
|
||||
<td>
|
||||
<select id="locale_select">
|
||||
${LANGUAGE_OPTIONS}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="dateFormatSelect">QBT_TR(Date format:)QBT_TR[CONTEXT=OptionsDialog]</label></td>
|
||||
<td>
|
||||
<select id="dateFormatSelect">
|
||||
<option value="default">QBT_TR(Browser default)QBT_TR[CONTEXT=OptionsDialog]</option>
|
||||
<option value="MM/dd/yyyy, h:mm:ss AM/PM">QBT_TR(08/23/2025, 10:32:46 PM)QBT_TR[CONTEXT=OptionsDialog]</option>
|
||||
<option value="MM/dd/yyyy, HH:mm:ss">QBT_TR(08/23/2025, 22:32:46)QBT_TR[CONTEXT=OptionsDialog]</option>
|
||||
<option value="dd/MM/yyyy, HH:mm:ss">QBT_TR(23/08/2025, 22:32:46)QBT_TR[CONTEXT=OptionsDialog]</option>
|
||||
<option value="yyyy-MM-dd HH:mm:ss">QBT_TR(2025-08-23 22:32:46)QBT_TR[CONTEXT=OptionsDialog]</option>
|
||||
<option value="yyyy/MM/dd HH:mm:ss">QBT_TR(2025/08/23 22:32:46)QBT_TR[CONTEXT=OptionsDialog]</option>
|
||||
<option value="dd.MM.yyyy, HH:mm:ss">QBT_TR(23.08.2025, 22:32:46)QBT_TR[CONTEXT=OptionsDialog]</option>
|
||||
<option value="MMM dd, yyyy, h:mm:ss AM/PM">QBT_TR(Aug 23, 2025, 10:32:46 PM)QBT_TR[CONTEXT=OptionsDialog]</option>
|
||||
<option value="dd MMM yyyy, HH:mm:ss">QBT_TR(23 Aug 2025, 22:32:46)QBT_TR[CONTEXT=OptionsDialog]</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="settings">
|
||||
|
@ -2229,6 +2253,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD
|
|||
window.parent.qBittorrent.Cache.preferences.init()
|
||||
.then((pref) => {
|
||||
const clientData = window.parent.qBittorrent.ClientData;
|
||||
const dateFormat = clientData.getCached("date_format");
|
||||
const colorScheme = clientData.getCached("color_scheme");
|
||||
const fullUrlTrackerColumn = clientData.getCached("full_url_tracker_column");
|
||||
const useVirtualList = clientData.getCached("use_virtual_list");
|
||||
|
@ -2239,8 +2264,13 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD
|
|||
const useAltRowColors = clientData.getCached("use_alt_row_colors");
|
||||
|
||||
// Behavior tab
|
||||
// Language
|
||||
// Localization
|
||||
updateWebuiLocaleSelect(pref.locale);
|
||||
const dateFormatSelect = document.getElementById("dateFormatSelect");
|
||||
if ((dateFormat?.length > 0) && ([...dateFormatSelect.options].find(o => o.value === dateFormat) !== undefined))
|
||||
dateFormatSelect.value = dateFormat;
|
||||
|
||||
// Interface
|
||||
updateColoSchemeSelect(colorScheme);
|
||||
|
||||
document.getElementById("statusBarExternalIP").checked = pref.status_bar_external_ip;
|
||||
|
@ -2673,6 +2703,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD
|
|||
// Behavior tab
|
||||
// Language
|
||||
settings["locale"] = document.getElementById("locale_select").value;
|
||||
clientData.date_format = document.getElementById("dateFormatSelect").value;
|
||||
const colorScheme = Number(document.getElementById("colorSchemeSelect").value);
|
||||
if (colorScheme === 0)
|
||||
clientData.color_scheme = null;
|
||||
|
|
|
@ -456,7 +456,7 @@
|
|||
torrentDate.append(torrentDateDesc);
|
||||
|
||||
const torrentDateData = document.createElement("span");
|
||||
torrentDateData.textContent = new Date(articleDate).toLocaleString();
|
||||
torrentDateData.textContent = window.qBittorrent.Misc.formatDate(new Date(articleDate));
|
||||
torrentDate.append(torrentDateData);
|
||||
|
||||
detailsView.append(torrentDate);
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
import { expect, test } from "vitest";
|
||||
import { vi, expect, test } from "vitest";
|
||||
import "../../private/scripts/misc.js";
|
||||
|
||||
test("Test toFixedPointString()", () => {
|
||||
|
@ -76,3 +76,57 @@ test("Test toFixedPointString()", () => {
|
|||
expect(toFixedPointString(-100.00, 1)).toBe("-100.0");
|
||||
expect(toFixedPointString(-100.00, 2)).toBe("-100.00");
|
||||
});
|
||||
|
||||
test("Test formatDate() - Format Coverage", () => {
|
||||
const formatDate = window.qBittorrent.Misc.formatDate;
|
||||
const testDate = new Date(2025, 7, 23, 22, 32, 46); // Aug 23, 2025 10:32:46 PM
|
||||
|
||||
expect(formatDate(testDate, "MM/dd/yyyy, h:mm:ss AM/PM")).toBe("08/23/2025, 10:32:46 PM");
|
||||
expect(formatDate(testDate, "MM/dd/yyyy, HH:mm:ss")).toBe("08/23/2025, 22:32:46");
|
||||
expect(formatDate(testDate, "dd/MM/yyyy, HH:mm:ss")).toBe("23/08/2025, 22:32:46");
|
||||
expect(formatDate(testDate, "yyyy-MM-dd HH:mm:ss")).toBe("2025-08-23 22:32:46");
|
||||
expect(formatDate(testDate, "yyyy/MM/dd HH:mm:ss")).toBe("2025/08/23 22:32:46");
|
||||
expect(formatDate(testDate, "dd.MM.yyyy, HH:mm:ss")).toBe("23.08.2025, 22:32:46");
|
||||
expect(formatDate(testDate, "MMM dd, yyyy, h:mm:ss AM/PM")).toBe("Aug 23, 2025, 10:32:46 PM");
|
||||
expect(formatDate(testDate, "dd MMM yyyy, HH:mm:ss")).toBe("23 Aug 2025, 22:32:46");
|
||||
});
|
||||
|
||||
test("Test formatDate() - Fallback Behavior", () => {
|
||||
// Mock ClientData.getCached
|
||||
const mockGetCached = vi.fn().mockReturnValue("default");
|
||||
const originalParent = window.parent;
|
||||
|
||||
window.parent = {
|
||||
qBittorrent: {
|
||||
ClientData: {
|
||||
getCached: mockGetCached
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const formatDate = window.qBittorrent.Misc.formatDate;
|
||||
const testDate = new Date(2025, 7, 23, 22, 32, 46); // Aug 23, 2025 10:32:46 PM
|
||||
const expectedDefault = testDate.toLocaleString();
|
||||
|
||||
// Test that "default" format uses toLocaleString()
|
||||
expect(formatDate(testDate, "default")).toBe(expectedDefault);
|
||||
|
||||
// Test default behavior when no format argument is provided
|
||||
expect(mockGetCached).toHaveBeenCalledTimes(0);
|
||||
expect(formatDate(testDate)).toBe(expectedDefault);
|
||||
expect(mockGetCached).toHaveBeenCalledWith("date_format");
|
||||
expect(mockGetCached).toHaveBeenCalledTimes(1);
|
||||
|
||||
// Test with unknown/invalid format strings
|
||||
expect(formatDate(testDate, "invalid-format")).toBe(expectedDefault);
|
||||
expect(formatDate(testDate, "")).toBe(expectedDefault);
|
||||
expect(formatDate(testDate, null)).toBe(expectedDefault);
|
||||
|
||||
expect(mockGetCached).toHaveBeenCalledTimes(1);
|
||||
expect(formatDate(testDate, undefined)).toBe(expectedDefault);
|
||||
expect(mockGetCached).toHaveBeenCalledWith("date_format");
|
||||
expect(mockGetCached).toHaveBeenCalledTimes(2);
|
||||
|
||||
// Restore original window.parent
|
||||
window.parent = originalParent;
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue