Skip to content

Instantly share code, notes, and snippets.

@AfroThundr3007730
Last active June 9, 2025 15:14
Show Gist options
  • Save AfroThundr3007730/1ec8a10df9345b337c19363a8094ab04 to your computer and use it in GitHub Desktop.
Save AfroThundr3007730/1ec8a10df9345b337c19363a8094ab04 to your computer and use it in GitHub Desktop.
Fixup JSON dates exported by .NET JavaScriptSerializer
#!/usr/bin/env python3
# Updated 2025-06-02 by AfroThundr
'''
Fixup JSON dates exported by .NET JavaScriptSerializer
Example input file:
> $FormatEnumerationLimit = -1
> Get-ADUser -Filter * -Properties * |
> ConvertTo-Json -Compress | Out-File AD_User.json
'''
from datetime import datetime as dt
from json import load, dump
from pathlib import Path
from re import search
from sys import argv, maxsize
from chardet import detect
def parse_netdate(obj: object, _: str = None, regex: str = None) -> object:
'''Reads .NET JSON timestamps and returns ISO-8601 timestamps'''
regex = regex or r'\/Date\((-?\d+)\)\/'
if isinstance(obj, str) and (match := search(regex, obj)):
return dt.fromtimestamp(
int(match.groups()[0]) / 1000).strftime('%FT%TZ')
if isinstance(obj, int) and 18 <= len(str(obj)) <= 19:
if obj == maxsize:
return 'Never'
epoch1 = -int(dt.strptime('0001', "%Y").strftime('%s'))
epoch2 = -int(dt.strptime('1601', "%Y").strftime('%s'))
obj /= 10_000_000
obj = obj - epoch1 if obj > epoch1 else obj - epoch2
return dt.fromtimestamp(obj).strftime("%FT%TZ")
return obj
def make_walker(func: callable) -> callable:
'''Create a function that walks an object and calls a method on members'''
def _walk(node: object, key: str = None) -> object:
'''Walks an object and calls a method on members'''
if isinstance(node, dict):
return {k: _walk(v, k) for k, v in node.items()}
if isinstance(node, list):
return [_walk(v, key) for v in node]
return func(node, key)
return _walk
def fix_encoding(file: str, encoding: str = 'utf-8') -> None:
'''Autodetect file encoding and convert to UTF-8'''
with Path(file).open('rb') as f:
detected=detect(f.read(20))['encoding']
Path(file).write_text(
Path(file).read_text(encoding=detected), encoding=encoding)
if __name__ == '__main__':
for entry in argv[1:]:
print('Processing:', entry)
fix_encoding(entry)
with open(entry, 'r+', encoding='utf-8') as f1, \
open(entry, 'r', encoding='utf-8') as f2:
dump(load(f2, object_hook=make_walker(parse_netdate)), f1, indent=2)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment