Python Write JSON to File With Pretty Print (Tested)
Save a Python dict as a readable JSON file with json.dump(indent=4), keep £ and é as they are, handle datetimes, overwrite safely and append the right way.
To format a date as dd/mm/yyyy in JavaScript, take the day with getDate(), the month with getMonth() + 1 and the year with getFullYear(), pad the day and month to two digits with padStart(2, '0'), and join them with slashes. If you don't need full control, date.toLocaleDateString('en-GB') gives you the same 05/10/2026 in one line. Below you'll find both, with the real output from running them, plus the two things that catch people out with UK dates: time zones and parsing a dd/mm/yyyy string back into a Date.
I needed this when I built my UK working days calculator. JavaScript's default toString() prints something like Mon Oct 05 2026 14:30:00 GMT+0100, and American examples online all use mm/dd/yyyy. For a UK audience, 05/10/2026 has to mean 5 October, not 10 May. Every snippet here was run with Node 20, and the outputs are copied from the terminal.
This is the version I use when the format must never change, whatever browser or language setting the visitor has:
// format.js: format a Date as dd/mm/yyyy
function formatDateUK(date, sep = '/') {
const dd = String(date.getDate()).padStart(2, '0');
const mm = String(date.getMonth() + 1).padStart(2, '0'); // months start at 0
const yyyy = date.getFullYear();
return [dd, mm, yyyy].join(sep);
}
const d = new Date(2026, 9, 5); // 5 October 2026 (month 9 = October)
console.log(formatDateUK(d));
console.log(formatDateUK(d, '-'));
console.log(formatDateUK(d, '.'));
console.log(formatDateUK(new Date(2026, 0, 9)));
Output:
05/10/2026
05-10-2026
05.10.2026
09/01/2026
Three details make it work:
getMonth() starts at 0. January is 0 and December is 11, so you must add 1. Forgetting this is the most common date bug in JavaScript, and it's why new Date(2026, 9, 5) is October, not September.padStart(2, '0') adds the leading zero. Without it, 9 January comes out as 9/1/2026. Day and month are numbers, so turn them into strings first with String().getDate(), not getDay(). getDay() is the day of the week (0 for Sunday). It gives believable but wrong numbers, so the bug can hide for days.The optional sep argument lets the same function produce 05-10-2026 or 05.10.2026. For a two-digit year (dd/mm/yy), use String(yyyy).slice(-2).
The browser already knows how British dates look. Pass the en-GB locale and it does the padding for you:
const d = new Date(2026, 9, 5, 14, 30);
console.log(d.toLocaleDateString('en-GB'));
console.log(d.toLocaleDateString('en-US'));
console.log(d.toLocaleDateString('en-GB', { day: 'numeric', month: 'long', year: 'numeric' }));
console.log(d.toLocaleDateString('en-GB', { weekday: 'long', day: 'numeric', month: 'short', year: 'numeric' }));
console.log(d.toLocaleString('en-GB'));
console.log(d.toLocaleDateString('en-GB', { day: '2-digit', month: '2-digit', year: '2-digit' }));
Output:
05/10/2026
10/5/2026
5 October 2026
Monday, 5 Oct 2026
05/10/2026, 14:30:00
05/10/26
The second line shows why you should always pass a locale. If you call toLocaleDateString() with no argument, you get the visitor's format, so the same code shows 05/10/2026 in London and 10/5/2026 in New York. That's fine for a friendly display, but not for anything a person has to type back in or that you store.
The options object is handy for readable dates. { day: 'numeric', month: 'long', year: 'numeric' } gives 5 October 2026, which is clearer than any numeric format and impossible to misread on either side of the Atlantic. I use that style on blog posts and keep dd/mm/yyyy for tables and forms.
One caution: the exact text comes from the ICU data shipped with the browser or Node, so small details (such as a comma after the weekday) can differ slightly between versions. If you're comparing strings in a test, use the manual function above instead.
A Date is really a moment in time, stored in UTC. The day it shows depends on the time zone used to display it. Late evening is where this bites:
// 11:30pm on 31 March 2026 in UTC is already 1 April in London (BST, UTC+1)
const d = new Date('2026-03-31T23:30:00Z');
const fmt = new Intl.DateTimeFormat('en-GB', { timeZone: 'Europe/London' });
console.log('London:', fmt.format(d));
const utc = new Intl.DateTimeFormat('en-GB', { timeZone: 'UTC' });
console.log('UTC: ', utc.format(d));
const dhaka = new Intl.DateTimeFormat('en-GB', { timeZone: 'Asia/Dhaka' });
console.log('Dhaka: ', dhaka.format(d));
Output (run with the computer set to Dhaka time):
London: 01/04/2026
UTC: 31/03/2026
Dhaka: 01/04/2026
At 23:30 UTC on 31 March 2026, the UK is already on British Summer Time (UTC+1), so it's 00:30 on 1 April in London. If your server runs in UTC and formats the date there, a UK user who ordered at half past midnight sees the wrong day on their receipt.
The fix is to say which zone you mean. Intl.DateTimeFormat and toLocaleDateString both accept timeZone: 'Europe/London', which handles GMT and BST for you, including the clock changes in March and October. The manual formatDateUK() function uses the computer's local zone, which is right in a browser but risky on a server.
Formatting is half the job. When a user types 05/10/2026 into a text box, you need to turn it back into a date. Don't hand it to new Date():
// parse.js: turn "dd/mm/yyyy" into a Date, or null if it isn't a real date
function parseDateUK(text) {
const match = /^(\d{1,2})[\/\-.](\d{1,2})[\/\-.](\d{4})$/.exec(text.trim());
if (!match) return null;
const day = Number(match[1]);
const month = Number(match[2]);
const year = Number(match[3]);
const date = new Date(year, month - 1, day);
// new Date(2026, 1, 30) quietly rolls over to 2 March, so check it round-trips
if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) {
return null;
}
return date;
}
console.log('new Date("05/10/2026") ->', new Date('05/10/2026').toDateString());
for (const input of ['05/10/2026', '5/10/2026', '05-10-2026', '31/12/2026', '30/02/2026', '29/02/2028', '13/13/2026', '2026-10-05']) {
const date = parseDateUK(input);
console.log(input.padEnd(11), '->', date ? date.toDateString() : null);
}
Output (run with the computer set to London time):
new Date("05/10/2026") -> Sun May 10 2026
05/10/2026 -> Mon Oct 05 2026
5/10/2026 -> Mon Oct 05 2026
05-10-2026 -> Mon Oct 05 2026
31/12/2026 -> Thu Dec 31 2026
30/02/2026 -> null
29/02/2028 -> Tue Feb 29 2028
13/13/2026 -> null
2026-10-05 -> null
The first line is the trap. new Date('05/10/2026') reads the string as American mm/dd/yyyy and gives 10 May. There's no error, just a wrong date. Non-ISO strings like this aren't even guaranteed to parse the same way in every engine.
parseDateUK() reads the parts with a regular expression, then builds the date with new Date(year, month - 1, day). The round-trip check matters: JavaScript happily turns 30 February into 2 March, so the function rebuilds the parts and rejects anything that moved. That's how 30/02/2026 and 13/13/2026 come back as null, while the leap day 29/02/2028 is accepted. It also accepts dashes and dots, and single-digit days. If you want a refresher on how regex groups work, my post on regex for email validation in JavaScript walks through a pattern step by step.
HTML's <input type="date"> always gives you yyyy-mm-dd in its value, even though a UK user sees dd/mm/yyyy on screen. APIs and databases usually want ISO dates too, so you'll often convert between the two:
// <input type="date"> uses yyyy-mm-dd, whatever the user's locale shows
function isoToUK(iso) {
const [yyyy, mm, dd] = iso.split('-');
return `${dd}/${mm}/${yyyy}`;
}
function ukToIso(uk) {
const [dd, mm, yyyy] = uk.split('/');
return `${yyyy}-${mm}-${dd}`;
}
console.log(isoToUK('2026-10-05'));
console.log(ukToIso('05/10/2026'));
// Careful: toISOString() is UTC, so it can give the wrong day
const lateEvening = new Date(2026, 6, 1, 0, 30); // 00:30 on 1 July, London time (BST)
console.log(lateEvening.toISOString().slice(0, 10));
Output (London time):
05/10/2026
2026-10-05
2026-06-30
String splitting is the simplest and safest way to swap formats, because no time zone is involved. The last line shows a mistake I made myself: toISOString().slice(0, 10) looks like a neat way to get yyyy-mm-dd, but toISOString() is always UTC. At 00:30 on 1 July in London (BST), it's still 30 June in UTC, so you get the day before. For local dates, build the string from getFullYear(), getMonth() and getDate() as in the first function, with dashes instead of slashes. You can read more about date input in my guide to HTML form input types and validation.
toLocaleDateString('en-GB'), or the long 5 October 2026 style for readability.formatDateUK() function, so the output never depends on the browser.Intl.DateTimeFormat('en-GB', { timeZone: 'Europe/London' }).yyyy-mm-dd, and only format as dd/mm/yyyy when you show it.You don't need Moment.js for any of this. It's in maintenance mode, and the built-in Intl API covers formatting in every modern browser. Libraries such as date-fns are worth it for heavy date maths, but for a format string, ten lines of plain JavaScript are enough. If you work with dates in UK tools, my post on counting working days in JavaScript with UK bank holidays builds on the same ideas, and to show prices next to those dates, see how to format a number as currency in JavaScript.
Call new Date().toLocaleDateString('en-GB'), or pass new Date() to the formatDateUK() function above. Both return today's date in the visitor's local time zone, for example 05/10/2026.
Because getMonth() returns 0 for January and 11 for December. Add 1 when you format, and subtract 1 when you pass a month into new Date(year, month, day).
JavaScript treats slash dates as American mm/dd/yyyy. Split the string yourself and build the date with new Date(year, month - 1, day), as in parseDateUK().
Use a dash as the separator: formatDateUK(date, '-'). With toLocaleDateString('en-GB'), you can call .replaceAll('/', '-') on the result.
The numeric dd/mm/yyyy output is the same in all modern browsers. Long formats with weekdays and month names can differ slightly in punctuation, so don't rely on them in exact string comparisons.
The MDN page on Intl.DateTimeFormat lists every option if you need more.
// note
This is a learning note from studying the web. It is one small topic, written so I can remember it. It is not a course and not a claim that I have finished the subject.
If a sentence is wrong, say so from the contact page and name this title. Drafts never appear here. Related notes, when they exist, are other published posts, and the same sample rule applies to each of them.
Save a Python dict as a readable JSON file with json.dump(indent=4), keep £ and é as they are, handle datetimes, overwrite safely and append the right way.
Hash passwords in PHP with password_hash(), check them with password_verify(), and build a PDO register and login that upgrades old hashes. Real output.
Stop tracking a file in Git without deleting it: git rm --cached, .gitignore, whole folders, a dry run, undoing it, and the catch for teammates and secrets.