Skip to content

Instantly share code, notes, and snippets.

@James-E-A
Last active July 31, 2025 18:02
Show Gist options
  • Save James-E-A/8435ea5af710c7d77a0e1c4ce4e0d3b5 to your computer and use it in GitHub Desktop.
Save James-E-A/8435ea5af710c7d77a0e1c4ce4e0d3b5 to your computer and use it in GitHub Desktop.
Format timestamp safe for filename
const TZLETTER = new Map([
[0, "Z"],
[60, "N"],
[120, "O"],
[180, "P"],
[210, "P30"],
[240, "Q"],
[300, "R"],
[360, "S"],
[420, "T"],
[480, "U"],
[540, "V"],
[570, "V30"],
[600, "W"],
[660, "X"],
[720, "Y"],
[-840, "MB"],
[-780, "MA"],
[-765, "M45"],
[-720, "M"],
[-660, "L"],
[-630, "K30"],
[-600, "K"],
[-570, "I30"],
[-540, "I"],
[-525, "H45"],
[-480, "H"],
[-420, "G"],
[-390, "F30"],
[-360, "F"],
[-330, "E30"],
[-300, "E"],
[-270, "D30"],
[-240, "D"],
[-210, "C30"],
[-180, "C"],
[-120, "B"],
[-60, "A"],
]);
TZLETTER.default = "J";
export default function filenameSafeTimestamp(d, useLocalTime=true) {
if (d === undefined)
d = new Date();
return (
useLocalTime
? `${d.getFullYear().toString().padStart(4, "0")}-${(d.getMonth()+1).toString().padStart(2, "0")}-${d.getDate().toString().padStart(2, "0")}T${d.getHours().toString().padStart(2, "0")}-${d.getMinutes().toString().padStart(2, "0")}-${d.getSeconds().toString().padStart(2, "0")}${TZLETTER.get(d.getTimezoneOffset()) ?? TZLETTER.default}`
: `${d.getUTCFullYear().toString().padStart(4, "0")}-${(d.getUTCMonth()+1).toString().padStart(2, "0")}-${d.getUTCDate().toString().padStart(2, "0")}T${d.getUTCHours().toString().padStart(2, "0")}-${d.getUTCMinutes().toString().padStart(2, "0")}-${d.getUTCSeconds().toString().padStart(2, "0")}${TZLETTER.get(0)}`
);
}
import datetime
import sys
__all__ = ['datetime_format_filename']
TZLETTER = {
datetime.timedelta(hours=0): "Z",
datetime.timedelta(hours=-1): "N",
datetime.timedelta(hours=-2): "O",
datetime.timedelta(hours=-3): "P",
datetime.timedelta(hours=-4.5): "P30",
datetime.timedelta(hours=-4): "Q",
datetime.timedelta(hours=-5): "R",
datetime.timedelta(hours=-6): "S",
datetime.timedelta(hours=-7): "T",
datetime.timedelta(hours=-8): "U",
datetime.timedelta(hours=-9): "V",
datetime.timedelta(hours=-10.5): "V30",
datetime.timedelta(hours=-10): "W",
datetime.timedelta(hours=-11): "X",
datetime.timedelta(hours=-12): "Y",
datetime.timedelta(hours=14): "MB",
datetime.timedelta(hours=13): "MA",
datetime.timedelta(hours=12.75): "M45",
datetime.timedelta(hours=12): "M",
datetime.timedelta(hours=11): "L",
datetime.timedelta(hours=10.5): "K30",
datetime.timedelta(hours=10): "K",
datetime.timedelta(hours=9.5): "I30",
datetime.timedelta(hours=9): "I",
datetime.timedelta(hours=8.75): "H45",
datetime.timedelta(hours=8): "H",
datetime.timedelta(hours=7): "G",
datetime.timedelta(hours=6.5): "F30",
datetime.timedelta(hours=6): "F",
datetime.timedelta(hours=5.5): "E30",
datetime.timedelta(hours=5): "E",
datetime.timedelta(hours=4.5): "D30",
datetime.timedelta(hours=4): "D",
datetime.timedelta(hours=3.5): "C30",
datetime.timedelta(hours=3): "C",
datetime.timedelta(hours=2): "B",
datetime.timedelta(hours=1): "A",
None: "J"
}
if sys.version_info >= (3, 12):
UTC = datetime.UTC
_UTC_REPR = "datetime.UTC"
def _naivelocalnow():
return datetime.now()
def localnow():
return _naivelocalnow().astimezone()
def utcnow_():
return datetime.now(tz=utc)
else:
UTC = datetime.timezone.utc
_UTC_REPR = "datetime.timezone.utc"
def _naivelocalnow():
return datetime.datetime.now()
def localnow():
return _naivelocalnow().astimezone()
def utcnow_():
return datetime.datetime.now(tz=UTC)
def datetime_format_filename(dt=None, *, _format="%Y-%m-%dT%H-%M-%S%!Z", local=True):
if dt is None:
dt = localnow() if local else utcnow_()
utcoffset = dt.utcoffset()
if utcoffset is None:
if not local:
raise TypeError(f"datetime_format_filename(..., local=False) called on a naive datetime object. If you meant it to be interpreted as being in the system timezone, call .astimezone() on it first; if you meant it to be interpreted as UTC, call .replace(tzinfo={_UTC_REPR}) on it first.")
if not local:
dt = dt.astimezone(UTC)
return dt.strftime(_format.replace("%!Z", TZLETTER.get(utcoffset, TZLETTER[None])))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment