Fix blank Query Tool screen caused by malformed runtime locale (#10066)

The Query History panel formats entry dates/times with
Date.prototype.toLocaleDateString()/toLocaleTimeString(). On runtimes
whose default locale (derived from the OS/environment) is malformed,
these throw "RangeError: Incorrect locale information provided".

Because getDateFormatted()/getTimeFormatted() are called during render
(via getGroups/getGroupHeader/getDatePrefix), the uncaught exception
unmounts the whole SQL editor React tree, leaving the user with a blank
white screen and losing any unsaved query work.

Guard both helpers and fall back to a moment-based format (moment uses
its own locale data and does not depend on the broken Intl default) so
the editor keeps working instead of crashing.

Closes #7596
This commit is contained in:
Dave Page
2026-06-12 22:22:18 +05:30
committed by GitHub
parent f6961bce49
commit cc8ce7e153
3 changed files with 105 additions and 4 deletions
+1
View File
@@ -42,6 +42,7 @@ Bug fixes
*********
| `Issue #6308 <https://github.com/pgadmin-org/pgadmin4/issues/6308>`_ - Fix the infinite loading spinner after an idle database connection is silently dropped, by detecting stale connections and offering a reconnect dialog.
| `Issue #7596 <https://github.com/pgadmin-org/pgadmin4/issues/7596>`_ - Fix the Query Tool turning into a blank white screen when the runtime has a malformed default locale, by guarding the Query History date/time formatting against the resulting RangeError.
| `Issue #9091 <https://github.com/pgadmin-org/pgadmin4/issues/9091>`_ - Fix the Query Tool re-prompting for an unsaved password in a loop and rejecting the re-entered password, by caching the entered password on the server manager when the primary connection is already established.
| `Issue #9595 <https://github.com/pgadmin-org/pgadmin4/issues/9595>`_ - Fix missing ALTER ... SET DEFAULT statements for inherited columns in the generated table SQL/EDIT script.
| `Issue #9677 <https://github.com/pgadmin-org/pgadmin4/issues/9677>`_ - Fix the Unlogged table toggle in table properties not generating any ALTER TABLE ... SET LOGGED/UNLOGGED statement.
@@ -124,12 +124,27 @@ export const QuerySources = {
},
};
function getDateFormatted(date) {
return date.toLocaleDateString();
// On some runtimes the default locale (derived from the OS/environment) is
// malformed, which makes Date.prototype.toLocaleDateString/toLocaleTimeString
// throw "RangeError: Incorrect locale information provided". As these are
// called while rendering the Query History panel, an uncaught throw unmounts
// the whole SQL editor and the user sees a blank white screen, losing any
// unsaved work. Fall back to a moment-based format (moment uses its own
// locale data and does not depend on the broken Intl default). See #7596.
export function getDateFormatted(date) {
try {
return date.toLocaleDateString();
} catch {
return moment(date).format('L');
}
}
function getTimeFormatted(time) {
return time.toLocaleTimeString();
export function getTimeFormatted(time) {
try {
return time.toLocaleTimeString();
} catch {
return moment(time).format('LTS');
}
}
class QueryHistoryUtils {
@@ -0,0 +1,85 @@
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2025, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
// Mock url_for
jest.mock('sources/url_for', () => ({
__esModule: true,
default: jest.fn((endpoint) => `/mock/${endpoint}`),
}));
// Mock the QueryToolComponent to avoid importing all its dependencies
jest.mock('../../../pgadmin/tools/sqleditor/static/js/components/QueryToolComponent.jsx', () => {
const React = require('react');
return {
QueryToolContext: React.createContext(null),
QueryToolConnectionContext: React.createContext(null),
QueryToolEventsContext: React.createContext(null),
};
});
// Mock CodeMirror
jest.mock('../../../pgadmin/static/js/components/ReactCodeMirror', () => ({
__esModule: true,
default: ({ value }) => value,
}));
import { getDateFormatted, getTimeFormatted } from '../../../pgadmin/tools/sqleditor/static/js/components/sections/QueryHistory.jsx';
describe('QueryHistory date/time formatting', () => {
it('formats a date using the native locale formatter', () => {
const date = new Date(2025, 0, 15);
expect(getDateFormatted(date)).toBe(date.toLocaleDateString());
});
it('formats a time using the native locale formatter', () => {
const time = new Date(2025, 0, 15, 10, 30, 45);
expect(getTimeFormatted(time)).toBe(time.toLocaleTimeString());
});
// Regression test for #7596: a malformed default locale makes
// toLocaleDateString/toLocaleTimeString throw "RangeError: Incorrect
// locale information provided". The helpers must not propagate the throw
// (which would white-screen the SQL editor) and must return a usable
// string instead.
describe('when the runtime locale is broken', () => {
let dateSpy, timeSpy;
beforeEach(() => {
dateSpy = jest.spyOn(Date.prototype, 'toLocaleDateString')
.mockImplementation(() => {
throw new RangeError('Incorrect locale information provided');
});
timeSpy = jest.spyOn(Date.prototype, 'toLocaleTimeString')
.mockImplementation(() => {
throw new RangeError('Incorrect locale information provided');
});
});
afterEach(() => {
dateSpy.mockRestore();
timeSpy.mockRestore();
});
it('does not throw and returns a non-empty date string', () => {
const date = new Date(2025, 0, 15);
let result;
expect(() => { result = getDateFormatted(date); }).not.toThrow();
expect(typeof result).toBe('string');
expect(result.length).toBeGreaterThan(0);
});
it('does not throw and returns a non-empty time string', () => {
const time = new Date(2025, 0, 15, 10, 30, 45);
let result;
expect(() => { result = getTimeFormatted(time); }).not.toThrow();
expect(typeof result).toBe('string');
expect(result.length).toBeGreaterThan(0);
});
});
});