Last active
November 26, 2020 18:35
-
-
Save metaquanta/06326f00fe9a3167c0bc684f4c8db899 to your computer and use it in GitHub Desktop.
This file contains 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
#!/bin/env -S deno -q run | |
import yargs from 'https://deno.land/x/yargs/deno.ts' | |
function readStdin(): AsyncIterable<string> { | |
return { | |
[Symbol.asyncIterator]: async function* () { | |
const buffer = new Uint8Array(1024); | |
const decoder = new TextDecoder(); | |
while (true) { | |
const bytesRead = await Deno.stdin.read(buffer); | |
if (bytesRead == null) return; | |
yield decoder.decode(buffer.slice(0, bytesRead)); | |
} | |
} | |
} | |
} | |
async function scan(search: RegExp, replace: (s: string) => string) { | |
const encoder = new TextEncoder(); | |
const memo = new Map<string, string>(); | |
const replacement = (a: string[]): string => { | |
//console.log(a); | |
const [s, open, date, ...rest] = a; | |
if (!memo.has(date)) { | |
memo.set(date, replace(date)); | |
} | |
return open + memo.get(date) + rest[rest.length - 1]; | |
} | |
for await (const s of readStdin()) { | |
let replaced = s; | |
for (const match of s.matchAll(search)) { | |
replaced = replaced.replaceAll(match[0], replacement(match)) | |
} | |
Deno.stdout.write(encoder.encode(replaced)); | |
} | |
} | |
const laxStart = /(^|[\s{\[=(:;,'")\]}|])/.source; | |
const laxStop = /($|[\s{\[=(;'")\]}|])/.source; | |
const start = /(^|[\s'"])/.source; | |
const stop = /($|[\s'"])/.source; | |
// These match epoch times in a 250+ year interval beginning September 9th, 2001. | |
const microEpoch = /(\d{16})/.source; | |
const milliEpoch = /(\d{13})/.source; | |
const epoch = /(\d{10})/.source; | |
// These cover 2020-11-25T22:14Z or 2020-11-25T22:14:00.000Z or 2020-11-25T22:14:00-05 or 20201126T031400Z etc (ISO8601) | |
const basicIso = /\d{8}T\d{6}(Z|[+-−]\d\d(|\d\d))/.source; | |
// The date can by separated by either a hyphen (‐) or a minus-hyphen (-). | |
const extendedIsoDate = /\d{4}[-‐]\d\d[-‐]\d\d/.source; | |
// The fractional prefix can begin with either a period (.) or a comma (,). | |
const extendedIsoTime = /\d\d(|:\d\d(|:\d\d))(|[\.,]\d+)/.source; | |
// Western hemisphere timezones can begin with either a minus (−) or a minus-hyphen (-). | |
const extendedIsoZone = /(Z|[+-−]\d\d(|:\d\d))/.source; | |
const extendedIso = new RegExp(`${extendedIsoDate}T${extendedIsoTime}${extendedIsoZone}`).source; | |
const iso = new RegExp(`(${basicIso}|${extendedIso})`).source; | |
const reg = (s: string, argv: { lax: boolean, iso: boolean }) => new RegExp(`${(argv.iso && !argv.lax) ? start : laxStart}${s}${(argv.iso && !argv.lax) ? stop : laxStop}`, "g"); | |
const fromMicroEpoch = (s: string) => new Date(parseInt(s) / 1000); | |
const fromMilliEpoch = (s: string) => new Date(parseInt(s)); | |
const fromEpoch = (s: string) => new Date(parseInt(s) * 1000); | |
const fromIso = (s: string) => new Date(Date.parse(s)); | |
const argv = yargs(Deno.args) | |
.usage('Usage: $0 [--iso] [--micro] [--milli] [--utc] [--lax]') | |
.options({ | |
iso: { type: 'boolean', default: false }, | |
micro: { type: 'boolean', default: false }, | |
milli: { type: 'boolean', default: false }, | |
utc: { type: 'boolean', default: false }, | |
lax: { type: 'boolean', default: false }, | |
}) | |
.argv; | |
function getSearch(argv: { iso: boolean, micro: boolean, milli: boolean, utc: boolean }): string { | |
if (argv.iso) return iso; | |
if (argv.micro) return microEpoch; | |
if (argv.milli) return milliEpoch; | |
return epoch; | |
} | |
function getTransform(argv: { iso: boolean, micro: boolean, milli: boolean, utc: boolean }): ((s: string) => Date) { | |
if (argv.iso) return fromIso; | |
if (argv.micro) return fromMicroEpoch; | |
if (argv.milli) return fromMilliEpoch; | |
return fromEpoch; | |
} | |
function getFormatter(argv: { micro: boolean, milli: boolean, utc: boolean }): ((s: Date) => string) { | |
if (argv.utc) return (date) => date.toUTCString(); | |
return (date) => date.toLocaleString(); | |
} | |
scan(reg(getSearch(argv), argv), (s) => getFormatter(argv)(getTransform(argv)(s))); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment