-
Notifications
You must be signed in to change notification settings - Fork 273
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(template): add date template helper functions (#5997)
* feat: add date template helper functions Signed-off-by: Manuel Ruck <[email protected]> * refactor: separate `modifyDate` and `shiftDate` functions The hierarchical switch statements are hard to maintain. The `mode` argument served as a routing param to pick up necessary function behaviour. It's better to get rid of it and to maintain 2 independent functions. Each function can have its own limitations and time unit types. * improvement: type-safe time units TODO: * consider binding `ShiftDateTimeUnit` to `Duration` from `date-fns` * add more tests * test: tests and docs for `modifyDate` function * test: tests and docs for `shiftDate` function * fix: remove unsupported time unit from `shiftDate` Milliseconds are not supported by the underlying `add` function. * test: enable test for `formatDate` * test: mute failing test * refactor: replace switch-statement with lookup-index * chore: rename some functions args * feat: stick date modifiers to UTC TZ * feat: stick date formatter to UTC TZ * chore: use arrow-type instead of `Function` in type declaration Type `Function` is not recommended to use by `@typescript-eslint/ban-types`. Let's use a bit more specific function interface. * chore: use `unknown` instead of `any` if possible * chore: added tests and examples with TZ for hour modifiers --------- Signed-off-by: Manuel Ruck <[email protected]> Co-authored-by: Manuel Ruck <[email protected]> Co-authored-by: Vladimir Vagaytsev <[email protected]>
- Loading branch information
1 parent
7d8034b
commit 39d2396
Showing
5 changed files
with
192 additions
and
4 deletions.
There are no files selected for viewing
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
/* | ||
* Copyright (C) 2018-2024 Garden Technologies, Inc. <[email protected]> | ||
* | ||
* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
*/ | ||
|
||
import type { TemplateHelperFunction } from "./functions.js" | ||
import { joi } from "../config/common.js" | ||
import { format as formatFns, add, type Duration } from "date-fns" | ||
import { UTCDateMini } from "@date-fns/utc" | ||
|
||
type ShiftDateTimeUnit = keyof Duration | ||
const validShiftDateTimeUnits: ShiftDateTimeUnit[] = [ | ||
"years", | ||
"months", | ||
"weeks", | ||
"days", | ||
"hours", | ||
"minutes", | ||
"seconds", | ||
] as const | ||
|
||
const validModifyDateTimeUnits = ["years", "months", "days", "hours", "minutes", "seconds", "milliseconds"] as const | ||
type ModifyDateTimeUnit = (typeof validModifyDateTimeUnits)[number] | ||
// This is still type-safe because every entry of ModifyDateTimeUnit must be declared in the index below. | ||
const modifyDateFunctions: { [k in ModifyDateTimeUnit]: (date: Date, timeUnits: number) => void } = { | ||
years: (date, timeUnits) => date.setUTCFullYear(timeUnits), | ||
months: (date, timeUnits) => date.setUTCMonth(timeUnits), | ||
days: (date, timeUnits) => date.setUTCDate(timeUnits), | ||
hours: (date, timeUnits) => date.setUTCHours(timeUnits), | ||
minutes: (date, timeUnits) => date.setUTCMinutes(timeUnits), | ||
seconds: (date, timeUnits) => date.setUTCSeconds(timeUnits), | ||
milliseconds: (date, timeUnits) => date.setUTCMilliseconds(timeUnits), | ||
} as const | ||
|
||
const timeZoneComment = | ||
"The input date is always converted to the UTC time zone before the modification. If no explicit timezone is specified on the input date, then the system default one will be used. The output date is always returned in the UTC time zone too." | ||
|
||
export const dateHelperFunctionSpecs: TemplateHelperFunction[] = [ | ||
{ | ||
name: "formatDateUtc", | ||
description: `Formats the given date using the specified format. ${timeZoneComment}`, | ||
arguments: { | ||
date: joi.string().required().description("The date to format."), | ||
format: joi | ||
.string() | ||
.required() | ||
.description("The format to use. See https://date-fns.org/v2.21.1/docs/format for details."), | ||
}, | ||
outputSchema: joi.string(), | ||
exampleArguments: [ | ||
{ input: ["2021-01-01T00:00:00Z", "yyyy-MM-dd"], output: "2021-01-01" }, | ||
{ input: ["2021-01-01T00:00:00+0200", "yyyy-MM-dd"], output: "2020-12-31" }, | ||
{ input: ["2021-01-01T00:00:00Z", "yyyy-MM-dd HH:mm:ss"], output: "2021-01-01 00:00:00" }, | ||
{ input: ["2021-01-01T00:00:00+0200", "yyyy-MM-dd HH:mm:ss"], output: "2020-12-31 22:00:00" }, | ||
], | ||
fn: (date: string, format: string) => { | ||
const utcDate = new UTCDateMini(date) | ||
return formatFns(utcDate, format) | ||
}, | ||
}, | ||
{ | ||
name: "shiftDateUtc", | ||
description: `Shifts the date by the specified amount of time units. ${timeZoneComment}`, | ||
arguments: { | ||
date: joi.string().required().description("The date to shift."), | ||
amount: joi.number().required().description("The amount of time units to shift the date by."), | ||
unit: joi | ||
.string() | ||
.valid(...validShiftDateTimeUnits) | ||
.required() | ||
.description("The time unit to shift the date by."), | ||
}, | ||
outputSchema: joi.string(), | ||
exampleArguments: [ | ||
{ input: ["2021-01-01T00:00:00Z", 1, "seconds"], output: "2021-01-01T00:00:01.000Z" }, | ||
{ input: ["2021-01-01T00:00:00Z", -1, "seconds"], output: "2020-12-31T23:59:59.000Z" }, | ||
{ input: ["2021-01-01T00:00:00Z", 1, "minutes"], output: "2021-01-01T00:01:00.000Z" }, | ||
{ input: ["2021-01-01T00:00:00Z", -1, "minutes"], output: "2020-12-31T23:59:00.000Z" }, | ||
{ input: ["2021-01-01T00:00:00Z", 1, "hours"], output: "2021-01-01T01:00:00.000Z" }, | ||
{ input: ["2021-01-01T00:00:00Z", -1, "hours"], output: "2020-12-31T23:00:00.000Z" }, | ||
{ input: ["2021-01-01T10:00:00+0200", 1, "hours"], output: "2021-01-01T09:00:00.000Z" }, | ||
{ input: ["2021-01-01T00:00:00Z", 1, "days"], output: "2021-01-02T00:00:00.000Z" }, | ||
{ input: ["2021-01-01T00:00:00Z", -1, "days"], output: "2020-12-31T00:00:00.000Z" }, | ||
{ input: ["2021-01-01T00:00:00Z", 1, "months"], output: "2021-02-01T00:00:00.000Z" }, | ||
{ input: ["2021-01-01T00:00:00Z", -1, "months"], output: "2020-12-01T00:00:00.000Z" }, | ||
{ input: ["2021-01-01T00:00:00Z", 1, "years"], output: "2022-01-01T00:00:00.000Z" }, | ||
{ input: ["2021-01-01T00:00:00Z", -1, "years"], output: "2020-01-01T00:00:00.000Z" }, | ||
], | ||
fn: (date: string, timeUnitAmount: number, unit: ShiftDateTimeUnit) => { | ||
const dateClone = new Date(date) | ||
return add(dateClone, { [unit]: timeUnitAmount }).toISOString() | ||
}, | ||
}, | ||
{ | ||
name: "modifyDateUtc", | ||
description: `Modifies the date by setting the specified amount of time units. ${timeZoneComment}`, | ||
arguments: { | ||
date: joi.string().required().description("The date to modify."), | ||
amount: joi.number().required().description("The amount of time units to set."), | ||
unit: joi | ||
.string() | ||
.valid(...validModifyDateTimeUnits) | ||
.required() | ||
.description("The time unit to set."), | ||
}, | ||
outputSchema: joi.string(), | ||
exampleArguments: [ | ||
{ input: ["2021-01-01T00:00:00.234Z", 345, "milliseconds"], output: "2021-01-01T00:00:00.345Z" }, | ||
{ input: ["2021-01-01T00:00:05Z", 30, "seconds"], output: "2021-01-01T00:00:30.000Z" }, | ||
{ input: ["2021-01-01T00:01:00Z", 15, "minutes"], output: "2021-01-01T00:15:00.000Z" }, | ||
{ input: ["2021-01-01T12:00:00Z", 11, "hours"], output: "2021-01-01T11:00:00.000Z" }, | ||
{ input: ["2021-01-01T10:00:00+0200", 11, "hours"], output: "2021-01-01T11:00:00.000Z" }, | ||
{ input: ["2021-01-31T00:00:00Z", 1, "days"], output: "2021-01-01T00:00:00.000Z" }, | ||
{ input: ["2021-03-01T00:00:00Z", 0, "months"], output: "2021-01-01T00:00:00.000Z" }, // 0 (Jan) - 11 (Dec) | ||
{ input: ["2021-01-01T00:00:00Z", 2024, "years"], output: "2024-01-01T00:00:00.000Z" }, | ||
], | ||
fn: (date: string, timeUnitAmount: number, unit: ModifyDateTimeUnit) => { | ||
const dateClone = new Date(date) | ||
const dateModifier = modifyDateFunctions[unit] | ||
dateModifier(dateClone, timeUnitAmount) | ||
return dateClone.toISOString() | ||
}, | ||
}, | ||
] |
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
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
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.