From 893df272cb9b25fc8effcb550ea674aa5c9af610 Mon Sep 17 00:00:00 2001 From: Michal Michalowski Date: Wed, 20 Nov 2024 15:59:48 +0000 Subject: [PATCH 01/10] chore: update package-lock.json --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index af83eac..5f2e055 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@time-loop/hot-formula-parser", - "version": "4.1.0", + "version": "4.1.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@time-loop/hot-formula-parser", - "version": "4.1.0", + "version": "4.1.2", "dependencies": { "@formulajs/formulajs": "^2.3.0", "json5": "^2.2.3", From 22c84c84f0ec5ddfb579c24ec2a34fd642e67afc Mon Sep 17 00:00:00 2001 From: Michal Michalowski Date: Wed, 20 Nov 2024 16:00:32 +0000 Subject: [PATCH 02/10] test: add tests with nulls --- .../{date-time.js => date-time.test.js} | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) rename test/integration/parsing/formula/{date-time.js => date-time.test.js} (72%) diff --git a/test/integration/parsing/formula/date-time.js b/test/integration/parsing/formula/date-time.test.js similarity index 72% rename from test/integration/parsing/formula/date-time.js rename to test/integration/parsing/formula/date-time.test.js index 94a8a1c..8f2bdbc 100644 --- a/test/integration/parsing/formula/date-time.js +++ b/test/integration/parsing/formula/date-time.test.js @@ -4,6 +4,9 @@ import Parser from '../../../../src/parser'; describe('.parse() date & time formulas', () => { it.each([new Parser(), ClickUpParser.create()])('DATE', (parser) => { expect(parser.parse('DATE()')).toMatchObject({ error: '#VALUE!', result: null }); + expect(parser.parse('DATE(null, 5, 12)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('DATE(2001, null, 12)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('DATE(2001, 5, null)')).toMatchObject({ error: null, result: null }); const { error, result } = parser.parse('DATE(2001, 5, 12)'); @@ -15,6 +18,7 @@ describe('.parse() date & time formulas', () => { it.each([new Parser(), ClickUpParser.create()])('DATEVALUE', (parser) => { expect(parser.parse('DATEVALUE()')).toMatchObject({ error: '#VALUE!', result: null }); + expect(parser.parse('DATEVALUE(null)')).toMatchObject({ error: null, result: null }); expect(parser.parse('DATEVALUE("1/1/1900")')).toMatchObject({ error: null, result: 1 }); expect(parser.parse('DATEVALUE("1/1/2000")')).toMatchObject({ error: null, result: 36526 }); }); @@ -24,6 +28,7 @@ describe('.parse() date & time formulas', () => { expect(parser.parse('DAY(1)')).toMatchObject({ error: null, result: 1 }); expect(parser.parse('DAY(2958465)')).toMatchObject({ error: null, result: 31 }); expect(parser.parse('DAY("2958465")')).toMatchObject({ error: null, result: 31 }); + expect(parser.parse('DAY(null)')).toMatchObject({ error: null, result: null }); }); it.each([new Parser(), ClickUpParser.create()])('DAYS', (parser) => { @@ -31,6 +36,9 @@ describe('.parse() date & time formulas', () => { expect(parser.parse('DAYS(1)')).toMatchObject({ error: '#VALUE!', result: null }); expect(parser.parse('DAYS(1, 6)')).toMatchObject({ error: null, result: -5 }); expect(parser.parse('DAYS("1/2/2000", "1/10/2001")')).toMatchObject({ error: null, result: -374 }); + expect(parser.parse('DAYS(null, 1)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('DAYS(1, null)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('DAYS(null, null)')).toMatchObject({ error: null, result: null }); }); it.each([new Parser(), ClickUpParser.create()])('DAYS360', (parser) => { @@ -39,23 +47,33 @@ describe('.parse() date & time formulas', () => { expect(parser.parse('DAYS360(1, 6)')).toMatchObject({ error: '#VALUE!', result: null }); expect(parser.parse('DAYS360("1/1/1901", "2/1/1901", TRUE)')).toMatchObject({ error: null, result: 30 }); expect(parser.parse('DAYS360("1/1/1901", "12/31/1901", FALSE)')).toMatchObject({ error: null, result: 360 }); + expect(parser.parse('DAYS360(null, "1/1/1901")')).toMatchObject({ error: null, result: null }); + expect(parser.parse('DAYS360("1/1/1901", null)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('DAYS360(null, null)')).toMatchObject({ error: null, result: null }); }); it.each([new Parser(), ClickUpParser.create()])('EDATE', (parser) => { expect(parser.parse('EDATE()')).toMatchObject({ error: '#VALUE!', result: null }); expect(parser.parse('EDATE(1)')).toMatchObject({ error: '#VALUE!', result: null }); expect(parser.parse('EDATE("1/1/1900", 1)')).toMatchObject({ error: null, result: 32 }); + expect(parser.parse('EDATE(null, 1)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('EDATE("1/1/1900", null)')).toMatchObject({ error: null, result: 1 }); + expect(parser.parse('EDATE(null, null)')).toMatchObject({ error: null, result: null }); }); it.each([new Parser(), ClickUpParser.create()])('EOMONTH', (parser) => { expect(parser.parse('EOMONTH()')).toMatchObject({ error: '#VALUE!', result: null }); expect(parser.parse('EOMONTH(1)')).toMatchObject({ error: '#VALUE!', result: null }); expect(parser.parse('EOMONTH("1/1/1900", 1)')).toMatchObject({ error: null, result: 59 }); + expect(parser.parse('EOMONTH(null, 1)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('EOMONTH("1/1/1900", null)')).toMatchObject({ error: null, result: 31 }); + expect(parser.parse('EOMONTH(null, null)')).toMatchObject({ error: null, result: null }); }); it.each([new Parser(), ClickUpParser.create()])('HOUR', (parser) => { expect(parser.parse('HOUR()')).toMatchObject({ error: '#VALUE!', result: null }); expect(parser.parse('HOUR("1/1/1900 16:33")')).toMatchObject({ error: null, result: 16 }); + expect(parser.parse('HOUR(null)')).toMatchObject({ error: null, result: null }); }); it.each([new Parser(), ClickUpParser.create()])('INTERVAL', (parser) => { @@ -64,24 +82,28 @@ describe('.parse() date & time formulas', () => { expect(parser.parse('INTERVAL(1)')).toMatchObject({ error: null, result: 'PT1S' }); expect(parser.parse('INTERVAL(60)')).toMatchObject({ error: null, result: 'PT1M' }); expect(parser.parse('INTERVAL(10000000)')).toMatchObject({ error: null, result: 'P3M25DT17H46M40S' }); + expect(parser.parse('INTERVAL(null)')).toMatchObject({ error: null, result: 'PT' }); }); it.each([new Parser(), ClickUpParser.create()])('ISOWEEKNUM', (parser) => { expect(parser.parse('ISOWEEKNUM()')).toMatchObject({ error: '#VALUE!', result: null }); expect(parser.parse('ISOWEEKNUM("1/8/1901")')).toMatchObject({ error: null, result: 2 }); expect(parser.parse('ISOWEEKNUM("6/6/1902")')).toMatchObject({ error: null, result: 23 }); + expect(parser.parse('ISOWEEKNUM(null)')).toMatchObject({ error: null, result: null }); }); it.each([new Parser(), ClickUpParser.create()])('MINUTE', (parser) => { expect(parser.parse('MINUTE()')).toMatchObject({ error: '#VALUE!', result: null }); expect(parser.parse('MINUTE("1/1/1901 1:01")')).toMatchObject({ error: null, result: 1 }); expect(parser.parse('MINUTE("1/1/1901 15:36")')).toMatchObject({ error: null, result: 36 }); + expect(parser.parse('MINUTE(null)')).toMatchObject({ error: null, result: null }); }); it.each([new Parser(), ClickUpParser.create()])('MONTH', (parser) => { expect(parser.parse('MONTH()')).toMatchObject({ error: '#VALUE!', result: null }); expect(parser.parse('MONTH("2/1/1901")')).toMatchObject({ error: null, result: 2 }); expect(parser.parse('MONTH("10/1/1901")')).toMatchObject({ error: null, result: 10 }); + expect(parser.parse('MONTH(null)')).toMatchObject({ error: null, result: null }); }); it.each([new Parser(), ClickUpParser.create()])('NETWORKDAYS', (parser) => { @@ -93,6 +115,9 @@ describe('.parse() date & time formulas', () => { error: null, result: 109, }); + expect(parser.parse('NETWORKDAYS(null, "2013-12-05")')).toMatchObject({ error: null, result: null }); + expect(parser.parse('NETWORKDAYS("2013-12-05", null)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('NETWORKDAYS(null, null)')).toMatchObject({ error: null, result: null }); }); it.each([new Parser(), ClickUpParser.create()])('NOW', (parser) => { @@ -106,6 +131,7 @@ describe('.parse() date & time formulas', () => { it.each([new Parser(), ClickUpParser.create()])('SECOND', (parser) => { expect(parser.parse('SECOND()')).toMatchObject({ error: '#VALUE!', result: null }); expect(parser.parse('SECOND("2/1/1901 13:33:12")')).toMatchObject({ error: null, result: 12 }); + expect(parser.parse('SECOND(null)')).toMatchObject({ error: null, result: null }); }); it.each([new Parser(), ClickUpParser.create()])('TIME', (parser) => { @@ -115,6 +141,9 @@ describe('.parse() date & time formulas', () => { expect(parser.parse('TIME(0, 0, 0)')).toMatchObject({ error: null, result: 0 }); expect(parser.parse('TIME(1, 1, 1)')).toMatchObject({ error: null, result: 0.04237268518518519 }); expect(parser.parse('TIME(24, 0, 0)')).toMatchObject({ error: null, result: 1 }); + expect(parser.parse('TIME(null, 0, 0)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('TIME(0, null, 0)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('TIME(0, 0, null)')).toMatchObject({ error: null, result: null }); }); it.each([new Parser(), ClickUpParser.create()])('TIMEVALUE', (parser) => { @@ -124,6 +153,7 @@ describe('.parse() date & time formulas', () => { error: null, result: 0.9583333333333334, }); + expect(parser.parse('TIMEVALUE(null)')).toMatchObject({ error: null, result: null }); }); it.each([new Parser(), ClickUpParser.create()])('TODAY', (parser) => { @@ -138,17 +168,21 @@ describe('.parse() date & time formulas', () => { expect(parser.parse('WEEKDAY()')).toMatchObject({ error: '#VALUE!', result: null }); expect(parser.parse('WEEKDAY("1/1/1901")')).toMatchObject({ error: null, result: 3 }); expect(parser.parse('WEEKDAY("1/1/1901", 2)')).toMatchObject({ error: null, result: 2 }); + expect(parser.parse('WEEKDAY(null)')).toMatchObject({ error: null, result: null }); }); it.each([new Parser(), ClickUpParser.create()])('WEEKNUM', (parser) => { expect(parser.parse('WEEKNUM()')).toMatchObject({ error: '#VALUE!', result: null }); expect(parser.parse('WEEKNUM("2/1/1900")')).toMatchObject({ error: null, result: 5 }); expect(parser.parse('WEEKNUM("2/1/1909", 2)')).toMatchObject({ error: null, result: 6 }); + expect(parser.parse('WEEKNUM(null)')).toMatchObject({ error: null, result: null }); }); it.each([new Parser(), ClickUpParser.create()])('WORKDAY', (parser) => { expect(parser.parse('WORKDAY()')).toMatchObject({ error: '#VALUE!', result: null }); expect(parser.parse('WORKDAY("1/1/1900")')).toMatchObject({ error: '#VALUE!', result: null }); + expect(parser.parse('WORKDAY(null)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('WORKDAY("1/1/1900", null)')).toMatchObject({ error: null, result: new Date(1900, 0, 1) }); const { result, error } = parser.parse('WORKDAY("1/1/1900", 1)'); @@ -160,6 +194,7 @@ describe('.parse() date & time formulas', () => { expect(parser.parse('YEAR()')).toMatchObject({ error: '#VALUE!', result: null }); expect(parser.parse('YEAR("1/1/1904")')).toMatchObject({ error: null, result: 1904 }); expect(parser.parse('YEAR("12/12/2001")')).toMatchObject({ error: null, result: 2001 }); + expect(parser.parse('YEAR(null)')).toMatchObject({ error: null, result: null }); }); it.each([new Parser(), ClickUpParser.create()])('YEARFRAC', (parser) => { @@ -169,5 +204,8 @@ describe('.parse() date & time formulas', () => { error: null, result: 0.002777777777777778, }); + expect(parser.parse('YEARFRAC(null, "1/2/1900")')).toMatchObject({ error: null, result: null }); + expect(parser.parse('YEARFRAC("1/1/1900", null)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('YEARFRAC(null, null)')).toMatchObject({ error: null, result: null }); }); }); From 12a328b83f3537e6cf496159925759406c430573 Mon Sep 17 00:00:00 2001 From: Michal Michalowski Date: Wed, 20 Nov 2024 16:04:07 +0000 Subject: [PATCH 03/10] feat: add proxy for date functions to handle null params --- src/clickup/formulajsProxy.ts | 51 +++++++++++++++++++ .../operator/formula-function.js | 2 +- src/helper/number.js | 2 +- 3 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 src/clickup/formulajsProxy.ts diff --git a/src/clickup/formulajsProxy.ts b/src/clickup/formulajsProxy.ts new file mode 100644 index 0000000..9610094 --- /dev/null +++ b/src/clickup/formulajsProxy.ts @@ -0,0 +1,51 @@ +import * as formulajs from '@formulajs/formulajs'; + +const isNull = (...args: unknown[]) => args.some((arg) => arg === null); +const nullToZero = (arg: unknown) => (isNull(arg) ? 0 : arg); + +const overrides = { + DATE: (year: unknown, month: unknown, day: unknown) => + isNull(year, month, day) ? null : formulajs.DATE(year, month, day), + DATEVALUE: (dateText: unknown) => (isNull(dateText) ? null : formulajs.DATEVALUE(dateText)), + DAY: (date: unknown) => (isNull(date) ? null : formulajs.DAY(date)), + DAYS: (startDate: unknown, endDate: unknown) => + isNull(startDate, endDate) ? null : formulajs.DAYS(startDate, endDate), + DAYS360: (startDate: unknown, endDate: unknown, method: unknown) => + isNull(startDate, endDate) ? null : formulajs.DAYS360(startDate, endDate, method), + EDATE: (startDate: unknown, months: unknown) => + isNull(startDate) ? null : formulajs.EDATE(startDate, nullToZero(months)), + EOMONTH: (startDate: unknown, months: unknown) => + isNull(startDate) ? null : formulajs.EOMONTH(startDate, nullToZero(months)), + HOUR: (date: unknown) => (isNull(date) ? null : formulajs.HOUR(date)), + INTERVAL: (seconds: unknown) => formulajs.INTERVAL(nullToZero(seconds)), + ISOWEEKNUM: (date: unknown) => (isNull(date) ? null : formulajs.ISOWEEKNUM(date)), + MINUTE: (serialNumber: unknown) => (isNull(serialNumber) ? null : formulajs.MINUTE(serialNumber)), + MONTH: (date: unknown) => (isNull(date) ? null : formulajs.MONTH(date)), + SECOND: (serialNumber: unknown) => (isNull(serialNumber) ? null : formulajs.SECOND(serialNumber)), + TIME: (hour: unknown, minute: unknown, second: unknown) => + isNull(hour, minute, second) ? null : formulajs.TIME(hour, minute, second), + TIMEVALUE: (timeText: unknown) => (isNull(timeText) ? null : formulajs.TIMEVALUE(timeText)), + WEEKDAY: (serialNumber: unknown, returnType: unknown) => + isNull(serialNumber) ? null : formulajs.WEEKDAY(serialNumber, returnType), + WEEKNUM: (serialNumber: unknown, returnType: unknown) => + isNull(serialNumber) ? null : formulajs.WEEKNUM(serialNumber, returnType), + YEAR: (date: unknown) => (isNull(date) ? null : formulajs.YEAR(date)), + YEARFRAC: (startDate: unknown, endDate: unknown, basis: unknown) => + isNull(startDate, endDate) ? null : formulajs.YEARFRAC(startDate, endDate, basis), + WORKDAY: (startDate: unknown, days: unknown, holidays: unknown) => + isNull(startDate) ? null : formulajs.WORKDAY(startDate, nullToZero(days), holidays), + NETWORKDAYS: (startDate: unknown, endDate: unknown, holidays: unknown) => + isNull(startDate, endDate) ? null : formulajs.NETWORKDAYS(startDate, endDate, holidays), +}; + +export const formulajsProxy = new Proxy(formulajs, { + get: (target, prop) => { + if (prop in overrides) { + return overrides[prop as keyof typeof overrides]; + } + if (prop in target) { + return target[prop]; + } + return null; + }, +}); diff --git a/src/evaluate-by-operator/operator/formula-function.js b/src/evaluate-by-operator/operator/formula-function.js index 8f2a11b..ef58f2b 100644 --- a/src/evaluate-by-operator/operator/formula-function.js +++ b/src/evaluate-by-operator/operator/formula-function.js @@ -1,4 +1,4 @@ -import * as formulajs from '@formulajs/formulajs'; +import { formulajsProxy as formulajs } from '../../clickup/formulajsProxy'; import SUPPORTED_FORMULAS from '../../supported-formulas'; import { ERROR_NAME } from '../../error'; diff --git a/src/helper/number.js b/src/helper/number.js index 8b902cd..a2da138 100644 --- a/src/helper/number.js +++ b/src/helper/number.js @@ -1,4 +1,4 @@ -import * as formulajs from '@formulajs/formulajs'; +import { formulajsProxy as formulajs } from '../clickup/formulajsProxy'; import splitFormula from './formula'; import { getNumberOfDaysSinceEpoch, isDate } from './date'; From 33b63f8d4308789305a28bbc541cd90ba2e6b7db Mon Sep 17 00:00:00 2001 From: Michal Michalowski Date: Wed, 20 Nov 2024 16:04:20 +0000 Subject: [PATCH 04/10] chore: update version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7668777..a39081f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@time-loop/hot-formula-parser", - "version": "4.1.2", + "version": "4.1.3", "description": "Formula parser", "type": "commonjs", "main": "dist/index.js", From 36a92abc5949636539604e1261518a3a3f7a635f Mon Sep 17 00:00:00 2001 From: Michal Michalowski Date: Wed, 20 Nov 2024 16:22:07 +0000 Subject: [PATCH 05/10] feat: return null to all falsy values --- src/clickup/formulajsProxy.ts | 45 +++++++------- .../parsing/formula/date-time.test.js | 60 +++++++++---------- 2 files changed, 53 insertions(+), 52 deletions(-) diff --git a/src/clickup/formulajsProxy.ts b/src/clickup/formulajsProxy.ts index 9610094..652c2ef 100644 --- a/src/clickup/formulajsProxy.ts +++ b/src/clickup/formulajsProxy.ts @@ -1,41 +1,42 @@ import * as formulajs from '@formulajs/formulajs'; -const isNull = (...args: unknown[]) => args.some((arg) => arg === null); -const nullToZero = (arg: unknown) => (isNull(arg) ? 0 : arg); +const isNil = (...args: unknown[]) => + args.some((arg) => arg === null || arg === undefined || arg === '' || arg === false); +const nullToZero = (arg: unknown) => (isNil(arg) ? 0 : arg); const overrides = { DATE: (year: unknown, month: unknown, day: unknown) => - isNull(year, month, day) ? null : formulajs.DATE(year, month, day), - DATEVALUE: (dateText: unknown) => (isNull(dateText) ? null : formulajs.DATEVALUE(dateText)), - DAY: (date: unknown) => (isNull(date) ? null : formulajs.DAY(date)), + isNil(year, month, day) ? null : formulajs.DATE(year, month, day), + DATEVALUE: (dateText: unknown) => (isNil(dateText) ? null : formulajs.DATEVALUE(dateText)), + DAY: (date: unknown) => (isNil(date) ? null : formulajs.DAY(date)), DAYS: (startDate: unknown, endDate: unknown) => - isNull(startDate, endDate) ? null : formulajs.DAYS(startDate, endDate), + isNil(startDate, endDate) ? null : formulajs.DAYS(startDate, endDate), DAYS360: (startDate: unknown, endDate: unknown, method: unknown) => - isNull(startDate, endDate) ? null : formulajs.DAYS360(startDate, endDate, method), + isNil(startDate, endDate) ? null : formulajs.DAYS360(startDate, endDate, method), EDATE: (startDate: unknown, months: unknown) => - isNull(startDate) ? null : formulajs.EDATE(startDate, nullToZero(months)), + isNil(startDate) ? null : formulajs.EDATE(startDate, nullToZero(months)), EOMONTH: (startDate: unknown, months: unknown) => - isNull(startDate) ? null : formulajs.EOMONTH(startDate, nullToZero(months)), - HOUR: (date: unknown) => (isNull(date) ? null : formulajs.HOUR(date)), + isNil(startDate) ? null : formulajs.EOMONTH(startDate, nullToZero(months)), + HOUR: (date: unknown) => (isNil(date) ? null : formulajs.HOUR(date)), INTERVAL: (seconds: unknown) => formulajs.INTERVAL(nullToZero(seconds)), - ISOWEEKNUM: (date: unknown) => (isNull(date) ? null : formulajs.ISOWEEKNUM(date)), - MINUTE: (serialNumber: unknown) => (isNull(serialNumber) ? null : formulajs.MINUTE(serialNumber)), - MONTH: (date: unknown) => (isNull(date) ? null : formulajs.MONTH(date)), - SECOND: (serialNumber: unknown) => (isNull(serialNumber) ? null : formulajs.SECOND(serialNumber)), + ISOWEEKNUM: (date: unknown) => (isNil(date) ? null : formulajs.ISOWEEKNUM(date)), + MINUTE: (serialNumber: unknown) => (isNil(serialNumber) ? null : formulajs.MINUTE(serialNumber)), + MONTH: (date: unknown) => (isNil(date) ? null : formulajs.MONTH(date)), + SECOND: (serialNumber: unknown) => (isNil(serialNumber) ? null : formulajs.SECOND(serialNumber)), TIME: (hour: unknown, minute: unknown, second: unknown) => - isNull(hour, minute, second) ? null : formulajs.TIME(hour, minute, second), - TIMEVALUE: (timeText: unknown) => (isNull(timeText) ? null : formulajs.TIMEVALUE(timeText)), + isNil(hour, minute, second) ? null : formulajs.TIME(hour, minute, second), + TIMEVALUE: (timeText: unknown) => (isNil(timeText) ? null : formulajs.TIMEVALUE(timeText)), WEEKDAY: (serialNumber: unknown, returnType: unknown) => - isNull(serialNumber) ? null : formulajs.WEEKDAY(serialNumber, returnType), + isNil(serialNumber) ? null : formulajs.WEEKDAY(serialNumber, returnType), WEEKNUM: (serialNumber: unknown, returnType: unknown) => - isNull(serialNumber) ? null : formulajs.WEEKNUM(serialNumber, returnType), - YEAR: (date: unknown) => (isNull(date) ? null : formulajs.YEAR(date)), + isNil(serialNumber) ? null : formulajs.WEEKNUM(serialNumber, returnType), + YEAR: (date: unknown) => (isNil(date) ? null : formulajs.YEAR(date)), YEARFRAC: (startDate: unknown, endDate: unknown, basis: unknown) => - isNull(startDate, endDate) ? null : formulajs.YEARFRAC(startDate, endDate, basis), + isNil(startDate, endDate) ? null : formulajs.YEARFRAC(startDate, endDate, basis), WORKDAY: (startDate: unknown, days: unknown, holidays: unknown) => - isNull(startDate) ? null : formulajs.WORKDAY(startDate, nullToZero(days), holidays), + isNil(startDate) ? null : formulajs.WORKDAY(startDate, nullToZero(days), holidays), NETWORKDAYS: (startDate: unknown, endDate: unknown, holidays: unknown) => - isNull(startDate, endDate) ? null : formulajs.NETWORKDAYS(startDate, endDate, holidays), + isNil(startDate, endDate) ? null : formulajs.NETWORKDAYS(startDate, endDate, holidays), }; export const formulajsProxy = new Proxy(formulajs, { diff --git a/test/integration/parsing/formula/date-time.test.js b/test/integration/parsing/formula/date-time.test.js index 8f2bdbc..588c43c 100644 --- a/test/integration/parsing/formula/date-time.test.js +++ b/test/integration/parsing/formula/date-time.test.js @@ -3,7 +3,7 @@ import Parser from '../../../../src/parser'; describe('.parse() date & time formulas', () => { it.each([new Parser(), ClickUpParser.create()])('DATE', (parser) => { - expect(parser.parse('DATE()')).toMatchObject({ error: '#VALUE!', result: null }); + expect(parser.parse('DATE()')).toMatchObject({ error: null, result: null }); expect(parser.parse('DATE(null, 5, 12)')).toMatchObject({ error: null, result: null }); expect(parser.parse('DATE(2001, null, 12)')).toMatchObject({ error: null, result: null }); expect(parser.parse('DATE(2001, 5, null)')).toMatchObject({ error: null, result: null }); @@ -17,14 +17,14 @@ describe('.parse() date & time formulas', () => { }); it.each([new Parser(), ClickUpParser.create()])('DATEVALUE', (parser) => { - expect(parser.parse('DATEVALUE()')).toMatchObject({ error: '#VALUE!', result: null }); + expect(parser.parse('DATEVALUE()')).toMatchObject({ error: null, result: null }); expect(parser.parse('DATEVALUE(null)')).toMatchObject({ error: null, result: null }); expect(parser.parse('DATEVALUE("1/1/1900")')).toMatchObject({ error: null, result: 1 }); expect(parser.parse('DATEVALUE("1/1/2000")')).toMatchObject({ error: null, result: 36526 }); }); it.each([new Parser(), ClickUpParser.create()])('DAY', (parser) => { - expect(parser.parse('DAY()')).toMatchObject({ error: '#VALUE!', result: null }); + expect(parser.parse('DAY()')).toMatchObject({ error: null, result: null }); expect(parser.parse('DAY(1)')).toMatchObject({ error: null, result: 1 }); expect(parser.parse('DAY(2958465)')).toMatchObject({ error: null, result: 31 }); expect(parser.parse('DAY("2958465")')).toMatchObject({ error: null, result: 31 }); @@ -32,8 +32,8 @@ describe('.parse() date & time formulas', () => { }); it.each([new Parser(), ClickUpParser.create()])('DAYS', (parser) => { - expect(parser.parse('DAYS()')).toMatchObject({ error: '#VALUE!', result: null }); - expect(parser.parse('DAYS(1)')).toMatchObject({ error: '#VALUE!', result: null }); + expect(parser.parse('DAYS()')).toMatchObject({ error: null, result: null }); + expect(parser.parse('DAYS(1)')).toMatchObject({ error: null, result: null }); expect(parser.parse('DAYS(1, 6)')).toMatchObject({ error: null, result: -5 }); expect(parser.parse('DAYS("1/2/2000", "1/10/2001")')).toMatchObject({ error: null, result: -374 }); expect(parser.parse('DAYS(null, 1)')).toMatchObject({ error: null, result: null }); @@ -42,8 +42,8 @@ describe('.parse() date & time formulas', () => { }); it.each([new Parser(), ClickUpParser.create()])('DAYS360', (parser) => { - expect(parser.parse('DAYS360()')).toMatchObject({ error: '#VALUE!', result: null }); - expect(parser.parse('DAYS360(1)')).toMatchObject({ error: '#VALUE!', result: null }); + expect(parser.parse('DAYS360()')).toMatchObject({ error: null, result: null }); + expect(parser.parse('DAYS360(1)')).toMatchObject({ error: null, result: null }); expect(parser.parse('DAYS360(1, 6)')).toMatchObject({ error: '#VALUE!', result: null }); expect(parser.parse('DAYS360("1/1/1901", "2/1/1901", TRUE)')).toMatchObject({ error: null, result: 30 }); expect(parser.parse('DAYS360("1/1/1901", "12/31/1901", FALSE)')).toMatchObject({ error: null, result: 360 }); @@ -53,8 +53,8 @@ describe('.parse() date & time formulas', () => { }); it.each([new Parser(), ClickUpParser.create()])('EDATE', (parser) => { - expect(parser.parse('EDATE()')).toMatchObject({ error: '#VALUE!', result: null }); - expect(parser.parse('EDATE(1)')).toMatchObject({ error: '#VALUE!', result: null }); + expect(parser.parse('EDATE()')).toMatchObject({ error: null, result: null }); + expect(parser.parse('EDATE(1)')).toMatchObject({ error: null, result: 1 }); expect(parser.parse('EDATE("1/1/1900", 1)')).toMatchObject({ error: null, result: 32 }); expect(parser.parse('EDATE(null, 1)')).toMatchObject({ error: null, result: null }); expect(parser.parse('EDATE("1/1/1900", null)')).toMatchObject({ error: null, result: 1 }); @@ -62,8 +62,8 @@ describe('.parse() date & time formulas', () => { }); it.each([new Parser(), ClickUpParser.create()])('EOMONTH', (parser) => { - expect(parser.parse('EOMONTH()')).toMatchObject({ error: '#VALUE!', result: null }); - expect(parser.parse('EOMONTH(1)')).toMatchObject({ error: '#VALUE!', result: null }); + expect(parser.parse('EOMONTH()')).toMatchObject({ error: null, result: null }); + expect(parser.parse('EOMONTH(1)')).toMatchObject({ error: null, result: 31 }); expect(parser.parse('EOMONTH("1/1/1900", 1)')).toMatchObject({ error: null, result: 59 }); expect(parser.parse('EOMONTH(null, 1)')).toMatchObject({ error: null, result: null }); expect(parser.parse('EOMONTH("1/1/1900", null)')).toMatchObject({ error: null, result: 31 }); @@ -71,13 +71,13 @@ describe('.parse() date & time formulas', () => { }); it.each([new Parser(), ClickUpParser.create()])('HOUR', (parser) => { - expect(parser.parse('HOUR()')).toMatchObject({ error: '#VALUE!', result: null }); + expect(parser.parse('HOUR()')).toMatchObject({ error: null, result: null }); expect(parser.parse('HOUR("1/1/1900 16:33")')).toMatchObject({ error: null, result: 16 }); expect(parser.parse('HOUR(null)')).toMatchObject({ error: null, result: null }); }); it.each([new Parser(), ClickUpParser.create()])('INTERVAL', (parser) => { - expect(parser.parse('INTERVAL()')).toMatchObject({ error: '#VALUE!', result: null }); + expect(parser.parse('INTERVAL()')).toMatchObject({ error: null, result: 'PT' }); expect(parser.parse('INTERVAL(0)')).toMatchObject({ error: null, result: 'PT' }); expect(parser.parse('INTERVAL(1)')).toMatchObject({ error: null, result: 'PT1S' }); expect(parser.parse('INTERVAL(60)')).toMatchObject({ error: null, result: 'PT1M' }); @@ -86,29 +86,29 @@ describe('.parse() date & time formulas', () => { }); it.each([new Parser(), ClickUpParser.create()])('ISOWEEKNUM', (parser) => { - expect(parser.parse('ISOWEEKNUM()')).toMatchObject({ error: '#VALUE!', result: null }); + expect(parser.parse('ISOWEEKNUM()')).toMatchObject({ error: null, result: null }); expect(parser.parse('ISOWEEKNUM("1/8/1901")')).toMatchObject({ error: null, result: 2 }); expect(parser.parse('ISOWEEKNUM("6/6/1902")')).toMatchObject({ error: null, result: 23 }); expect(parser.parse('ISOWEEKNUM(null)')).toMatchObject({ error: null, result: null }); }); it.each([new Parser(), ClickUpParser.create()])('MINUTE', (parser) => { - expect(parser.parse('MINUTE()')).toMatchObject({ error: '#VALUE!', result: null }); + expect(parser.parse('MINUTE()')).toMatchObject({ error: null, result: null }); expect(parser.parse('MINUTE("1/1/1901 1:01")')).toMatchObject({ error: null, result: 1 }); expect(parser.parse('MINUTE("1/1/1901 15:36")')).toMatchObject({ error: null, result: 36 }); expect(parser.parse('MINUTE(null)')).toMatchObject({ error: null, result: null }); }); it.each([new Parser(), ClickUpParser.create()])('MONTH', (parser) => { - expect(parser.parse('MONTH()')).toMatchObject({ error: '#VALUE!', result: null }); + expect(parser.parse('MONTH()')).toMatchObject({ error: null, result: null }); expect(parser.parse('MONTH("2/1/1901")')).toMatchObject({ error: null, result: 2 }); expect(parser.parse('MONTH("10/1/1901")')).toMatchObject({ error: null, result: 10 }); expect(parser.parse('MONTH(null)')).toMatchObject({ error: null, result: null }); }); it.each([new Parser(), ClickUpParser.create()])('NETWORKDAYS', (parser) => { - expect(parser.parse('NETWORKDAYS()')).toMatchObject({ error: '#VALUE!', result: null }); - expect(parser.parse('NETWORKDAYS("2/1/1901")')).toMatchObject({ error: '#VALUE!', result: null }); + expect(parser.parse('NETWORKDAYS()')).toMatchObject({ error: null, result: null }); + expect(parser.parse('NETWORKDAYS("2/1/1901")')).toMatchObject({ error: null, result: null }); expect(parser.parse('NETWORKDAYS("2013-12-04", "2013-12-05")')).toMatchObject({ error: null, result: 2 }); expect(parser.parse('NETWORKDAYS("2013-11-04", "2013-12-05")')).toMatchObject({ error: null, result: 24 }); expect(parser.parse('NETWORKDAYS("10/1/2012", "3/1/2013", [\'11/22/2012\'])')).toMatchObject({ @@ -129,15 +129,15 @@ describe('.parse() date & time formulas', () => { }); it.each([new Parser(), ClickUpParser.create()])('SECOND', (parser) => { - expect(parser.parse('SECOND()')).toMatchObject({ error: '#VALUE!', result: null }); + expect(parser.parse('SECOND()')).toMatchObject({ error: null, result: null }); expect(parser.parse('SECOND("2/1/1901 13:33:12")')).toMatchObject({ error: null, result: 12 }); expect(parser.parse('SECOND(null)')).toMatchObject({ error: null, result: null }); }); it.each([new Parser(), ClickUpParser.create()])('TIME', (parser) => { - expect(parser.parse('TIME()')).toMatchObject({ error: '#VALUE!', result: null }); - expect(parser.parse('TIME(0)')).toMatchObject({ error: '#VALUE!', result: null }); - expect(parser.parse('TIME(0, 0)')).toMatchObject({ error: '#VALUE!', result: null }); + expect(parser.parse('TIME()')).toMatchObject({ error: null, result: null }); + expect(parser.parse('TIME(0)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('TIME(0, 0)')).toMatchObject({ error: null, result: null }); expect(parser.parse('TIME(0, 0, 0)')).toMatchObject({ error: null, result: 0 }); expect(parser.parse('TIME(1, 1, 1)')).toMatchObject({ error: null, result: 0.04237268518518519 }); expect(parser.parse('TIME(24, 0, 0)')).toMatchObject({ error: null, result: 1 }); @@ -147,7 +147,7 @@ describe('.parse() date & time formulas', () => { }); it.each([new Parser(), ClickUpParser.create()])('TIMEVALUE', (parser) => { - expect(parser.parse('TIMEVALUE()')).toMatchObject({ error: '#VALUE!', result: null }); + expect(parser.parse('TIMEVALUE()')).toMatchObject({ error: null, result: null }); expect(parser.parse('TIMEVALUE("1/1/1900 00:00:00")')).toMatchObject({ error: null, result: 0 }); expect(parser.parse('TIMEVALUE("1/1/1900 23:00:00")')).toMatchObject({ error: null, @@ -165,22 +165,22 @@ describe('.parse() date & time formulas', () => { }); it.each([new Parser(), ClickUpParser.create()])('WEEKDAY', (parser) => { - expect(parser.parse('WEEKDAY()')).toMatchObject({ error: '#VALUE!', result: null }); + expect(parser.parse('WEEKDAY()')).toMatchObject({ error: null, result: null }); expect(parser.parse('WEEKDAY("1/1/1901")')).toMatchObject({ error: null, result: 3 }); expect(parser.parse('WEEKDAY("1/1/1901", 2)')).toMatchObject({ error: null, result: 2 }); expect(parser.parse('WEEKDAY(null)')).toMatchObject({ error: null, result: null }); }); it.each([new Parser(), ClickUpParser.create()])('WEEKNUM', (parser) => { - expect(parser.parse('WEEKNUM()')).toMatchObject({ error: '#VALUE!', result: null }); + expect(parser.parse('WEEKNUM()')).toMatchObject({ error: null, result: null }); expect(parser.parse('WEEKNUM("2/1/1900")')).toMatchObject({ error: null, result: 5 }); expect(parser.parse('WEEKNUM("2/1/1909", 2)')).toMatchObject({ error: null, result: 6 }); expect(parser.parse('WEEKNUM(null)')).toMatchObject({ error: null, result: null }); }); it.each([new Parser(), ClickUpParser.create()])('WORKDAY', (parser) => { - expect(parser.parse('WORKDAY()')).toMatchObject({ error: '#VALUE!', result: null }); - expect(parser.parse('WORKDAY("1/1/1900")')).toMatchObject({ error: '#VALUE!', result: null }); + expect(parser.parse('WORKDAY()')).toMatchObject({ error: null, result: null }); + expect(parser.parse('WORKDAY("1/1/1900")')).toMatchObject({ error: null, result: new Date(1900, 0, 1) }); expect(parser.parse('WORKDAY(null)')).toMatchObject({ error: null, result: null }); expect(parser.parse('WORKDAY("1/1/1900", null)')).toMatchObject({ error: null, result: new Date(1900, 0, 1) }); @@ -191,15 +191,15 @@ describe('.parse() date & time formulas', () => { }); it.each([new Parser(), ClickUpParser.create()])('YEAR', (parser) => { - expect(parser.parse('YEAR()')).toMatchObject({ error: '#VALUE!', result: null }); + expect(parser.parse('YEAR()')).toMatchObject({ error: null, result: null }); expect(parser.parse('YEAR("1/1/1904")')).toMatchObject({ error: null, result: 1904 }); expect(parser.parse('YEAR("12/12/2001")')).toMatchObject({ error: null, result: 2001 }); expect(parser.parse('YEAR(null)')).toMatchObject({ error: null, result: null }); }); it.each([new Parser(), ClickUpParser.create()])('YEARFRAC', (parser) => { - expect(parser.parse('YEARFRAC()')).toMatchObject({ error: '#VALUE!', result: null }); - expect(parser.parse('YEARFRAC("1/1/1904")')).toMatchObject({ error: '#VALUE!', result: null }); + expect(parser.parse('YEARFRAC()')).toMatchObject({ error: null, result: null }); + expect(parser.parse('YEARFRAC("1/1/1904")')).toMatchObject({ error: null, result: null }); expect(parser.parse('YEARFRAC("1/1/1900", "1/2/1900")')).toMatchObject({ error: null, result: 0.002777777777777778, From 15adcee64cc957c0d03cd402ba1e0fa8fb571e6c Mon Sep 17 00:00:00 2001 From: Michal Michalowski Date: Wed, 20 Nov 2024 16:27:29 +0000 Subject: [PATCH 06/10] chore: update version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5f2e055..226826f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@time-loop/hot-formula-parser", - "version": "4.1.2", + "version": "4.2.0-beta.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@time-loop/hot-formula-parser", - "version": "4.1.2", + "version": "4.2.0-beta.1", "dependencies": { "@formulajs/formulajs": "^2.3.0", "json5": "^2.2.3", diff --git a/package.json b/package.json index a39081f..38f3351 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@time-loop/hot-formula-parser", - "version": "4.1.3", + "version": "4.2.0-beta.1", "description": "Formula parser", "type": "commonjs", "main": "dist/index.js", From e0e5d232b2088ab05d98754e05f6a33498a73374 Mon Sep 17 00:00:00 2001 From: Michal Michalowski Date: Wed, 20 Nov 2024 17:27:34 +0000 Subject: [PATCH 07/10] chore: version update --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 226826f..f52cab1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@time-loop/hot-formula-parser", - "version": "4.2.0-beta.1", + "version": "4.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@time-loop/hot-formula-parser", - "version": "4.2.0-beta.1", + "version": "4.2.0", "dependencies": { "@formulajs/formulajs": "^2.3.0", "json5": "^2.2.3", diff --git a/package.json b/package.json index 38f3351..b0006c4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@time-loop/hot-formula-parser", - "version": "4.2.0-beta.1", + "version": "4.2.0", "description": "Formula parser", "type": "commonjs", "main": "dist/index.js", From 0a38864069996a26932029ea8a42cf8090686e91 Mon Sep 17 00:00:00 2001 From: Michal Michalowski Date: Thu, 21 Nov 2024 09:49:13 +0000 Subject: [PATCH 08/10] chore: use Number.NaN as an empty result --- src/clickup/formulajsProxy.ts | 40 +++--- .../parsing/formula/date-time.test.js | 121 +++++++++--------- 2 files changed, 81 insertions(+), 80 deletions(-) diff --git a/src/clickup/formulajsProxy.ts b/src/clickup/formulajsProxy.ts index 652c2ef..b6f6fd5 100644 --- a/src/clickup/formulajsProxy.ts +++ b/src/clickup/formulajsProxy.ts @@ -6,37 +6,37 @@ const nullToZero = (arg: unknown) => (isNil(arg) ? 0 : arg); const overrides = { DATE: (year: unknown, month: unknown, day: unknown) => - isNil(year, month, day) ? null : formulajs.DATE(year, month, day), - DATEVALUE: (dateText: unknown) => (isNil(dateText) ? null : formulajs.DATEVALUE(dateText)), - DAY: (date: unknown) => (isNil(date) ? null : formulajs.DAY(date)), + isNil(year, month, day) ? Number.NaN : formulajs.DATE(year, month, day), + DATEVALUE: (dateText: unknown) => (isNil(dateText) ? Number.NaN : formulajs.DATEVALUE(dateText)), + DAY: (date: unknown) => (isNil(date) ? Number.NaN : formulajs.DAY(date)), DAYS: (startDate: unknown, endDate: unknown) => - isNil(startDate, endDate) ? null : formulajs.DAYS(startDate, endDate), + isNil(startDate, endDate) ? Number.NaN : formulajs.DAYS(startDate, endDate), DAYS360: (startDate: unknown, endDate: unknown, method: unknown) => - isNil(startDate, endDate) ? null : formulajs.DAYS360(startDate, endDate, method), + isNil(startDate, endDate) ? Number.NaN : formulajs.DAYS360(startDate, endDate, method), EDATE: (startDate: unknown, months: unknown) => - isNil(startDate) ? null : formulajs.EDATE(startDate, nullToZero(months)), + isNil(startDate) ? Number.NaN : formulajs.EDATE(startDate, nullToZero(months)), EOMONTH: (startDate: unknown, months: unknown) => - isNil(startDate) ? null : formulajs.EOMONTH(startDate, nullToZero(months)), - HOUR: (date: unknown) => (isNil(date) ? null : formulajs.HOUR(date)), + isNil(startDate) ? Number.NaN : formulajs.EOMONTH(startDate, nullToZero(months)), + HOUR: (date: unknown) => (isNil(date) ? Number.NaN : formulajs.HOUR(date)), INTERVAL: (seconds: unknown) => formulajs.INTERVAL(nullToZero(seconds)), - ISOWEEKNUM: (date: unknown) => (isNil(date) ? null : formulajs.ISOWEEKNUM(date)), - MINUTE: (serialNumber: unknown) => (isNil(serialNumber) ? null : formulajs.MINUTE(serialNumber)), - MONTH: (date: unknown) => (isNil(date) ? null : formulajs.MONTH(date)), - SECOND: (serialNumber: unknown) => (isNil(serialNumber) ? null : formulajs.SECOND(serialNumber)), + ISOWEEKNUM: (date: unknown) => (isNil(date) ? Number.NaN : formulajs.ISOWEEKNUM(date)), + MINUTE: (serialNumber: unknown) => (isNil(serialNumber) ? Number.NaN : formulajs.MINUTE(serialNumber)), + MONTH: (date: unknown) => (isNil(date) ? Number.NaN : formulajs.MONTH(date)), + SECOND: (serialNumber: unknown) => (isNil(serialNumber) ? Number.NaN : formulajs.SECOND(serialNumber)), TIME: (hour: unknown, minute: unknown, second: unknown) => - isNil(hour, minute, second) ? null : formulajs.TIME(hour, minute, second), - TIMEVALUE: (timeText: unknown) => (isNil(timeText) ? null : formulajs.TIMEVALUE(timeText)), + isNil(hour, minute, second) ? Number.NaN : formulajs.TIME(hour, minute, second), + TIMEVALUE: (timeText: unknown) => (isNil(timeText) ? Number.NaN : formulajs.TIMEVALUE(timeText)), WEEKDAY: (serialNumber: unknown, returnType: unknown) => - isNil(serialNumber) ? null : formulajs.WEEKDAY(serialNumber, returnType), + isNil(serialNumber) ? Number.NaN : formulajs.WEEKDAY(serialNumber, returnType), WEEKNUM: (serialNumber: unknown, returnType: unknown) => - isNil(serialNumber) ? null : formulajs.WEEKNUM(serialNumber, returnType), - YEAR: (date: unknown) => (isNil(date) ? null : formulajs.YEAR(date)), + isNil(serialNumber) ? Number.NaN : formulajs.WEEKNUM(serialNumber, returnType), + YEAR: (date: unknown) => (isNil(date) ? Number.NaN : formulajs.YEAR(date)), YEARFRAC: (startDate: unknown, endDate: unknown, basis: unknown) => - isNil(startDate, endDate) ? null : formulajs.YEARFRAC(startDate, endDate, basis), + isNil(startDate, endDate) ? Number.NaN : formulajs.YEARFRAC(startDate, endDate, basis), WORKDAY: (startDate: unknown, days: unknown, holidays: unknown) => - isNil(startDate) ? null : formulajs.WORKDAY(startDate, nullToZero(days), holidays), + isNil(startDate) ? Number.NaN : formulajs.WORKDAY(startDate, nullToZero(days), holidays), NETWORKDAYS: (startDate: unknown, endDate: unknown, holidays: unknown) => - isNil(startDate, endDate) ? null : formulajs.NETWORKDAYS(startDate, endDate, holidays), + isNil(startDate, endDate) ? Number.NaN : formulajs.NETWORKDAYS(startDate, endDate, holidays), }; export const formulajsProxy = new Proxy(formulajs, { diff --git a/test/integration/parsing/formula/date-time.test.js b/test/integration/parsing/formula/date-time.test.js index 588c43c..4721b1c 100644 --- a/test/integration/parsing/formula/date-time.test.js +++ b/test/integration/parsing/formula/date-time.test.js @@ -3,10 +3,10 @@ import Parser from '../../../../src/parser'; describe('.parse() date & time formulas', () => { it.each([new Parser(), ClickUpParser.create()])('DATE', (parser) => { - expect(parser.parse('DATE()')).toMatchObject({ error: null, result: null }); - expect(parser.parse('DATE(null, 5, 12)')).toMatchObject({ error: null, result: null }); - expect(parser.parse('DATE(2001, null, 12)')).toMatchObject({ error: null, result: null }); - expect(parser.parse('DATE(2001, 5, null)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('DATE()')).toMatchObject({ error: null, result: Number.NaN }); + expect(parser.parse('DATE(null, 5, 12)')).toMatchObject({ error: null, result: Number.NaN }); + expect(parser.parse('DATE(2001, null, 12)')).toMatchObject({ error: null, result: Number.NaN }); + expect(parser.parse('DATE(2001, 5, null)')).toMatchObject({ error: null, result: Number.NaN }); const { error, result } = parser.parse('DATE(2001, 5, 12)'); @@ -17,63 +17,63 @@ describe('.parse() date & time formulas', () => { }); it.each([new Parser(), ClickUpParser.create()])('DATEVALUE', (parser) => { - expect(parser.parse('DATEVALUE()')).toMatchObject({ error: null, result: null }); - expect(parser.parse('DATEVALUE(null)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('DATEVALUE()')).toMatchObject({ error: null, result: Number.NaN }); + expect(parser.parse('DATEVALUE(null)')).toMatchObject({ error: null, result: Number.NaN }); expect(parser.parse('DATEVALUE("1/1/1900")')).toMatchObject({ error: null, result: 1 }); expect(parser.parse('DATEVALUE("1/1/2000")')).toMatchObject({ error: null, result: 36526 }); }); it.each([new Parser(), ClickUpParser.create()])('DAY', (parser) => { - expect(parser.parse('DAY()')).toMatchObject({ error: null, result: null }); + expect(parser.parse('DAY()')).toMatchObject({ error: null, result: Number.NaN }); expect(parser.parse('DAY(1)')).toMatchObject({ error: null, result: 1 }); expect(parser.parse('DAY(2958465)')).toMatchObject({ error: null, result: 31 }); expect(parser.parse('DAY("2958465")')).toMatchObject({ error: null, result: 31 }); - expect(parser.parse('DAY(null)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('DAY(null)')).toMatchObject({ error: null, result: Number.NaN }); }); it.each([new Parser(), ClickUpParser.create()])('DAYS', (parser) => { - expect(parser.parse('DAYS()')).toMatchObject({ error: null, result: null }); - expect(parser.parse('DAYS(1)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('DAYS()')).toMatchObject({ error: null, result: Number.NaN }); + expect(parser.parse('DAYS(1)')).toMatchObject({ error: null, result: Number.NaN }); expect(parser.parse('DAYS(1, 6)')).toMatchObject({ error: null, result: -5 }); expect(parser.parse('DAYS("1/2/2000", "1/10/2001")')).toMatchObject({ error: null, result: -374 }); - expect(parser.parse('DAYS(null, 1)')).toMatchObject({ error: null, result: null }); - expect(parser.parse('DAYS(1, null)')).toMatchObject({ error: null, result: null }); - expect(parser.parse('DAYS(null, null)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('DAYS(null, 1)')).toMatchObject({ error: null, result: Number.NaN }); + expect(parser.parse('DAYS(1, null)')).toMatchObject({ error: null, result: Number.NaN }); + expect(parser.parse('DAYS(null, null)')).toMatchObject({ error: null, result: Number.NaN }); }); it.each([new Parser(), ClickUpParser.create()])('DAYS360', (parser) => { - expect(parser.parse('DAYS360()')).toMatchObject({ error: null, result: null }); - expect(parser.parse('DAYS360(1)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('DAYS360()')).toMatchObject({ error: null, result: Number.NaN }); + expect(parser.parse('DAYS360(1)')).toMatchObject({ error: null, result: Number.NaN }); expect(parser.parse('DAYS360(1, 6)')).toMatchObject({ error: '#VALUE!', result: null }); expect(parser.parse('DAYS360("1/1/1901", "2/1/1901", TRUE)')).toMatchObject({ error: null, result: 30 }); expect(parser.parse('DAYS360("1/1/1901", "12/31/1901", FALSE)')).toMatchObject({ error: null, result: 360 }); - expect(parser.parse('DAYS360(null, "1/1/1901")')).toMatchObject({ error: null, result: null }); - expect(parser.parse('DAYS360("1/1/1901", null)')).toMatchObject({ error: null, result: null }); - expect(parser.parse('DAYS360(null, null)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('DAYS360(null, "1/1/1901")')).toMatchObject({ error: null, result: Number.NaN }); + expect(parser.parse('DAYS360("1/1/1901", null)')).toMatchObject({ error: null, result: Number.NaN }); + expect(parser.parse('DAYS360(null, null)')).toMatchObject({ error: null, result: Number.NaN }); }); it.each([new Parser(), ClickUpParser.create()])('EDATE', (parser) => { - expect(parser.parse('EDATE()')).toMatchObject({ error: null, result: null }); + expect(parser.parse('EDATE()')).toMatchObject({ error: null, result: Number.NaN }); expect(parser.parse('EDATE(1)')).toMatchObject({ error: null, result: 1 }); expect(parser.parse('EDATE("1/1/1900", 1)')).toMatchObject({ error: null, result: 32 }); - expect(parser.parse('EDATE(null, 1)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('EDATE(null, 1)')).toMatchObject({ error: null, result: Number.NaN }); expect(parser.parse('EDATE("1/1/1900", null)')).toMatchObject({ error: null, result: 1 }); - expect(parser.parse('EDATE(null, null)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('EDATE(null, null)')).toMatchObject({ error: null, result: Number.NaN }); }); it.each([new Parser(), ClickUpParser.create()])('EOMONTH', (parser) => { - expect(parser.parse('EOMONTH()')).toMatchObject({ error: null, result: null }); + expect(parser.parse('EOMONTH()')).toMatchObject({ error: null, result: Number.NaN }); expect(parser.parse('EOMONTH(1)')).toMatchObject({ error: null, result: 31 }); expect(parser.parse('EOMONTH("1/1/1900", 1)')).toMatchObject({ error: null, result: 59 }); - expect(parser.parse('EOMONTH(null, 1)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('EOMONTH(null, 1)')).toMatchObject({ error: null, result: Number.NaN }); expect(parser.parse('EOMONTH("1/1/1900", null)')).toMatchObject({ error: null, result: 31 }); - expect(parser.parse('EOMONTH(null, null)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('EOMONTH(null, null)')).toMatchObject({ error: null, result: Number.NaN }); }); it.each([new Parser(), ClickUpParser.create()])('HOUR', (parser) => { - expect(parser.parse('HOUR()')).toMatchObject({ error: null, result: null }); + expect(parser.parse('HOUR()')).toMatchObject({ error: null, result: Number.NaN }); expect(parser.parse('HOUR("1/1/1900 16:33")')).toMatchObject({ error: null, result: 16 }); - expect(parser.parse('HOUR(null)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('HOUR(null)')).toMatchObject({ error: null, result: Number.NaN }); }); it.each([new Parser(), ClickUpParser.create()])('INTERVAL', (parser) => { @@ -86,38 +86,38 @@ describe('.parse() date & time formulas', () => { }); it.each([new Parser(), ClickUpParser.create()])('ISOWEEKNUM', (parser) => { - expect(parser.parse('ISOWEEKNUM()')).toMatchObject({ error: null, result: null }); + expect(parser.parse('ISOWEEKNUM()')).toMatchObject({ error: null, result: Number.NaN }); expect(parser.parse('ISOWEEKNUM("1/8/1901")')).toMatchObject({ error: null, result: 2 }); expect(parser.parse('ISOWEEKNUM("6/6/1902")')).toMatchObject({ error: null, result: 23 }); - expect(parser.parse('ISOWEEKNUM(null)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('ISOWEEKNUM(null)')).toMatchObject({ error: null, result: Number.NaN }); }); it.each([new Parser(), ClickUpParser.create()])('MINUTE', (parser) => { - expect(parser.parse('MINUTE()')).toMatchObject({ error: null, result: null }); + expect(parser.parse('MINUTE()')).toMatchObject({ error: null, result: Number.NaN }); expect(parser.parse('MINUTE("1/1/1901 1:01")')).toMatchObject({ error: null, result: 1 }); expect(parser.parse('MINUTE("1/1/1901 15:36")')).toMatchObject({ error: null, result: 36 }); - expect(parser.parse('MINUTE(null)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('MINUTE(null)')).toMatchObject({ error: null, result: Number.NaN }); }); it.each([new Parser(), ClickUpParser.create()])('MONTH', (parser) => { - expect(parser.parse('MONTH()')).toMatchObject({ error: null, result: null }); + expect(parser.parse('MONTH()')).toMatchObject({ error: null, result: Number.NaN }); expect(parser.parse('MONTH("2/1/1901")')).toMatchObject({ error: null, result: 2 }); expect(parser.parse('MONTH("10/1/1901")')).toMatchObject({ error: null, result: 10 }); - expect(parser.parse('MONTH(null)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('MONTH(null)')).toMatchObject({ error: null, result: Number.NaN }); }); it.each([new Parser(), ClickUpParser.create()])('NETWORKDAYS', (parser) => { - expect(parser.parse('NETWORKDAYS()')).toMatchObject({ error: null, result: null }); - expect(parser.parse('NETWORKDAYS("2/1/1901")')).toMatchObject({ error: null, result: null }); + expect(parser.parse('NETWORKDAYS()')).toMatchObject({ error: null, result: Number.NaN }); + expect(parser.parse('NETWORKDAYS("2/1/1901")')).toMatchObject({ error: null, result: Number.NaN }); expect(parser.parse('NETWORKDAYS("2013-12-04", "2013-12-05")')).toMatchObject({ error: null, result: 2 }); expect(parser.parse('NETWORKDAYS("2013-11-04", "2013-12-05")')).toMatchObject({ error: null, result: 24 }); expect(parser.parse('NETWORKDAYS("10/1/2012", "3/1/2013", [\'11/22/2012\'])')).toMatchObject({ error: null, result: 109, }); - expect(parser.parse('NETWORKDAYS(null, "2013-12-05")')).toMatchObject({ error: null, result: null }); - expect(parser.parse('NETWORKDAYS("2013-12-05", null)')).toMatchObject({ error: null, result: null }); - expect(parser.parse('NETWORKDAYS(null, null)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('NETWORKDAYS(null, "2013-12-05")')).toMatchObject({ error: null, result: Number.NaN }); + expect(parser.parse('NETWORKDAYS("2013-12-05", null)')).toMatchObject({ error: null, result: Number.NaN }); + expect(parser.parse('NETWORKDAYS(null, null)')).toMatchObject({ error: null, result: Number.NaN }); }); it.each([new Parser(), ClickUpParser.create()])('NOW', (parser) => { @@ -129,31 +129,31 @@ describe('.parse() date & time formulas', () => { }); it.each([new Parser(), ClickUpParser.create()])('SECOND', (parser) => { - expect(parser.parse('SECOND()')).toMatchObject({ error: null, result: null }); + expect(parser.parse('SECOND()')).toMatchObject({ error: null, result: Number.NaN }); expect(parser.parse('SECOND("2/1/1901 13:33:12")')).toMatchObject({ error: null, result: 12 }); - expect(parser.parse('SECOND(null)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('SECOND(null)')).toMatchObject({ error: null, result: Number.NaN }); }); it.each([new Parser(), ClickUpParser.create()])('TIME', (parser) => { - expect(parser.parse('TIME()')).toMatchObject({ error: null, result: null }); - expect(parser.parse('TIME(0)')).toMatchObject({ error: null, result: null }); - expect(parser.parse('TIME(0, 0)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('TIME()')).toMatchObject({ error: null, result: Number.NaN }); + expect(parser.parse('TIME(0)')).toMatchObject({ error: null, result: Number.NaN }); + expect(parser.parse('TIME(0, 0)')).toMatchObject({ error: null, result: Number.NaN }); expect(parser.parse('TIME(0, 0, 0)')).toMatchObject({ error: null, result: 0 }); expect(parser.parse('TIME(1, 1, 1)')).toMatchObject({ error: null, result: 0.04237268518518519 }); expect(parser.parse('TIME(24, 0, 0)')).toMatchObject({ error: null, result: 1 }); - expect(parser.parse('TIME(null, 0, 0)')).toMatchObject({ error: null, result: null }); - expect(parser.parse('TIME(0, null, 0)')).toMatchObject({ error: null, result: null }); - expect(parser.parse('TIME(0, 0, null)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('TIME(null, 0, 0)')).toMatchObject({ error: null, result: Number.NaN }); + expect(parser.parse('TIME(0, null, 0)')).toMatchObject({ error: null, result: Number.NaN }); + expect(parser.parse('TIME(0, 0, null)')).toMatchObject({ error: null, result: Number.NaN }); }); it.each([new Parser(), ClickUpParser.create()])('TIMEVALUE', (parser) => { - expect(parser.parse('TIMEVALUE()')).toMatchObject({ error: null, result: null }); + expect(parser.parse('TIMEVALUE()')).toMatchObject({ error: null, result: Number.NaN }); expect(parser.parse('TIMEVALUE("1/1/1900 00:00:00")')).toMatchObject({ error: null, result: 0 }); expect(parser.parse('TIMEVALUE("1/1/1900 23:00:00")')).toMatchObject({ error: null, result: 0.9583333333333334, }); - expect(parser.parse('TIMEVALUE(null)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('TIMEVALUE(null)')).toMatchObject({ error: null, result: Number.NaN }); }); it.each([new Parser(), ClickUpParser.create()])('TODAY', (parser) => { @@ -165,23 +165,24 @@ describe('.parse() date & time formulas', () => { }); it.each([new Parser(), ClickUpParser.create()])('WEEKDAY', (parser) => { - expect(parser.parse('WEEKDAY()')).toMatchObject({ error: null, result: null }); + expect(parser.parse('WEEKDAY()')).toMatchObject({ error: null, result: Number.NaN }); expect(parser.parse('WEEKDAY("1/1/1901")')).toMatchObject({ error: null, result: 3 }); expect(parser.parse('WEEKDAY("1/1/1901", 2)')).toMatchObject({ error: null, result: 2 }); - expect(parser.parse('WEEKDAY(null)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('WEEKDAY(null)')).toMatchObject({ error: null, result: Number.NaN }); }); it.each([new Parser(), ClickUpParser.create()])('WEEKNUM', (parser) => { - expect(parser.parse('WEEKNUM()')).toMatchObject({ error: null, result: null }); + expect(parser.parse('WEEKNUM()')).toMatchObject({ error: null, result: Number.NaN }); expect(parser.parse('WEEKNUM("2/1/1900")')).toMatchObject({ error: null, result: 5 }); expect(parser.parse('WEEKNUM("2/1/1909", 2)')).toMatchObject({ error: null, result: 6 }); - expect(parser.parse('WEEKNUM(null)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('WEEKNUM(null)')).toMatchObject({ error: null, result: Number.NaN }); + expect(parser.parse('WEEKNUM("a")')).toMatchObject({ error: '#VALUE!', result: null }); }); it.each([new Parser(), ClickUpParser.create()])('WORKDAY', (parser) => { - expect(parser.parse('WORKDAY()')).toMatchObject({ error: null, result: null }); + expect(parser.parse('WORKDAY()')).toMatchObject({ error: null, result: Number.NaN }); expect(parser.parse('WORKDAY("1/1/1900")')).toMatchObject({ error: null, result: new Date(1900, 0, 1) }); - expect(parser.parse('WORKDAY(null)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('WORKDAY(null)')).toMatchObject({ error: null, result: Number.NaN }); expect(parser.parse('WORKDAY("1/1/1900", null)')).toMatchObject({ error: null, result: new Date(1900, 0, 1) }); const { result, error } = parser.parse('WORKDAY("1/1/1900", 1)'); @@ -191,21 +192,21 @@ describe('.parse() date & time formulas', () => { }); it.each([new Parser(), ClickUpParser.create()])('YEAR', (parser) => { - expect(parser.parse('YEAR()')).toMatchObject({ error: null, result: null }); + expect(parser.parse('YEAR()')).toMatchObject({ error: null, result: Number.NaN }); expect(parser.parse('YEAR("1/1/1904")')).toMatchObject({ error: null, result: 1904 }); expect(parser.parse('YEAR("12/12/2001")')).toMatchObject({ error: null, result: 2001 }); - expect(parser.parse('YEAR(null)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('YEAR(null)')).toMatchObject({ error: null, result: Number.NaN }); }); it.each([new Parser(), ClickUpParser.create()])('YEARFRAC', (parser) => { - expect(parser.parse('YEARFRAC()')).toMatchObject({ error: null, result: null }); - expect(parser.parse('YEARFRAC("1/1/1904")')).toMatchObject({ error: null, result: null }); + expect(parser.parse('YEARFRAC()')).toMatchObject({ error: null, result: Number.NaN }); + expect(parser.parse('YEARFRAC("1/1/1904")')).toMatchObject({ error: null, result: Number.NaN }); expect(parser.parse('YEARFRAC("1/1/1900", "1/2/1900")')).toMatchObject({ error: null, result: 0.002777777777777778, }); - expect(parser.parse('YEARFRAC(null, "1/2/1900")')).toMatchObject({ error: null, result: null }); - expect(parser.parse('YEARFRAC("1/1/1900", null)')).toMatchObject({ error: null, result: null }); - expect(parser.parse('YEARFRAC(null, null)')).toMatchObject({ error: null, result: null }); + expect(parser.parse('YEARFRAC(null, "1/2/1900")')).toMatchObject({ error: null, result: Number.NaN }); + expect(parser.parse('YEARFRAC("1/1/1900", null)')).toMatchObject({ error: null, result: Number.NaN }); + expect(parser.parse('YEARFRAC(null, null)')).toMatchObject({ error: null, result: Number.NaN }); }); }); From d88fd28b324c05edbef96c675fd0ae7fb082afb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Micha=C5=82owski?= Date: Thu, 21 Nov 2024 09:57:56 +0000 Subject: [PATCH 09/10] Update src/clickup/formulajsProxy.ts better function name Co-authored-by: Nikita Dmitriev <106996965+nikitaclicks@users.noreply.github.com> --- src/clickup/formulajsProxy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clickup/formulajsProxy.ts b/src/clickup/formulajsProxy.ts index b6f6fd5..f8c9f26 100644 --- a/src/clickup/formulajsProxy.ts +++ b/src/clickup/formulajsProxy.ts @@ -1,6 +1,6 @@ import * as formulajs from '@formulajs/formulajs'; -const isNil = (...args: unknown[]) => +const hasNil = (...args: unknown[]) => args.some((arg) => arg === null || arg === undefined || arg === '' || arg === false); const nullToZero = (arg: unknown) => (isNil(arg) ? 0 : arg); From 2309db00e74acd28e8b364dc57eba8c46cc5b06c Mon Sep 17 00:00:00 2001 From: Michal Michalowski Date: Thu, 21 Nov 2024 10:02:01 +0000 Subject: [PATCH 10/10] chore: function rename cont. --- src/clickup/formulajsProxy.ts | 42 +++++++++++++++++------------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/clickup/formulajsProxy.ts b/src/clickup/formulajsProxy.ts index f8c9f26..18e8887 100644 --- a/src/clickup/formulajsProxy.ts +++ b/src/clickup/formulajsProxy.ts @@ -2,41 +2,41 @@ import * as formulajs from '@formulajs/formulajs'; const hasNil = (...args: unknown[]) => args.some((arg) => arg === null || arg === undefined || arg === '' || arg === false); -const nullToZero = (arg: unknown) => (isNil(arg) ? 0 : arg); +const nullToZero = (arg: unknown) => (hasNil(arg) ? 0 : arg); const overrides = { DATE: (year: unknown, month: unknown, day: unknown) => - isNil(year, month, day) ? Number.NaN : formulajs.DATE(year, month, day), - DATEVALUE: (dateText: unknown) => (isNil(dateText) ? Number.NaN : formulajs.DATEVALUE(dateText)), - DAY: (date: unknown) => (isNil(date) ? Number.NaN : formulajs.DAY(date)), + hasNil(year, month, day) ? Number.NaN : formulajs.DATE(year, month, day), + DATEVALUE: (dateText: unknown) => (hasNil(dateText) ? Number.NaN : formulajs.DATEVALUE(dateText)), + DAY: (date: unknown) => (hasNil(date) ? Number.NaN : formulajs.DAY(date)), DAYS: (startDate: unknown, endDate: unknown) => - isNil(startDate, endDate) ? Number.NaN : formulajs.DAYS(startDate, endDate), + hasNil(startDate, endDate) ? Number.NaN : formulajs.DAYS(startDate, endDate), DAYS360: (startDate: unknown, endDate: unknown, method: unknown) => - isNil(startDate, endDate) ? Number.NaN : formulajs.DAYS360(startDate, endDate, method), + hasNil(startDate, endDate) ? Number.NaN : formulajs.DAYS360(startDate, endDate, method), EDATE: (startDate: unknown, months: unknown) => - isNil(startDate) ? Number.NaN : formulajs.EDATE(startDate, nullToZero(months)), + hasNil(startDate) ? Number.NaN : formulajs.EDATE(startDate, nullToZero(months)), EOMONTH: (startDate: unknown, months: unknown) => - isNil(startDate) ? Number.NaN : formulajs.EOMONTH(startDate, nullToZero(months)), - HOUR: (date: unknown) => (isNil(date) ? Number.NaN : formulajs.HOUR(date)), + hasNil(startDate) ? Number.NaN : formulajs.EOMONTH(startDate, nullToZero(months)), + HOUR: (date: unknown) => (hasNil(date) ? Number.NaN : formulajs.HOUR(date)), INTERVAL: (seconds: unknown) => formulajs.INTERVAL(nullToZero(seconds)), - ISOWEEKNUM: (date: unknown) => (isNil(date) ? Number.NaN : formulajs.ISOWEEKNUM(date)), - MINUTE: (serialNumber: unknown) => (isNil(serialNumber) ? Number.NaN : formulajs.MINUTE(serialNumber)), - MONTH: (date: unknown) => (isNil(date) ? Number.NaN : formulajs.MONTH(date)), - SECOND: (serialNumber: unknown) => (isNil(serialNumber) ? Number.NaN : formulajs.SECOND(serialNumber)), + ISOWEEKNUM: (date: unknown) => (hasNil(date) ? Number.NaN : formulajs.ISOWEEKNUM(date)), + MINUTE: (serialNumber: unknown) => (hasNil(serialNumber) ? Number.NaN : formulajs.MINUTE(serialNumber)), + MONTH: (date: unknown) => (hasNil(date) ? Number.NaN : formulajs.MONTH(date)), + SECOND: (serialNumber: unknown) => (hasNil(serialNumber) ? Number.NaN : formulajs.SECOND(serialNumber)), TIME: (hour: unknown, minute: unknown, second: unknown) => - isNil(hour, minute, second) ? Number.NaN : formulajs.TIME(hour, minute, second), - TIMEVALUE: (timeText: unknown) => (isNil(timeText) ? Number.NaN : formulajs.TIMEVALUE(timeText)), + hasNil(hour, minute, second) ? Number.NaN : formulajs.TIME(hour, minute, second), + TIMEVALUE: (timeText: unknown) => (hasNil(timeText) ? Number.NaN : formulajs.TIMEVALUE(timeText)), WEEKDAY: (serialNumber: unknown, returnType: unknown) => - isNil(serialNumber) ? Number.NaN : formulajs.WEEKDAY(serialNumber, returnType), + hasNil(serialNumber) ? Number.NaN : formulajs.WEEKDAY(serialNumber, returnType), WEEKNUM: (serialNumber: unknown, returnType: unknown) => - isNil(serialNumber) ? Number.NaN : formulajs.WEEKNUM(serialNumber, returnType), - YEAR: (date: unknown) => (isNil(date) ? Number.NaN : formulajs.YEAR(date)), + hasNil(serialNumber) ? Number.NaN : formulajs.WEEKNUM(serialNumber, returnType), + YEAR: (date: unknown) => (hasNil(date) ? Number.NaN : formulajs.YEAR(date)), YEARFRAC: (startDate: unknown, endDate: unknown, basis: unknown) => - isNil(startDate, endDate) ? Number.NaN : formulajs.YEARFRAC(startDate, endDate, basis), + hasNil(startDate, endDate) ? Number.NaN : formulajs.YEARFRAC(startDate, endDate, basis), WORKDAY: (startDate: unknown, days: unknown, holidays: unknown) => - isNil(startDate) ? Number.NaN : formulajs.WORKDAY(startDate, nullToZero(days), holidays), + hasNil(startDate) ? Number.NaN : formulajs.WORKDAY(startDate, nullToZero(days), holidays), NETWORKDAYS: (startDate: unknown, endDate: unknown, holidays: unknown) => - isNil(startDate, endDate) ? Number.NaN : formulajs.NETWORKDAYS(startDate, endDate, holidays), + hasNil(startDate, endDate) ? Number.NaN : formulajs.NETWORKDAYS(startDate, endDate, holidays), }; export const formulajsProxy = new Proxy(formulajs, {