Last active
June 9, 2025 15:14
-
-
Save AfroThundr3007730/1ec8a10df9345b337c19363a8094ab04 to your computer and use it in GitHub Desktop.
Fixup JSON dates exported by .NET JavaScriptSerializer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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