From 4c198f1fef1bb02dbe1676eeec93d28d2ad0075e Mon Sep 17 00:00:00 2001 From: Jonathan Delgado Date: Fri, 28 Jul 2023 13:09:36 -0400 Subject: [PATCH 01/22] feat: upgrade ndate command --- handler.ts | 137 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 92 insertions(+), 45 deletions(-) diff --git a/handler.ts b/handler.ts index 5b6d876..6beeb3a 100644 --- a/handler.ts +++ b/handler.ts @@ -1,70 +1,117 @@ -import { - toDateStyle, - toTimeStyle, - dateParse, -toHourCycle, - } from "./lib/common.ts"; +import { + dateParse, + toDateStyle, + toHourCycle, + toTimeStyle, +} from "./lib/common.ts"; import { renderDateTemplate } from "./lib/renderDateTemplate.ts"; import { makeFlags } from "./makeFlags.ts"; -export const handler = async function*(args: string[]): AsyncGenerator { - let { - hourCycle, dateStyle, timeStyle, insertFinalNewLine, local, timeZone, date, outputAsEpoch, outputAsEpochMS, outputAsJSON, outputAsUTC, stdinReadable, showHelp, template, transformOptions, optionsLabels, - } = makeFlags(args); - - if(showHelp) { - const items = Object.keys(transformOptions) - .map(item => { - const label = optionsLabels[item]?.label; - return label ? `[${item} ${label}]` : `[${item}]`; - }); +const makeHelpDialog = function* ( + transformOptions: Record< + string, + (nextArgument: () => string | undefined) => void + >, + optionsLabels: Record< + string, + { + label?: string | undefined; + } | undefined + >, +) { + const items = Object.keys(transformOptions) + .map((item) => { + const label = optionsLabels[item]?.label; + return label ? `[${item} ${label}]` : `[${item}]`; + }); - const textUsage = `Usage: ndate`; + const textUsage = `Usage: ndate`; - const lines: string[] = []; - let currentLine: string | undefined; - for(const item of items) { - if(!currentLine) { - currentLine = lines.length ? `${' '.repeat(textUsage.length)}` : `${textUsage}`; - } + const lines: string[] = []; + let currentLine: string | undefined; + for (const item of items) { + if (!currentLine) { + currentLine = lines.length + ? `${" ".repeat(textUsage.length)}` + : `${textUsage}`; + } - currentLine = `${currentLine} ${item}`; - if(currentLine.length > 80) { - lines.push(currentLine); - currentLine = undefined; - } + currentLine = `${currentLine} ${item}`; + if (currentLine.length > 80) { + lines.push(currentLine); + currentLine = undefined; } + } - if(currentLine) lines.push(currentLine); + if (currentLine) lines.push(currentLine); - for(const line of lines) { - yield new TextEncoder().encode(line); - yield new TextEncoder().encode('\n'); - } + for (const line of lines) { + yield new TextEncoder().encode(line); + yield new TextEncoder().encode("\n"); + } +}; +export const handler = async function* ( + args: string[], +): AsyncGenerator { + let { + hourCycle, + dateStyle, + timeStyle, + insertFinalNewLine, + local, + timeZone, + date, + outputAsEpoch, + outputAsEpochMS, + outputAsJSON, + outputAsUTC, + stdinReadable, + showHelp, + template, + transformOptions, + optionsLabels, + } = makeFlags(args); + + if (showHelp) { + yield* makeHelpDialog(transformOptions, optionsLabels); return; } - if(timeZone) Deno.env.set(`TZ`, timeZone); - if(local) Deno.env.set(`LANG`, local); + if (timeZone) Deno.env.set(`TZ`, timeZone); + if (local) Deno.env.set(`LANG`, local); - if(stdinReadable) { + if (stdinReadable) { const buff = new Uint8Array(256); await Deno.stdin.read(buff); - const text = new TextDecoder().decode(buff.subarray(0, buff.findIndex(p => p === 0))).trim(); + const text = new TextDecoder().decode( + buff.subarray(0, buff.findIndex((p) => p === 0)), + ).trim(); date = dateParse(text); } const toOutput = () => { - if(template) return renderDateTemplate(template, date, local, { dateStyle: toDateStyle(dateStyle), timeStyle: toTimeStyle(timeStyle), timeZone, hourCycle: toHourCycle(hourCycle) }); - if(outputAsEpochMS) return Math.floor(date.getTime()).toString(); - if(outputAsEpoch) return Math.floor(date.getTime() / 1000).toString(); - if(outputAsJSON) return date.toJSON(); - if(outputAsUTC) return date.toUTCString(); - return date.toLocaleString(local, { dateStyle: toDateStyle(dateStyle), timeStyle: toTimeStyle(timeStyle), timeZone, hourCycle: toHourCycle(hourCycle) }); + if (template) { + return renderDateTemplate(template, date, local, { + dateStyle: toDateStyle(dateStyle), + timeStyle: toTimeStyle(timeStyle), + timeZone, + hourCycle: toHourCycle(hourCycle), + }); + } + if (outputAsEpochMS) return Math.floor(date.getTime()).toString(); + if (outputAsEpoch) return Math.floor(date.getTime() / 1000).toString(); + if (outputAsJSON) return date.toJSON(); + if (outputAsUTC) return date.toUTCString(); + return date.toLocaleString(local, { + dateStyle: toDateStyle(dateStyle), + timeStyle: toTimeStyle(timeStyle), + timeZone, + hourCycle: toHourCycle(hourCycle), + }); }; yield new TextEncoder().encode(toOutput()); - if(insertFinalNewLine) yield new TextEncoder().encode(`\n`); + if (insertFinalNewLine) yield new TextEncoder().encode(`\n`); }; From b4d86755ce7cd38526bd90c723a6720d9cb9813d Mon Sep 17 00:00:00 2001 From: Jonathan Delgado Date: Fri, 28 Jul 2023 13:14:05 -0400 Subject: [PATCH 02/22] feat: rename main function per setup --- ndate.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ndate.ts b/ndate.ts index cd0a44f..11af4cc 100755 --- a/ndate.ts +++ b/ndate.ts @@ -2,10 +2,10 @@ import { handler } from "./handler.ts"; -const run = async () => { +const setup = async () => { for await (const buff of handler(Deno.args)) { Deno.stdout.write(buff) } } -await run(); +await setup(); From d94615dfad28004d34d223cd1b9a91df0f4d0c36 Mon Sep 17 00:00:00 2001 From: Jonathan Delgado Date: Sat, 28 Oct 2023 01:19:26 -0300 Subject: [PATCH 03/22] test: upgrade coverage cases --- __snapshots__/ndate.spec.ts.snap | 104 ++++++++++++++- ndate.spec.ts | 211 +++++++++++++++++++++++++++++-- 2 files changed, 305 insertions(+), 10 deletions(-) diff --git a/__snapshots__/ndate.spec.ts.snap b/__snapshots__/ndate.spec.ts.snap index d02ac0f..ceef966 100644 --- a/__snapshots__/ndate.spec.ts.snap +++ b/__snapshots__/ndate.spec.ts.snap @@ -11,4 +11,106 @@ snapshot[`show help 1`] = ` " `; -snapshot[`render date 1`] = `"miércoles, 13 de diciembre de 2023, 00:00:00 hora de verano de Chile"`; +snapshot[`render date 1`] = `"miércoles, 13 de diciembre de 2023, 01:00:00 hora de verano de Chile"`; + +snapshot[`format json 1`] = `"2023-08-08T03:38:58.570Z"`; + +snapshot[`format utc 1`] = `"Tue, 08 Aug 2023 03:38:58 GMT"`; + +snapshot[`format epoch 1`] = `"1691465938"`; + +snapshot[`format epoch ms 1`] = `"1691465938570"`; + +snapshot[`format template 1`] = ` +"epoch:1691465938 +" + + "epoch_ms:1691465938570 +" + + "json:2023-08-08T03:38:58.570Z +" + + "iso:2023-08-08T03:38:58.570Z +" + + "iso8601:2023-08-08T03:38:58.570Z +" + + "utc:Tue, 08 Aug 2023 03:38:58 GMT +" + + "rfc7231:Tue, 08 Aug 2023 03:38:58 GMT +" + + "local:lunes, 7 de agosto de 2023, 23:38:58 hora estándar de Chile +" + + "time:1691465938570 +" + + "YYYY:2023 +" + + "MM:08 +" + + "DD:07 +" + + "hh:23 +" + + "mm:38 +" + + "ss:58 +" + + "ms:570 +" + + "full_year:2023 +" + + "month:8 +" + + "date:7 +" + + "day:1 +" + + "hours:23 +" + + "minutes:38 +" + + "seconds:58 +" + + "milliseconds:570 +" + + "timezone_offset:240 +" + + "utc_full_year:2023 +" + + "utc_month:8 +" + + "utc_date:8 +" + + "utc_day:2 +" + + "utc_hours:3 +" + + "utc_minutes:38 +" + + "utc_seconds:58 +" + + "utc_milliseconds:570 +" + + "local_weekday:lunes +" + + "local_literal: +" + + "local_day:7 +" + + "local_month:agosto +" + + "local_year:2023 +" + + "local_hour:23 +" + + "local_minute:38 +" + + "local_second:58 +" + + "local_timeZoneName:hora estándar de Chile" +`; + +snapshot[`format template with transform 1`] = `'"Tue, 08 Aug 2023 03:38:58 GMT"'`; + +snapshot[`format template with transform 2`] = `"Tue"`; + +snapshot[`format template with transform 3`] = `"0008"`; + +snapshot[`format template with transform 4`] = `"8000"`; diff --git a/ndate.spec.ts b/ndate.spec.ts index b53c9ff..686c464 100644 --- a/ndate.spec.ts +++ b/ndate.spec.ts @@ -1,15 +1,21 @@ import { assertSnapshot } from "https://deno.land/std@0.195.0/testing/snapshot.ts"; import { handler } from "./handler.ts"; -const joinUint8ArrayGenerator = async ( - generator: AsyncGenerator, -) => { - let buffEnd = new Uint8Array(); - for await (const line of generator) { - buffEnd = new Uint8Array([...buffEnd, ...line]); +const ArrayFromAsync = async (iterator: AsyncIterable, mapFn?: (element: T, index: number) => R, thisArg?: any): Promise => { + const items: R[] = [] + let index = -1 + for await (const item of iterator) { + index = index + 1 + items.push(mapFn?.call(thisArg, item, index) ?? item as R) } - return buffEnd; -}; + return items +} + +const joinUint8ArrayGenerator = async ( + generator: AsyncIterable, +) => new Uint8Array( + (await ArrayFromAsync(generator, e => [...e])).flat() +); Deno.test("show help", async (t) => { await assertSnapshot( @@ -27,8 +33,195 @@ Deno.test("render date", async (t) => { t, new TextDecoder().decode( await joinUint8ArrayGenerator( - handler(["-z", "-d", "2023-12-13 00:00","-l","es-cl",'-tz',"America/Santiago"]), + handler(["-z", "-d", "2023-12-13 00:00", "-l", "es-cl", '-tz', "America/Santiago"]), ), ), ); }); + +Deno.test("format json", async (t) => { + await assertSnapshot( + t, + new TextDecoder().decode( + await joinUint8ArrayGenerator( + handler([ + "-z", + "-d", + "2023-08-08T03:38:58.570Z", + "-l", + "es-cl", + '-tz', + "America/Santiago", + '--json' + ]), + ), + ), + ); +}) + +Deno.test("format utc", async (t) => { + await assertSnapshot( + t, + new TextDecoder().decode( + await joinUint8ArrayGenerator( + handler([ + "-z", + "-d", + "2023-08-08T03:38:58.570Z", + "-l", + "es-cl", + '-tz', + "America/Santiago", + '--utc' + ]), + ), + ), + ); +}) + +Deno.test("format epoch", async (t) => { + await assertSnapshot( + t, + new TextDecoder().decode( + await joinUint8ArrayGenerator( + handler([ + "-z", + "-d", + "2023-08-08T03:38:58.570Z", + "-l", + "es-cl", + '-tz', + "America/Santiago", + '--epoch' + ]), + ), + ), + ); +}) + + +Deno.test("format epoch ms", async (t) => { + await assertSnapshot( + t, + new TextDecoder().decode( + await joinUint8ArrayGenerator( + handler([ + "-z", + "-d", + "2023-08-08T03:38:58.570Z", + "-l", + "es-cl", + '-tz', + "America/Santiago", + '--epoch-ms' + ]), + ), + ), + ); +}) + + +Deno.test("format template", async (t) => { + await assertSnapshot( + t, + new TextDecoder().decode( + await joinUint8ArrayGenerator( + handler([ + "-z", + "-d", + "2023-08-08T03:38:58.570Z", + "-l", + "es-cl", + '-tz', + "America/Santiago", + '--template', + 'epoch:{{epoch}}\nepoch_ms:{{epoch_ms}}\njson:{{json}}\niso:{{iso}}\niso8601:{{iso8601}}\nutc:{{utc}}\nrfc7231:{{rfc7231}}\nlocal:{{local}}\ntime:{{time}}\nYYYY:{{YYYY}}\nMM:{{MM}}\nDD:{{DD}}\nhh:{{hh}}\nmm:{{mm}}\nss:{{ss}}\nms:{{ms}}\nfull_year:{{full_year}}\nmonth:{{month}}\ndate:{{date}}\nday:{{day}}\nhours:{{hours}}\nminutes:{{minutes}}\nseconds:{{seconds}}\nmilliseconds:{{milliseconds}}\ntimezone_offset:{{timezone_offset}}\nutc_full_year:{{utc_full_year}}\nutc_month:{{utc_month}}\nutc_date:{{utc_date}}\nutc_day:{{utc_day}}\nutc_hours:{{utc_hours}}\nutc_minutes:{{utc_minutes}}\nutc_seconds:{{utc_seconds}}\nutc_milliseconds:{{utc_milliseconds}}\nlocal_weekday:{{local_weekday}}\nlocal_literal:{{local_literal}}\nlocal_day:{{local_day}}\nlocal_month:{{local_month}}\nlocal_year:{{local_year}}\nlocal_hour:{{local_hour}}\nlocal_minute:{{local_minute}}\nlocal_second:{{local_second}}\nlocal_timeZoneName:{{local_timeZoneName}}' + ]), + ), + ), + ); +}) + +Deno.test("format template with transform", async (t) => { + await assertSnapshot( + t, + new TextDecoder().decode( + await joinUint8ArrayGenerator( + handler([ + "-z", + "-d", + "2023-08-08T03:38:58.570Z", + "-l", + "es-cl", + '-tz', + "America/Santiago", + '--template', + '{{rfc7231:json}}' + ]), + ), + ), + ); +}) + +Deno.test("format template with transform", async (t) => { + await assertSnapshot( + t, + new TextDecoder().decode( + await joinUint8ArrayGenerator( + handler([ + "-z", + "-d", + "2023-08-08T03:38:58.570Z", + "-l", + "es-cl", + '-tz', + "America/Santiago", + '--template', + '{{rfc7231:sub:0:3}}' + ]), + ), + ), + ); +}) + +Deno.test("format template with transform", async (t) => { + await assertSnapshot( + t, + new TextDecoder().decode( + await joinUint8ArrayGenerator( + handler([ + "-z", + "-d", + "2023-08-08T03:38:58.570Z", + "-l", + "es-cl", + '-tz', + "America/Santiago", + '--template', + '{{month:padStart:4:0}}' + ]), + ), + ), + ); +}) + +Deno.test("format template with transform", async (t) => { + await assertSnapshot( + t, + new TextDecoder().decode( + await joinUint8ArrayGenerator( + handler([ + "-z", + "-d", + "2023-08-08T03:38:58.570Z", + "-l", + "es-cl", + '-tz', + "America/Santiago", + '--template', + '{{month:padEnd:4:0}}' + ]), + ), + ), + ); +}) From a83a598f2d11d7d02b7c10741011a0d48cbada0a Mon Sep 17 00:00:00 2001 From: Jonathan Delgado Date: Fri, 29 Sep 2023 16:48:42 -0300 Subject: [PATCH 04/22] feat: add support orender on sheet date --- handler.ts | 12 ++++++++++++ makeFlags.ts | 3 +++ 2 files changed, 15 insertions(+) diff --git a/handler.ts b/handler.ts index 6beeb3a..b5bfa41 100644 --- a/handler.ts +++ b/handler.ts @@ -7,6 +7,16 @@ import { import { renderDateTemplate } from "./lib/renderDateTemplate.ts"; import { makeFlags } from "./makeFlags.ts"; +const renderSheet = (date: Date) => { + const year = date.getFullYear(); + const month = date.getMonth() + 1; + const dayOfMonth = date.getDate(); + const hours = date.getHours(); + const minutes = date.getMinutes(); + const seconds = date.getSeconds(); + return `=DATE(${year};${month};${dayOfMonth})+TIME(${hours};${minutes};${seconds})`; +}; + const makeHelpDialog = function* ( transformOptions: Record< string, @@ -70,6 +80,7 @@ export const handler = async function* ( showHelp, template, transformOptions, + outputAsSheet, optionsLabels, } = makeFlags(args); @@ -103,6 +114,7 @@ export const handler = async function* ( if (outputAsEpoch) return Math.floor(date.getTime() / 1000).toString(); if (outputAsJSON) return date.toJSON(); if (outputAsUTC) return date.toUTCString(); + if (outputAsSheet) return renderSheet(date); return date.toLocaleString(local, { dateStyle: toDateStyle(dateStyle), timeStyle: toTimeStyle(timeStyle), diff --git a/makeFlags.ts b/makeFlags.ts index d8443ed..7da1418 100644 --- a/makeFlags.ts +++ b/makeFlags.ts @@ -22,6 +22,7 @@ export function makeFlags(args: string[]) { let outputAsEpochMS = false; let outputAsJSON = false; let outputAsUTC = false; + let outputAsSheet = false; let stdinReadable = false; let showHelp = false; let template: string | undefined; @@ -37,6 +38,7 @@ export function makeFlags(args: string[]) { '--local': (nextArgument) => { local = nextArgument(); }, '--template': (nextArgument) => { template = nextArgument(); }, '--json': () => { outputAsJSON = true; }, + '--sheet': () => { outputAsSheet = true; }, '--utc': () => { outputAsUTC = true; }, '--epoch': () => { outputAsEpoch = true; }, '--epoch-ms': () => { outputAsEpochMS = true; }, @@ -99,6 +101,7 @@ export function makeFlags(args: string[]) { outputAsEpochMS, outputAsJSON, outputAsUTC, + outputAsSheet, stdinReadable, showHelp, template, From cb39ab1ccc60977be857dee5784c1b2b031558de Mon Sep 17 00:00:00 2001 From: Jonathan Delgado Date: Sat, 28 Oct 2023 01:24:37 -0300 Subject: [PATCH 05/22] test: upgrade cases --- __snapshots__/ndate.spec.ts.snap | 8 ++++++-- ndate.spec.ts | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/__snapshots__/ndate.spec.ts.snap b/__snapshots__/ndate.spec.ts.snap index ceef966..5e8b663 100644 --- a/__snapshots__/ndate.spec.ts.snap +++ b/__snapshots__/ndate.spec.ts.snap @@ -5,9 +5,11 @@ snapshot[`show help 1`] = ` " + " [--hour-cycles ] [--time-zone ] [--local ] " + - " [--template