@cj-tech-master/excelts 9.2.1 → 9.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -2
- package/README_zh.md +29 -6
- package/dist/browser/index.browser.d.ts +1 -1
- package/dist/browser/index.browser.js +4 -0
- package/dist/browser/index.d.ts +1 -1
- package/dist/browser/index.js +4 -0
- package/dist/browser/modules/excel/cell.d.ts +17 -3
- package/dist/browser/modules/excel/cell.js +170 -22
- package/dist/browser/modules/excel/defined-names.d.ts +96 -1
- package/dist/browser/modules/excel/defined-names.js +411 -21
- package/dist/browser/modules/excel/image.d.ts +11 -0
- package/dist/browser/modules/excel/image.js +24 -1
- package/dist/browser/modules/excel/stream/workbook-reader.browser.d.ts +9 -3
- package/dist/browser/modules/excel/stream/workbook-reader.browser.js +14 -0
- package/dist/browser/modules/excel/stream/workbook-reader.d.ts +2 -1
- package/dist/browser/modules/excel/stream/workbook-writer.browser.d.ts +39 -5
- package/dist/browser/modules/excel/stream/workbook-writer.browser.js +48 -1
- package/dist/browser/modules/excel/stream/workbook-writer.d.ts +3 -2
- package/dist/browser/modules/excel/stream/worksheet-reader.js +17 -1
- package/dist/browser/modules/excel/stream/worksheet-writer.d.ts +39 -6
- package/dist/browser/modules/excel/stream/worksheet-writer.js +45 -5
- package/dist/browser/modules/excel/table.js +15 -2
- package/dist/browser/modules/excel/types.d.ts +133 -2
- package/dist/browser/modules/excel/utils/col-cache.d.ts +1 -0
- package/dist/browser/modules/excel/utils/col-cache.js +15 -0
- package/dist/browser/modules/excel/utils/drawing-utils.d.ts +3 -3
- package/dist/browser/modules/excel/utils/drawing-utils.js +4 -0
- package/dist/browser/modules/excel/utils/external-link-formula.d.ts +76 -0
- package/dist/browser/modules/excel/utils/external-link-formula.js +208 -0
- package/dist/browser/modules/excel/utils/iterate-stream.d.ts +9 -3
- package/dist/browser/modules/excel/utils/iterate-stream.js +3 -1
- package/dist/browser/modules/excel/utils/ooxml-paths.d.ts +19 -0
- package/dist/browser/modules/excel/utils/ooxml-paths.js +37 -2
- package/dist/browser/modules/excel/utils/shared-strings.d.ts +8 -3
- package/dist/browser/modules/excel/utils/shared-strings.js +21 -2
- package/dist/browser/modules/excel/utils/workbook-protection.d.ts +30 -0
- package/dist/browser/modules/excel/utils/workbook-protection.js +30 -0
- package/dist/browser/modules/excel/workbook.browser.d.ts +257 -6
- package/dist/browser/modules/excel/workbook.browser.js +318 -34
- package/dist/browser/modules/excel/workbook.d.ts +1 -1
- package/dist/browser/modules/excel/worksheet.d.ts +3 -1
- package/dist/browser/modules/excel/worksheet.js +21 -2
- package/dist/browser/modules/excel/xlsx/rel-type.d.ts +15 -0
- package/dist/browser/modules/excel/xlsx/rel-type.js +16 -1
- package/dist/browser/modules/excel/xlsx/xform/book/defined-name-xform.d.ts +6 -5
- package/dist/browser/modules/excel/xlsx/xform/book/defined-name-xform.js +21 -86
- package/dist/browser/modules/excel/xlsx/xform/book/external-link-xform.d.ts +84 -0
- package/dist/browser/modules/excel/xlsx/xform/book/external-link-xform.js +330 -0
- package/dist/browser/modules/excel/xlsx/xform/book/external-reference-xform.d.ts +17 -0
- package/dist/browser/modules/excel/xlsx/xform/book/external-reference-xform.js +24 -0
- package/dist/browser/modules/excel/xlsx/xform/book/workbook-calc-properties-xform.d.ts +3 -0
- package/dist/browser/modules/excel/xlsx/xform/book/workbook-calc-properties-xform.js +11 -2
- package/dist/browser/modules/excel/xlsx/xform/book/workbook-protection-xform.d.ts +20 -0
- package/dist/browser/modules/excel/xlsx/xform/book/workbook-protection-xform.js +66 -0
- package/dist/browser/modules/excel/xlsx/xform/book/workbook-xform.js +38 -5
- package/dist/browser/modules/excel/xlsx/xform/core/content-types-xform.js +19 -1
- package/dist/browser/modules/excel/xlsx/xform/core/metadata-xform.d.ts +56 -0
- package/dist/browser/modules/excel/xlsx/xform/core/metadata-xform.js +158 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.d.ts +26 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +105 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +3 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/drawing-xform.js +10 -2
- package/dist/browser/modules/excel/xlsx/xform/sheet/cell-xform.d.ts +1 -1
- package/dist/browser/modules/excel/xlsx/xform/sheet/cell-xform.js +166 -8
- package/dist/browser/modules/excel/xlsx/xform/sheet/data-validations-xform.js +1 -1
- package/dist/browser/modules/excel/xlsx/xform/sheet/ignored-errors-xform.d.ts +21 -0
- package/dist/browser/modules/excel/xlsx/xform/sheet/ignored-errors-xform.js +80 -0
- package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +9 -4
- package/dist/browser/modules/excel/xlsx/xform/style/border-xform.js +4 -1
- package/dist/browser/modules/excel/xlsx/xlsx.browser.d.ts +172 -13
- package/dist/browser/modules/excel/xlsx/xlsx.browser.js +410 -20
- package/dist/browser/modules/excel/xlsx/xlsx.d.ts +7 -4
- package/dist/browser/modules/excel/xlsx/xlsx.js +4 -5
- package/dist/browser/modules/formula/compile/address-utils.d.ts +62 -0
- package/dist/browser/modules/formula/compile/address-utils.js +83 -0
- package/dist/browser/modules/formula/compile/binder.d.ts +42 -0
- package/dist/browser/modules/formula/compile/binder.js +487 -0
- package/dist/browser/modules/formula/compile/bound-ast.d.ts +230 -0
- package/dist/browser/modules/formula/compile/bound-ast.js +80 -0
- package/dist/browser/modules/formula/compile/compiled-formula.d.ts +137 -0
- package/dist/browser/modules/formula/compile/compiled-formula.js +383 -0
- package/dist/browser/modules/formula/compile/dependency-analysis.d.ts +93 -0
- package/dist/browser/modules/formula/compile/dependency-analysis.js +432 -0
- package/dist/browser/modules/formula/compile/structured-ref-utils.d.ts +93 -0
- package/dist/browser/modules/formula/compile/structured-ref-utils.js +136 -0
- package/dist/browser/modules/formula/default-syntax-probe.d.ts +79 -0
- package/dist/browser/modules/formula/default-syntax-probe.js +83 -0
- package/dist/browser/modules/formula/functions/_date-context.d.ts +4 -0
- package/dist/browser/modules/formula/functions/_date-context.js +29 -0
- package/dist/browser/modules/formula/functions/_shared.d.ts +121 -0
- package/dist/browser/modules/formula/functions/_shared.js +381 -0
- package/dist/browser/modules/formula/functions/conditional.d.ts +27 -0
- package/dist/browser/modules/formula/functions/conditional.js +343 -0
- package/dist/browser/modules/formula/functions/database.d.ts +37 -0
- package/dist/browser/modules/formula/functions/database.js +274 -0
- package/dist/browser/modules/formula/functions/date.d.ts +61 -0
- package/dist/browser/modules/formula/functions/date.js +855 -0
- package/dist/browser/modules/formula/functions/dynamic-array.d.ts +23 -0
- package/dist/browser/modules/formula/functions/dynamic-array.js +860 -0
- package/dist/browser/modules/formula/functions/engineering.d.ts +57 -0
- package/dist/browser/modules/formula/functions/engineering.js +1128 -0
- package/dist/browser/modules/formula/functions/financial.d.ts +202 -0
- package/dist/browser/modules/formula/functions/financial.js +2296 -0
- package/dist/browser/modules/formula/functions/lookup.d.ts +18 -0
- package/dist/browser/modules/formula/functions/lookup.js +886 -0
- package/dist/browser/modules/formula/functions/math.d.ts +114 -0
- package/dist/browser/modules/formula/functions/math.js +1406 -0
- package/dist/browser/modules/formula/functions/statistical.d.ts +193 -0
- package/dist/browser/modules/formula/functions/statistical.js +3390 -0
- package/dist/browser/modules/formula/functions/text.d.ts +86 -0
- package/dist/browser/modules/formula/functions/text.js +1845 -0
- package/dist/browser/modules/formula/host-registry.d.ts +53 -0
- package/dist/browser/modules/formula/host-registry.js +69 -0
- package/dist/browser/modules/formula/index.d.ts +39 -0
- package/dist/browser/modules/formula/index.js +49 -0
- package/dist/browser/modules/formula/install.d.ts +62 -0
- package/dist/browser/modules/formula/install.js +88 -0
- package/dist/browser/modules/formula/integration/apply-writeback-plan.d.ts +26 -0
- package/dist/browser/modules/formula/integration/apply-writeback-plan.js +210 -0
- package/dist/browser/modules/formula/integration/calculate-formulas-impl.d.ts +30 -0
- package/dist/browser/modules/formula/integration/calculate-formulas-impl.js +616 -0
- package/dist/browser/modules/formula/integration/calculate-formulas.d.ts +67 -0
- package/dist/browser/modules/formula/integration/calculate-formulas.js +68 -0
- package/dist/browser/modules/formula/integration/formula-instance.d.ts +64 -0
- package/dist/browser/modules/formula/integration/formula-instance.js +79 -0
- package/dist/browser/modules/formula/integration/workbook-adapter.d.ts +26 -0
- package/dist/browser/modules/formula/integration/workbook-adapter.js +324 -0
- package/dist/browser/modules/formula/integration/workbook-snapshot.d.ts +267 -0
- package/dist/browser/modules/formula/integration/workbook-snapshot.js +77 -0
- package/dist/browser/modules/formula/materialize/build-writeback-plan.d.ts +34 -0
- package/dist/browser/modules/formula/materialize/build-writeback-plan.js +473 -0
- package/dist/browser/modules/formula/materialize/spill-engine.d.ts +9 -0
- package/dist/browser/modules/formula/materialize/spill-engine.js +38 -0
- package/dist/browser/modules/formula/materialize/types.d.ts +179 -0
- package/dist/browser/modules/formula/materialize/types.js +29 -0
- package/dist/browser/modules/formula/materialize/writeback-plan.d.ts +167 -0
- package/dist/browser/modules/formula/materialize/writeback-plan.js +27 -0
- package/dist/browser/modules/formula/runtime/evaluator.d.ts +151 -0
- package/dist/browser/modules/formula/runtime/evaluator.js +2291 -0
- package/dist/browser/modules/formula/runtime/function-registry.d.ts +47 -0
- package/dist/browser/modules/formula/runtime/function-registry.js +840 -0
- package/dist/browser/modules/formula/runtime/values.d.ts +211 -0
- package/dist/browser/modules/formula/runtime/values.js +385 -0
- package/dist/browser/modules/formula/syntax/ast.d.ts +129 -0
- package/dist/browser/modules/formula/syntax/ast.js +28 -0
- package/dist/browser/modules/formula/syntax/parser.d.ts +18 -0
- package/dist/browser/modules/formula/syntax/parser.js +439 -0
- package/dist/browser/modules/formula/syntax/token-types.d.ts +153 -0
- package/dist/browser/modules/formula/syntax/token-types.js +59 -0
- package/dist/browser/modules/formula/syntax/tokenizer.d.ts +10 -0
- package/dist/browser/modules/formula/syntax/tokenizer.js +1074 -0
- package/dist/browser/modules/pdf/excel-bridge.js +9 -0
- package/dist/cjs/index.js +4 -0
- package/dist/cjs/modules/excel/cell.js +170 -22
- package/dist/cjs/modules/excel/defined-names.js +411 -21
- package/dist/cjs/modules/excel/image.js +24 -1
- package/dist/cjs/modules/excel/stream/workbook-reader.browser.js +14 -0
- package/dist/cjs/modules/excel/stream/workbook-writer.browser.js +48 -1
- package/dist/cjs/modules/excel/stream/worksheet-reader.js +17 -1
- package/dist/cjs/modules/excel/stream/worksheet-writer.js +45 -5
- package/dist/cjs/modules/excel/table.js +15 -2
- package/dist/cjs/modules/excel/utils/col-cache.js +15 -0
- package/dist/cjs/modules/excel/utils/drawing-utils.js +4 -0
- package/dist/cjs/modules/excel/utils/external-link-formula.js +212 -0
- package/dist/cjs/modules/excel/utils/iterate-stream.js +3 -1
- package/dist/cjs/modules/excel/utils/ooxml-paths.js +42 -2
- package/dist/cjs/modules/excel/utils/shared-strings.js +21 -2
- package/dist/cjs/modules/excel/utils/workbook-protection.js +33 -0
- package/dist/cjs/modules/excel/workbook.browser.js +318 -34
- package/dist/cjs/modules/excel/worksheet.js +20 -1
- package/dist/cjs/modules/excel/xlsx/rel-type.js +16 -1
- package/dist/cjs/modules/excel/xlsx/xform/book/defined-name-xform.js +21 -86
- package/dist/cjs/modules/excel/xlsx/xform/book/external-link-xform.js +333 -0
- package/dist/cjs/modules/excel/xlsx/xform/book/external-reference-xform.js +27 -0
- package/dist/cjs/modules/excel/xlsx/xform/book/workbook-calc-properties-xform.js +11 -2
- package/dist/cjs/modules/excel/xlsx/xform/book/workbook-protection-xform.js +69 -0
- package/dist/cjs/modules/excel/xlsx/xform/book/workbook-xform.js +38 -5
- package/dist/cjs/modules/excel/xlsx/xform/core/content-types-xform.js +18 -0
- package/dist/cjs/modules/excel/xlsx/xform/core/metadata-xform.js +161 -0
- package/dist/cjs/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +108 -0
- package/dist/cjs/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +3 -0
- package/dist/cjs/modules/excel/xlsx/xform/drawing/drawing-xform.js +10 -2
- package/dist/cjs/modules/excel/xlsx/xform/sheet/cell-xform.js +166 -8
- package/dist/cjs/modules/excel/xlsx/xform/sheet/data-validations-xform.js +1 -1
- package/dist/cjs/modules/excel/xlsx/xform/sheet/ignored-errors-xform.js +83 -0
- package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +9 -4
- package/dist/cjs/modules/excel/xlsx/xform/style/border-xform.js +4 -1
- package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +408 -18
- package/dist/cjs/modules/excel/xlsx/xlsx.js +4 -5
- package/dist/cjs/modules/formula/compile/address-utils.js +89 -0
- package/dist/cjs/modules/formula/compile/binder.js +489 -0
- package/dist/cjs/modules/formula/compile/bound-ast.js +68 -0
- package/dist/cjs/modules/formula/compile/compiled-formula.js +387 -0
- package/dist/cjs/modules/formula/compile/dependency-analysis.js +437 -0
- package/dist/cjs/modules/formula/compile/structured-ref-utils.js +141 -0
- package/dist/cjs/modules/formula/default-syntax-probe.js +87 -0
- package/dist/cjs/modules/formula/functions/_date-context.js +33 -0
- package/dist/cjs/modules/formula/functions/_shared.js +396 -0
- package/dist/cjs/modules/formula/functions/conditional.js +354 -0
- package/dist/cjs/modules/formula/functions/database.js +288 -0
- package/dist/cjs/modules/formula/functions/date.js +883 -0
- package/dist/cjs/modules/formula/functions/dynamic-array.js +881 -0
- package/dist/cjs/modules/formula/functions/engineering.js +1183 -0
- package/dist/cjs/modules/formula/functions/financial.js +2348 -0
- package/dist/cjs/modules/formula/functions/lookup.js +902 -0
- package/dist/cjs/modules/formula/functions/math.js +1487 -0
- package/dist/cjs/modules/formula/functions/statistical.js +3488 -0
- package/dist/cjs/modules/formula/functions/text.js +1889 -0
- package/dist/cjs/modules/formula/host-registry.js +75 -0
- package/dist/cjs/modules/formula/index.js +58 -0
- package/dist/cjs/modules/formula/install.js +93 -0
- package/dist/cjs/modules/formula/integration/apply-writeback-plan.js +213 -0
- package/dist/cjs/modules/formula/integration/calculate-formulas-impl.js +619 -0
- package/dist/cjs/modules/formula/integration/calculate-formulas.js +71 -0
- package/dist/cjs/modules/formula/integration/formula-instance.js +82 -0
- package/dist/cjs/modules/formula/integration/workbook-adapter.js +327 -0
- package/dist/cjs/modules/formula/integration/workbook-snapshot.js +84 -0
- package/dist/cjs/modules/formula/materialize/build-writeback-plan.js +475 -0
- package/dist/cjs/modules/formula/materialize/spill-engine.js +42 -0
- package/dist/cjs/modules/formula/materialize/types.js +32 -0
- package/dist/cjs/modules/formula/materialize/writeback-plan.js +28 -0
- package/dist/cjs/modules/formula/runtime/evaluator.js +2298 -0
- package/dist/cjs/modules/formula/runtime/function-registry.js +846 -0
- package/dist/cjs/modules/formula/runtime/values.js +385 -0
- package/dist/cjs/modules/formula/syntax/ast.js +8 -0
- package/dist/cjs/modules/formula/syntax/parser.js +440 -0
- package/dist/cjs/modules/formula/syntax/token-types.js +32 -0
- package/dist/cjs/modules/formula/syntax/tokenizer.js +1076 -0
- package/dist/cjs/modules/pdf/excel-bridge.js +9 -0
- package/dist/esm/index.browser.js +4 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/modules/excel/cell.js +170 -22
- package/dist/esm/modules/excel/defined-names.js +411 -21
- package/dist/esm/modules/excel/image.js +24 -1
- package/dist/esm/modules/excel/stream/workbook-reader.browser.js +14 -0
- package/dist/esm/modules/excel/stream/workbook-writer.browser.js +48 -1
- package/dist/esm/modules/excel/stream/worksheet-reader.js +17 -1
- package/dist/esm/modules/excel/stream/worksheet-writer.js +45 -5
- package/dist/esm/modules/excel/table.js +15 -2
- package/dist/esm/modules/excel/utils/col-cache.js +15 -0
- package/dist/esm/modules/excel/utils/drawing-utils.js +4 -0
- package/dist/esm/modules/excel/utils/external-link-formula.js +208 -0
- package/dist/esm/modules/excel/utils/iterate-stream.js +3 -1
- package/dist/esm/modules/excel/utils/ooxml-paths.js +37 -2
- package/dist/esm/modules/excel/utils/shared-strings.js +21 -2
- package/dist/esm/modules/excel/utils/workbook-protection.js +30 -0
- package/dist/esm/modules/excel/workbook.browser.js +318 -34
- package/dist/esm/modules/excel/worksheet.js +21 -2
- package/dist/esm/modules/excel/xlsx/rel-type.js +16 -1
- package/dist/esm/modules/excel/xlsx/xform/book/defined-name-xform.js +21 -86
- package/dist/esm/modules/excel/xlsx/xform/book/external-link-xform.js +330 -0
- package/dist/esm/modules/excel/xlsx/xform/book/external-reference-xform.js +24 -0
- package/dist/esm/modules/excel/xlsx/xform/book/workbook-calc-properties-xform.js +11 -2
- package/dist/esm/modules/excel/xlsx/xform/book/workbook-protection-xform.js +66 -0
- package/dist/esm/modules/excel/xlsx/xform/book/workbook-xform.js +38 -5
- package/dist/esm/modules/excel/xlsx/xform/core/content-types-xform.js +19 -1
- package/dist/esm/modules/excel/xlsx/xform/core/metadata-xform.js +158 -0
- package/dist/esm/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +105 -0
- package/dist/esm/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +3 -0
- package/dist/esm/modules/excel/xlsx/xform/drawing/drawing-xform.js +10 -2
- package/dist/esm/modules/excel/xlsx/xform/sheet/cell-xform.js +166 -8
- package/dist/esm/modules/excel/xlsx/xform/sheet/data-validations-xform.js +1 -1
- package/dist/esm/modules/excel/xlsx/xform/sheet/ignored-errors-xform.js +80 -0
- package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +9 -4
- package/dist/esm/modules/excel/xlsx/xform/style/border-xform.js +4 -1
- package/dist/esm/modules/excel/xlsx/xlsx.browser.js +410 -20
- package/dist/esm/modules/excel/xlsx/xlsx.js +4 -5
- package/dist/esm/modules/formula/compile/address-utils.js +83 -0
- package/dist/esm/modules/formula/compile/binder.js +487 -0
- package/dist/esm/modules/formula/compile/bound-ast.js +80 -0
- package/dist/esm/modules/formula/compile/compiled-formula.js +383 -0
- package/dist/esm/modules/formula/compile/dependency-analysis.js +432 -0
- package/dist/esm/modules/formula/compile/structured-ref-utils.js +136 -0
- package/dist/esm/modules/formula/default-syntax-probe.js +83 -0
- package/dist/esm/modules/formula/functions/_date-context.js +29 -0
- package/dist/esm/modules/formula/functions/_shared.js +381 -0
- package/dist/esm/modules/formula/functions/conditional.js +343 -0
- package/dist/esm/modules/formula/functions/database.js +274 -0
- package/dist/esm/modules/formula/functions/date.js +855 -0
- package/dist/esm/modules/formula/functions/dynamic-array.js +860 -0
- package/dist/esm/modules/formula/functions/engineering.js +1128 -0
- package/dist/esm/modules/formula/functions/financial.js +2296 -0
- package/dist/esm/modules/formula/functions/lookup.js +886 -0
- package/dist/esm/modules/formula/functions/math.js +1406 -0
- package/dist/esm/modules/formula/functions/statistical.js +3390 -0
- package/dist/esm/modules/formula/functions/text.js +1845 -0
- package/dist/esm/modules/formula/host-registry.js +69 -0
- package/dist/esm/modules/formula/index.js +49 -0
- package/dist/esm/modules/formula/install.js +88 -0
- package/dist/esm/modules/formula/integration/apply-writeback-plan.js +210 -0
- package/dist/esm/modules/formula/integration/calculate-formulas-impl.js +616 -0
- package/dist/esm/modules/formula/integration/calculate-formulas.js +68 -0
- package/dist/esm/modules/formula/integration/formula-instance.js +79 -0
- package/dist/esm/modules/formula/integration/workbook-adapter.js +324 -0
- package/dist/esm/modules/formula/integration/workbook-snapshot.js +77 -0
- package/dist/esm/modules/formula/materialize/build-writeback-plan.js +473 -0
- package/dist/esm/modules/formula/materialize/spill-engine.js +38 -0
- package/dist/esm/modules/formula/materialize/types.js +29 -0
- package/dist/esm/modules/formula/materialize/writeback-plan.js +27 -0
- package/dist/esm/modules/formula/runtime/evaluator.js +2291 -0
- package/dist/esm/modules/formula/runtime/function-registry.js +840 -0
- package/dist/esm/modules/formula/runtime/values.js +385 -0
- package/dist/esm/modules/formula/syntax/ast.js +28 -0
- package/dist/esm/modules/formula/syntax/parser.js +439 -0
- package/dist/esm/modules/formula/syntax/token-types.js +59 -0
- package/dist/esm/modules/formula/syntax/tokenizer.js +1074 -0
- package/dist/esm/modules/pdf/excel-bridge.js +9 -0
- package/dist/iife/excelts.iife.js +2302 -373
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +34 -34
- package/dist/types/index.browser.d.ts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/modules/excel/cell.d.ts +17 -3
- package/dist/types/modules/excel/defined-names.d.ts +96 -1
- package/dist/types/modules/excel/image.d.ts +11 -0
- package/dist/types/modules/excel/stream/workbook-reader.browser.d.ts +9 -3
- package/dist/types/modules/excel/stream/workbook-reader.d.ts +2 -1
- package/dist/types/modules/excel/stream/workbook-writer.browser.d.ts +39 -5
- package/dist/types/modules/excel/stream/workbook-writer.d.ts +3 -2
- package/dist/types/modules/excel/stream/worksheet-writer.d.ts +39 -6
- package/dist/types/modules/excel/types.d.ts +133 -2
- package/dist/types/modules/excel/utils/col-cache.d.ts +1 -0
- package/dist/types/modules/excel/utils/drawing-utils.d.ts +3 -3
- package/dist/types/modules/excel/utils/external-link-formula.d.ts +76 -0
- package/dist/types/modules/excel/utils/iterate-stream.d.ts +9 -3
- package/dist/types/modules/excel/utils/ooxml-paths.d.ts +19 -0
- package/dist/types/modules/excel/utils/shared-strings.d.ts +8 -3
- package/dist/types/modules/excel/utils/workbook-protection.d.ts +30 -0
- package/dist/types/modules/excel/workbook.browser.d.ts +257 -6
- package/dist/types/modules/excel/workbook.d.ts +1 -1
- package/dist/types/modules/excel/worksheet.d.ts +3 -1
- package/dist/types/modules/excel/xlsx/rel-type.d.ts +15 -0
- package/dist/types/modules/excel/xlsx/xform/book/defined-name-xform.d.ts +6 -5
- package/dist/types/modules/excel/xlsx/xform/book/external-link-xform.d.ts +84 -0
- package/dist/types/modules/excel/xlsx/xform/book/external-reference-xform.d.ts +17 -0
- package/dist/types/modules/excel/xlsx/xform/book/workbook-calc-properties-xform.d.ts +3 -0
- package/dist/types/modules/excel/xlsx/xform/book/workbook-protection-xform.d.ts +20 -0
- package/dist/types/modules/excel/xlsx/xform/core/metadata-xform.d.ts +56 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.d.ts +26 -0
- package/dist/types/modules/excel/xlsx/xform/sheet/cell-xform.d.ts +1 -1
- package/dist/types/modules/excel/xlsx/xform/sheet/ignored-errors-xform.d.ts +21 -0
- package/dist/types/modules/excel/xlsx/xlsx.browser.d.ts +172 -13
- package/dist/types/modules/excel/xlsx/xlsx.d.ts +7 -4
- package/dist/types/modules/formula/compile/address-utils.d.ts +62 -0
- package/dist/types/modules/formula/compile/binder.d.ts +42 -0
- package/dist/types/modules/formula/compile/bound-ast.d.ts +230 -0
- package/dist/types/modules/formula/compile/compiled-formula.d.ts +137 -0
- package/dist/types/modules/formula/compile/dependency-analysis.d.ts +93 -0
- package/dist/types/modules/formula/compile/structured-ref-utils.d.ts +93 -0
- package/dist/types/modules/formula/default-syntax-probe.d.ts +79 -0
- package/dist/types/modules/formula/functions/_date-context.d.ts +4 -0
- package/dist/types/modules/formula/functions/_shared.d.ts +121 -0
- package/dist/types/modules/formula/functions/conditional.d.ts +27 -0
- package/dist/types/modules/formula/functions/database.d.ts +37 -0
- package/dist/types/modules/formula/functions/date.d.ts +61 -0
- package/dist/types/modules/formula/functions/dynamic-array.d.ts +23 -0
- package/dist/types/modules/formula/functions/engineering.d.ts +57 -0
- package/dist/types/modules/formula/functions/financial.d.ts +202 -0
- package/dist/types/modules/formula/functions/lookup.d.ts +18 -0
- package/dist/types/modules/formula/functions/math.d.ts +114 -0
- package/dist/types/modules/formula/functions/statistical.d.ts +193 -0
- package/dist/types/modules/formula/functions/text.d.ts +86 -0
- package/dist/types/modules/formula/host-registry.d.ts +53 -0
- package/dist/types/modules/formula/index.d.ts +39 -0
- package/dist/types/modules/formula/install.d.ts +62 -0
- package/dist/types/modules/formula/integration/apply-writeback-plan.d.ts +26 -0
- package/dist/types/modules/formula/integration/calculate-formulas-impl.d.ts +30 -0
- package/dist/types/modules/formula/integration/calculate-formulas.d.ts +67 -0
- package/dist/types/modules/formula/integration/formula-instance.d.ts +64 -0
- package/dist/types/modules/formula/integration/workbook-adapter.d.ts +26 -0
- package/dist/types/modules/formula/integration/workbook-snapshot.d.ts +267 -0
- package/dist/types/modules/formula/materialize/build-writeback-plan.d.ts +34 -0
- package/dist/types/modules/formula/materialize/spill-engine.d.ts +9 -0
- package/dist/types/modules/formula/materialize/types.d.ts +179 -0
- package/dist/types/modules/formula/materialize/writeback-plan.d.ts +167 -0
- package/dist/types/modules/formula/runtime/evaluator.d.ts +151 -0
- package/dist/types/modules/formula/runtime/function-registry.d.ts +47 -0
- package/dist/types/modules/formula/runtime/values.d.ts +211 -0
- package/dist/types/modules/formula/syntax/ast.d.ts +129 -0
- package/dist/types/modules/formula/syntax/parser.d.ts +18 -0
- package/dist/types/modules/formula/syntax/token-types.d.ts +153 -0
- package/dist/types/modules/formula/syntax/tokenizer.d.ts +10 -0
- package/package.json +28 -28
|
@@ -0,0 +1,855 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Date / Time Functions — Native RuntimeValue implementation.
|
|
3
|
+
*
|
|
4
|
+
* All Date objects returned by `excelToDate()` represent an Excel serial on
|
|
5
|
+
* the UTC timeline (midnight UTC for the corresponding date). Consequently
|
|
6
|
+
* every field accessor must be the UTC variant (`getUTCFullYear`,
|
|
7
|
+
* `getUTCMonth`, …) and every `Date` constructed from y/m/d components must
|
|
8
|
+
* be built with `Date.UTC(...)`. Using local-time accessors would make
|
|
9
|
+
* results depend on the host timezone — e.g. `YEAR(DATE(2024,1,1))` would
|
|
10
|
+
* return 2023 when evaluated on a machine west of UTC at midnight UTC.
|
|
11
|
+
*
|
|
12
|
+
* Exceptions (intentionally use local time):
|
|
13
|
+
* - `TODAY` / `NOW` — read the user's wall clock, which is genuinely in the
|
|
14
|
+
* local timezone. The Excel serial is then constructed using
|
|
15
|
+
* `Date.UTC(year, month, day)` so that the resulting serial round-trips
|
|
16
|
+
* correctly through `excelToDate()`.
|
|
17
|
+
*/
|
|
18
|
+
import { dateToExcel, excelToDate } from "../../../utils/utils.base.js";
|
|
19
|
+
import { RVKind, ERRORS, isError, isArray, toNumberRV, toStringRV, toBooleanRV, rvNumber, rvBoolean } from "../runtime/values.js";
|
|
20
|
+
import { isDate1904 } from "./_date-context.js";
|
|
21
|
+
import { argToNumber, checkError } from "./_shared.js";
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// Helpers
|
|
24
|
+
// ============================================================================
|
|
25
|
+
/** Convert an Excel serial to a UTC `Date`, honouring the active date1904 mode. */
|
|
26
|
+
function toDate(serial) {
|
|
27
|
+
return excelToDate(serial, isDate1904());
|
|
28
|
+
}
|
|
29
|
+
/** Convert a UTC `Date` back to an Excel serial, honouring the active date1904 mode. */
|
|
30
|
+
function fromDate(d) {
|
|
31
|
+
return dateToExcel(d, isDate1904());
|
|
32
|
+
}
|
|
33
|
+
/** Collect holiday serial numbers from a RuntimeValue argument (array or scalar). */
|
|
34
|
+
function collectHolidays(arg) {
|
|
35
|
+
const set = new Set();
|
|
36
|
+
if (isArray(arg)) {
|
|
37
|
+
for (const row of arg.rows) {
|
|
38
|
+
for (const cell of row) {
|
|
39
|
+
if (cell.kind === RVKind.Number) {
|
|
40
|
+
set.add(Math.floor(cell.value));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
const n = toNumberRV(arg);
|
|
47
|
+
if (n.kind === RVKind.Number) {
|
|
48
|
+
set.add(Math.floor(n.value));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return set;
|
|
52
|
+
}
|
|
53
|
+
// ============================================================================
|
|
54
|
+
// Date Functions
|
|
55
|
+
// ============================================================================
|
|
56
|
+
/**
|
|
57
|
+
* TODAY — today's date (at the user's local timezone).
|
|
58
|
+
*
|
|
59
|
+
* The user's concept of "today" is based on their wall clock, so we read
|
|
60
|
+
* local-time fields from `new Date()`. The resulting y/m/d components are
|
|
61
|
+
* then packed into a UTC serial so downstream date arithmetic is consistent.
|
|
62
|
+
*/
|
|
63
|
+
export const fnTODAY = () => {
|
|
64
|
+
const now = new Date();
|
|
65
|
+
return rvNumber(fromDate(new Date(Date.UTC(now.getFullYear(), now.getMonth(), now.getDate()))));
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* NOW — current date and time.
|
|
69
|
+
*
|
|
70
|
+
* Excel stores the result as an untimezoned serial, but the user expects
|
|
71
|
+
* their local wall-clock reading. `dateToExcel(new Date())` effectively
|
|
72
|
+
* takes `Date.now()` in UTC-ms; any conversion to the user's timezone
|
|
73
|
+
* would require tz metadata we do not have. We therefore keep the current
|
|
74
|
+
* UTC-ms based conversion, matching historical behaviour.
|
|
75
|
+
*/
|
|
76
|
+
export const fnNOW = () => {
|
|
77
|
+
const now = new Date();
|
|
78
|
+
return rvNumber(fromDate(now));
|
|
79
|
+
};
|
|
80
|
+
export const fnYEAR = args => {
|
|
81
|
+
const n = argToNumber(args[0]);
|
|
82
|
+
if (isError(n)) {
|
|
83
|
+
return n;
|
|
84
|
+
}
|
|
85
|
+
return rvNumber(toDate(n.value).getUTCFullYear());
|
|
86
|
+
};
|
|
87
|
+
export const fnMONTH = args => {
|
|
88
|
+
const n = argToNumber(args[0]);
|
|
89
|
+
if (isError(n)) {
|
|
90
|
+
return n;
|
|
91
|
+
}
|
|
92
|
+
return rvNumber(toDate(n.value).getUTCMonth() + 1);
|
|
93
|
+
};
|
|
94
|
+
export const fnDAY = args => {
|
|
95
|
+
const n = argToNumber(args[0]);
|
|
96
|
+
if (isError(n)) {
|
|
97
|
+
return n;
|
|
98
|
+
}
|
|
99
|
+
return rvNumber(toDate(n.value).getUTCDate());
|
|
100
|
+
};
|
|
101
|
+
export const fnDATE = args => {
|
|
102
|
+
const year = argToNumber(args[0]);
|
|
103
|
+
if (isError(year)) {
|
|
104
|
+
return year;
|
|
105
|
+
}
|
|
106
|
+
const month = argToNumber(args[1]);
|
|
107
|
+
if (isError(month)) {
|
|
108
|
+
return month;
|
|
109
|
+
}
|
|
110
|
+
const day = argToNumber(args[2]);
|
|
111
|
+
if (isError(day)) {
|
|
112
|
+
return day;
|
|
113
|
+
}
|
|
114
|
+
// Excel's DATE interprets a year in [0, 1899] as (year + 1900). JavaScript's
|
|
115
|
+
// Date constructor already applies this convention for [0, 99], but it does
|
|
116
|
+
// NOT apply it for [100, 1899] — we have to do it ourselves. Years below 0
|
|
117
|
+
// or above 9999 are rejected as #NUM! (Excel's documented range).
|
|
118
|
+
let y = Math.trunc(year.value);
|
|
119
|
+
if (y < 0 || y > 9999) {
|
|
120
|
+
return ERRORS.NUM;
|
|
121
|
+
}
|
|
122
|
+
if (y < 1900) {
|
|
123
|
+
y += 1900;
|
|
124
|
+
// Excel rejects years outside [1900, 9999] after this coercion.
|
|
125
|
+
if (y > 9999) {
|
|
126
|
+
return ERRORS.NUM;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Lotus 1-2-3 bug: DATE(1900,2,29) should return serial 60 even though
|
|
130
|
+
// 1900 is not a leap year. JavaScript's Date constructor rolls Feb 29, 1900
|
|
131
|
+
// forward to March 1, 1900, so we handle this specially. Run this check
|
|
132
|
+
// *after* the `y < 1900 → y + 1900` coercion so that `DATE(0, 2, 29)` and
|
|
133
|
+
// `DATE(1900, 2, 29)` resolve to the same serial (R6-P1-1).
|
|
134
|
+
if (y === 1900 && month.value === 2 && day.value === 29) {
|
|
135
|
+
return rvNumber(60); // The fictitious Feb 29, 1900
|
|
136
|
+
}
|
|
137
|
+
const d = new Date(Date.UTC(y, month.value - 1, day.value));
|
|
138
|
+
// The `Date.UTC` constructor maps two-digit years through its own legacy
|
|
139
|
+
// rule (+1900), so for `y` in [0, 99] we end up with the same value we
|
|
140
|
+
// already coerced above. Force the full year to be safe — but preserve
|
|
141
|
+
// month/day carry-over from out-of-range values (Excel allows DATE(2024,
|
|
142
|
+
// 13, 1) → 2025-01-01).
|
|
143
|
+
if (y < 100) {
|
|
144
|
+
d.setUTCFullYear(y);
|
|
145
|
+
}
|
|
146
|
+
return rvNumber(fromDate(d));
|
|
147
|
+
};
|
|
148
|
+
export const fnTIME = args => {
|
|
149
|
+
const hour = argToNumber(args[0]);
|
|
150
|
+
if (isError(hour)) {
|
|
151
|
+
return hour;
|
|
152
|
+
}
|
|
153
|
+
const minute = argToNumber(args[1]);
|
|
154
|
+
if (isError(minute)) {
|
|
155
|
+
return minute;
|
|
156
|
+
}
|
|
157
|
+
const second = argToNumber(args[2]);
|
|
158
|
+
if (isError(second)) {
|
|
159
|
+
return second;
|
|
160
|
+
}
|
|
161
|
+
// Excel's TIME rejects negative arguments outright and wraps anything >= 24
|
|
162
|
+
// hours back into the [0, 1) fraction-of-day range. Without the modulo,
|
|
163
|
+
// `TIME(25, 0, 0)` would produce a value > 1, which breaks downstream
|
|
164
|
+
// date-time arithmetic that expects a pure time fraction.
|
|
165
|
+
if (hour.value < 0 || minute.value < 0 || second.value < 0) {
|
|
166
|
+
return ERRORS.NUM;
|
|
167
|
+
}
|
|
168
|
+
const total = (hour.value * 3600 + minute.value * 60 + second.value) / 86400;
|
|
169
|
+
// total could still be >= 1 if e.g. hour=25; wrap into [0, 1).
|
|
170
|
+
return rvNumber(total - Math.floor(total));
|
|
171
|
+
};
|
|
172
|
+
export const fnHOUR = args => {
|
|
173
|
+
const n = argToNumber(args[0]);
|
|
174
|
+
if (isError(n)) {
|
|
175
|
+
return n;
|
|
176
|
+
}
|
|
177
|
+
if (n.value < 0) {
|
|
178
|
+
return ERRORS.NUM;
|
|
179
|
+
}
|
|
180
|
+
const totalSeconds = Math.round((n.value % 1) * 86400);
|
|
181
|
+
return rvNumber(Math.floor(totalSeconds / 3600) % 24);
|
|
182
|
+
};
|
|
183
|
+
export const fnMINUTE = args => {
|
|
184
|
+
const n = argToNumber(args[0]);
|
|
185
|
+
if (isError(n)) {
|
|
186
|
+
return n;
|
|
187
|
+
}
|
|
188
|
+
if (n.value < 0) {
|
|
189
|
+
return ERRORS.NUM;
|
|
190
|
+
}
|
|
191
|
+
const totalSeconds = Math.round((n.value % 1) * 86400);
|
|
192
|
+
return rvNumber(Math.floor(totalSeconds / 60) % 60);
|
|
193
|
+
};
|
|
194
|
+
export const fnSECOND = args => {
|
|
195
|
+
const n = argToNumber(args[0]);
|
|
196
|
+
if (isError(n)) {
|
|
197
|
+
return n;
|
|
198
|
+
}
|
|
199
|
+
if (n.value < 0) {
|
|
200
|
+
return ERRORS.NUM;
|
|
201
|
+
}
|
|
202
|
+
const totalSeconds = Math.round((n.value % 1) * 86400);
|
|
203
|
+
return rvNumber(totalSeconds % 60);
|
|
204
|
+
};
|
|
205
|
+
export const fnWEEKDAY = args => {
|
|
206
|
+
const n = argToNumber(args[0]);
|
|
207
|
+
if (isError(n)) {
|
|
208
|
+
return n;
|
|
209
|
+
}
|
|
210
|
+
const d = toDate(n.value);
|
|
211
|
+
const returnType = args.length > 1 ? argToNumber(args[1]) : rvNumber(1);
|
|
212
|
+
if (isError(returnType)) {
|
|
213
|
+
return returnType;
|
|
214
|
+
}
|
|
215
|
+
const day = d.getUTCDay(); // 0=Sun, 6=Sat
|
|
216
|
+
switch (returnType.value) {
|
|
217
|
+
case 1:
|
|
218
|
+
return rvNumber(day + 1); // 1=Sun, 7=Sat
|
|
219
|
+
case 2:
|
|
220
|
+
return rvNumber(day === 0 ? 7 : day); // 1=Mon, 7=Sun
|
|
221
|
+
case 3:
|
|
222
|
+
return rvNumber(day === 0 ? 6 : day - 1); // 0=Mon, 6=Sun
|
|
223
|
+
case 11: // Mon=1..Sun=7
|
|
224
|
+
case 12: // Tue=1..Mon=7
|
|
225
|
+
case 13: // Wed=1..Tue=7
|
|
226
|
+
case 14: // Thu=1..Wed=7
|
|
227
|
+
case 15: // Fri=1..Thu=7
|
|
228
|
+
case 16: // Sat=1..Fri=7
|
|
229
|
+
case 17: // Sun=1..Sat=7
|
|
230
|
+
return rvNumber(((((day - (returnType.value - 10)) % 7) + 7) % 7) + 1);
|
|
231
|
+
default:
|
|
232
|
+
return ERRORS.NUM;
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
export const fnEOMONTH = args => {
|
|
236
|
+
const startDate = argToNumber(args[0]);
|
|
237
|
+
if (isError(startDate)) {
|
|
238
|
+
return startDate;
|
|
239
|
+
}
|
|
240
|
+
const months = argToNumber(args[1]);
|
|
241
|
+
if (isError(months)) {
|
|
242
|
+
return months;
|
|
243
|
+
}
|
|
244
|
+
const d = toDate(startDate.value);
|
|
245
|
+
const result = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth() + months.value + 1, 0));
|
|
246
|
+
return rvNumber(fromDate(result));
|
|
247
|
+
};
|
|
248
|
+
export const fnEDATE = args => {
|
|
249
|
+
const startDate = argToNumber(args[0]);
|
|
250
|
+
if (isError(startDate)) {
|
|
251
|
+
return startDate;
|
|
252
|
+
}
|
|
253
|
+
const months = argToNumber(args[1]);
|
|
254
|
+
if (isError(months)) {
|
|
255
|
+
return months;
|
|
256
|
+
}
|
|
257
|
+
const d = toDate(startDate.value);
|
|
258
|
+
const result = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth() + months.value, d.getUTCDate()));
|
|
259
|
+
return rvNumber(fromDate(result));
|
|
260
|
+
};
|
|
261
|
+
export const fnDATEDIF = args => {
|
|
262
|
+
const startN = argToNumber(args[0]);
|
|
263
|
+
if (isError(startN)) {
|
|
264
|
+
return startN;
|
|
265
|
+
}
|
|
266
|
+
const endN = argToNumber(args[1]);
|
|
267
|
+
if (isError(endN)) {
|
|
268
|
+
return endN;
|
|
269
|
+
}
|
|
270
|
+
// DATEDIF requires end >= start; otherwise #NUM! per Excel.
|
|
271
|
+
if (endN.value < startN.value) {
|
|
272
|
+
return ERRORS.NUM;
|
|
273
|
+
}
|
|
274
|
+
const unit = toStringRV(args[2]).toUpperCase();
|
|
275
|
+
const startD = toDate(startN.value);
|
|
276
|
+
const endD = toDate(endN.value);
|
|
277
|
+
const sy = startD.getUTCFullYear();
|
|
278
|
+
const sm = startD.getUTCMonth();
|
|
279
|
+
const sd = startD.getUTCDate();
|
|
280
|
+
const ey = endD.getUTCFullYear();
|
|
281
|
+
const em = endD.getUTCMonth();
|
|
282
|
+
const ed = endD.getUTCDate();
|
|
283
|
+
switch (unit) {
|
|
284
|
+
case "Y":
|
|
285
|
+
return rvNumber(ey - sy - (em < sm || (em === sm && ed < sd) ? 1 : 0));
|
|
286
|
+
case "M":
|
|
287
|
+
return rvNumber((ey - sy) * 12 + em - sm - (ed < sd ? 1 : 0));
|
|
288
|
+
case "D":
|
|
289
|
+
return rvNumber(Math.floor((endD.getTime() - startD.getTime()) / 86400000));
|
|
290
|
+
case "MD": {
|
|
291
|
+
// Days between the dates, ignoring months and years.
|
|
292
|
+
// If end.day >= start.day → ed - sd; otherwise borrow days from the
|
|
293
|
+
// previous month of endD (last-day-of-prev-month - start.day + end.day).
|
|
294
|
+
if (ed >= sd) {
|
|
295
|
+
return rvNumber(ed - sd);
|
|
296
|
+
}
|
|
297
|
+
// days in the month before endD's month
|
|
298
|
+
const daysInPrevMonth = new Date(Date.UTC(ey, em, 0)).getUTCDate();
|
|
299
|
+
return rvNumber(daysInPrevMonth - sd + ed);
|
|
300
|
+
}
|
|
301
|
+
case "YM": {
|
|
302
|
+
// Months between the dates, ignoring days and years.
|
|
303
|
+
let months = em - sm;
|
|
304
|
+
if (ed < sd) {
|
|
305
|
+
months -= 1;
|
|
306
|
+
}
|
|
307
|
+
if (months < 0) {
|
|
308
|
+
months += 12;
|
|
309
|
+
}
|
|
310
|
+
return rvNumber(months);
|
|
311
|
+
}
|
|
312
|
+
case "YD": {
|
|
313
|
+
// Days between the dates as though they were in the same year, ignoring years.
|
|
314
|
+
// Align endD to startD's year (or next year if end < start in same-year terms).
|
|
315
|
+
const sameYearEnd = Date.UTC(sy, em, ed);
|
|
316
|
+
const startUTC = Date.UTC(sy, sm, sd);
|
|
317
|
+
let diff = sameYearEnd - startUTC;
|
|
318
|
+
if (diff < 0) {
|
|
319
|
+
// end falls earlier in the year than start → roll forward one year
|
|
320
|
+
const nextYearEnd = Date.UTC(sy + 1, em, ed);
|
|
321
|
+
diff = nextYearEnd - startUTC;
|
|
322
|
+
}
|
|
323
|
+
return rvNumber(Math.floor(diff / 86400000));
|
|
324
|
+
}
|
|
325
|
+
default:
|
|
326
|
+
return ERRORS.NUM;
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
export const fnDAYS = args => {
|
|
330
|
+
const end = argToNumber(args[0]);
|
|
331
|
+
if (isError(end)) {
|
|
332
|
+
return end;
|
|
333
|
+
}
|
|
334
|
+
const start = argToNumber(args[1]);
|
|
335
|
+
if (isError(start)) {
|
|
336
|
+
return start;
|
|
337
|
+
}
|
|
338
|
+
return rvNumber(Math.floor(end.value) - Math.floor(start.value));
|
|
339
|
+
};
|
|
340
|
+
export const fnISOWEEKNUM = args => {
|
|
341
|
+
const n = argToNumber(args[0]);
|
|
342
|
+
if (isError(n)) {
|
|
343
|
+
return n;
|
|
344
|
+
}
|
|
345
|
+
const d = toDate(n.value);
|
|
346
|
+
const temp = new Date(d.getTime());
|
|
347
|
+
temp.setUTCDate(temp.getUTCDate() + 3 - ((temp.getUTCDay() + 6) % 7));
|
|
348
|
+
const week1 = new Date(Date.UTC(temp.getUTCFullYear(), 0, 4));
|
|
349
|
+
return rvNumber(1 +
|
|
350
|
+
Math.round(((temp.getTime() - week1.getTime()) / 86400000 - 3 + ((week1.getUTCDay() + 6) % 7)) / 7));
|
|
351
|
+
};
|
|
352
|
+
export const fnWEEKNUM = args => {
|
|
353
|
+
const n = argToNumber(args[0]);
|
|
354
|
+
if (isError(n)) {
|
|
355
|
+
return n;
|
|
356
|
+
}
|
|
357
|
+
const d = toDate(n.value);
|
|
358
|
+
const returnType = args.length > 1 ? argToNumber(args[1]) : rvNumber(1);
|
|
359
|
+
if (isError(returnType)) {
|
|
360
|
+
return returnType;
|
|
361
|
+
}
|
|
362
|
+
const rt = returnType.value;
|
|
363
|
+
// Type 21 is ISO 8601 week.
|
|
364
|
+
if (rt === 21) {
|
|
365
|
+
return fnISOWEEKNUM(args);
|
|
366
|
+
}
|
|
367
|
+
// Excel maps `return_type` to the day-of-week that starts the week:
|
|
368
|
+
// 1 (default) → Sunday
|
|
369
|
+
// 2 or 11 → Monday
|
|
370
|
+
// 12 → Tuesday, 13 → Wednesday, … 17 → Saturday
|
|
371
|
+
// (16 → Friday, 17 → Saturday)
|
|
372
|
+
// Any other value is #NUM!.
|
|
373
|
+
let startDay; // 0 = Sunday … 6 = Saturday
|
|
374
|
+
switch (rt) {
|
|
375
|
+
case 1:
|
|
376
|
+
startDay = 0;
|
|
377
|
+
break;
|
|
378
|
+
case 2:
|
|
379
|
+
case 11:
|
|
380
|
+
startDay = 1;
|
|
381
|
+
break;
|
|
382
|
+
case 12:
|
|
383
|
+
startDay = 2;
|
|
384
|
+
break;
|
|
385
|
+
case 13:
|
|
386
|
+
startDay = 3;
|
|
387
|
+
break;
|
|
388
|
+
case 14:
|
|
389
|
+
startDay = 4;
|
|
390
|
+
break;
|
|
391
|
+
case 15:
|
|
392
|
+
startDay = 5;
|
|
393
|
+
break;
|
|
394
|
+
case 16:
|
|
395
|
+
startDay = 6;
|
|
396
|
+
break;
|
|
397
|
+
case 17:
|
|
398
|
+
startDay = 0;
|
|
399
|
+
break;
|
|
400
|
+
default:
|
|
401
|
+
return ERRORS.NUM;
|
|
402
|
+
}
|
|
403
|
+
const jan1 = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
|
|
404
|
+
const jan1Day = jan1.getUTCDay();
|
|
405
|
+
const dayOfYear = Math.floor((d.getTime() - jan1.getTime()) / 86400000);
|
|
406
|
+
return rvNumber(Math.floor((dayOfYear + ((jan1Day - startDay + 7) % 7)) / 7) + 1);
|
|
407
|
+
};
|
|
408
|
+
function networkdaysHelper(startN, endN, holidays) {
|
|
409
|
+
const s = Math.floor(Math.min(startN, endN));
|
|
410
|
+
const e = Math.floor(Math.max(startN, endN));
|
|
411
|
+
const sign = startN <= endN ? 1 : -1;
|
|
412
|
+
let count = 0;
|
|
413
|
+
for (let d = s; d <= e; d++) {
|
|
414
|
+
const dt = toDate(d);
|
|
415
|
+
const dow = dt.getUTCDay();
|
|
416
|
+
if (dow !== 0 && dow !== 6 && !holidays.has(d)) {
|
|
417
|
+
count++;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
return count * sign;
|
|
421
|
+
}
|
|
422
|
+
export const fnNETWORKDAYS = args => {
|
|
423
|
+
const startN = argToNumber(args[0]);
|
|
424
|
+
if (isError(startN)) {
|
|
425
|
+
return startN;
|
|
426
|
+
}
|
|
427
|
+
const endN = argToNumber(args[1]);
|
|
428
|
+
if (isError(endN)) {
|
|
429
|
+
return endN;
|
|
430
|
+
}
|
|
431
|
+
const holidays = args.length > 2 ? collectHolidays(args[2]) : new Set();
|
|
432
|
+
return rvNumber(networkdaysHelper(startN.value, endN.value, holidays));
|
|
433
|
+
};
|
|
434
|
+
export const fnWORKDAY = args => {
|
|
435
|
+
const startN = argToNumber(args[0]);
|
|
436
|
+
if (isError(startN)) {
|
|
437
|
+
return startN;
|
|
438
|
+
}
|
|
439
|
+
const days = argToNumber(args[1]);
|
|
440
|
+
if (isError(days)) {
|
|
441
|
+
return days;
|
|
442
|
+
}
|
|
443
|
+
const holidays = args.length > 2 ? collectHolidays(args[2]) : new Set();
|
|
444
|
+
let current = Math.floor(startN.value);
|
|
445
|
+
const step = days.value >= 0 ? 1 : -1;
|
|
446
|
+
let remaining = Math.abs(days.value);
|
|
447
|
+
while (remaining > 0) {
|
|
448
|
+
current += step;
|
|
449
|
+
const dt = toDate(current);
|
|
450
|
+
const dow = dt.getUTCDay();
|
|
451
|
+
if (dow !== 0 && dow !== 6 && !holidays.has(current)) {
|
|
452
|
+
remaining--;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
return rvNumber(current);
|
|
456
|
+
};
|
|
457
|
+
export const fnYEARFRAC = args => {
|
|
458
|
+
const startN = argToNumber(args[0]);
|
|
459
|
+
if (isError(startN)) {
|
|
460
|
+
return startN;
|
|
461
|
+
}
|
|
462
|
+
const endN = argToNumber(args[1]);
|
|
463
|
+
if (isError(endN)) {
|
|
464
|
+
return endN;
|
|
465
|
+
}
|
|
466
|
+
const basis = args.length > 2 ? argToNumber(args[2]) : rvNumber(0);
|
|
467
|
+
if (isError(basis)) {
|
|
468
|
+
return basis;
|
|
469
|
+
}
|
|
470
|
+
const sd = toDate(Math.min(startN.value, endN.value));
|
|
471
|
+
const ed = toDate(Math.max(startN.value, endN.value));
|
|
472
|
+
const diffDays = Math.abs(Math.floor(endN.value) - Math.floor(startN.value));
|
|
473
|
+
switch (basis.value) {
|
|
474
|
+
case 0: {
|
|
475
|
+
// US (NASD) 30/360
|
|
476
|
+
let d1 = sd.getUTCDate();
|
|
477
|
+
const m1 = sd.getUTCMonth() + 1;
|
|
478
|
+
const y1 = sd.getUTCFullYear();
|
|
479
|
+
let d2 = ed.getUTCDate();
|
|
480
|
+
const m2 = ed.getUTCMonth() + 1;
|
|
481
|
+
const y2 = ed.getUTCFullYear();
|
|
482
|
+
// NASD adjustment rules
|
|
483
|
+
if (d1 === 31) {
|
|
484
|
+
d1 = 30;
|
|
485
|
+
}
|
|
486
|
+
if (d2 === 31 && d1 >= 30) {
|
|
487
|
+
d2 = 30;
|
|
488
|
+
}
|
|
489
|
+
// Handle end-of-Feb for start date
|
|
490
|
+
const feb1 = new Date(Date.UTC(y1, 1, 29)).getUTCMonth() === 1 ? 29 : 28;
|
|
491
|
+
if (m1 === 2 && d1 === feb1) {
|
|
492
|
+
d1 = 30;
|
|
493
|
+
if (m2 === 2) {
|
|
494
|
+
const feb2 = new Date(Date.UTC(y2, 1, 29)).getUTCMonth() === 1 ? 29 : 28;
|
|
495
|
+
if (d2 === feb2) {
|
|
496
|
+
d2 = 30;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
const days30_360 = (y2 - y1) * 360 + (m2 - m1) * 30 + (d2 - d1);
|
|
501
|
+
return rvNumber(days30_360 / 360);
|
|
502
|
+
}
|
|
503
|
+
case 1: {
|
|
504
|
+
// Actual/Actual (ISDA convention, matches Excel's behaviour).
|
|
505
|
+
//
|
|
506
|
+
// Same-year is easy: divide by the length of that calendar year.
|
|
507
|
+
// Across years we split the interval into its leap-year portion and
|
|
508
|
+
// non-leap-year portion, then sum `leapDays/366 + nonLeapDays/365`.
|
|
509
|
+
// This is the ISDA "Act/Act" rule; simple averaging of year lengths
|
|
510
|
+
// (the previous implementation) produces visibly wrong answers like
|
|
511
|
+
// `YEARFRAC("2020-01-01","2021-01-01",1) ≈ 1.001368` where Excel
|
|
512
|
+
// returns exactly 1.
|
|
513
|
+
const y1 = sd.getUTCFullYear();
|
|
514
|
+
const y2 = ed.getUTCFullYear();
|
|
515
|
+
if (y1 === y2) {
|
|
516
|
+
const yearDays = (Date.UTC(y1 + 1, 0, 1) - Date.UTC(y1, 0, 1)) / 86400000;
|
|
517
|
+
return rvNumber(diffDays / yearDays);
|
|
518
|
+
}
|
|
519
|
+
let leapDays = 0;
|
|
520
|
+
let nonLeapDays = 0;
|
|
521
|
+
const sdMs = sd.getTime();
|
|
522
|
+
const edMs = ed.getTime();
|
|
523
|
+
for (let y = y1; y <= y2; y++) {
|
|
524
|
+
const yStart = Math.max(sdMs, Date.UTC(y, 0, 1));
|
|
525
|
+
const yEnd = Math.min(edMs, Date.UTC(y + 1, 0, 1));
|
|
526
|
+
if (yEnd <= yStart) {
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
const d = (yEnd - yStart) / 86400000;
|
|
530
|
+
const isLeap = (y % 4 === 0 && y % 100 !== 0) || y % 400 === 0;
|
|
531
|
+
if (isLeap) {
|
|
532
|
+
leapDays += d;
|
|
533
|
+
}
|
|
534
|
+
else {
|
|
535
|
+
nonLeapDays += d;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
return rvNumber(leapDays / 366 + nonLeapDays / 365);
|
|
539
|
+
}
|
|
540
|
+
case 2: // Actual/360
|
|
541
|
+
return rvNumber(diffDays / 360);
|
|
542
|
+
case 3: // Actual/365
|
|
543
|
+
return rvNumber(diffDays / 365);
|
|
544
|
+
case 4: {
|
|
545
|
+
// European 30/360
|
|
546
|
+
const d1 = Math.min(sd.getUTCDate(), 30);
|
|
547
|
+
const d2 = Math.min(ed.getUTCDate(), 30);
|
|
548
|
+
const m1 = sd.getUTCMonth() + 1;
|
|
549
|
+
const m2 = ed.getUTCMonth() + 1;
|
|
550
|
+
const y1 = sd.getUTCFullYear();
|
|
551
|
+
const y2 = ed.getUTCFullYear();
|
|
552
|
+
const days30_360 = (y2 - y1) * 360 + (m2 - m1) * 30 + (d2 - d1);
|
|
553
|
+
return rvNumber(days30_360 / 360);
|
|
554
|
+
}
|
|
555
|
+
default:
|
|
556
|
+
return ERRORS.NUM;
|
|
557
|
+
}
|
|
558
|
+
};
|
|
559
|
+
export const fnDATEVALUE = args => {
|
|
560
|
+
const err = checkError(args[0]);
|
|
561
|
+
if (err) {
|
|
562
|
+
return err;
|
|
563
|
+
}
|
|
564
|
+
const text = toStringRV(args[0]).trim();
|
|
565
|
+
// Lotus 1-2-3 bug: "2/29/1900" or "February 29, 1900" etc. should return 60
|
|
566
|
+
const lotus29 = /^(2[/-]29[/-]1900|1900[/-]2[/-]29|1900[/-]02[/-]29|02[/-]29[/-]1900|Feb(ruary)?\s+29[,]?\s+1900)$/i;
|
|
567
|
+
if (lotus29.test(text)) {
|
|
568
|
+
return rvNumber(60);
|
|
569
|
+
}
|
|
570
|
+
const parsed = parseDateOnly(text);
|
|
571
|
+
if (!parsed) {
|
|
572
|
+
return ERRORS.VALUE;
|
|
573
|
+
}
|
|
574
|
+
return rvNumber(fromDate(new Date(Date.UTC(parsed.y, parsed.m - 1, parsed.d))));
|
|
575
|
+
};
|
|
576
|
+
export const fnTIMEVALUE = args => {
|
|
577
|
+
const err = checkError(args[0]);
|
|
578
|
+
if (err) {
|
|
579
|
+
return err;
|
|
580
|
+
}
|
|
581
|
+
const text = toStringRV(args[0]).trim();
|
|
582
|
+
const parsed = parseTimeOnly(text);
|
|
583
|
+
if (parsed === null) {
|
|
584
|
+
return ERRORS.VALUE;
|
|
585
|
+
}
|
|
586
|
+
return rvNumber(parsed);
|
|
587
|
+
};
|
|
588
|
+
// ============================================================================
|
|
589
|
+
// Date / Time parsing (deterministic, independent of host locale)
|
|
590
|
+
//
|
|
591
|
+
// `new Date(text)` is unreliable across engines (Chrome parses "1/2/3" as
|
|
592
|
+
// US MDY, Node varies by version, and locale influences both). We hand-
|
|
593
|
+
// roll a small parser that accepts the formats Excel's DATEVALUE does and
|
|
594
|
+
// rejects everything else.
|
|
595
|
+
// ============================================================================
|
|
596
|
+
const MONTH_NAMES = {
|
|
597
|
+
jan: 1,
|
|
598
|
+
january: 1,
|
|
599
|
+
feb: 2,
|
|
600
|
+
february: 2,
|
|
601
|
+
mar: 3,
|
|
602
|
+
march: 3,
|
|
603
|
+
apr: 4,
|
|
604
|
+
april: 4,
|
|
605
|
+
may: 5,
|
|
606
|
+
jun: 6,
|
|
607
|
+
june: 6,
|
|
608
|
+
jul: 7,
|
|
609
|
+
july: 7,
|
|
610
|
+
aug: 8,
|
|
611
|
+
august: 8,
|
|
612
|
+
sep: 9,
|
|
613
|
+
sept: 9,
|
|
614
|
+
september: 9,
|
|
615
|
+
oct: 10,
|
|
616
|
+
october: 10,
|
|
617
|
+
nov: 11,
|
|
618
|
+
november: 11,
|
|
619
|
+
dec: 12,
|
|
620
|
+
december: 12
|
|
621
|
+
};
|
|
622
|
+
/** Parse a date-only string into `{y, m, d}` or `null` on rejection. */
|
|
623
|
+
function parseDateOnly(raw) {
|
|
624
|
+
const s = raw.trim();
|
|
625
|
+
// Strip a trailing time component (e.g. "2024-01-15 14:30") — but only
|
|
626
|
+
// split at a space that is followed by a digit, so a spelled-out date
|
|
627
|
+
// like "Jan 15, 2024" (which has month-name and day-number separated
|
|
628
|
+
// by spaces) survives for the "Mmm D, YYYY" matcher below.
|
|
629
|
+
// A naive `indexOf(" ")` truncated "Jan 15, 2024" to just "Jan".
|
|
630
|
+
const trailingTime = /^(.+?)\s(\d{1,2}:[\d:]+(?:\s*[AaPp]\.?[Mm]\.?)?)$/.exec(s);
|
|
631
|
+
const datePart = trailingTime ? trailingTime[1].trim() : s;
|
|
632
|
+
// ISO YYYY-MM-DD or YYYY/MM/DD
|
|
633
|
+
let m = /^(\d{4})[/-](\d{1,2})[/-](\d{1,2})$/.exec(datePart);
|
|
634
|
+
if (m) {
|
|
635
|
+
return validateYmd(+m[1], +m[2], +m[3]);
|
|
636
|
+
}
|
|
637
|
+
// US M/D/YYYY or M-D-YYYY
|
|
638
|
+
m = /^(\d{1,2})[/-](\d{1,2})[/-](\d{2,4})$/.exec(datePart);
|
|
639
|
+
if (m) {
|
|
640
|
+
let y = +m[3];
|
|
641
|
+
if (y < 100) {
|
|
642
|
+
// Excel's pivot: 00-29 → 2000s, 30-99 → 1900s
|
|
643
|
+
y += y < 30 ? 2000 : 1900;
|
|
644
|
+
}
|
|
645
|
+
return validateYmd(y, +m[1], +m[2]);
|
|
646
|
+
}
|
|
647
|
+
// D-Mmm-YY or D-Mmm-YYYY
|
|
648
|
+
m = /^(\d{1,2})[ /-]([A-Za-z]{3,9})[ /-]?(\d{2,4})?$/.exec(datePart);
|
|
649
|
+
if (m) {
|
|
650
|
+
const month = MONTH_NAMES[m[2].toLowerCase()];
|
|
651
|
+
if (!month) {
|
|
652
|
+
return null;
|
|
653
|
+
}
|
|
654
|
+
// Excel's DATEVALUE substitutes the host's current calendar year when
|
|
655
|
+
// the input omits a year (e.g. "15-Jan"). This matches Excel on the
|
|
656
|
+
// desktop, but note that the return value is not stable across time
|
|
657
|
+
// zones or years — tests that exercise this branch should freeze the
|
|
658
|
+
// clock (or supply a year) if they need reproducibility.
|
|
659
|
+
let y = m[3] ? +m[3] : new Date().getFullYear();
|
|
660
|
+
if (y < 100) {
|
|
661
|
+
y += y < 30 ? 2000 : 1900;
|
|
662
|
+
}
|
|
663
|
+
return validateYmd(y, month, +m[1]);
|
|
664
|
+
}
|
|
665
|
+
// "Mmm D, YYYY" or "Month D, YYYY"
|
|
666
|
+
m = /^([A-Za-z]{3,9})\s+(\d{1,2}),?\s+(\d{2,4})$/.exec(datePart);
|
|
667
|
+
if (m) {
|
|
668
|
+
const month = MONTH_NAMES[m[1].toLowerCase()];
|
|
669
|
+
if (!month) {
|
|
670
|
+
return null;
|
|
671
|
+
}
|
|
672
|
+
let y = +m[3];
|
|
673
|
+
if (y < 100) {
|
|
674
|
+
y += y < 30 ? 2000 : 1900;
|
|
675
|
+
}
|
|
676
|
+
return validateYmd(y, month, +m[2]);
|
|
677
|
+
}
|
|
678
|
+
return null;
|
|
679
|
+
}
|
|
680
|
+
/** Validate a calendar date and return null for out-of-range components. */
|
|
681
|
+
function validateYmd(y, mo, d) {
|
|
682
|
+
if (y < 0 || y > 9999 || mo < 1 || mo > 12 || d < 1 || d > 31) {
|
|
683
|
+
return null;
|
|
684
|
+
}
|
|
685
|
+
// Day-of-month range check using UTC (leap years included).
|
|
686
|
+
const dt = new Date(Date.UTC(y, mo - 1, d));
|
|
687
|
+
if (dt.getUTCFullYear() !== y || dt.getUTCMonth() !== mo - 1 || dt.getUTCDate() !== d) {
|
|
688
|
+
return null;
|
|
689
|
+
}
|
|
690
|
+
return { y, m: mo, d };
|
|
691
|
+
}
|
|
692
|
+
/** Parse a time-only string into a fraction-of-day in [0, 1). */
|
|
693
|
+
function parseTimeOnly(raw) {
|
|
694
|
+
// Optional AM/PM suffix; captured case-insensitively.
|
|
695
|
+
const m = /^(\d{1,2})(?::(\d{1,2}))?(?::(\d{1,2}(?:\.\d+)?))?(?:\s*([AaPp])\.?\s*[Mm]\.?)?$/.exec(raw);
|
|
696
|
+
if (!m) {
|
|
697
|
+
return null;
|
|
698
|
+
}
|
|
699
|
+
let h = +m[1];
|
|
700
|
+
const min = m[2] ? +m[2] : 0;
|
|
701
|
+
const sec = m[3] ? +m[3] : 0;
|
|
702
|
+
if (min >= 60 || sec >= 60 || min < 0 || sec < 0) {
|
|
703
|
+
return null;
|
|
704
|
+
}
|
|
705
|
+
if (m[4]) {
|
|
706
|
+
const pm = m[4].toLowerCase() === "p";
|
|
707
|
+
if (h < 1 || h > 12) {
|
|
708
|
+
return null;
|
|
709
|
+
}
|
|
710
|
+
if (pm && h < 12) {
|
|
711
|
+
h += 12;
|
|
712
|
+
}
|
|
713
|
+
else if (!pm && h === 12) {
|
|
714
|
+
h = 0;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
else {
|
|
718
|
+
if (h < 0 || h > 23) {
|
|
719
|
+
return null;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
return (h * 3600 + min * 60 + sec) / 86400;
|
|
723
|
+
}
|
|
724
|
+
// ============================================================================
|
|
725
|
+
// More Date/Time Functions
|
|
726
|
+
// ============================================================================
|
|
727
|
+
export const fnDAYS360 = args => {
|
|
728
|
+
const startN = argToNumber(args[0]);
|
|
729
|
+
if (isError(startN)) {
|
|
730
|
+
return startN;
|
|
731
|
+
}
|
|
732
|
+
const endN = argToNumber(args[1]);
|
|
733
|
+
if (isError(endN)) {
|
|
734
|
+
return endN;
|
|
735
|
+
}
|
|
736
|
+
const methodRV = args.length > 2 ? toBooleanRV(args[2]) : rvBoolean(false);
|
|
737
|
+
if (isError(methodRV)) {
|
|
738
|
+
return methodRV;
|
|
739
|
+
}
|
|
740
|
+
const method = methodRV.value;
|
|
741
|
+
const sd = toDate(startN.value);
|
|
742
|
+
const ed = toDate(endN.value);
|
|
743
|
+
let d1 = sd.getUTCDate();
|
|
744
|
+
let d2 = ed.getUTCDate();
|
|
745
|
+
const m1 = sd.getUTCMonth() + 1;
|
|
746
|
+
const m2 = ed.getUTCMonth() + 1;
|
|
747
|
+
const y1 = sd.getUTCFullYear();
|
|
748
|
+
const y2 = ed.getUTCFullYear();
|
|
749
|
+
if (method) {
|
|
750
|
+
if (d1 === 31) {
|
|
751
|
+
d1 = 30;
|
|
752
|
+
}
|
|
753
|
+
if (d2 === 31) {
|
|
754
|
+
d2 = 30;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
else {
|
|
758
|
+
if (d1 === 31) {
|
|
759
|
+
d1 = 30;
|
|
760
|
+
}
|
|
761
|
+
if (d2 === 31 && d1 >= 30) {
|
|
762
|
+
d2 = 30;
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
return rvNumber((y2 - y1) * 360 + (m2 - m1) * 30 + (d2 - d1));
|
|
766
|
+
};
|
|
767
|
+
/** Map weekend-type code to the set of day-of-week indices (0=Sun..6=Sat) that are weekends. */
|
|
768
|
+
function getWeekendDays(weekendType) {
|
|
769
|
+
switch (weekendType) {
|
|
770
|
+
case 1:
|
|
771
|
+
return new Set([0, 6]); // Sat, Sun
|
|
772
|
+
case 2:
|
|
773
|
+
return new Set([0, 1]); // Sun, Mon
|
|
774
|
+
case 3:
|
|
775
|
+
return new Set([1, 2]); // Mon, Tue
|
|
776
|
+
case 4:
|
|
777
|
+
return new Set([2, 3]); // Tue, Wed
|
|
778
|
+
case 5:
|
|
779
|
+
return new Set([3, 4]); // Wed, Thu
|
|
780
|
+
case 6:
|
|
781
|
+
return new Set([4, 5]); // Thu, Fri
|
|
782
|
+
case 7:
|
|
783
|
+
return new Set([5, 6]); // Fri, Sat
|
|
784
|
+
case 11:
|
|
785
|
+
return new Set([0]); // Sun only
|
|
786
|
+
case 12:
|
|
787
|
+
return new Set([1]); // Mon only
|
|
788
|
+
case 13:
|
|
789
|
+
return new Set([2]); // Tue only
|
|
790
|
+
case 14:
|
|
791
|
+
return new Set([3]); // Wed only
|
|
792
|
+
case 15:
|
|
793
|
+
return new Set([4]); // Thu only
|
|
794
|
+
case 16:
|
|
795
|
+
return new Set([5]); // Fri only
|
|
796
|
+
case 17:
|
|
797
|
+
return new Set([6]); // Sat only
|
|
798
|
+
default:
|
|
799
|
+
return new Set([0, 6]); // Default: Sat, Sun
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
export const fnNETWORKDAYS_INTL = args => {
|
|
803
|
+
const startN = argToNumber(args[0]);
|
|
804
|
+
if (isError(startN)) {
|
|
805
|
+
return startN;
|
|
806
|
+
}
|
|
807
|
+
const endN = argToNumber(args[1]);
|
|
808
|
+
if (isError(endN)) {
|
|
809
|
+
return endN;
|
|
810
|
+
}
|
|
811
|
+
const weekendArg = args.length > 2 ? argToNumber(args[2]) : rvNumber(1);
|
|
812
|
+
if (isError(weekendArg)) {
|
|
813
|
+
return weekendArg;
|
|
814
|
+
}
|
|
815
|
+
const holidays = args.length > 3 ? collectHolidays(args[3]) : new Set();
|
|
816
|
+
const weekendDays = getWeekendDays(weekendArg.value);
|
|
817
|
+
const s = Math.floor(Math.min(startN.value, endN.value));
|
|
818
|
+
const e = Math.floor(Math.max(startN.value, endN.value));
|
|
819
|
+
const sign = startN.value <= endN.value ? 1 : -1;
|
|
820
|
+
let count = 0;
|
|
821
|
+
for (let d = s; d <= e; d++) {
|
|
822
|
+
const dt = toDate(d);
|
|
823
|
+
if (!weekendDays.has(dt.getUTCDay()) && !holidays.has(d)) {
|
|
824
|
+
count++;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
return rvNumber(count * sign);
|
|
828
|
+
};
|
|
829
|
+
export const fnWORKDAY_INTL = args => {
|
|
830
|
+
const startN = argToNumber(args[0]);
|
|
831
|
+
if (isError(startN)) {
|
|
832
|
+
return startN;
|
|
833
|
+
}
|
|
834
|
+
const days = argToNumber(args[1]);
|
|
835
|
+
if (isError(days)) {
|
|
836
|
+
return days;
|
|
837
|
+
}
|
|
838
|
+
const weekendArg = args.length > 2 ? argToNumber(args[2]) : rvNumber(1);
|
|
839
|
+
if (isError(weekendArg)) {
|
|
840
|
+
return weekendArg;
|
|
841
|
+
}
|
|
842
|
+
const holidays = args.length > 3 ? collectHolidays(args[3]) : new Set();
|
|
843
|
+
const weekendDays = getWeekendDays(weekendArg.value);
|
|
844
|
+
let current = Math.floor(startN.value);
|
|
845
|
+
const step = days.value >= 0 ? 1 : -1;
|
|
846
|
+
let remaining = Math.abs(days.value);
|
|
847
|
+
while (remaining > 0) {
|
|
848
|
+
current += step;
|
|
849
|
+
const dt = toDate(current);
|
|
850
|
+
if (!weekendDays.has(dt.getUTCDay()) && !holidays.has(current)) {
|
|
851
|
+
remaining--;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
return rvNumber(current);
|
|
855
|
+
};
|