├── .editorconfig
├── .github
└── workflows
│ └── ci.yml
├── .gitignore
├── .gitmodules
├── .npmrc
├── .vscode
├── extensions.json
├── launch.json
└── settings.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── biome.json
├── examples
└── temporal-polyfill-cjs
│ ├── README.md
│ ├── index.ts
│ ├── package.json
│ └── tsconfig.json
├── lefthook.yml
├── package.json
├── packages
├── datetimeformat-tokens
│ ├── package.json
│ ├── scripts
│ │ └── compile-localeOrdinalsData.cjs
│ ├── src
│ │ ├── index.test.ts
│ │ ├── index.ts
│ │ ├── localeOrdinals.ts
│ │ ├── localeOrdinalsData.ts
│ │ ├── localeOrdinalsSpecial.ts
│ │ ├── ordinals.test.ts
│ │ └── ordinals.ts
│ └── tsconfig.json
├── durationformat-polyfill
│ ├── numberformat-worksheet.md
│ ├── numberformat.html
│ ├── package.json
│ ├── src
│ │ ├── index.test.ts
│ │ └── index.ts
│ └── tsconfig.json
├── locale-data
│ ├── locales
│ │ ├── af.json
│ │ ├── ar-DZ.json
│ │ ├── ar-KW.json
│ │ ├── ar-LY.json
│ │ ├── ar-MA.json
│ │ ├── ar-SA.json
│ │ ├── ar-TN.json
│ │ ├── ar.json
│ │ ├── az.json
│ │ ├── be.json
│ │ ├── bg.json
│ │ ├── bm.json
│ │ ├── bn-BD.json
│ │ ├── bn.json
│ │ ├── bo.json
│ │ ├── br.json
│ │ ├── bs.json
│ │ ├── ca.json
│ │ ├── cs.json
│ │ ├── cv.json
│ │ ├── cy.json
│ │ ├── da.json
│ │ ├── de-AT.json
│ │ ├── de-CH.json
│ │ ├── de.json
│ │ ├── dv.json
│ │ ├── el.json
│ │ ├── en-AU.json
│ │ ├── en-CA.json
│ │ ├── en-GB.json
│ │ ├── en-IE.json
│ │ ├── en-IL.json
│ │ ├── en-IN.json
│ │ ├── en-NZ.json
│ │ ├── en-SG.json
│ │ ├── en.json
│ │ ├── eo.json
│ │ ├── es-DO.json
│ │ ├── es-MX.json
│ │ ├── es-US.json
│ │ ├── es.json
│ │ ├── et.json
│ │ ├── eu.json
│ │ ├── fa.json
│ │ ├── fi.json
│ │ ├── fil.json
│ │ ├── fo.json
│ │ ├── fr-CA.json
│ │ ├── fr-CH.json
│ │ ├── fr.json
│ │ ├── fy.json
│ │ ├── ga.json
│ │ ├── gd.json
│ │ ├── gl.json
│ │ ├── gom-DEVA.json
│ │ ├── gom-LATN.json
│ │ ├── gu.json
│ │ ├── he.json
│ │ ├── hi.json
│ │ ├── hr.json
│ │ ├── hu.json
│ │ ├── hy-AM.json
│ │ ├── id.json
│ │ ├── is.json
│ │ ├── it-CH.json
│ │ ├── it.json
│ │ ├── ja.json
│ │ ├── jv.json
│ │ ├── ka.json
│ │ ├── kk.json
│ │ ├── km.json
│ │ ├── kn.json
│ │ ├── ko.json
│ │ ├── ku.json
│ │ ├── ky.json
│ │ ├── lb.json
│ │ ├── lo.json
│ │ ├── lt.json
│ │ ├── lv.json
│ │ ├── me.json
│ │ ├── mi.json
│ │ ├── mk.json
│ │ ├── ml.json
│ │ ├── mn.json
│ │ ├── mr.json
│ │ ├── ms-MY.json
│ │ ├── ms.json
│ │ ├── mt.json
│ │ ├── my.json
│ │ ├── nb.json
│ │ ├── ne.json
│ │ ├── nl-BE.json
│ │ ├── nl.json
│ │ ├── nn.json
│ │ ├── oc-LNC.json
│ │ ├── pa-IN.json
│ │ ├── pl.json
│ │ ├── pt-BR.json
│ │ ├── pt.json
│ │ ├── ro.json
│ │ ├── ru.json
│ │ ├── sd.json
│ │ ├── se.json
│ │ ├── si.json
│ │ ├── sk.json
│ │ ├── sl.json
│ │ ├── sq.json
│ │ ├── sr-CYRL.json
│ │ ├── sr.json
│ │ ├── ss.json
│ │ ├── sv.json
│ │ ├── sw.json
│ │ ├── ta.json
│ │ ├── te.json
│ │ ├── tet.json
│ │ ├── tg.json
│ │ ├── th.json
│ │ ├── tk.json
│ │ ├── tl-PH.json
│ │ ├── tlh.json
│ │ ├── tr.json
│ │ ├── tzl.json
│ │ ├── tzm-LATN.json
│ │ ├── tzm.json
│ │ ├── ug-CN.json
│ │ ├── uk.json
│ │ ├── ur.json
│ │ ├── uz-LATN.json
│ │ ├── uz.json
│ │ ├── vi.json
│ │ ├── x-PSEUDO.json
│ │ ├── yo.json
│ │ ├── zh-CN.json
│ │ ├── zh-HK.json
│ │ ├── zh-MO.json
│ │ └── zh-TW.json
│ ├── package.json
│ └── scripts
│ │ ├── lib
│ │ └── localesList.cjs
│ │ └── localesScrape.cjs
├── locale-textinfo
│ ├── package.json
│ ├── scripts
│ │ └── compile-directions.cjs
│ ├── src
│ │ ├── direction.ts
│ │ ├── index.test.ts
│ │ └── index.ts
│ └── tsconfig.json
├── locale-weekinfo
│ ├── package.json
│ ├── scripts
│ │ ├── compile-firstDays.cjs
│ │ └── compile-minimalDays.cjs
│ ├── src
│ │ ├── firstDay.ts
│ │ ├── index.ts
│ │ └── minimalDays.ts
│ └── tsconfig.json
├── temporal-polyfill
│ ├── biome.json
│ ├── dist
│ │ └── .npmignore
│ ├── manual.html
│ ├── package.json
│ ├── scripts
│ │ ├── bundle.js
│ │ ├── clean.js
│ │ ├── lib
│ │ │ ├── config.js
│ │ │ ├── pure-top-level.js
│ │ │ ├── terser-simple.js
│ │ │ └── utils.js
│ │ ├── manifest.js
│ │ ├── size.js
│ │ ├── test262-config
│ │ │ ├── expected-failures-builtin-calls.txt
│ │ │ ├── expected-failures-descriptor.txt
│ │ │ ├── expected-failures-node-gte16.txt
│ │ │ ├── expected-failures-node-gte18.txt
│ │ │ ├── expected-failures-node-gte22.txt
│ │ │ ├── expected-failures-node-lte14.txt
│ │ │ ├── expected-failures-node-lte16.txt
│ │ │ ├── expected-failures-node-lte18.txt
│ │ │ ├── expected-failures-node-lte20.txt
│ │ │ └── expected-failures.txt
│ │ └── test262.js
│ ├── src
│ │ ├── classApi
│ │ │ ├── calendarArg.ts
│ │ │ ├── calendarRefiners.ts
│ │ │ ├── duration.ts
│ │ │ ├── instant.ts
│ │ │ ├── intlDateTimeFormat.ts
│ │ │ ├── intlExtended.ts
│ │ │ ├── intlFormatConfig.ts
│ │ │ ├── mixins.ts
│ │ │ ├── now.ts
│ │ │ ├── plainDate.ts
│ │ │ ├── plainDateTime.ts
│ │ │ ├── plainMonthDay.ts
│ │ │ ├── plainTime.ts
│ │ │ ├── plainYearMonth.ts
│ │ │ ├── slotClass.ts
│ │ │ ├── temporal.ts
│ │ │ ├── timeZoneArg.ts
│ │ │ └── zonedDateTime.ts
│ │ ├── funcApi
│ │ │ ├── calendarUtils.ts
│ │ │ ├── diffUtils.ts
│ │ │ ├── duration.test.ts
│ │ │ ├── duration.ts
│ │ │ ├── instant.test.ts
│ │ │ ├── instant.ts
│ │ │ ├── intlFormatCache.ts
│ │ │ ├── moveUtils.ts
│ │ │ ├── now.test.ts
│ │ │ ├── now.ts
│ │ │ ├── plainDate.test.ts
│ │ │ ├── plainDate.ts
│ │ │ ├── plainDateTime.test.ts
│ │ │ ├── plainDateTime.ts
│ │ │ ├── plainMonthDay.test.ts
│ │ │ ├── plainMonthDay.ts
│ │ │ ├── plainTime.test.ts
│ │ │ ├── plainTime.ts
│ │ │ ├── plainYearMonth.test.ts
│ │ │ ├── plainYearMonth.ts
│ │ │ ├── roundUtils.ts
│ │ │ ├── testUtils.ts
│ │ │ ├── timeZone.test.ts
│ │ │ ├── timeZone.ts
│ │ │ ├── zonedDateTime.test.ts
│ │ │ └── zonedDateTime.ts
│ │ ├── global.ts
│ │ ├── impl.test.ts
│ │ ├── impl.ts
│ │ ├── index.ts
│ │ ├── internal
│ │ │ ├── bagRefine.ts
│ │ │ ├── bigNano.ts
│ │ │ ├── calendarConfig.ts
│ │ │ ├── calendarId.ts
│ │ │ ├── calendarNative.ts
│ │ │ ├── calendarNativeQuery.ts
│ │ │ ├── calendarOps.ts
│ │ │ ├── cast.ts
│ │ │ ├── compare.ts
│ │ │ ├── construct.ts
│ │ │ ├── convert.ts
│ │ │ ├── current.ts
│ │ │ ├── diff.ts
│ │ │ ├── durationFields.ts
│ │ │ ├── durationMath.ts
│ │ │ ├── errorMessages.ts
│ │ │ ├── fields.ts
│ │ │ ├── intlFormatPrep.ts
│ │ │ ├── intlFormatUtils.ts
│ │ │ ├── intlMath.ts
│ │ │ ├── isoFields.ts
│ │ │ ├── isoFormat.ts
│ │ │ ├── isoMath.ts
│ │ │ ├── isoParse.ts
│ │ │ ├── markerSystem.ts
│ │ │ ├── modify.ts
│ │ │ ├── move.ts
│ │ │ ├── options.ts
│ │ │ ├── optionsRefine.ts
│ │ │ ├── round.ts
│ │ │ ├── slots.ts
│ │ │ ├── timeMath.ts
│ │ │ ├── timeZoneConfig.ts
│ │ │ ├── timeZoneId.ts
│ │ │ ├── timeZoneNative.ts
│ │ │ ├── timeZoneOps.ts
│ │ │ ├── total.ts
│ │ │ ├── units.ts
│ │ │ └── utils.ts
│ │ └── typeOverrides
│ │ │ ├── global.ts
│ │ │ ├── impl.ts
│ │ │ └── index.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── vitest.config.js
├── temporal-spec
│ ├── LICENSE
│ ├── global.cjs
│ ├── global.d.cts
│ ├── global.d.ts
│ ├── global.js
│ ├── index.cjs
│ ├── index.d.cts
│ ├── index.d.ts
│ ├── index.js
│ └── package.json
└── temporal-utils
│ ├── package.json
│ ├── src
│ ├── diff.test.ts
│ ├── diff.ts
│ ├── endOf.test.ts
│ ├── endOf.ts
│ ├── index.ts
│ ├── round.test.ts
│ ├── round.ts
│ ├── startOf.test.ts
│ ├── startOf.ts
│ ├── utils.ts
│ ├── with.test.ts
│ └── with.ts
│ ├── tsconfig.json
│ └── vitest.config.js
├── patches
└── rollup-plugin-sourcemaps@0.6.3.patch
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
└── tsconfig.base.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | charset = utf-8
7 | indent_style = space
8 | indent_size = 2
9 | end_of_line = lf
10 | insert_final_newline = true
11 | trim_trailing_whitespace = true
12 | quote_type = single
13 | trim_semicolon = true
14 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 |
2 | name: CI
3 |
4 | on:
5 | push:
6 | branches:
7 | - main
8 |
9 | env:
10 | TZ: "America/New_York"
11 |
12 | jobs:
13 | ci:
14 | name: CI
15 | runs-on: ubuntu-latest
16 |
17 | strategy:
18 | fail-fast: false
19 | matrix:
20 | TEST262_NODE_VERSION: [14.21.3, 16.20.0, 18.19.0, 20.10.0, 22.14.0]
21 | include:
22 | - TEST262_NODE_VERSION: 14.21.3
23 | LINTING: 1
24 | - TEST262_NODE_VERSION: 16.20.0
25 | TEST262_MIN: 1
26 | - TEST262_NODE_VERSION: 18.19.0
27 | TEST262_ESM: 1
28 | - TEST262_NODE_VERSION: 20.10.0
29 | TEST262_ESM: terser
30 | - TEST262_NODE_VERSION: 22.14.0
31 | TEST262_ESM: swc
32 |
33 | steps:
34 | - name: Checkout
35 | uses: actions/checkout@v3
36 | with:
37 | submodules: recursive
38 |
39 | - name: Setup PNPM
40 | uses: pnpm/action-setup@v4
41 | with:
42 | version: 9.14.4
43 |
44 | - name: Setup Node
45 | uses: actions/setup-node@v3
46 | with:
47 | # NOTE: keep synced with .npmrc
48 | node-version: 22.14.0
49 | cache: 'pnpm'
50 |
51 | - name: Install dependencies
52 | run: pnpm install
53 |
54 | - name: Lint
55 | if: matrix.LINTING
56 | run: pnpm run lint
57 |
58 | - name: Build
59 | run: >
60 | TEST262_ESM=${{ matrix.TEST262_ESM }}
61 | pnpm run build
62 |
63 | - name: Test
64 | run: >
65 | TEST262_NODE_VERSION=${{ matrix.TEST262_NODE_VERSION }}
66 | TEST262_MIN=${{ matrix.TEST262_MIN }}
67 | TEST262_ESM=${{ matrix.TEST262_ESM }}
68 | pnpm run test262
69 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
3 | /packages/*/dist/*
4 | /!packages/*/dist/.npmignore
5 | /packages/*/export-size-output
6 |
7 | /examples/*/dist
8 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "scripts/data/moment"]
2 | path = packages/locale-data/data/moment
3 | url = https://github.com/moment/moment.git
4 | [submodule "scripts/data/fullcalendar"]
5 | path = packages/locale-data/data/fullcalendar
6 | url = https://github.com/fullcalendar/fullcalendar.git
7 | [submodule "test262"]
8 | path = test262
9 | url = https://github.com/tc39/test262.git
10 | [submodule "packages/export-size"]
11 | path = packages/export-size
12 | url = https://github.com/fullcalendar/export-size.git
13 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | prefer-workspace-packages = true
2 | engine-strict = true
3 |
4 | # NOTE: keep synced with ci.yml
5 | use-node-version = ${PNPM_NODE_VERSION:-22.14.0}
6 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "biomejs.biome"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "configurations": [
3 | {
4 | "name": "Test262: Debug File",
5 | "type": "node",
6 | "request": "launch",
7 | "cwd": "${workspaceFolder}/packages/temporal-polyfill",
8 | "runtimeExecutable": "pnpm",
9 | "runtimeArgs": ["run", "test262", "--no-timeout", "${file}"],
10 | "console": "integratedTerminal",
11 | "internalConsoleOptions": "neverOpen",
12 | "autoAttachChildProcesses": true,
13 | "outFiles": [
14 | "${workspaceFolder}/packages/temporal-polyfill/dist/**/*.(m|c|)js"
15 | ]
16 | },
17 | {
18 | "name": "Test262: Debug File (Node 16)",
19 | "type": "node",
20 | "request": "launch",
21 | "cwd": "${workspaceFolder}/packages/temporal-polyfill",
22 | "runtimeVersion": "16",
23 | "runtimeExecutable": "npm",
24 | "runtimeArgs": ["run", "test262", "--", "--no-timeout", "${file}"],
25 | "console": "integratedTerminal",
26 | "internalConsoleOptions": "neverOpen",
27 | "autoAttachChildProcesses": true,
28 | "outFiles": [
29 | "${workspaceFolder}/packages/temporal-polyfill/dist/**/*.(m|c|)js"
30 | ]
31 | },
32 | {
33 | "name": "Test262: Debug File (Node 14)",
34 | "type": "node",
35 | "request": "launch",
36 | "cwd": "${workspaceFolder}/packages/temporal-polyfill",
37 | "runtimeVersion": "14",
38 | "runtimeExecutable": "npm",
39 | "runtimeArgs": ["run", "test262", "--", "--no-timeout", "${file}"],
40 | "console": "integratedTerminal",
41 | "internalConsoleOptions": "neverOpen",
42 | "autoAttachChildProcesses": true,
43 | "outFiles": [
44 | "${workspaceFolder}/packages/temporal-polyfill/dist/**/*.(m|c|)js"
45 | ]
46 | },
47 | {
48 | "name": "Vitest: Debug File",
49 | "type": "node",
50 | "request": "launch",
51 | "cwd": "${fileDirname}",
52 | "runtimeExecutable": "pnpm",
53 | "runtimeArgs": ["run", "vitest:debug", "${file}"],
54 | "console": "integratedTerminal",
55 | "autoAttachChildProcesses": true,
56 | "smartStep": true
57 | }
58 | ]
59 | }
60 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.rulers": [80],
3 | "editor.formatOnSave": true,
4 | "editor.defaultFormatter": "biomejs.biome",
5 | "editor.codeActionsOnSave": {
6 | "quickfix.biome": "explicit",
7 | "source.organizeImports.biome": "explicit"
8 | },
9 | "search.exclude": {
10 | "**/node_modules": true,
11 | "**/*.code-search": true,
12 | "packages/locale-data/data/**": true
13 | },
14 | "typescript.enablePromptUseWorkspaceTsdk": true,
15 | "typescript.tsdk": "packages/temporal-polyfill/node_modules/typescript/lib",
16 | "biome.lspBin": "packages/temporal-polyfill/node_modules/@biomejs/biome/bin/biome"
17 | }
18 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Adam Shaw
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://biomejs.dev/schemas/1.5.1/schema.json",
3 | "files": {
4 | "ignore": [
5 | "**/node_modules/**",
6 | "**/dist/**",
7 | "**/export-size-output/**",
8 | "test262",
9 | "packages/export-size",
10 | "packages/temporal-spec",
11 | "packages/temporal-test262-runner"
12 | ]
13 | },
14 | "linter": {
15 | "rules": {
16 | "complexity": {
17 | "noBannedTypes": "off",
18 | "useLiteralKeys": "off",
19 | "useOptionalChain": "off"
20 | },
21 | "correctness": {
22 | "noUnusedVariables": "error"
23 | },
24 | "performance": {
25 | "noDelete": "off"
26 | },
27 | "suspicious": {
28 | "noAssignInExpressions": "off",
29 | "noConstEnum": "off",
30 | "noExplicitAny": "off",
31 | "noGlobalIsNan": "off",
32 | "noMisleadingInstantiator": "off",
33 | "noShadowRestrictedNames": "off",
34 | "noUnsafeDeclarationMerging": "off"
35 | },
36 | "style": {
37 | "noCommaOperator": "off",
38 | "noNonNullAssertion": "off",
39 | "noParameterAssign": "off",
40 | "useTemplate": "off"
41 | }
42 | }
43 | },
44 | "formatter": {
45 | "indentStyle": "space"
46 | },
47 | "javascript": {
48 | "formatter": {
49 | "quoteStyle": "single",
50 | "quoteProperties": "preserve",
51 | "semicolons": "asNeeded"
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/examples/temporal-polyfill-cjs/README.md:
--------------------------------------------------------------------------------
1 | # Test case for [#35] (FIXED)
2 |
3 | ```shell-session
4 | $ pnpm run typecheck
5 |
6 | > temporal-polyfill-bug@1.0.0 typecheck /src/temporal-polyfill-bug
7 | > tsc --build tsconfig.json
8 |
9 | index.ts:1:26 - error TS1479: The current file is a CommonJS module whose imports will produce 'require' calls; however, the referenced file is an ECMAScript module and cannot be imported with 'require'. Consider writing a dynamic 'import("temporal-polyfill")' call instead.
10 | To convert this file to an ECMAScript module, change its file extension to '.mts', or add the field `"type": "module"` to '/src/temporal-polyfill-bug/package.json'.
11 |
12 | 1 import { Temporal } from 'temporal-polyfill';
13 | ~~~~~~~~~~~~~~~~~~~
14 |
15 |
16 | Found 1 error.
17 |
18 | ELIFECYCLE Command failed with exit code 1.
19 | ```
20 |
21 | [#35]: https://github.com/fullcalendar/temporal-polyfill/issues/35
22 |
--------------------------------------------------------------------------------
/examples/temporal-polyfill-cjs/index.ts:
--------------------------------------------------------------------------------
1 | import { Temporal } from 'temporal-polyfill';
2 |
3 | console.log(Temporal.Now.instant().toString());
--------------------------------------------------------------------------------
/examples/temporal-polyfill-cjs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "temporal-polyfill-cjs",
4 | "version": "0.0.0",
5 | "description": "",
6 | "type": "commonjs",
7 | "main": "index.js",
8 | "scripts": {
9 | "build": "tsc --build tsconfig.json",
10 | "clean": "rm -rf dist"
11 | },
12 | "keywords": [],
13 | "author": "",
14 | "license": "ISC",
15 | "dependencies": {
16 | "temporal-polyfill": "workspace:*"
17 | },
18 | "devDependencies": {
19 | "@types/node": "^20.11.30",
20 | "typescript": "^5.4.3"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/examples/temporal-polyfill-cjs/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig.json",
3 | "compilerOptions": {
4 | "lib": ["es2022"],
5 | "module": "node16",
6 | "moduleResolution": "node16",
7 | "strict": true,
8 | "target": "es2022",
9 | "types": ["node"],
10 | "outDir": "dist",
11 | "declaration": true
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/lefthook.yml:
--------------------------------------------------------------------------------
1 | pre-push:
2 | commands:
3 | lint:
4 | run: pnpm run lint
5 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "temporal-workspace",
3 | "private": true,
4 | "author": {
5 | "name": "Adam Shaw",
6 | "email": "arshaw@users.noreply.github.com",
7 | "url": "http://arshaw.com/"
8 | },
9 | "contributors": [
10 | {
11 | "name": "Ishaan Bharal",
12 | "email": "ishbharal@gmail.com"
13 | }
14 | ],
15 | "license": "MIT",
16 | "copyright": "2024 Adam Shaw",
17 | "type": "module",
18 | "scripts": {
19 | "preinstall": "npx only-allow pnpm",
20 | "postinstall": "lefthook install",
21 | "ci": "pnpm run clean && pnpm run lint && pnpm run build && pnpm run test",
22 | "lint": "pnpm -r run lint",
23 | "build": "pnpm -r run build",
24 | "test": "pnpm -r --stream run test",
25 | "test262": "pnpm -r --stream run test262",
26 | "clean": "pnpm -r run clean"
27 | },
28 | "devDependencies": {
29 | "lefthook": "^1.6.1"
30 | },
31 | "pnpm": {
32 | "patchedDependencies": {
33 | "rollup-plugin-sourcemaps@0.6.3": "patches/rollup-plugin-sourcemaps@0.6.3.patch"
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/packages/datetimeformat-tokens/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "datetimeformat-tokens",
3 | "version": "0.0.0",
4 | "title": "DateTimeFormat Tokens",
5 | "author": {
6 | "name": "Ishaan Bharal",
7 | "email": "ishbharal@gmail.com"
8 | },
9 | "contributors": [
10 | {
11 | "name": "Adam Shaw",
12 | "email": "arshaw@users.noreply.github.com",
13 | "url": "http://arshaw.com/"
14 | }
15 | ],
16 | "license": "MIT",
17 | "copyright": "2024 Adam Shaw",
18 | "type": "module",
19 | "main": "./dist/index.cjs",
20 | "module": "./dist/index.mjs",
21 | "types": "./dist/index.d.ts",
22 | "exports": {
23 | "require": "./dist/index.cjs",
24 | "import": "./dist/index.mjs"
25 | },
26 | "files": ["/dist", "/src"],
27 | "scripts": {
28 | "compile-localeOrdinalsData": "node ./scripts/compile-localeOrdinalsData.cjs",
29 | "locales-compile": "pnpm run compile-localeOrdinalsData"
30 | },
31 | "dependencies": {
32 | "temporal-spec": "workspace:*"
33 | },
34 | "devDependencies": {
35 | "locale-data": "workspace:*",
36 | "temporal-polyfill": "workspace:*"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/datetimeformat-tokens/scripts/compile-localeOrdinalsData.cjs:
--------------------------------------------------------------------------------
1 | const { writeFileSync } = require('fs')
2 | const { resolve } = require('path')
3 | const { getAllLocalesData } = require('../../../scripts/lib/localesList.cjs')
4 |
5 | const ordinals = {}
6 |
7 | for (const [locale, { ordinal }] of Object.entries(getAllLocalesData())) {
8 | if (ordinal) {
9 | const existingKey = Object.keys(ordinals).find((key) => {
10 | return ordinals[key] === ordinal
11 | })
12 |
13 | if (existingKey) {
14 | ordinals[`${existingKey}|${locale}`] = ordinal
15 | delete ordinals[existingKey]
16 | } else {
17 | ordinals[locale] = ordinal
18 | }
19 | }
20 | }
21 |
22 | writeFileSync(resolve('src/localeOrdinalsData.ts'), templateCode(ordinals), {
23 | encoding: 'utf8',
24 | flag: 'w',
25 | })
26 |
27 | console.log('Wrote localeOrdinalsData.ts')
28 |
29 | function templateCode(obj) {
30 | return `export const localeOrdinalsData = ${JSON.stringify(obj, null, 2)}`
31 | }
32 |
--------------------------------------------------------------------------------
/packages/datetimeformat-tokens/src/index.test.ts:
--------------------------------------------------------------------------------
1 | import { Temporal } from 'temporal-polyfill'
2 | import { TokenDateTimeFormat } from './index'
3 |
4 | const { ZonedDateTime, PlainDateTime } = Temporal
5 |
6 | const plainDates = [
7 | new PlainDateTime(1970, 1, 1, 2, 46, 40),
8 | new PlainDateTime(2020, 1, 1, 0, 0, 0),
9 | new PlainDateTime(2000, 5, 5, 8, 32),
10 | new PlainDateTime(2021, 6, 1, 15, 18, 6),
11 | new PlainDateTime(2021, 1, 30, 1, 2, 3),
12 | new PlainDateTime(2021, 1, 8, 1, 1, 1),
13 | new PlainDateTime(2021, 8, 13, 1, 1, 1),
14 | ]
15 |
16 | test.each`
17 | tokenStr | date | expected
18 | ${'YYYY-MM-DD'} | ${plainDates[0]} | ${'1970-01-01'}
19 | ${'ddd D, M, YYYY'} | ${plainDates[1]} | ${'Wed 1, 1, 2020'}
20 | ${'hh:mm:ss'} | ${plainDates[0]} | ${'02:46:40'}
21 | ${'hh A'} | ${plainDates[2]} | ${'08 AM'}
22 | ${'HH a'} | ${plainDates[2]} | ${'08 am'}
23 | ${'E'} | ${plainDates[3]} | ${'2'}
24 | ${'W [weeks]'} | ${plainDates[4]} | ${'4 weeks'}
25 | ${'Do [day]'} | ${plainDates[4]} | ${'30th day'}
26 | ${'Wo [week]'} | ${plainDates[5]} | ${'1st week'}
27 | `(
28 | 'token string formatting($tokenStr) for PlainDateTime',
29 | ({ tokenStr, date, expected }) => {
30 | const formatter = new TokenDateTimeFormat(tokenStr)
31 | expect(formatter.format(date)).toBe(expected)
32 | },
33 | )
34 |
35 | const zonedDates = [
36 | new ZonedDateTime(0n, 'UTC'),
37 | new ZonedDateTime(BigInt(Date.UTC(2020, 0, 1)) * 1000000n, 'America/New_York'),
38 | new ZonedDateTime(BigInt(Date.UTC(1970, 0, 1, 2, 46, 40)) * 1000000n, 'America/New_York'),
39 | ]
40 |
41 | test.each`
42 | tokenStr | date | expected
43 | ${'YYYY-MM-DD'} | ${zonedDates[0]} | ${'1970-01-01'}
44 | ${'ddd D, M, YYYY'} | ${zonedDates[1]} | ${'Tue 31, 12, 2019'}
45 | ${'HH:mm:ss'} | ${zonedDates[2]} | ${'21:46:40'}
46 | `(
47 | 'token string formatting($tokenStr) for ZonedDateTime',
48 | ({ tokenStr, date, expected }) => {
49 | const formatter = new TokenDateTimeFormat(tokenStr)
50 | expect(formatter.format(date)).toBe(expected)
51 | },
52 | )
53 |
54 | test.each`
55 | tokenStr | date | expected
56 | ${'YYYY[YYYY]-MM[MM]-DD[DD]'} | ${zonedDates[0]} | ${'1970YYYY-01MM-01DD'}
57 | ${'YYYY[YYYY]-MM[MM]-DD[DD]'} | ${plainDates[0]} | ${'1970YYYY-01MM-01DD'}
58 | ${'YYYY[YYYY]-MM[MM]-DD[DD]'} | ${plainDates[6]} | ${'2021YYYY-08MM-13DD'}
59 | ${'[word] YYYY-MM-DD'} | ${plainDates[6]} | ${'word 2021-08-13'}
60 | ${'MM [a literal MMMM]'} | ${plainDates[6]} | ${'08 a literal MMMM'}
61 | `('escaped literals work for "%s"', ({ tokenStr, date, expected }) => {
62 | const formatter = new TokenDateTimeFormat(tokenStr)
63 | expect(formatter.format(date)).toBe(expected)
64 | })
65 |
--------------------------------------------------------------------------------
/packages/datetimeformat-tokens/src/localeOrdinals.ts:
--------------------------------------------------------------------------------
1 | import { localeOrdinalsData } from './localeOrdinalsData'
2 | import { Ordinals } from './ordinals'
3 |
4 | function expandOrdinals(): { [key: string]: Ordinals } {
5 | const obj = {} as { [key: string]: Ordinals }
6 |
7 | for (const key of Object.keys(localeOrdinalsData)) { // guarantees own properties
8 | const val = localeOrdinalsData[key as keyof typeof localeOrdinalsData]
9 |
10 | for (const locale of key.split('|')) {
11 | obj[locale] = val
12 | }
13 | }
14 |
15 | return obj
16 | }
17 |
18 | export const localeOrdinals = expandOrdinals()
19 |
--------------------------------------------------------------------------------
/packages/datetimeformat-tokens/src/localeOrdinalsData.ts:
--------------------------------------------------------------------------------
1 | export const localeOrdinalsData = {
2 | "cv": "-мӗш",
3 | "el": "η",
4 | "en": {
5 | "one": "st",
6 | "two": "nd",
7 | "few": "rd",
8 | "other": "th"
9 | },
10 | "eo": "a",
11 | "fa": "م",
12 | "fr": {
13 | "m": "er",
14 | "f": "re"
15 | },
16 | "es-DO|es-MX|es-US|es|gl|it-CH|it|mi|mt|pt-BR|pt": "º",
17 | "te": "వ",
18 | "bs|cs|da|de-AT|de-CH|de|et|eu|fi|fo|hr|hu|is|lb|lv|me|nb|nn|pl|se|sk|sl|sq|sr-CYRL|sr|tlh|tzl": "."
19 | }
20 |
--------------------------------------------------------------------------------
/packages/datetimeformat-tokens/src/localeOrdinalsSpecial.ts:
--------------------------------------------------------------------------------
1 | export const localeOrdinalsSpecial: {
2 | [key: string]: (
3 | ordinalData: any,
4 | num: number,
5 | unit: string,
6 | pluralRule: Intl.LDMLPluralRule
7 | ) => string
8 | } = {
9 | fr: (
10 | ordinalData: {
11 | m: string
12 | f: string
13 | },
14 | num: number,
15 | unit: string,
16 | ): string => {
17 | return num === 1 ? 'e' : ordinalData[unit === 'day' ? 'm' : 'f']
18 | },
19 | }
20 |
--------------------------------------------------------------------------------
/packages/datetimeformat-tokens/src/ordinals.test.ts:
--------------------------------------------------------------------------------
1 | import { getOrdinalForValue } from './ordinals'
2 |
3 | test.each`
4 | locale | num | unit | expected
5 | ${'et'} | ${1} | ${'day'} | ${'.'}
6 | ${'es'} | ${1} | ${'day'} | ${'º'}
7 | ${'en'} | ${1} | ${'day'} | ${'st'}
8 | ${'en'} | ${2} | ${'day'} | ${'nd'}
9 | ${'en-us'} | ${1} | ${'day'} | ${'st'}
10 | ${'fr'} | ${1} | ${'day'} | ${'e'}
11 | ${'fr'} | ${2} | ${'day'} | ${'er'}
12 | `(
13 | 'can get ordinal for \'$locale\' to be \'$expected\'',
14 | ({ locale, num, unit, expected }) => {
15 | expect(getOrdinalForValue(num, unit, locale)).toBe(expected)
16 | },
17 | )
18 |
--------------------------------------------------------------------------------
/packages/datetimeformat-tokens/src/ordinals.ts:
--------------------------------------------------------------------------------
1 | import { localeOrdinals } from './localeOrdinals'
2 | import { localeOrdinalsSpecial } from './localeOrdinalsSpecial'
3 |
4 | export type Ordinals = string | { [key: string]: string }
5 |
6 | export function getOrdinalForValue(
7 | num: number,
8 | unit: string,
9 | locale: string,
10 | ): string {
11 | // Use prefix as backup if specific locale cannot be found
12 | const prefix = locale.split('-')[0]
13 | const ordinals: Ordinals = localeOrdinals[locale] ?? localeOrdinals[prefix]
14 |
15 | // Short circuit to avoid PluralRules call in case of string
16 | if (typeof ordinals === 'string') {
17 | return ordinals
18 | }
19 |
20 | // Gets one of 'one', 'two', 'few', 'many', 'other'
21 | const count = new Intl.PluralRules(locale, { type: 'ordinal' }).select(num)
22 | const specialFunc =
23 | localeOrdinalsSpecial[locale] ?? localeOrdinalsSpecial[prefix]
24 |
25 | if (specialFunc) {
26 | // In this case ordinals refers only to the data part
27 | // Use prefix as backup if specific locale cannot be found
28 | return specialFunc(ordinals, num, unit, count)
29 | }
30 |
31 | // Default to using PluralRules
32 | return ordinals[count]
33 | }
34 |
--------------------------------------------------------------------------------
/packages/datetimeformat-tokens/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base",
3 | "compilerOptions": {
4 | "rootDir": "src",
5 | "outDir": "dist"
6 | },
7 | "include": ["src/**/*"],
8 | "references": [
9 | { "path": "../temporal-polyfill" }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/packages/durationformat-polyfill/numberformat-worksheet.md:
--------------------------------------------------------------------------------
1 |
2 | # Intl.NumberFormat Browser Support Worksheet
3 |
4 | ## Final Results
5 |
6 | ### Full Support
7 |
8 | Supports all duration units including `microsecond`/`nanoseconds`:
9 |
10 |
11 |
12 |
13 | Chrome |
14 | Firefox |
15 | Opera |
16 | Edge |
17 | Safari |
18 |
19 |
20 |
21 |
22 | 110 (Feb 2023) |
23 | 108 (Dec 2022) |
24 | 96 (Feb 2023) |
25 | 110 (Feb 2023) |
26 | 16.5 (May 2023) |
27 |
28 |
29 |
30 |
31 | ### Partial Support
32 |
33 | Supports unis `year`/`month`/`week`/`day`/`hour`/`minute`/`second`/`millisecond`:
34 |
35 |
36 |
37 |
38 | Chrome |
39 | Firefox |
40 | Opera |
41 | Edge |
42 | Safari |
43 |
44 |
45 |
46 |
47 | 77 (Sep 2019) |
48 | 78 (Jun 2020) |
49 | 64 (Oct 2019) |
50 | 79 (Jan 2020) |
51 | 14.1 (Apr 2021) |
52 |
53 |
54 |
55 |
56 | ### If Leveraging `Intl.RelativeTimeFormat` Instead
57 |
58 |
59 |
60 |
61 | Chrome |
62 | Firefox |
63 | Opera |
64 | Edge |
65 | Safari |
66 |
67 |
68 |
69 |
70 | 71 (Dec 2018) |
71 | 65 (Jan 2019) |
72 | 58 (Jan 2019) |
73 | 79 (Jan 2020) |
74 | 14 (Sep 2020) |
75 |
76 |
77 |
78 |
79 |
80 | ## Via LambdaTest Screenshot Testing
81 |
82 | Marks the *first* browser version that supports:
83 |
84 | Chrome (Windows 11)
85 | - 110 - full support
86 | - 77 - support w/o sub-milliseconds
87 |
88 | Firefox (Windows 11)
89 | - 108 - full support
90 | - 78 - support w/o sub-milliseconds
91 |
92 | Opera (Windows 11)
93 | - 96 - full support
94 | - 64 - support w/o sub-milliseconds
95 |
96 | Edge (Windows 11)
97 | - 110 - full support
98 | - 79 - support w/o sub-milliseconds (lowest possible in LambdaTest)
99 |
100 | Safari (MacOS)
101 | - 16.6 (Ventura) - full support
102 | - 14.1.1 (Big Sur) - support w/o sub-milliseconds
103 |
104 | Safari (iOS) - *confusing version numbers*
105 |
106 |
107 | ## Via BrowserStack Live Testing
108 |
109 | Grab bag of versions:
110 |
111 | Safari (MacOS)
112 | - 16.5 - yes
113 | - 15.6 - yes, no sub-milliseconds
114 | - 14.1 - yes, no sub-milliseconds
115 | - 13.1 - no
116 |
117 | Safari (iOS)
118 | - 16.3 - yes, no sub-milliseconds
119 | - 14.1 - yes, no sub-milliseconds
120 | - 15.5 - yes, no sub-milliseconds
121 | - 15.4 - yes, no sub-milliseconds
122 | - 15.3 - yes, no sub-milliseconds
123 | - 14.1.2 - yes, no sub-milliseconds
124 | - 14.0.3 - no
125 | - 14.0.1 - no
126 | - 14.0 - no
127 | - 13.1 - no
128 | - 11.0 - no
129 |
130 | ## Resources
131 |
132 | - [StackOverflow](https://stackoverflow.com/questions/60566942/why-doesnt-intl-numberformat-work-with-units-in-safari-and-firefox/60588156#60588156)
133 | - [V8 Intl.NumberFormat](https://v8.dev/features/intl-numberformat)
134 | - [Safari 14.1](https://developer.apple.com/documentation/safari-release-notes/safari-14_1-release-notes/)
135 |
--------------------------------------------------------------------------------
/packages/durationformat-polyfill/numberformat.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
15 |
16 |
17 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/packages/durationformat-polyfill/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "durationformat-polyfill",
3 | "version": "0.0.0",
4 | "title": "DurationFormat Polyfill",
5 | "author": {
6 | "name": "Ishaan Bharal",
7 | "email": "ishbharal@gmail.com"
8 | },
9 | "contributors": [
10 | {
11 | "name": "Adam Shaw",
12 | "email": "arshaw@users.noreply.github.com",
13 | "url": "http://arshaw.com/"
14 | }
15 | ],
16 | "license": "MIT",
17 | "copyright": "2024 Adam Shaw",
18 | "type": "module",
19 | "main": "./dist/index.cjs",
20 | "module": "./dist/index.mjs",
21 | "types": "./dist/index.d.ts",
22 | "exports": {
23 | "require": "./dist/index.cjs",
24 | "import": "./dist/index.mjs"
25 | },
26 | "files": ["/dist", "/src"],
27 | "dependencies": {
28 | "temporal-spec": "workspace:*"
29 | },
30 | "devDependencies": {
31 | "temporal-polyfill": "workspace:*"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/packages/durationformat-polyfill/src/index.test.ts:
--------------------------------------------------------------------------------
1 | import { Temporal } from 'temporal-polyfill'
2 | import { DurationFormat } from './index'
3 |
4 | const { Duration } = Temporal
5 |
6 | test.each`
7 | dur | expected
8 | ${new Duration(1)} | ${[{
9 | type: 'integer',
10 | value: '1',
11 | unit: 'year',
12 | }, {
13 | type: 'literal',
14 | value: ' year',
15 | }]}
16 | ${new Duration(3)} | ${[{
17 | type: 'integer',
18 | value: '3',
19 | unit: 'year',
20 | }, {
21 | type: 'literal',
22 | value: ' years',
23 | }]}
24 | ${new Duration(0, 0, 0, 0, 0, 52)} | ${[{
25 | type: 'integer',
26 | value: '52',
27 | unit: 'minute',
28 | }, {
29 | type: 'literal',
30 | value: ' minutes',
31 | }]}
32 | ${new Duration(0, 1, 0, 1)} | ${[{
33 | type: 'integer',
34 | value: '1',
35 | unit: 'month',
36 | }, {
37 | type: 'literal',
38 | value: ' month and ',
39 | }, {
40 | type: 'integer',
41 | value: '1',
42 | unit: 'day',
43 | }, {
44 | type: 'literal',
45 | value: ' day',
46 | }]}
47 | `('can formatToParts $dur', ({ dur, expected }) => {
48 | const formatter = new DurationFormat()
49 | expect(formatter.formatToParts(dur)).toEqual(expected)
50 | })
51 |
52 | test.each`
53 | dur | expected
54 | ${new Duration(0, 1, 0, 1)} | ${'1 month and 1 day'}
55 | ${new Duration(0, 0, 1, 5)} | ${'1 week and 5 days'}
56 | ${new Duration(2, 2, 2, 2, 2, 2, 2, 2)} | ${'2 years, 2 months, 2 weeks, 2 days, 2 hours, 2 minutes, and 2 seconds'}
57 | `('can format $dur', ({ dur, expected }) => {
58 | const formatter = new DurationFormat()
59 | expect(formatter.format(dur)).toEqual(expected)
60 | })
61 |
62 | test.each`
63 | dur | locale | expected
64 | ${new Duration(1, 1, 1)} | ${'fr'} | ${'1 an, 1 mois et 1 semaine'}
65 | ${new Duration(0, 0, 0, 0, 1, 10)} | ${'es'} | ${'1 hora y 10 minutos'}
66 | ${new Duration(0, 2, 0, 5)} | ${'ja'} | ${'2 か月、5 日'}
67 | `('can format $dur using \'$locale\'', ({ dur, locale, expected }) => {
68 | const formatter = new DurationFormat(locale)
69 | expect(formatter.format(dur)).toEqual(expected)
70 | })
71 |
--------------------------------------------------------------------------------
/packages/durationformat-polyfill/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base",
3 | "compilerOptions": {
4 | "rootDir": "src",
5 | "outDir": "dist"
6 | },
7 | "include": ["src/**/*"],
8 | "references": [
9 | { "path": "../temporal-polyfill" }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/packages/locale-data/locales/af.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/ar-DZ.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "rtl"
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/ar-KW.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "rtl"
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 1
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/ar-LY.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "rtl"
4 | },
5 | "week": {
6 | "firstDay": 7,
7 | "minimalDays": 1
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/ar-MA.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "rtl"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/ar-SA.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "rtl"
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 6
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/ar-TN.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "rtl"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/ar.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "rtl"
4 | },
5 | "week": {
6 | "firstDay": 7,
7 | "minimalDays": 1
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/az.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 7
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/be.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 7
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/bg.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 7
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/bm.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/bn-BD.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 6
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/bn.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 6
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/bo.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 6
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/br.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/bs.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 7
8 | },
9 | "ordinal": "."
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/ca.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/cs.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": "."
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/cv.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 7
8 | },
9 | "ordinal": "-мӗш"
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/cy.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/da.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": "."
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/de-AT.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": "."
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/de-CH.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": "."
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/de.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": "."
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/dv.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 8,
7 | "minimalDays": 1
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/el.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": "η"
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/en-AU.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/en-CA.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 6
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/en-GB.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/en-IE.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/en-IL.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 6
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/en-IN.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 6
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/en-NZ.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/en-SG.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 4
8 | },
9 | "ordinal": {
10 | "one": "st",
11 | "two": "nd",
12 | "few": "rd",
13 | "other": "th"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/packages/locale-data/locales/eo.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 7
8 | },
9 | "ordinal": "a"
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/es-DO.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": "º"
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/es-MX.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 4
8 | },
9 | "ordinal": "º"
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/es-US.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 6
8 | },
9 | "ordinal": "º"
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/es.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": "º"
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/et.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": "."
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/eu.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 7
8 | },
9 | "ordinal": "."
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/fa.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "rtl"
4 | },
5 | "week": {
6 | "firstDay": 7,
7 | "minimalDays": 1
8 | },
9 | "ordinal": "م"
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/fi.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": "."
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/fil.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/fo.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": "."
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/fr-CA.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 6
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/fr-CH.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/fr.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": {
10 | "m": "er",
11 | "f": "re"
12 | }
13 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/fy.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/ga.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/gd.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/gl.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": "º"
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/gom-DEVA.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 3
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/gom-LATN.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 3
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/gu.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 6
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/he.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "rtl"
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 6
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/hi.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 6
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/hr.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 7
8 | },
9 | "ordinal": "."
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/hu.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": "."
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/hy-AM.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 7
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/id.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 6
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/is.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": "."
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/it-CH.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": "º"
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/it.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": "º"
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/ja.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 6
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/jv.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 7
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/ka.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 7
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/kk.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 7
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/km.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/kn.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 6
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/ko.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 6
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/ku.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 7,
7 | "minimalDays": 1
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/ky.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 7
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/lb.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": "."
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/lo.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 6
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/lt.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/lv.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": "."
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/me.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 7
8 | },
9 | "ordinal": "."
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/mi.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": "º"
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/mk.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 7
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/ml.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 6
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/mn.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 6
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/mr.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 6
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/ms-MY.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 7
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/ms.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 7
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/mt.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": "º"
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/my.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/nb.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": "."
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/ne.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 6
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/nl-BE.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/nl.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/nn.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": "."
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/oc-LNC.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/pa-IN.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 6
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/pl.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": "."
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/pt-BR.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 6
8 | },
9 | "ordinal": "º"
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/pt.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": "º"
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/ro.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 7
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/ru.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/sd.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/se.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": "."
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/si.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 6
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/sk.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": "."
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/sl.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 7
8 | },
9 | "ordinal": "."
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/sq.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": "."
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/sr-CYRL.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 7
8 | },
9 | "ordinal": "."
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/sr.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 7
8 | },
9 | "ordinal": "."
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/ss.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": ""
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/sv.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/sw.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 7
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/ta.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 6
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/te.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 6
8 | },
9 | "ordinal": "వ"
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/tet.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/tg.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 7
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/th.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 6
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/tk.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 7
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/tl-PH.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/tlh.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": "."
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/tr.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 7
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/tzl.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": "."
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/tzm-LATN.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 7,
7 | "minimalDays": 1
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/tzm.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 7,
7 | "minimalDays": 1
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/ug-CN.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 7
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/uk.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 7
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/ur.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/uz-LATN.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 7
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/uz.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 7
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/vi.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/x-PSEUDO.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/yo.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/zh-CN.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 2,
7 | "minimalDays": 4
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/zh-HK.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 6
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/zh-MO.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": null
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 6
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/locales/zh-TW.json:
--------------------------------------------------------------------------------
1 | {
2 | "text": {
3 | "direction": "ltr"
4 | },
5 | "week": {
6 | "firstDay": 1,
7 | "minimalDays": 6
8 | },
9 | "ordinal": null
10 | }
--------------------------------------------------------------------------------
/packages/locale-data/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "locale-data",
3 | "version": "0.0.0",
4 | "dependencies": {
5 | "colors": "^1.4.0",
6 | "deepmerge": "^4.2.2",
7 | "yargs": "^17.0.1"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/packages/locale-data/scripts/lib/localesList.cjs:
--------------------------------------------------------------------------------
1 | const { readFileSync, readdirSync } = require('fs')
2 | const { resolve } = require('path')
3 | const { fileURLToPath } = require('url')
4 |
5 | // Get localeRoot relative to this file
6 | const localeRoot = resolve(fileURLToPath(import.meta.url), '../../../locales')
7 |
8 | module.exports = {
9 | listLocales,
10 | getAllLocalesData,
11 | mapLocaleProperty,
12 | }
13 |
14 | function listLocales() {
15 | const localeList = readdirSync(localeRoot)
16 |
17 | // Error out if no locales
18 | if (localeList.length === 0) {
19 | console.error('Locales have not been generated')
20 | process.exit()
21 | }
22 |
23 | return localeList
24 | }
25 |
26 | function getAllLocalesData() {
27 | const obj = {}
28 |
29 | for (const fileName of listLocales()) {
30 | const json = JSON.parse(
31 | readFileSync(resolve(localeRoot, fileName), {
32 | encoding: 'utf8',
33 | }),
34 | )
35 |
36 | obj[fileName.replace('.json', '')] = json
37 | }
38 |
39 | return obj
40 | }
41 |
42 | /**
43 | * Creates a map of locales with a property as keys
44 | * @param getProp {(locale: string, json: unknown) => string} Function to fetch property from json
45 | */
46 | function mapLocaleProperty(
47 | getProp = (locale) => locale,
48 | ) {
49 | const obj = {}
50 |
51 | for (const [locale, json] of Object.entries(getAllLocalesData())) {
52 | const prop = getProp(locale, json)
53 |
54 | if (obj[prop]) {
55 | obj[prop].push(locale)
56 | } else {
57 | obj[prop] = [locale]
58 | }
59 | }
60 |
61 | return obj
62 | }
63 |
--------------------------------------------------------------------------------
/packages/locale-textinfo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "locale-textinfo",
3 | "version": "0.0.0",
4 | "title": "Locale textInfo",
5 | "author": {
6 | "name": "Ishaan Bharal",
7 | "email": "ishbharal@gmail.com"
8 | },
9 | "contributors": [
10 | {
11 | "name": "Adam Shaw",
12 | "email": "arshaw@users.noreply.github.com",
13 | "url": "http://arshaw.com/"
14 | }
15 | ],
16 | "license": "MIT",
17 | "copyright": "2024 Adam Shaw",
18 | "type": "module",
19 | "main": "./dist/index.cjs",
20 | "module": "./dist/index.mjs",
21 | "types": "./dist/index.d.ts",
22 | "exports": {
23 | "require": "./dist/index.cjs",
24 | "import": "./dist/index.mjs"
25 | },
26 | "files": ["/src", "/dist"],
27 | "scripts": {
28 | "compile-directions": "node ./scripts/compile-directions.cjs",
29 | "locales-compile": "pnpm run compile-directions"
30 | },
31 | "devDependencies": {
32 | "locale-data": "workspace:*"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/packages/locale-textinfo/scripts/compile-directions.cjs:
--------------------------------------------------------------------------------
1 | const { writeFileSync } = require('fs')
2 | const { resolve } = require('path')
3 | const { getAllLocalesData } = require('../../../scripts/lib/localesList.cjs')
4 |
5 | const locales = getAllLocalesData()
6 | const rtlArr = []
7 |
8 | for (const locale in locales) {
9 | const direction = locales[locale].text.direction
10 |
11 | if (direction === 'rtl') {
12 | const prefix = locale.split('-')[0]
13 |
14 | // Checks if either value is a prefix or if the values direction is 'rtl'
15 | // as compared to the prefix's 'ltr'
16 | if (locale === prefix || locales[prefix].text.direction !== direction) {
17 | rtlArr.push(locale)
18 | }
19 | }
20 | }
21 |
22 | writeFileSync(resolve('src/direction.ts'), templateCode(rtlArr), {
23 | encoding: 'utf8',
24 | flag: 'w',
25 | })
26 |
27 | console.log('Wrote direction.ts')
28 |
29 | function templateCode(arr) {
30 | return `
31 | export function getDirection(locale: string): 'ltr' | 'rtl' {
32 | return locale.match(/^((?:${arr.join('|')})(?:-\\w{2})?)$/)
33 | ? 'rtl'
34 | : 'ltr'
35 | }
36 | `
37 | }
38 |
--------------------------------------------------------------------------------
/packages/locale-textinfo/src/direction.ts:
--------------------------------------------------------------------------------
1 |
2 | export function getDirection(locale: string): 'ltr' | 'rtl' {
3 | return locale.match(/^((?:ar|fa|he)(?:-\w{2})?)$/)
4 | ? 'rtl'
5 | : 'ltr'
6 | }
7 |
--------------------------------------------------------------------------------
/packages/locale-textinfo/src/index.test.ts:
--------------------------------------------------------------------------------
1 | import { getLocaleTextInfo } from './index'
2 |
3 | test.each`
4 | locale | direction
5 | ${'af'} | ${'ltr'}
6 | ${'ar'} | ${'rtl'}
7 | ${'en-au'} | ${'ltr'}
8 | ${'gu'} | ${'ltr'}
9 | `('can get locale($locale) direction($direction)', ({ locale, direction }) => {
10 | expect(getLocaleTextInfo(locale)).toEqual({ direction })
11 | })
12 |
--------------------------------------------------------------------------------
/packages/locale-textinfo/src/index.ts:
--------------------------------------------------------------------------------
1 | import { getDirection } from './direction'
2 |
3 | type LocaleTextInfo = {
4 | direction: 'ltr' | 'rtl'
5 | }
6 |
7 | export function getLocaleTextInfo(locale: string): LocaleTextInfo {
8 | return { direction: getDirection(locale) }
9 | }
10 |
--------------------------------------------------------------------------------
/packages/locale-textinfo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base",
3 | "compilerOptions": {
4 | "rootDir": "src",
5 | "outDir": "dist"
6 | },
7 | "include": ["src/**/*"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/locale-weekinfo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "locale-weekinfo",
3 | "version": "0.0.0",
4 | "title": "Locale weekInfo",
5 | "author": {
6 | "name": "Ishaan Bharal",
7 | "email": "ishbharal@gmail.com"
8 | },
9 | "contributors": [
10 | {
11 | "name": "Adam Shaw",
12 | "email": "arshaw@users.noreply.github.com",
13 | "url": "http://arshaw.com/"
14 | }
15 | ],
16 | "license": "MIT",
17 | "copyright": "2024 Adam Shaw",
18 | "type": "module",
19 | "main": "./dist/index.cjs",
20 | "module": "./dist/index.mjs",
21 | "types": "./dist/index.d.ts",
22 | "exports": {
23 | "require": "./dist/index.cjs",
24 | "import": "./dist/index.mjs"
25 | },
26 | "files": ["/dist", "/src"],
27 | "scripts": {
28 | "compile-firstDays": "node ./scripts/compile-firstDays.cjs",
29 | "compile-minimalDays": "node ./scripts/compile-minimalDays.cjs",
30 | "locales-compile": "pnpm compile-firstDays && pnpm compile-minimalDays"
31 | },
32 | "devDependencies": {
33 | "locale-data": "workspace:*"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/packages/locale-weekinfo/scripts/compile-firstDays.cjs:
--------------------------------------------------------------------------------
1 | const { writeFileSync } = require('fs')
2 | const { resolve } = require('path')
3 | const { mapLocaleProperty } = require('../../../scripts/lib/localesList.cjs')
4 |
5 | const fdObj = mapLocaleProperty((_locale, json) => {
6 | return json.week.firstDay
7 | })
8 |
9 | const sortedFirstDays = Object.entries(fdObj).sort(([, a], [, b]) => {
10 | return b.length - a.length
11 | })
12 | const largest = sortedFirstDays[0][0]
13 | const condArr = []
14 |
15 | for (const [day, locales] of sortedFirstDays) {
16 | // Short circuit for largest firstDay
17 | if (day === largest) {
18 | continue
19 | }
20 |
21 | // Filter out locales that are the same as prefix
22 | const noRepeatLocales = locales.filter((val) => {
23 | const prefix = val.split('-')[0]
24 | return val === prefix || !locales.includes(prefix)
25 | })
26 |
27 | condArr.push(templateConditional(day, noRepeatLocales))
28 | }
29 |
30 | writeFileSync(resolve('src/firstDay.ts'), templateCode(condArr, largest), {
31 | encoding: 'utf8',
32 | flag: 'w',
33 | })
34 | console.log('Wrote firstDay.ts')
35 |
36 | function templateCode(conditionals, largest) {
37 | return `
38 | export function getFirstDay(locale: string): number {
39 | ${conditionals.join(' } else ')} }
40 |
41 | return ${largest}
42 | }
43 | `
44 | }
45 |
46 | function templateConditional(day, locales) {
47 | return `if (locale.match(/^((?:${locales.join('|')})(?:-\\w{2})?)$/)) {
48 | return ${day}
49 | `
50 | }
51 |
--------------------------------------------------------------------------------
/packages/locale-weekinfo/scripts/compile-minimalDays.cjs:
--------------------------------------------------------------------------------
1 | const { writeFileSync } = require('fs')
2 | const { resolve } = require('path')
3 | const { mapLocaleProperty } = require('../../../scripts/lib/localesList.cjs')
4 |
5 | const mdObj = mapLocaleProperty((_locale, json) => {
6 | return json.week.minimalDays
7 | })
8 |
9 | const sortedMinimalDays = Object.entries(mdObj).sort(([, a], [, b]) => {
10 | return b.length - a.length
11 | })
12 | const largest = sortedMinimalDays[0][0]
13 | const condArr = []
14 |
15 | for (const [day, locales] of sortedMinimalDays) {
16 | // Short circuit for largest minimalDay
17 | if (day === largest) {
18 | continue
19 | }
20 |
21 | // Filter out locales that are the same as prefix
22 | const noRepeatLocales = locales.filter((val) => {
23 | const prefix = val.split('-')[0]
24 | return val === prefix || !locales.includes(prefix)
25 | })
26 |
27 | condArr.push(templateConditional(day, noRepeatLocales))
28 | }
29 |
30 | writeFileSync(resolve('src/minimalDays.ts'), templateCode(condArr, largest), {
31 | encoding: 'utf8',
32 | flag: 'w',
33 | })
34 |
35 | console.log('Wrote minimalDays.ts')
36 |
37 | function templateCode(conditionals, largest) {
38 | return `
39 | export function getMinimalDays(locale: string): number {
40 | ${conditionals.join(' } else ')} }
41 |
42 | return ${largest}
43 | }
44 | `
45 | }
46 |
47 | function templateConditional(day, locales) {
48 | return `if (locale.match(/^((?:${locales.join('|')})(?:-\\w{2})?)$/)) {
49 | return ${day}
50 | `
51 | }
52 |
--------------------------------------------------------------------------------
/packages/locale-weekinfo/src/firstDay.ts:
--------------------------------------------------------------------------------
1 |
2 | export function getFirstDay(locale: string): number {
3 | if (locale.match(/^((?:ar-DZ|ar-KW|ar-SA|bn|bo|en|es-MX|es-US|fr-CA|gom-DEVA|gom-LATN|gu|he|hi|id|ja|kn|ko|lo|ml|mn|mr|ne|pa-IN|pt-BR|si|ta|te|th|zh-HK|zh-MO|zh-TW)(?:-\w{2})?)$/)) {
4 | return 1
5 | } else if (locale.match(/^((?:ar|fa|ku|tzm)(?:-\w{2})?)$/)) {
6 | return 7
7 | } else if (locale.match(/^((?:dv)(?:-\w{2})?)$/)) {
8 | return 8
9 | }
10 |
11 | return 2
12 | }
13 |
--------------------------------------------------------------------------------
/packages/locale-weekinfo/src/index.ts:
--------------------------------------------------------------------------------
1 | import { getFirstDay } from './firstDay'
2 | import { getMinimalDays } from './minimalDays'
3 |
4 | type LocaleWeekInfo = {
5 | firstDay: number
6 | minimalDays: number
7 | }
8 |
9 | export function getLocaleWeekInfo(locale: string): LocaleWeekInfo {
10 | return { firstDay: getFirstDay(locale), minimalDays: getMinimalDays(locale) }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/locale-weekinfo/src/minimalDays.ts:
--------------------------------------------------------------------------------
1 |
2 | export function getMinimalDays(locale: string): number {
3 | if (locale.match(/^((?:ar-SA|bn|bo|en-CA|en-IL|en-IN|es-US|fr-CA|gu|he|hi|id|ja|kn|ko|lo|ml|mn|mr|ne|pa-IN|pt-BR|si|ta|te|th|zh-HK|zh-MO|zh-TW)(?:-\w{2})?)$/)) {
4 | return 6
5 | } else if (locale.match(/^((?:az|be|bg|bs|cv|eo|eu|hr|hy-AM|jv|ka|kk|ky|me|mk|ms|ro|sl|sr|sw|tg|tk|tr|ug-CN|uk|uz)(?:-\w{2})?)$/)) {
6 | return 7
7 | } else if (locale.match(/^((?:ar|dv|fa|ku|tzm)(?:-\w{2})?)$/)) {
8 | return 1
9 | } else if (locale.match(/^((?:gom-DEVA|gom-LATN)(?:-\w{2})?)$/)) {
10 | return 3
11 | }
12 |
13 | return 4
14 | }
15 |
--------------------------------------------------------------------------------
/packages/locale-weekinfo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base",
3 | "compilerOptions": {
4 | "rootDir": "src",
5 | "outDir": "dist"
6 | },
7 | "include": ["src/**/*"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://biomejs.dev/schemas/1.5.1/schema.json",
3 | "extends": ["../../biome.json"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/dist/.npmignore:
--------------------------------------------------------------------------------
1 | # ignore hidden files
2 | .*
3 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/manual.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
15 | Open the dev console and start using the global Temporal
object.
16 |
17 |
18 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "temporal-polyfill",
4 | "version": "0.3.0",
5 | "title": "Temporal Polyfill",
6 | "description": "A lightweight polyfill for Temporal, successor to the JavaScript Date object",
7 | "author": {
8 | "name": "Adam Shaw",
9 | "email": "arshaw@users.noreply.github.com",
10 | "url": "http://arshaw.com/"
11 | },
12 | "contributors": [
13 | {
14 | "name": "Ishaan Bharal",
15 | "email": "ishbharal@gmail.com"
16 | }
17 | ],
18 | "license": "MIT",
19 | "copyright": "2024 Adam Shaw",
20 | "repository": {
21 | "type": "git",
22 | "url": "https://github.com/fullcalendar/temporal-polyfill.git"
23 | },
24 | "scripts": {
25 | "lint": "biome check .",
26 | "build": "./scripts/clean.js && pnpm run tsc && concurrently -c auto npm:manifest npm:readme npm:license npm:bundle",
27 | "dev": "./scripts/clean.js && pnpm run tsc && concurrently -c auto npm:manifest:dev npm:tsc:dev npm:bundle:dev",
28 | "tsc": "tsc --build tsconfig.build.json",
29 | "tsc:dev": "tsc --build tsconfig.build.json --watch --preserveWatchOutput --pretty",
30 | "bundle": "./scripts/bundle.js",
31 | "bundle:dev": "./scripts/bundle.js --dev",
32 | "manifest": "./scripts/manifest.js",
33 | "manifest:dev": "./scripts/manifest.js --dev",
34 | "readme": "cp ../../README.md dist",
35 | "license": "cp ../../LICENSE dist",
36 | "test": "pnpm run vitest && pnpm run test262",
37 | "vitest": "vitest run",
38 | "vitest:dev": "vitest dev",
39 | "vitest:debug": "vitest run --no-isolate --no-file-parallelism --test-timeout 3600000",
40 | "test262": "./scripts/test262.js",
41 | "clean": "./scripts/clean.js --tsc",
42 | "size": "./scripts/size.js"
43 | },
44 | "type": "module",
45 | "buildConfig": {
46 | "exports": {
47 | ".": {
48 | "types": "typeOverrides/index"
49 | },
50 | "./impl": {
51 | "types": "typeOverrides/impl"
52 | },
53 | "./global": {
54 | "types": "typeOverrides/global",
55 | "iife": true
56 | }
57 | }
58 | },
59 | "disabledBuildConfig": {
60 | "exports": {
61 | "./fns/instant": {
62 | "src": "funcApi/instant"
63 | },
64 | "./fns/zoneddatetime": {
65 | "src": "funcApi/zonedDateTime"
66 | },
67 | "./fns/plaindatetime": {
68 | "src": "funcApi/plainDateTime"
69 | },
70 | "./fns/plaindate": {
71 | "src": "funcApi/plainDate"
72 | },
73 | "./fns/plaintime": {
74 | "src": "funcApi/plainTime"
75 | },
76 | "./fns/plainyearmonth": {
77 | "src": "funcApi/plainYearMonth"
78 | },
79 | "./fns/plainmonthday": {
80 | "src": "funcApi/plainMonthDay"
81 | },
82 | "./fns/duration": {
83 | "src": "funcApi/duration"
84 | },
85 | "./fns/now": {
86 | "src": "funcApi/now"
87 | }
88 | }
89 | },
90 | "publishConfig": {
91 | "directory": "./dist",
92 | "linkDirectory": true
93 | },
94 | "dependencies": {
95 | "temporal-spec": "workspace:0.3.0"
96 | },
97 | "devDependencies": {
98 | "@biomejs/biome": "1.5.1",
99 | "@js-temporal/temporal-test262-runner": "fullcalendar/temporal-test262-runner#main",
100 | "@swc/core": "1.4.11",
101 | "@types/node": "^18.11.9",
102 | "concurrently": "^8.2.0",
103 | "export-size": "workspace:*",
104 | "gzip-size-cli": "^5.1.0",
105 | "rollup": "^4.9.6",
106 | "rollup-plugin-dts": "^6.1.0",
107 | "rollup-plugin-sourcemaps": "^0.6.3",
108 | "rollup-plugin-swc3": "^0.11.0",
109 | "terser": "^5.27.0",
110 | "typescript": "~5.3.3",
111 | "vitest": "^1.2.2",
112 | "yargs": "^17.7.2"
113 | },
114 | "devDependenciesNotes": {
115 | "@biomejs/biome": [
116 | "BUG: Pinned to 1.5.1 due to this regression in 1.5.3:",
117 | "https://github.com/biomejs/biome/issues/1654"
118 | ],
119 | "@swc/core": [
120 | "Locked a version to test workaround for this bug:",
121 | "https://github.com/swc-project/swc/issues/8806"
122 | ]
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/scripts/clean.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | import { join as joinPaths } from 'path'
4 | import { readdir, rm } from 'fs/promises'
5 |
6 | cleanPkg(
7 | joinPaths(process.argv[1], '../..'),
8 | process.argv.slice(2).includes('--tsc'),
9 | )
10 |
11 | async function cleanPkg(pkgDir, cleanTsc) {
12 | const distDir = joinPaths(pkgDir, 'dist')
13 | const files = await readdir(distDir)
14 |
15 | for (const file of files) {
16 | if (
17 | file !== '.npmignore' &&
18 | (cleanTsc || !(file === '.tsc' || file.endsWith('.tsbuildinfo')))
19 | ) {
20 | await rm(joinPaths(distDir, file), { recursive: true })
21 | }
22 | }
23 |
24 | await rm(joinPaths(pkgDir, 'export-size-output'), {
25 | recursive: true,
26 | force: true,
27 | })
28 | }
29 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/scripts/lib/config.js:
--------------------------------------------------------------------------------
1 | export const extensions = {
2 | esm: '.js',
3 |
4 | // will cause all buildConfig paths with iife:true to ALSO be outputted
5 | // as an ESM module that can be imported by another script
6 | esmWhenIifePrefix: '.esm',
7 |
8 | cjs: '.cjs',
9 | iife: '.js',
10 | iifeMin: '.min.js',
11 | dts: '.d.ts',
12 | }
13 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/scripts/lib/pure-top-level.js:
--------------------------------------------------------------------------------
1 | const chunkRe = // groups:
2 | //12-----------3----------------------------45-------------6------------7------------------
3 | /^((export\s+)?(const|let|var)\s+\w+\s*=\s*)(([\w\.]+\()|\[([^\]]+)\]|\{([^\}]+)\}|new\s+)/gms
4 |
5 | const openingRe = /\{|\[|\(/
6 | const pureComment = '/*@__PURE__*/ '
7 |
8 | export function pureTopLevel() {
9 | return {
10 | name: 'pure-top-level',
11 | renderChunk(code) {
12 | return code.replace(chunkRe, (g0, g1, _g2, _g3, g4, g5, g6, g7) => {
13 | if (g5) {
14 | // function call
15 | return g1 + pureComment + g5
16 | }
17 | if (g6) {
18 | // array literal
19 | if (g6.includes('...')) {
20 | if (openingRe.test(g6)) {
21 | // console.warn('Complicated spread', g6)
22 | } else {
23 | return g1 + transformArraySpread(g6)
24 | }
25 | }
26 | } else if (g7) {
27 | // object literal
28 | if (g7.includes('...')) {
29 | if (openingRe.test(g7)) {
30 | // console.warn('Complicated spread', g7)
31 | } else {
32 | return g1 + transformObjectSpread(g7)
33 | }
34 | }
35 | } else {
36 | // `new` operator (no specific capture)
37 | return g1 + pureComment + g4
38 | }
39 | // no replacement, return whole match
40 | return g0
41 | })
42 | },
43 | }
44 | }
45 |
46 | // Transform
47 | // -----------------------------------------------------------------------------
48 |
49 | function transformArraySpread(s) {
50 | const [tokens, isMultiline] = parseTokens(s)
51 | const formatTokenAsArray = formatToken.bind(undefined, '[', ']', isMultiline)
52 |
53 | if (tokens.length === 1) {
54 | return formatTokenAsArray(tokens[0])
55 | }
56 |
57 | return (
58 | pureComment +
59 | formatTokenAsArray(tokens.shift()) +
60 | '.concat(' +
61 | tokens.map(formatTokenAsArray).join(', ') +
62 | ')'
63 | )
64 | }
65 |
66 | function transformObjectSpread(s) {
67 | const [tokens, isMultiline] = parseTokens(s)
68 | const formatTokenAsObject = formatToken.bind(undefined, '{', '}', isMultiline)
69 |
70 | if (tokens.length === 1) {
71 | return formatTokenAsObject(tokens[0])
72 | }
73 |
74 | return (
75 | pureComment +
76 | 'Object.assign(' +
77 | (Array.isArray(tokens[0]) ? '' : '{}, ') +
78 | tokens.map(formatTokenAsObject).join(', ') +
79 | ')'
80 | )
81 | }
82 |
83 | // Token Parsing
84 | // -----------------------------------------------------------------------------
85 |
86 | function parseTokens(s) {
87 | const tokens = consolidateTokens(parseIndividualTokens(s))
88 | const isMultiline = Boolean(s.match(/[\n\r]/))
89 | return [tokens, isMultiline]
90 | }
91 |
92 | function parseIndividualTokens(s) {
93 | const parts = s
94 | .split(',')
95 | .map((part) => part.trim())
96 | .filter((part) => Boolean(part))
97 |
98 | const tokens = parts.map((part) => {
99 | const m = part.match(/^\.\.\.(.*)$/)
100 | return m
101 | ? m[1] // spreading. get variable name
102 | : [part] // individual item
103 | })
104 |
105 | return tokens
106 | }
107 |
108 | function consolidateTokens(tokens) {
109 | const consolidated = []
110 | let currentGroup
111 |
112 | function depositCurrentGroup() {
113 | if (currentGroup) {
114 | consolidated.push(currentGroup)
115 | currentGroup = undefined
116 | }
117 | }
118 |
119 | for (const token of tokens) {
120 | if (Array.isArray(token)) {
121 | if (currentGroup) {
122 | currentGroup.push(...token)
123 | } else {
124 | currentGroup = token
125 | }
126 | } else {
127 | depositCurrentGroup()
128 | consolidated.push(token)
129 | }
130 | }
131 |
132 | depositCurrentGroup()
133 | return consolidated
134 | }
135 |
136 | // Formatting
137 | // -----------------------------------------------------------------------------
138 |
139 | function formatToken(open, close, isMultiline, token) {
140 | if (!Array.isArray(token)) {
141 | return token
142 | }
143 | return open + formatStrings(token, isMultiline) + close
144 | }
145 |
146 | function formatStrings(strings, isMultiline) {
147 | const pre = isMultiline ? ' ' : ''
148 | const pad = isMultiline ? '\n' : ' '
149 | return pad + pre + strings.join(',' + pad + pre) + pad
150 | }
151 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/scripts/lib/terser-simple.js:
--------------------------------------------------------------------------------
1 | import { minify } from 'terser'
2 |
3 | /*
4 | Official @rollup/plugin-terser package has bug with nameCache and parallel workers
5 | (even when maxWorkers=1). TODO: file a bug. Until it's fixed, use simplified plugin based on:
6 | https://github.com/rollup/plugins/blob/master/packages/terser/src/module.ts
7 | */
8 | export function terserSimple(options) {
9 | return {
10 | name: 'terser-simple',
11 | async renderChunk(code, _chunk, outputOptions) {
12 | const defaultOptions = {
13 | sourceMap:
14 | outputOptions.sourcemap === true ||
15 | typeof outputOptions.sourcemap === 'string',
16 | }
17 | if (outputOptions.format === 'es') {
18 | defaultOptions.module = true
19 | }
20 | if (outputOptions.format === 'cjs') {
21 | defaultOptions.toplevel = true
22 | }
23 |
24 | const mergedOptions = { ...defaultOptions, ...options }
25 | const { code: result, sourceMap } = await minify(code, mergedOptions)
26 |
27 | if (mergedOptions.sourceMap && typeof sourceMap === 'object') {
28 | return {
29 | code: result,
30 | map: sourceMap,
31 | }
32 | }
33 |
34 | return result
35 | },
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/scripts/lib/utils.js:
--------------------------------------------------------------------------------
1 | import { spawn } from 'child_process'
2 |
3 | export function execLive(cmdParts, options = {}) {
4 | return new Promise((resolve, reject) => {
5 | spawn(cmdParts[0], cmdParts.slice(1), {
6 | shell: false,
7 | stdio: 'inherit',
8 | ...options,
9 | }).on('close', (status) => {
10 | if (status === 0) {
11 | resolve()
12 | } else {
13 | reject(new Error(`Command failed with status code ${status}`))
14 | }
15 | })
16 | })
17 | }
18 |
19 | export function popFlag(argv, arg) {
20 | const i = argv.indexOf(arg)
21 | if (i !== -1) {
22 | argv.splice(i, 1)
23 | return true
24 | }
25 | return false
26 | }
27 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/scripts/manifest.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | import { join as joinPaths } from 'path'
4 | import { readFile, writeFile } from 'fs/promises'
5 | import { extensions } from './lib/config.js'
6 |
7 | writePkgJson(
8 | joinPaths(process.argv[1], '../..'),
9 | process.argv.slice(2).includes('--dev'),
10 | )
11 |
12 | async function writePkgJson(pkgDir, isDev) {
13 | const srcManifestPath = joinPaths(pkgDir, 'package.json')
14 | const distManifestPath = joinPaths(pkgDir, 'dist/package.json')
15 |
16 | const srcManifest = JSON.parse(await readFile(srcManifestPath))
17 | const distManifest = { ...srcManifest }
18 |
19 | const exportMap = srcManifest.buildConfig.exports
20 | const distExportMap = {}
21 | const sideEffectsList = []
22 |
23 | let rootCjsPath
24 | let rootEsmPath
25 | let rootTypesPath
26 | let rootIifeMinPath
27 |
28 | for (const exportPath in exportMap) {
29 | const exportConfig = exportMap[exportPath]
30 | const exportName =
31 | exportPath === '.' ? 'index' : exportPath.replace(/^\.\//, '')
32 |
33 | const esmExtension =
34 | (exportConfig.iife ? extensions.esmWhenIifePrefix : '') + extensions.esm
35 | const esmPath = './' + exportName + esmExtension
36 | const cjsPath = './' + exportName + extensions.cjs
37 | const typesPath = isDev
38 | ? './.tsc/' +
39 | (exportConfig.types || exportConfig.src || exportName) +
40 | extensions.dts
41 | : './' + exportName + extensions.dts
42 |
43 | distExportMap[exportPath] = {
44 | require: cjsPath,
45 | import:
46 | isDev || exportConfig.iife
47 | ? { types: typesPath, default: esmPath }
48 | : esmPath,
49 | }
50 |
51 | if (!rootCjsPath) {
52 | rootCjsPath = cjsPath
53 | }
54 | if (!rootEsmPath) {
55 | rootEsmPath = esmPath
56 | }
57 | if (!rootTypesPath) {
58 | rootTypesPath = typesPath
59 | }
60 |
61 | if (exportConfig.iife) {
62 | sideEffectsList.push(
63 | './' + exportName + extensions.cjs,
64 | './' + exportName + esmExtension,
65 | './' + exportName + extensions.iife,
66 | './' + exportName + extensions.iifeMin,
67 | )
68 |
69 | if (!rootIifeMinPath) {
70 | rootIifeMinPath = './' + exportName + extensions.iifeMin
71 | }
72 | }
73 | }
74 |
75 | distManifest.main = rootCjsPath
76 | distManifest.types = rootTypesPath
77 | distManifest.module = rootEsmPath
78 | distManifest.exports = distExportMap
79 |
80 | if (rootIifeMinPath) {
81 | distManifest.unpkg = distManifest.jsdelivr = rootIifeMinPath
82 | }
83 |
84 | distManifest.sideEffects = sideEffectsList.length ? sideEffectsList : false
85 |
86 | delete distManifest.private
87 | delete distManifest.scripts
88 | delete distManifest.buildConfig
89 | delete distManifest.publishConfig
90 | delete distManifest.devDependencies
91 | delete distManifest.devDependenciesNotes
92 | delete distManifest.disabledBuildConfig // temporary
93 |
94 | await writeFile(distManifestPath, JSON.stringify(distManifest, undefined, 2))
95 | }
96 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/scripts/size.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | import { readFile } from 'fs/promises'
4 | import { execLive, popFlag } from './lib/utils.js'
5 |
6 | const argv = process.argv.slice(2)
7 | displaySizes(
8 | popFlag(argv, '--output'),
9 | popFlag(argv, '--raw'),
10 | popFlag(argv, '--all'),
11 | argv,
12 | )
13 |
14 | /*
15 | Only works when run from PNPM-run context, for bin paths
16 | */
17 | async function displaySizes(
18 | debugOutput,
19 | rawSizes,
20 | allEntryPoints,
21 | entryPoints,
22 | ) {
23 | if (allEntryPoints) {
24 | const pkgJson = JSON.parse(await readFile('./package.json'))
25 | entryPoints = Object.keys(pkgJson.buildConfig.exports)
26 | }
27 |
28 | if (!entryPoints.length) {
29 | entryPoints = ['.']
30 | }
31 |
32 | // normalize, remove leading ./ (except for root, which leaves '.')
33 | entryPoints = entryPoints
34 | .map((entryPoint) => entryPoint.replace(/^\.\//, ''))
35 | .filter((entryPoint) => Boolean(entryPoint))
36 |
37 | if (entryPoints.length > 1 && debugOutput) {
38 | throw RangeError('Cannot debug output with multiple entry points')
39 | }
40 |
41 | const globalIifePath = './dist/global.min.js'
42 | console.log(`Size of ${globalIifePath} ...`)
43 | await execLive([
44 | 'gzip-size',
45 | '--include-original',
46 | ...(rawSizes ? ['--raw'] : []),
47 | globalIifePath,
48 | ])
49 | console.log()
50 |
51 | for (const entryPoint of entryPoints) {
52 | await execLive([
53 | 'export-size',
54 | '--bundler',
55 | 'rollup',
56 | '--compression',
57 | 'gzip',
58 | ...(debugOutput ? ['--output'] : []),
59 | ...(rawSizes ? ['--raw'] : []),
60 | './dist' + (entryPoint === '.' ? '' : `:${entryPoint}`),
61 | ])
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/scripts/test262-config/expected-failures-builtin-calls.txt:
--------------------------------------------------------------------------------
1 |
2 | # TODO: RETHINK
3 | # not allowed to call built-in utilities like Number.isFinite or Math.sign
4 | built-ins/Temporal/Duration/call-builtin.js
5 |
6 | # TODO: RETHINK
7 | # we use array spreading internally
8 | built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin-calendar-no-array-iteration.js
9 | built-ins/Temporal/PlainYearMonth/prototype/since/builtin-calendar-no-array-iteration.js
10 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/scripts/test262-config/expected-failures-node-gte16.txt:
--------------------------------------------------------------------------------
1 |
2 | # WONTFIX
3 | # We do not patch Intl.DateTimeFormat's constructor to reject legacy ICU time
4 | # zones IDs.
5 | #
6 | # Ironically, older Node <16 correctly rejects them, but Node >=16 incorrectly
7 | # accepts them.
8 | #
9 | intl402/DateTimeFormat/timezone-legacy-non-iana.js
10 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/scripts/test262-config/expected-failures-node-gte18.txt:
--------------------------------------------------------------------------------
1 |
2 | ##
3 | ## Issues with environment, already ignored in proposal-temporal/polyfill
4 | ## https://github.com/tc39/proposal-temporal/blob/main/polyfill/test/expected-failures.txt
5 | ##
6 |
7 | # ENV-ISSUE
8 | # Node >=18 has bug
9 | # Caused by https://issues.chromium.org/issues/40893567
10 | # Remove these lines after that bug is fixed
11 | staging/Intl402/Temporal/old/instant-toLocaleString.js
12 | staging/Intl402/Temporal/old/time-toLocaleString.js
13 | intl402/DateTimeFormat/prototype/format/temporal-objects-resolved-time-zone.js
14 | intl402/DateTimeFormat/prototype/format/timedatestyle-en.js
15 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/scripts/test262-config/expected-failures-node-gte22.txt:
--------------------------------------------------------------------------------
1 |
2 | # WONTFIX
3 | # We do not patch Intl.DateTimeFormat's constructor to reject time zone IDs
4 | # with invalid characters.
5 | #
6 | # Ironically, older Node <22 correctly rejects them, but Node 22 incorrectly
7 | # accepts them.
8 | #
9 | intl402/DateTimeFormat/offset-timezone-no-unicode-minus-sign.js
10 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/scripts/test262-config/expected-failures-node-lte16.txt:
--------------------------------------------------------------------------------
1 |
2 | # ENV-ISSUE
3 | # Node <=16 does not explicitly support the islamic-rgsa calendar,
4 | # and instead falls back to islamic, which is okay
5 | staging/Intl402/Temporal/old/islamic-calendars.js
6 |
7 | # ENV-ISSUE
8 | # Node <=16, native Intl.DateTimeFormat's timeZoneName does not accept
9 | # shortOffset/longOffset/shortGeneric/longGeneric
10 | intl402/DateTimeFormat/constructor-options-timeZoneName-valid.js
11 |
12 | # ENV-ISSUE
13 | # Node <=16, native Intl.supportedValuesOf("timeZone") not implemented
14 | intl402/Temporal/ZonedDateTime/supported-values-of.js
15 | intl402/Temporal/ZonedDateTime/timezone-case-insensitive.js
16 | intl402/Temporal/ZonedDateTime/prototype/equals/canonical-not-equal.js
17 | intl402/Temporal/ZonedDateTime/prototype/equals/timezone-case-insensitive.js
18 | intl402/Temporal/ZonedDateTime/prototype/getTimeZoneTransition/transition-at-instant-boundaries.js
19 | intl402/Temporal/ZonedDateTime/from/timezone-case-insensitive.js
20 |
21 | # ENV-ISSUE
22 | # Node <=16, native Intl.supportedValuesOf("calendar") not implemented
23 | staging/sm/Temporal/Calendar/compare-to-datetimeformat.js
24 | intl402/Temporal/PlainDate/prototype/toLocaleString/calendar-mismatch.js
25 | intl402/Temporal/PlainDateTime/prototype/toLocaleString/calendar-mismatch.js
26 | intl402/Temporal/PlainMonthDay/prototype/toLocaleString/calendar-mismatch.js
27 | intl402/Temporal/PlainYearMonth/prototype/toLocaleString/calendar-mismatch.js
28 | intl402/Temporal/ZonedDateTime/prototype/toLocaleString/calendar-mismatch.js
29 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/scripts/test262-config/expected-failures-node-lte18.txt:
--------------------------------------------------------------------------------
1 |
2 | # ENV-ISSUE
3 | # Node <=18, native Intl.DateTimeFormat's formatRange/formatRangeToParts methods
4 | # incorrectly throw error when arguments' epoch-milliseconds are the same
5 | intl402/DateTimeFormat/prototype/formatRange/date-x-greater-than-y-not-throws.js
6 | intl402/DateTimeFormat/prototype/formatRangeToParts/date-x-greater-than-y-not-throws.js
7 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/scripts/test262-config/expected-failures-node-lte20.txt:
--------------------------------------------------------------------------------
1 |
2 | # ENV-ISSUE
3 | # Node <=20, native Intl.DateTimeFormat's constructor accesses options object's
4 | # properties twice
5 | intl402/DateTimeFormat/constructor-options-order-dayPeriod.js
6 | intl402/DateTimeFormat/constructor-options-order-fractionalSecondDigits.js
7 | intl402/DateTimeFormat/constructor-options-order-timedate-style.js
8 | intl402/DateTimeFormat/constructor-options-order.js
9 |
10 |
11 | ##
12 | ## Issues with environment, already ignored in proposal-temporal/polyfill
13 | ## https://github.com/tc39/proposal-temporal/blob/main/polyfill/test/expected-failures.txt
14 | ##
15 |
16 | # ENV-ISSUE
17 | # Node <=20, native Intl.DateTimeFormat's constructor does not accept
18 | # offset-string timeZone option
19 | intl402/DateTimeFormat/prototype/format/offset-timezone-gmt-same.js
20 | intl402/DateTimeFormat/prototype/formatToParts/offset-timezone-correct.js
21 | intl402/DateTimeFormat/prototype/resolvedOptions/offset-timezone-basic.js
22 | intl402/DateTimeFormat/prototype/resolvedOptions/offset-timezone-change.js
23 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/classApi/calendarArg.ts:
--------------------------------------------------------------------------------
1 | import { isoCalendarId } from '../internal/calendarConfig'
2 | import { resolveCalendarId } from '../internal/calendarId'
3 | import { requireString } from '../internal/cast'
4 | import * as errorMessages from '../internal/errorMessages'
5 | import { parseCalendarId } from '../internal/isoParse'
6 | import { isObjectLike } from '../internal/utils'
7 | import { PlainDate } from './plainDate'
8 | import { PlainDateTime } from './plainDateTime'
9 | import { PlainMonthDay } from './plainMonthDay'
10 | import { PlainYearMonth } from './plainYearMonth'
11 | import { getSlots } from './slotClass'
12 | import { ZonedDateTime } from './zonedDateTime'
13 |
14 | export type CalendarArg =
15 | | string
16 | | PlainDate
17 | | PlainDateTime
18 | | ZonedDateTime
19 | | PlainMonthDay
20 | | PlainYearMonth
21 |
22 | /*
23 | Falls back to ISO
24 | */
25 | export function getCalendarIdFromBag(bag: {
26 | calendar?: CalendarArg
27 | }): string {
28 | return extractCalendarIdFromBag(bag) || isoCalendarId
29 | }
30 |
31 | /*
32 | Can return undefined
33 | */
34 | export function extractCalendarIdFromBag(bag: { calendar?: CalendarArg }):
35 | | string
36 | | undefined {
37 | const { calendar: calendarArg } = bag
38 | if (calendarArg !== undefined) {
39 | return refineCalendarArg(calendarArg)
40 | }
41 | }
42 |
43 | /*
44 | Returns a calendarId
45 | */
46 | export function refineCalendarArg(arg: CalendarArg): string {
47 | if (isObjectLike(arg)) {
48 | const { calendar } = (getSlots(arg) || {}) as { calendar?: string }
49 | if (!calendar) {
50 | // TODO: better message how non-Temporal objects aren't allowed
51 | throw new TypeError(errorMessages.invalidCalendar(arg as any))
52 | }
53 | return calendar // other object already refined it
54 | }
55 | return refineCalendarString(arg)
56 | }
57 |
58 | /*
59 | Like refineCalendarId, but allows different string formats, like datetime string
60 | */
61 | function refineCalendarString(arg: string): string {
62 | return resolveCalendarId(parseCalendarId(requireString(arg)))
63 | }
64 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/classApi/calendarRefiners.ts:
--------------------------------------------------------------------------------
1 | import {
2 | requireBoolean,
3 | requireInteger,
4 | requireIntegerOrUndefined,
5 | requirePositiveInteger,
6 | requirePositiveIntegerOrUndefined,
7 | requireString,
8 | requireStringOrUndefined,
9 | } from '../internal/cast'
10 |
11 | export const yearMonthOnlyRefiners = {
12 | era: requireStringOrUndefined,
13 | eraYear: requireIntegerOrUndefined,
14 | year: requireInteger,
15 | month: requirePositiveInteger,
16 |
17 | daysInMonth: requirePositiveInteger,
18 | daysInYear: requirePositiveInteger,
19 | inLeapYear: requireBoolean,
20 | monthsInYear: requirePositiveInteger,
21 | }
22 |
23 | export const monthOnlyRefiners = {
24 | monthCode: requireString,
25 | }
26 |
27 | export const dayOnlyRefiners = {
28 | day: requirePositiveInteger,
29 | }
30 |
31 | export const dateOnlyRefiners = {
32 | dayOfWeek: requirePositiveInteger,
33 | dayOfYear: requirePositiveInteger,
34 | weekOfYear: requirePositiveIntegerOrUndefined,
35 | yearOfWeek: requireIntegerOrUndefined,
36 | daysInWeek: requirePositiveInteger,
37 | }
38 |
39 | export const dateRefiners = {
40 | ...yearMonthOnlyRefiners,
41 | ...monthOnlyRefiners,
42 | ...dayOnlyRefiners,
43 | ...dateOnlyRefiners,
44 | }
45 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/classApi/intlExtended.ts:
--------------------------------------------------------------------------------
1 | import { createPropDescriptors } from '../internal/utils'
2 | import { DateTimeFormat } from './intlDateTimeFormat'
3 |
4 | /*
5 | An extended version of the Intl global namespace
6 | */
7 | export const IntlExtended = Object.defineProperties(
8 | Object.create(Intl),
9 | createPropDescriptors({ DateTimeFormat }),
10 | )
11 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/classApi/intlFormatConfig.ts:
--------------------------------------------------------------------------------
1 | import {
2 | ClassFormatConfig,
3 | createFormatPrepper,
4 | dateConfig,
5 | dateTimeConfig,
6 | instantConfig,
7 | monthDayConfig,
8 | timeConfig,
9 | yearMonthConfig,
10 | zonedConfig,
11 | } from '../internal/intlFormatPrep'
12 |
13 | // For Intl.DateTimeFormat
14 | // -----------------------------------------------------------------------------
15 |
16 | export const classFormatConfigs: Record> = {
17 | Instant: instantConfig,
18 | // ZonedDateTime not allowed to be formatted by Intl.DateTimeFormat
19 | PlainDateTime: dateTimeConfig,
20 | PlainDate: dateConfig,
21 | PlainTime: timeConfig,
22 | PlainYearMonth: yearMonthConfig,
23 | PlainMonthDay: monthDayConfig,
24 | }
25 |
26 | // For toLocaleString
27 | // -----------------------------------------------------------------------------
28 |
29 | export const prepInstantFormat = createFormatPrepper(instantConfig)
30 | export const prepZonedDateTimeFormat = createFormatPrepper(zonedConfig)
31 | export const prepPlainDateTimeFormat = createFormatPrepper(dateTimeConfig)
32 | export const prepPlainDateFormat = createFormatPrepper(dateConfig)
33 | export const prepPlainTimeFormat = createFormatPrepper(timeConfig)
34 | export const prepPlainYearMonthFormat = createFormatPrepper(yearMonthConfig)
35 | export const prepPlainMonthDayFormat = createFormatPrepper(monthDayConfig)
36 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/classApi/mixins.ts:
--------------------------------------------------------------------------------
1 | import { createNativeStandardOps } from '../internal/calendarNativeQuery'
2 | import { durationFieldNamesAsc } from '../internal/durationFields'
3 | import * as errorMessages from '../internal/errorMessages'
4 | import { timeFieldNamesAsc } from '../internal/fields'
5 | import { isoTimeFieldNamesAsc } from '../internal/isoFields'
6 | import { DurationSlots, getEpochMilli, getEpochNano } from '../internal/slots'
7 | import { mapPropNames } from '../internal/utils'
8 | import {
9 | dateRefiners,
10 | dayOnlyRefiners,
11 | monthOnlyRefiners,
12 | yearMonthOnlyRefiners,
13 | } from './calendarRefiners'
14 |
15 | // For PlainDate/etc
16 | // -----------------------------------------------------------------------------
17 | // Assumes general calendar (native/adapter)
18 |
19 | function createCalendarGetters(methodNameMap: M): {
20 | [K in keyof M]: () => any
21 | } {
22 | const methods = {} as any
23 |
24 | for (const methodName in methodNameMap) {
25 | methods[methodName] = function (this: any, slots: any) {
26 | const { calendar } = slots
27 |
28 | // NOTE: this is inefficient to recreate each time!!!
29 | const ops = createNativeStandardOps(calendar) as any
30 | return ops[methodName](slots)
31 | }
32 | }
33 |
34 | return methods
35 | }
36 |
37 | export const dateGetters = createCalendarGetters(dateRefiners)
38 | export const yearMonthGetters = createCalendarGetters({
39 | ...yearMonthOnlyRefiners,
40 | ...monthOnlyRefiners,
41 | })
42 | export const monthDayGetters = createCalendarGetters({
43 | ...monthOnlyRefiners,
44 | ...dayOnlyRefiners,
45 | })
46 | export const calendarIdGetters = {
47 | calendarId(slots: any): string {
48 | return slots.calendar
49 | },
50 | }
51 |
52 | // Duration
53 | // -----------------------------------------------------------------------------
54 |
55 | export const durationGetters = mapPropNames(
56 | (propName: keyof DurationSlots) => {
57 | return function (this: any, slots: any) {
58 | return slots[propName]
59 | }
60 | },
61 | (durationFieldNamesAsc as (keyof DurationSlots)[]).concat('sign'),
62 | )
63 |
64 | // Time
65 | // -----------------------------------------------------------------------------
66 |
67 | export const timeGetters = mapPropNames((_name, i) => {
68 | return function (this: any, slots: any) {
69 | return slots[isoTimeFieldNamesAsc[i]]
70 | }
71 | }, timeFieldNamesAsc)
72 |
73 | // Epoch
74 | // -----------------------------------------------------------------------------
75 |
76 | export const epochGetters = {
77 | epochMilliseconds: getEpochMilli,
78 | epochNanoseconds: getEpochNano,
79 | }
80 |
81 | // Misc
82 | // -----------------------------------------------------------------------------
83 |
84 | export function neverValueOf() {
85 | throw new TypeError(errorMessages.forbiddenValueOf)
86 | }
87 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/classApi/now.ts:
--------------------------------------------------------------------------------
1 | import { isoCalendarId } from '../internal/calendarConfig'
2 | import {
3 | getCurrentEpochNano,
4 | getCurrentIsoDateTime,
5 | getCurrentTimeZoneId,
6 | } from '../internal/current'
7 | import {
8 | createInstantSlots,
9 | createPlainDateSlots,
10 | createPlainDateTimeSlots,
11 | createPlainTimeSlots,
12 | createZonedDateTimeSlots,
13 | } from '../internal/slots'
14 | import { queryNativeTimeZone } from '../internal/timeZoneNative'
15 | import {
16 | createPropDescriptors,
17 | createStringTagDescriptors,
18 | } from '../internal/utils'
19 | import { Instant, createInstant } from './instant'
20 | import { PlainDate, createPlainDate } from './plainDate'
21 | import { PlainDateTime, createPlainDateTime } from './plainDateTime'
22 | import { PlainTime, createPlainTime } from './plainTime'
23 | import { TimeZoneArg, refineTimeZoneArg } from './timeZoneArg'
24 | import { ZonedDateTime, createZonedDateTime } from './zonedDateTime'
25 |
26 | export const Now = Object.defineProperties(
27 | {},
28 | {
29 | ...createStringTagDescriptors('Temporal.Now'),
30 | ...createPropDescriptors({
31 | timeZoneId() {
32 | return getCurrentTimeZoneId() // we call separately to return function.name
33 | },
34 |
35 | instant(): Instant {
36 | return createInstant(createInstantSlots(getCurrentEpochNano()))
37 | },
38 |
39 | zonedDateTimeISO(
40 | timeZoneArg: TimeZoneArg = getCurrentTimeZoneId(),
41 | ): ZonedDateTime {
42 | return createZonedDateTime(
43 | createZonedDateTimeSlots(
44 | getCurrentEpochNano(),
45 | refineTimeZoneArg(timeZoneArg),
46 | isoCalendarId,
47 | ),
48 | )
49 | },
50 |
51 | plainDateTimeISO(
52 | timeZoneArg: TimeZoneArg = getCurrentTimeZoneId(),
53 | ): PlainDateTime {
54 | return createPlainDateTime(
55 | createPlainDateTimeSlots(
56 | getCurrentIsoDateTime(
57 | queryNativeTimeZone(refineTimeZoneArg(timeZoneArg)),
58 | ),
59 | isoCalendarId,
60 | ),
61 | )
62 | },
63 |
64 | plainDateISO(
65 | timeZoneArg: TimeZoneArg = getCurrentTimeZoneId(),
66 | ): PlainDate {
67 | return createPlainDate(
68 | createPlainDateSlots(
69 | getCurrentIsoDateTime(
70 | queryNativeTimeZone(refineTimeZoneArg(timeZoneArg)),
71 | ),
72 | isoCalendarId,
73 | ),
74 | )
75 | },
76 |
77 | plainTimeISO(
78 | timeZoneArg: TimeZoneArg = getCurrentTimeZoneId(),
79 | ): PlainTime {
80 | return createPlainTime(
81 | createPlainTimeSlots(
82 | getCurrentIsoDateTime(
83 | queryNativeTimeZone(refineTimeZoneArg(timeZoneArg)),
84 | ),
85 | ),
86 | )
87 | },
88 | }),
89 | },
90 | )
91 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/classApi/plainMonthDay.ts:
--------------------------------------------------------------------------------
1 | import {
2 | PlainMonthDayBag,
3 | plainMonthDayWithFields,
4 | refinePlainMonthDayBag,
5 | } from '../internal/bagRefine'
6 | import { isoCalendarId } from '../internal/calendarConfig'
7 | import { refineCalendarId } from '../internal/calendarId'
8 | import { createNativeStandardOps } from '../internal/calendarNativeQuery'
9 | import { plainMonthDaysEqual } from '../internal/compare'
10 | import { constructPlainMonthDaySlots } from '../internal/construct'
11 | import { plainMonthDayToPlainDate } from '../internal/convert'
12 | import { MonthDayBag, MonthDayFields, YearFields } from '../internal/fields'
13 | import { LocalesArg } from '../internal/intlFormatUtils'
14 | import { formatPlainMonthDayIso } from '../internal/isoFormat'
15 | import { parsePlainMonthDay } from '../internal/isoParse'
16 | import {
17 | OverflowOptions,
18 | refineOverflowOptions,
19 | } from '../internal/optionsRefine'
20 | import { PlainMonthDayBranding, PlainMonthDaySlots } from '../internal/slots'
21 | import { bindArgs, isObjectLike } from '../internal/utils'
22 | import { extractCalendarIdFromBag } from './calendarArg'
23 | import { prepPlainMonthDayFormat } from './intlFormatConfig'
24 | import { calendarIdGetters, monthDayGetters, neverValueOf } from './mixins'
25 | import { PlainDate, createPlainDate } from './plainDate'
26 | import { createSlotClass, getSlots, rejectInvalidBag } from './slotClass'
27 |
28 | export type PlainMonthDay = any & MonthDayFields
29 | export type PlainMonthDayArg = PlainMonthDay | PlainMonthDayBag | string
30 |
31 | export const [PlainMonthDay, createPlainMonthDay, getPlainMonthDaySlots] =
32 | createSlotClass(
33 | PlainMonthDayBranding,
34 | bindArgs(constructPlainMonthDaySlots, refineCalendarId),
35 | {
36 | ...calendarIdGetters,
37 | ...monthDayGetters,
38 | },
39 | {
40 | with(
41 | slots: PlainMonthDaySlots,
42 | mod: MonthDayBag,
43 | options?: OverflowOptions,
44 | ): PlainMonthDay {
45 | return createPlainMonthDay(
46 | plainMonthDayWithFields(
47 | createNativeStandardOps,
48 | slots,
49 | rejectInvalidBag(mod),
50 | options,
51 | ),
52 | )
53 | },
54 | equals(slots: PlainMonthDaySlots, otherArg: PlainMonthDayArg): boolean {
55 | return plainMonthDaysEqual(slots, toPlainMonthDaySlots(otherArg))
56 | },
57 | toPlainDate(slots: PlainMonthDaySlots, bag: YearFields): PlainDate {
58 | return createPlainDate(
59 | plainMonthDayToPlainDate(createNativeStandardOps, slots, this, bag),
60 | )
61 | },
62 | toLocaleString(
63 | slots: PlainMonthDaySlots,
64 | locales?: LocalesArg,
65 | options?: Intl.DateTimeFormatOptions,
66 | ): string {
67 | const [format, epochMilli] = prepPlainMonthDayFormat(
68 | locales,
69 | options,
70 | slots,
71 | )
72 | return format.format(epochMilli)
73 | },
74 | toString: formatPlainMonthDayIso,
75 | toJSON(slots: PlainMonthDaySlots): string {
76 | return formatPlainMonthDayIso(slots)
77 | },
78 | valueOf: neverValueOf,
79 | },
80 | {
81 | from(arg: PlainMonthDayArg, options?: OverflowOptions): PlainMonthDay {
82 | return createPlainMonthDay(toPlainMonthDaySlots(arg, options))
83 | },
84 | },
85 | )
86 |
87 | // Utils
88 | // -----------------------------------------------------------------------------
89 |
90 | export function toPlainMonthDaySlots(
91 | arg: PlainMonthDayArg,
92 | options?: OverflowOptions,
93 | ): PlainMonthDaySlots {
94 | if (isObjectLike(arg)) {
95 | const slots = getSlots(arg)
96 |
97 | if (slots && slots.branding === PlainMonthDayBranding) {
98 | refineOverflowOptions(options) // parse unused options
99 | return slots as PlainMonthDaySlots
100 | }
101 |
102 | const calendarIdMaybe = extractCalendarIdFromBag(arg as PlainMonthDaySlots)
103 | const calendarId = calendarIdMaybe || isoCalendarId
104 |
105 | return refinePlainMonthDayBag(
106 | createNativeStandardOps(calendarId),
107 | !calendarIdMaybe,
108 | arg as MonthDayBag,
109 | options,
110 | )
111 | }
112 |
113 | const res = parsePlainMonthDay(createNativeStandardOps, arg)
114 | refineOverflowOptions(options) // parse unused options
115 | return res
116 | }
117 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/classApi/slotClass.ts:
--------------------------------------------------------------------------------
1 | import * as errorMessages from '../internal/errorMessages'
2 | import { BrandingSlots } from '../internal/slots'
3 | import {
4 | createGetterDescriptors,
5 | createNameDescriptors,
6 | createPropDescriptors,
7 | createStringTagDescriptors,
8 | mapProps,
9 | } from '../internal/utils'
10 |
11 | const slotsMap = new WeakMap()
12 |
13 | // TODO: allow type-input, so caller doesn't need to cast so much
14 | export const getSlots = slotsMap.get.bind(slotsMap)
15 | const setSlots = slotsMap.set.bind(slotsMap)
16 |
17 | export function createSlotClass(
18 | branding: string,
19 | construct: any,
20 | getters: any,
21 | methods: any,
22 | staticMethods: any,
23 | ): any {
24 | function Class(this: any, ...args: any[]) {
25 | if (this instanceof Class) {
26 | setSlots(this, construct(...args))
27 | } else {
28 | throw new TypeError(errorMessages.invalidCallingContext)
29 | }
30 | }
31 |
32 | Object.defineProperties(Class.prototype, {
33 | ...createGetterDescriptors(mapProps(bindMethod as any, getters) as any), // !!!
34 | ...createPropDescriptors(mapProps(bindMethod as any, methods)),
35 | ...createStringTagDescriptors('Temporal.' + branding),
36 | })
37 |
38 | Object.defineProperties(Class, {
39 | ...createPropDescriptors(staticMethods),
40 | ...createNameDescriptors(branding),
41 | })
42 |
43 | function bindMethod(method: any, methodName: string) {
44 | return Object.defineProperties(function (this: any, ...args: any[]) {
45 | return method.call(this, getSpecificSlots(this), ...args)
46 | }, createNameDescriptors(methodName))
47 | }
48 |
49 | function getSpecificSlots(obj: any): any {
50 | const slots = getSlots(obj)
51 | if (!slots || slots.branding !== branding) {
52 | throw new TypeError(errorMessages.invalidCallingContext)
53 | }
54 | return slots
55 | }
56 |
57 | function createViaSlots(slots: BrandingSlots) {
58 | const instance = Object.create(Class.prototype)
59 | setSlots(instance, slots)
60 | return instance
61 | }
62 |
63 | return [Class, createViaSlots, getSpecificSlots]
64 | }
65 |
66 | // Utils
67 | // -----------------------------------------------------------------------------
68 |
69 | export function rejectInvalidBag(bag: B): B {
70 | if (
71 | getSlots(bag) ||
72 | (bag as any).calendar !== undefined ||
73 | (bag as any).timeZone !== undefined
74 | ) {
75 | throw new TypeError(errorMessages.invalidBag)
76 | }
77 | return bag
78 | }
79 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/classApi/temporal.ts:
--------------------------------------------------------------------------------
1 | import {
2 | createPropDescriptors,
3 | createStringTagDescriptors,
4 | } from '../internal/utils'
5 |
6 | import { Duration } from './duration'
7 | import { Instant } from './instant'
8 | import { Now } from './now'
9 | import { PlainDate } from './plainDate'
10 | import { PlainDateTime } from './plainDateTime'
11 | import { PlainMonthDay } from './plainMonthDay'
12 | import { PlainTime } from './plainTime'
13 | import { PlainYearMonth } from './plainYearMonth'
14 | import { ZonedDateTime } from './zonedDateTime'
15 |
16 | export const Temporal = Object.defineProperties(
17 | {},
18 | {
19 | ...createStringTagDescriptors('Temporal'),
20 | ...createPropDescriptors({
21 | PlainYearMonth,
22 | PlainMonthDay,
23 | PlainDate,
24 | PlainTime,
25 | PlainDateTime,
26 | ZonedDateTime,
27 | Instant,
28 | Duration,
29 | Now,
30 | }),
31 | },
32 | ) as any // !!! (for tests)
33 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/classApi/timeZoneArg.ts:
--------------------------------------------------------------------------------
1 | import { requireString } from '../internal/cast'
2 | import * as errorMessages from '../internal/errorMessages'
3 | import { parseTimeZoneId } from '../internal/isoParse'
4 | import { resolveTimeZoneId } from '../internal/timeZoneId'
5 | import { isObjectLike } from '../internal/utils'
6 | import { getSlots } from './slotClass'
7 | import { ZonedDateTime } from './zonedDateTime'
8 |
9 | export type TimeZoneArg = string | ZonedDateTime
10 |
11 | /*
12 | Returns a timeZoneId
13 | */
14 | export function refineTimeZoneArg(arg: TimeZoneArg): string {
15 | if (isObjectLike(arg)) {
16 | const { timeZone } = (getSlots(arg) || {}) as { timeZone?: string }
17 | if (!timeZone) {
18 | // TODO: better message how non-Temporal objects aren't allowed
19 | throw new TypeError(errorMessages.invalidTimeZone(arg as any)) // !!!
20 | }
21 | return timeZone
22 | }
23 | return refineTimeZoneString(arg)
24 | }
25 |
26 | /*
27 | Like refineTimeZoneId, but allows different string formats, like datetime string
28 | */
29 | function refineTimeZoneString(arg: string): string {
30 | return resolveTimeZoneId(parseTimeZoneId(requireString(arg)))
31 | }
32 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/funcApi/calendarUtils.ts:
--------------------------------------------------------------------------------
1 | import { isoCalendarId } from '../internal/calendarConfig'
2 | import { refineCalendarId } from '../internal/calendarId'
3 | import { formatMonthCode } from '../internal/calendarNative'
4 | import {
5 | createNativeDayOfYearOps,
6 | createNativeDaysInMonthOps,
7 | createNativeDaysInYearOps,
8 | createNativeInLeapYearOps,
9 | createNativeMonthsInYearOps,
10 | createNativePartOps,
11 | createNativeWeekOps,
12 | } from '../internal/calendarNativeQuery'
13 | import { DateFields, MonthDayFields, YearMonthFields } from '../internal/fields'
14 | import { DateSlots } from '../internal/slots'
15 |
16 | // Calendar ID
17 | // -----------------------------------------------------------------------------
18 |
19 | export function getCalendarId(slots: { calendar: string }): string {
20 | return slots.calendar
21 | }
22 |
23 | export function getCalendarIdFromBag(bag: { calendar?: string }): string {
24 | return extractCalendarIdFromBag(bag) || isoCalendarId
25 | }
26 |
27 | export function extractCalendarIdFromBag(bag: { calendar?: string }):
28 | | string
29 | | undefined {
30 | const { calendar } = bag
31 | if (calendar !== undefined) {
32 | return refineCalendarId(calendar)
33 | }
34 | }
35 |
36 | // Fields
37 | // -----------------------------------------------------------------------------
38 |
39 | export function computeDateFields(slots: DateSlots): DateFields {
40 | const calendarOps = createNativePartOps(slots.calendar)
41 | const [year, month, day] = calendarOps.dateParts(slots)
42 | const [era, eraYear] = calendarOps.eraParts(slots)
43 | const [monthCodeNumber, isLeapMonth] = calendarOps.monthCodeParts(year, month)
44 | const monthCode = formatMonthCode(monthCodeNumber, isLeapMonth)
45 | return { era, eraYear, year, monthCode, month, day }
46 | }
47 |
48 | export function computeYearMonthFields(slots: DateSlots): YearMonthFields {
49 | const calendarOps = createNativePartOps(slots.calendar)
50 | const [year, month] = calendarOps.dateParts(slots)
51 | const [era, eraYear] = calendarOps.eraParts(slots)
52 | const [monthCodeNumber, isLeapMonth] = calendarOps.monthCodeParts(year, month)
53 | const monthCode = formatMonthCode(monthCodeNumber, isLeapMonth)
54 | return { era, eraYear, year, monthCode, month }
55 | }
56 |
57 | export function computeMonthDayFields(slots: DateSlots): MonthDayFields {
58 | const calendarOps = createNativePartOps(slots.calendar)
59 | const [year, month, day] = calendarOps.dateParts(slots)
60 | const [monthCodeNumber, isLeapMonth] = calendarOps.monthCodeParts(year, month)
61 | const monthCode = formatMonthCode(monthCodeNumber, isLeapMonth)
62 | return { monthCode, month, day }
63 | }
64 |
65 | // Stats
66 | // -----------------------------------------------------------------------------
67 |
68 | export function computeInLeapYear(slots: DateSlots): boolean {
69 | const calendarOps = createNativeInLeapYearOps(slots.calendar)
70 | return calendarOps.inLeapYear(slots)
71 | }
72 |
73 | export function computeMonthsInYear(slots: DateSlots): number {
74 | const calendarOps = createNativeMonthsInYearOps(slots.calendar)
75 | return calendarOps.monthsInYear(slots)
76 | }
77 |
78 | export function computeDaysInMonth(slots: DateSlots): number {
79 | const calendarOps = createNativeDaysInMonthOps(slots.calendar)
80 | return calendarOps.daysInMonth(slots)
81 | }
82 |
83 | export function computeDaysInYear(slots: DateSlots): number {
84 | const calendarOps = createNativeDaysInYearOps(slots.calendar)
85 | return calendarOps.daysInYear(slots)
86 | }
87 |
88 | export function computeDayOfYear(slots: DateSlots): number {
89 | const calendarOps = createNativeDayOfYearOps(slots.calendar)
90 | return calendarOps.dayOfYear(slots)
91 | }
92 |
93 | export function computeWeekOfYear(slots: DateSlots): number | undefined {
94 | const calendarOps = createNativeWeekOps(slots.calendar)
95 | return calendarOps.weekOfYear(slots)
96 | }
97 |
98 | export function computeYearOfWeek(slots: DateSlots): number | undefined {
99 | const calendarOps = createNativeWeekOps(slots.calendar)
100 | return calendarOps.yearOfWeek(slots)
101 | }
102 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/funcApi/intlFormatCache.ts:
--------------------------------------------------------------------------------
1 | import {
2 | FormatQuerier,
3 | OptionsTransformer,
4 | createFormatForPrep,
5 | } from '../internal/intlFormatPrep'
6 | import { LocalesArg } from '../internal/intlFormatUtils'
7 | import { memoize } from '../internal/utils'
8 |
9 | /*
10 | Keyed by forcedTimeZoneId+locales+options
11 | */
12 | export function createFormatCache(): FormatQuerier {
13 | const queryFormatFactory = memoize((options: Intl.DateTimeFormatOptions) => {
14 | const map = new Map()
15 |
16 | return (
17 | forcedTimeZoneId: string | undefined,
18 | locales: LocalesArg | undefined,
19 | transformOptions: OptionsTransformer,
20 | ) => {
21 | const key = ([] as string[])
22 | .concat(forcedTimeZoneId || [], locales || [])
23 | .join()
24 |
25 | let format = map.get(key)
26 | if (!format) {
27 | format = createFormatForPrep(
28 | forcedTimeZoneId,
29 | locales,
30 | options,
31 | transformOptions,
32 | /* strictOptions = */ false,
33 | )
34 | map.set(key, format!)
35 | }
36 |
37 | return format
38 | }
39 | }, WeakMap)
40 |
41 | return (forcedTimeZoneId, locales, options, transformOptions) => {
42 | return queryFormatFactory(options)(
43 | forcedTimeZoneId,
44 | locales,
45 | transformOptions,
46 | )
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/funcApi/now.ts:
--------------------------------------------------------------------------------
1 | import { isoCalendarId } from '../internal/calendarConfig'
2 | import { refineCalendarId } from '../internal/calendarId'
3 | import {
4 | getCurrentEpochNano,
5 | getCurrentIsoDateTime,
6 | getCurrentTimeZoneId,
7 | } from '../internal/current'
8 | import {
9 | createInstantSlots,
10 | createPlainDateSlots,
11 | createPlainDateTimeSlots,
12 | createPlainTimeSlots,
13 | createZonedDateTimeSlots,
14 | } from '../internal/slots'
15 | import { refineTimeZoneId } from '../internal/timeZoneId'
16 | import { queryNativeTimeZone } from '../internal/timeZoneNative'
17 | import * as InstantFns from './instant'
18 | import * as PlainDateFns from './plainDate'
19 | import * as PlainDateTimeFns from './plainDateTime'
20 | import * as PlainTimeFns from './plainTime'
21 | import * as ZonedDateTimeFns from './zonedDateTime'
22 |
23 | export const timeZoneId = getCurrentTimeZoneId
24 |
25 | export function instant(): InstantFns.Record {
26 | return createInstantSlots(getCurrentEpochNano())
27 | }
28 |
29 | export function zonedDateTime(
30 | calendar: string,
31 | timeZone: string = getCurrentTimeZoneId(),
32 | ): ZonedDateTimeFns.Record {
33 | return createZonedDateTimeSlots(
34 | getCurrentEpochNano(),
35 | refineTimeZoneId(timeZone),
36 | refineCalendarId(calendar),
37 | )
38 | }
39 |
40 | export function zonedDateTimeISO(
41 | timeZone: string = getCurrentTimeZoneId(),
42 | ): ZonedDateTimeFns.Record {
43 | return createZonedDateTimeSlots(
44 | getCurrentEpochNano(),
45 | refineTimeZoneId(timeZone),
46 | isoCalendarId,
47 | )
48 | }
49 |
50 | export function plainDateTime(
51 | calendar: string,
52 | timeZone: string = getCurrentTimeZoneId(),
53 | ): PlainDateTimeFns.Record {
54 | return createPlainDateTimeSlots(
55 | getCurrentIsoDateTime(queryNativeTimeZone(refineTimeZoneId(timeZone))),
56 | refineCalendarId(calendar),
57 | )
58 | }
59 |
60 | export function plainDateTimeISO(
61 | timeZone: string = getCurrentTimeZoneId(),
62 | ): PlainDateTimeFns.Record {
63 | return createPlainDateTimeSlots(
64 | getCurrentIsoDateTime(queryNativeTimeZone(refineTimeZoneId(timeZone))),
65 | isoCalendarId,
66 | )
67 | }
68 |
69 | export function plainDate(
70 | calendar: string,
71 | timeZone: string = getCurrentTimeZoneId(),
72 | ): PlainDateFns.Record {
73 | return createPlainDateSlots(
74 | getCurrentIsoDateTime(queryNativeTimeZone(refineTimeZoneId(timeZone))),
75 | refineCalendarId(calendar),
76 | )
77 | }
78 |
79 | export function plainDateISO(
80 | timeZone: string = getCurrentTimeZoneId(),
81 | ): PlainDateFns.Record {
82 | return createPlainDateSlots(
83 | getCurrentIsoDateTime(queryNativeTimeZone(refineTimeZoneId(timeZone))),
84 | isoCalendarId,
85 | )
86 | }
87 |
88 | export function plainTimeISO(
89 | timeZone: string = getCurrentTimeZoneId(),
90 | ): PlainTimeFns.Record {
91 | return createPlainTimeSlots(
92 | getCurrentIsoDateTime(queryNativeTimeZone(refineTimeZoneId(timeZone))),
93 | )
94 | }
95 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/funcApi/roundUtils.ts:
--------------------------------------------------------------------------------
1 | import { createNativeConvertOps } from '../internal/calendarNativeQuery'
2 | import {
3 | IsoDateFields,
4 | IsoDateTimeFields,
5 | clearIsoFields,
6 | isoTimeFieldDefaults,
7 | } from '../internal/isoFields'
8 | import { computeIsoDayOfWeek } from '../internal/isoMath'
9 | import { moveByDays } from '../internal/move'
10 | import { RoundingMode } from '../internal/options'
11 | import { IsoDateTimeInterval, roundWithMode } from '../internal/round'
12 | import { DateSlots } from '../internal/slots'
13 | import { epochMilliToIso, isoToEpochNano } from '../internal/timeMath'
14 | import { computeEpochNanoFrac } from '../internal/total'
15 | import { Unit } from '../internal/units'
16 | import { bindArgs } from '../internal/utils'
17 | import { moveByIsoWeeks } from './moveUtils'
18 |
19 | // Floor
20 | // -----------------------------------------------------------------------------
21 |
22 | export function computeYearFloor(
23 | slots: DateSlots,
24 | calendarOps = createNativeConvertOps(slots.calendar),
25 | ): IsoDateTimeFields & { year: number } {
26 | const [year0] = calendarOps.dateParts(slots)
27 | const isoFields0 = epochMilliToIso(calendarOps.epochMilli(year0))
28 | return { ...isoFields0, year: year0 }
29 | }
30 |
31 | export function computeMonthFloor(
32 | slots: DateSlots,
33 | calendarOps = createNativeConvertOps(slots.calendar),
34 | ): IsoDateTimeFields & { year: number; month: number } {
35 | const [year0, month0] = calendarOps.dateParts(slots)
36 | const isoFields0 = epochMilliToIso(calendarOps.epochMilli(year0, month0))
37 | return { ...isoFields0, year: year0, month: month0 }
38 | }
39 |
40 | export function computeIsoWeekFloor(slots: IsoDateFields): IsoDateTimeFields {
41 | const dayOfWeek = computeIsoDayOfWeek(slots)
42 | const isoDateFields0 = moveByDays(slots, 1 - dayOfWeek)
43 | return { ...isoDateFields0, ...isoTimeFieldDefaults }
44 | }
45 |
46 | export const computeHourFloor = bindArgs(clearIsoFields, Unit.Hour)
47 | export const computeMinuteFloor = bindArgs(clearIsoFields, Unit.Minute)
48 | export const computeSecFloor = bindArgs(clearIsoFields, Unit.Second)
49 | export const computeMilliFloor = bindArgs(clearIsoFields, Unit.Millisecond)
50 | export const computeMicroFloor = bindArgs(clearIsoFields, Unit.Microsecond)
51 |
52 | // Ceil
53 | // -----------------------------------------------------------------------------
54 |
55 | export function computeYearCeil(slots: DateSlots): IsoDateTimeFields {
56 | return computeYearInterval(slots)[1]
57 | }
58 |
59 | export function computeMonthCeil(slots: DateSlots): IsoDateTimeFields {
60 | return computeMonthInterval(slots)[1]
61 | }
62 |
63 | export function computeIsoWeekCeil(slots: IsoDateFields): IsoDateTimeFields {
64 | return computeIsoWeekInterval(slots)[1]
65 | }
66 |
67 | // Interval
68 | // -----------------------------------------------------------------------------
69 |
70 | export function computeYearInterval(slots: DateSlots): IsoDateTimeInterval {
71 | const calendarOps = createNativeConvertOps(slots.calendar)
72 | const isoFields0 = computeYearFloor(slots)
73 | const year1 = isoFields0.year + 1
74 | const isoFields1 = epochMilliToIso(calendarOps.epochMilli(year1))
75 | return [isoFields0, isoFields1]
76 | }
77 |
78 | export function computeMonthInterval(slots: DateSlots): IsoDateTimeInterval {
79 | const calendarOps = createNativeConvertOps(slots.calendar)
80 | const isoFields0 = computeMonthFloor(slots, calendarOps)
81 | const [year1, month1] = calendarOps.monthAdd(
82 | isoFields0.year,
83 | isoFields0.month,
84 | 1,
85 | )
86 | const isoFields1 = epochMilliToIso(calendarOps.epochMilli(year1, month1))
87 | return [isoFields0, isoFields1]
88 | }
89 |
90 | export function computeIsoWeekInterval(
91 | slots: IsoDateFields,
92 | ): IsoDateTimeInterval {
93 | const isoFields0 = computeIsoWeekFloor(slots)
94 | const isoFields1 = moveByIsoWeeks(isoFields0, 1)
95 | return [isoFields0, isoFields1]
96 | }
97 |
98 | /*
99 | For year/month/week only
100 | */
101 | export function roundDateTimeToInterval(
102 | computeInterval: (slots: S) => IsoDateTimeInterval,
103 | slots: S,
104 | roundingMode: RoundingMode,
105 | ): S & IsoDateTimeFields {
106 | const [isoFields0, isoFields1] = computeInterval(slots)
107 | const epochNano0 = isoToEpochNano(isoFields0)!
108 | const epochNano1 = isoToEpochNano(isoFields1)!
109 | const epochNano = isoToEpochNano(slots)!
110 | const frac = computeEpochNanoFrac(epochNano, epochNano0, epochNano1)
111 | const grow = roundWithMode(frac, roundingMode)
112 | const isoFieldsRounded = grow ? isoFields1 : isoFields0
113 | return {
114 | ...slots,
115 | ...isoFieldsRounded,
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/funcApi/timeZone.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect, it } from 'vitest'
2 | import * as InstantFns from './instant'
3 | import * as PlainDateTimeFns from './plainDateTime'
4 | import { expectInstantEquals } from './testUtils'
5 | import * as TimeZoneFns from './timeZone'
6 |
7 | describe('getPossibleInstantsFor', () => {
8 | it('can return zero results', () => {
9 | const tzId = 'America/New_York'
10 | const pdt = PlainDateTimeFns.fromString('2024-03-10T02:00:00')
11 | const insts = TimeZoneFns.getPossibleInstantsFor(tzId, pdt)
12 | expect(insts.length).toBe(0)
13 | })
14 |
15 | it('can return one result', () => {
16 | const tzId = 'America/New_York'
17 | const pdt = PlainDateTimeFns.fromString('2024-04-01T05:00:00')
18 | const insts = TimeZoneFns.getPossibleInstantsFor(tzId, pdt)
19 | expect(insts.length).toBe(1)
20 | expectInstantEquals(insts[0], 1711962000000000000n)
21 | })
22 |
23 | it('can return two results', () => {
24 | const tzId = 'America/New_York'
25 | const pdt = PlainDateTimeFns.fromString('2024-11-03T01:00:00')
26 | const insts = TimeZoneFns.getPossibleInstantsFor(tzId, pdt)
27 | expect(insts.length).toBe(2)
28 | expectInstantEquals(insts[0], 1730610000000000000n)
29 | expectInstantEquals(insts[1], 1730613600000000000n)
30 | })
31 | })
32 |
33 | describe('getNextTransition', () => {
34 | it('can return an instant', () => {
35 | const tzId = 'America/New_York'
36 | const inst0 = InstantFns.create(1711962000000000000n)
37 | const inst1 = TimeZoneFns.getNextTransition(tzId, inst0)
38 | expectInstantEquals(inst1!, 1730613600000000000n)
39 | })
40 |
41 | it('can return null', () => {
42 | const tzId = 'UTC'
43 | const inst0 = InstantFns.create(1711962000000000000n)
44 | const inst1 = TimeZoneFns.getNextTransition(tzId, inst0)
45 | expect(inst1).toBe(null)
46 | })
47 | })
48 |
49 | describe('getPreviousTransition', () => {
50 | it('can return an instant', () => {
51 | const tzId = 'America/New_York'
52 | const inst0 = InstantFns.create(1711962000000000000n)
53 | const inst1 = TimeZoneFns.getPreviousTransition(tzId, inst0)
54 | expectInstantEquals(inst1!, 1710054000000000000n)
55 | })
56 |
57 | it('can return null', () => {
58 | const tzId = 'UTC'
59 | const inst0 = InstantFns.create(1711962000000000000n)
60 | const inst1 = TimeZoneFns.getPreviousTransition(tzId, inst0)
61 | expect(inst1).toBe(null)
62 | })
63 | })
64 |
65 | describe('equals', () => {
66 | it('returns true for case-different IDs', () => {
67 | const tzId0 = 'America/New_York'
68 | const tsId1 = 'AMERICA/NEW_YORK'
69 | expect(TimeZoneFns.equals(tzId0, tsId1)).toBe(true)
70 | })
71 |
72 | it('returns true for canonicalized IDs', () => {
73 | const tzId0 = 'Asia/Calcutta'
74 | const tsId1 = 'Asia/Kolkata'
75 | expect(TimeZoneFns.equals(tzId0, tsId1)).toBe(true)
76 | })
77 |
78 | it('returns false for different IDs', () => {
79 | const tzId0 = 'America/New_York'
80 | const tsId1 = 'America/Chicago'
81 | expect(TimeZoneFns.equals(tzId0, tsId1)).toBe(false)
82 | })
83 | })
84 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/funcApi/timeZone.ts:
--------------------------------------------------------------------------------
1 | import { createInstantSlots } from '../internal/slots'
2 | import { getTimeZoneAtomic, refineTimeZoneId } from '../internal/timeZoneId'
3 | import { queryNativeTimeZone } from '../internal/timeZoneNative'
4 | import { bindArgs } from '../internal/utils'
5 | import * as InstantFns from './instant'
6 | import * as PlainDateTimeFns from './plainDateTime'
7 |
8 | export function getPossibleInstantsFor(
9 | timeZoneId: string,
10 | plainDateTime: PlainDateTimeFns.Record,
11 | ): InstantFns.Record[] {
12 | const timeZoneOps = queryNativeTimeZone(refineTimeZoneId(timeZoneId))
13 | return timeZoneOps
14 | .getPossibleInstantsFor(plainDateTime)
15 | .map(createInstantSlots)
16 | }
17 |
18 | function getTransition(
19 | dir: 1 | -1,
20 | timeZoneId: string,
21 | instant: InstantFns.Record,
22 | ): InstantFns.Record | null {
23 | const timeZoneOps = queryNativeTimeZone(refineTimeZoneId(timeZoneId))
24 | const epochNano = timeZoneOps.getTransition(instant.epochNanoseconds, dir)
25 | return epochNano ? createInstantSlots(epochNano) : null
26 | }
27 |
28 | export const getNextTransition = bindArgs(getTransition, 1)
29 | export const getPreviousTransition = bindArgs(getTransition, -1)
30 |
31 | export function equals(timeZoneId0: string, timeZoneId1: string): boolean {
32 | return getTimeZoneAtomic(timeZoneId0) === getTimeZoneAtomic(timeZoneId1)
33 | }
34 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/global.ts:
--------------------------------------------------------------------------------
1 | import { toTemporalInstant } from './classApi/instant'
2 | import { DateTimeFormat } from './classApi/intlDateTimeFormat'
3 | import { Temporal } from './classApi/temporal'
4 | import { createPropDescriptors } from './internal/utils'
5 |
6 | Object.defineProperties(globalThis, createPropDescriptors({ Temporal }))
7 | Object.defineProperties(Intl, createPropDescriptors({ DateTimeFormat }))
8 | Object.defineProperties(
9 | Date.prototype,
10 | createPropDescriptors({ toTemporalInstant }),
11 | )
12 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/impl.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect, it } from 'vitest'
2 | import { Intl, Temporal, toTemporalInstant } from './impl'
3 |
4 | describe('Temporal.Duration', () => {
5 | it('most likely falls back to toString', () => {
6 | const d = Temporal.Duration.from({ days: 2 })
7 | const s = d.toLocaleString(d)
8 | expect(s).toBeTruthy()
9 | })
10 |
11 | it('gives readable error message when no valid field', () => {
12 | let error: TypeError | undefined
13 |
14 | try {
15 | const d = Temporal.Duration.from({ day: 5 })
16 | expect(d).toBeTruthy() // won't reach
17 | } catch (e: any) {
18 | error = e
19 | }
20 |
21 | expect(error).toBeInstanceOf(TypeError)
22 | expect(error!.toString()).toMatch(
23 | [
24 | 'days',
25 | 'hours',
26 | 'microseconds',
27 | 'milliseconds',
28 | 'minutes',
29 | 'months',
30 | 'nanoseconds',
31 | 'seconds',
32 | 'weeks',
33 | 'years',
34 | ].join(','),
35 | )
36 | })
37 | })
38 |
39 | describe('Temporal.ZonedDateTime', () => {
40 | describe('round', () => {
41 | it('only accepts day/time smallestUnit', () => {
42 | const zdt0 = new Temporal.ZonedDateTime(
43 | 1709254884041880537n,
44 | 'America/New_York',
45 | )
46 | let error: RangeError | undefined
47 |
48 | try {
49 | const zdt1 = zdt0.round({ smallestUnit: 'year' })
50 | expect(zdt1).toBeTruthy() // won't reach
51 | } catch (e: any) {
52 | error = e
53 | }
54 |
55 | expect(error).toBeInstanceOf(RangeError)
56 | expect(error!.toString()).toMatch('year') // provided
57 | expect(error!.toString()).toMatch('day') // max
58 | expect(error!.toString()).toMatch('nanosecond') // min
59 | })
60 | })
61 | })
62 |
63 | describe('Intl.DateTimeFormat', () => {
64 | describe('constructor', () => {
65 | // https://github.com/fullcalendar/temporal-polyfill/issues/25
66 | it('can be called without new', () => {
67 | const format = Intl.DateTimeFormat('en-US', { year: 'numeric' })
68 | const pd = Temporal.PlainDate.from('2024-01-01')
69 | const s = format.format(pd)
70 | expect(s).toBe('2024')
71 | })
72 | })
73 |
74 | describe('format', () => {
75 | it('is always bound', () => {
76 | const format = new Intl.DateTimeFormat('en-US', { year: 'numeric' })
77 | const pd = Temporal.PlainDate.from('2024-01-01')
78 | const formatMethod = format.format
79 | const s = formatMethod(pd)
80 | expect(s).toBe('2024')
81 | })
82 | })
83 |
84 | describe('supportedLocalesOf', () => {
85 | it('works', () => {
86 | const locales = Intl.DateTimeFormat.supportedLocalesOf(['en', 'es'])
87 | expect(locales.length).toBe(2)
88 | })
89 | })
90 | })
91 |
92 | describe('Intl', () => {
93 | it('Has members aside from DateTimeFormat', () => {
94 | expect(Intl.NumberFormat).toBeTruthy()
95 | })
96 | })
97 |
98 | describe('toTemporalInstant', () => {
99 | it('works when call with Date as this-context', () => {
100 | const legacyDate = new Date(1708485414855)
101 | const inst = toTemporalInstant.call(legacyDate)
102 | expect(inst.epochNanoseconds).toBe(1708485414855000000n)
103 | })
104 | })
105 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/impl.ts:
--------------------------------------------------------------------------------
1 | export { Temporal } from './classApi/temporal'
2 | export { IntlExtended as Intl } from './classApi/intlExtended'
3 | export { toTemporalInstant } from './classApi/instant'
4 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './impl'
2 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/internal/bigNano.ts:
--------------------------------------------------------------------------------
1 | import { nanoInUtcDay } from './units'
2 | import { NumberSign, compareNumbers, divModFloor, divModTrunc } from './utils'
3 |
4 | /*
5 | Given a raw nanoseconds value,
6 | `days` is truncated (towards 0)
7 | `timeNano` is the remainder (sign agrees with days)
8 | */
9 | export type BigNano = [days: number, timeNano: number]
10 |
11 | /*
12 | does balancing
13 | */
14 | export function createBigNano(days: number, timeNano: number): BigNano {
15 | let [extraDays, newTimeNano] = divModTrunc(timeNano, nanoInUtcDay)
16 | let newDays = days + extraDays
17 | const newDaysSign = Math.sign(newDays)
18 |
19 | // ensure nonconflicting signs
20 | if (newDaysSign && newDaysSign === -Math.sign(newTimeNano)) {
21 | newDays -= newDaysSign
22 | newTimeNano += newDaysSign * nanoInUtcDay
23 | }
24 |
25 | return [newDays, newTimeNano]
26 | }
27 |
28 | // Math
29 | // -----------------------------------------------------------------------------
30 |
31 | export function addBigNanos(
32 | a: BigNano,
33 | b: BigNano,
34 | sign: NumberSign = 1,
35 | ): BigNano {
36 | return createBigNano(a[0] + b[0] * sign, a[1] + b[1] * sign)
37 | }
38 |
39 | export function moveBigNano(a: BigNano, b: number): BigNano {
40 | return createBigNano(a[0], a[1] + b)
41 | }
42 |
43 | export function diffBigNanos(a: BigNano, b: BigNano): BigNano {
44 | return addBigNanos(b, a, -1)
45 | }
46 |
47 | // Compare
48 | // -----------------------------------------------------------------------------
49 |
50 | export function compareBigNanos(a: BigNano, b: BigNano): NumberSign {
51 | return compareNumbers(a[0], b[0]) || compareNumbers(a[1], b[1])
52 | }
53 |
54 | export function bigNanoOutside(
55 | subject: BigNano,
56 | rangeStart: BigNano,
57 | rangeEndExcl: BigNano,
58 | ): boolean {
59 | return (
60 | compareBigNanos(subject, rangeStart) === -1 ||
61 | compareBigNanos(subject, rangeEndExcl) === 1
62 | )
63 | }
64 |
65 | // Conversion
66 | // -----------------------------------------------------------------------------
67 |
68 | // other -> BigNano
69 | // (BigNano needs trunc)
70 |
71 | export function bigIntToBigNano(num: bigint, multiplierNano = 1): BigNano {
72 | const wholeInDay = BigInt(nanoInUtcDay / multiplierNano)
73 | const days = Number(num / wholeInDay) // does trunc
74 | const remainder = Number(num % wholeInDay) // does trunc
75 | return [days, remainder * multiplierNano] // scaled. doesn't need balancing
76 | }
77 |
78 | /*
79 | Expects a proper integer. For user input, call toStrictInteger
80 | */
81 | export function numberToBigNano(num: number, multiplierNano = 1): BigNano {
82 | const wholeInDay = nanoInUtcDay / multiplierNano
83 | const [days, remainder] = divModTrunc(num, wholeInDay)
84 | return [days, remainder * multiplierNano] // scaled. doesn't need balancing
85 | }
86 |
87 | // BigNano -> other
88 | // (other units need floor)
89 | // (divisorNano always a denominator of day-nanoseconds, always positive)
90 |
91 | export function bigNanoToBigInt(bigNano: BigNano, divisorNano = 1): bigint {
92 | const [days, timeNano] = bigNano
93 | const whole = Math.floor(timeNano / divisorNano)
94 | const wholeInDay = nanoInUtcDay / divisorNano
95 | return BigInt(days) * BigInt(wholeInDay) + BigInt(whole)
96 | }
97 |
98 | export function bigNanoToNumber(
99 | bigNano: BigNano,
100 | divisorNano = 1,
101 | exact?: boolean,
102 | ): number {
103 | const [days, timeNano] = bigNano
104 | const [whole, remainderNano] = divModTrunc(timeNano, divisorNano)
105 | const wholeInDay = nanoInUtcDay / divisorNano
106 | // adding fraction to whole first results in better precision
107 | return days * wholeInDay + (whole + (exact ? remainderNano / divisorNano : 0))
108 | }
109 |
110 | export function bigNanoToExactDays(bigNano: BigNano): number {
111 | return bigNano[0] + bigNano[1] / nanoInUtcDay
112 | }
113 |
114 | export function divModBigNano(
115 | bigNano: BigNano,
116 | divisorNano: number,
117 | divModFunc = divModFloor,
118 | ): [whole: number, remainderNano: number] {
119 | const [days, timeNano] = bigNano
120 | const [whole, remainderNano] = divModFunc(timeNano, divisorNano)
121 | const wholeInDay = nanoInUtcDay / divisorNano
122 | return [days * wholeInDay + whole, remainderNano]
123 | }
124 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/internal/calendarConfig.ts:
--------------------------------------------------------------------------------
1 | import { dayFieldNames, yearFieldNames } from './fields'
2 |
3 | export const isoCalendarId = 'iso8601'
4 | export const gregoryCalendarId = 'gregory'
5 | export const japaneseCalendarId = 'japanese'
6 |
7 | /*
8 | for converting from [era,eraYear] -> year
9 | if origin is >=0,
10 | year = origin + eraYear
11 | if origin is <0, consider the era to be 'reverse' direction
12 | year = -origin - eraYear, same as...
13 | year = -(origin + eraYear)
14 | */
15 | export const eraOriginsByCalendarId: {
16 | [calendarId: string]: Record
17 | } = {
18 | [gregoryCalendarId]: {
19 | 'gregory-inverse': -1,
20 | 'gregory': 0,
21 | },
22 | [japaneseCalendarId]: {
23 | 'japanese-inverse': -1,
24 | 'japanese': 0,
25 | 'meiji': 1867,
26 | 'taisho': 1911,
27 | 'showa': 1925,
28 | 'heisei': 1988,
29 | 'reiwa': 2018,
30 | },
31 | 'ethiopic': {
32 | 'ethioaa': 0,
33 | 'ethiopic': 5500,
34 | },
35 | 'coptic': {
36 | 'coptic-inverse': -1,
37 | 'coptic': 0,
38 | },
39 | 'roc': {
40 | 'roc-inverse': -1,
41 | 'roc': 0,
42 | },
43 | 'buddhist': {
44 | 'be': 0,
45 | },
46 | 'islamic': {
47 | 'ah': 0,
48 | },
49 | 'indian': {
50 | 'saka': 0,
51 | },
52 | 'persian': {
53 | 'ap': 0,
54 | },
55 | }
56 |
57 | export const eraRemapsByCalendarId: {
58 | [calendarId: string]: Record
59 | } = {
60 | [gregoryCalendarId]: {
61 | 'bce': 'gregory-inverse',
62 | 'ce': 'gregory',
63 | },
64 | [japaneseCalendarId]: {
65 | 'bce': 'japanese-inverse',
66 | 'ce': 'japanese',
67 | },
68 | 'ethiopic': {
69 | 'era0': 'ethioaa',
70 | 'era1': 'ethiopic',
71 | },
72 | 'coptic': {
73 | 'era0': 'coptic-inverse',
74 | 'era1': 'coptic',
75 | },
76 | 'roc': {
77 | 'broc': 'roc-inverse',
78 | 'minguo': 'roc',
79 | },
80 | }
81 |
82 | export const leapMonthMetas: Record = {
83 | 'chinese': 13, // (positive) max possible leap month
84 | 'dangi': 13, // "
85 | 'hebrew': -6, // (negative) constant leap month
86 | }
87 |
88 | // only used by calendar
89 | // ---------------------
90 |
91 | export function getRequiredYearMonthFields(calendarId: string): string[] {
92 | return calendarId === isoCalendarId ? yearFieldNames : []
93 | }
94 |
95 | export function getRequiredMonthDayFields(calendarId: string): string[] {
96 | return calendarId === isoCalendarId ? dayFieldNames : []
97 | }
98 |
99 | export function getRequiredDateFields(calendarId: string): string[] {
100 | return calendarId === isoCalendarId ? ['year', 'day'] : []
101 | }
102 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/internal/calendarId.ts:
--------------------------------------------------------------------------------
1 | import { gregoryCalendarId, isoCalendarId } from './calendarConfig'
2 | import { requireString } from './cast'
3 | import * as errorMessages from './errorMessages'
4 | import { queryCalendarIntlFormat } from './intlMath'
5 |
6 | export function refineCalendarId(id: string): string {
7 | return resolveCalendarId(requireString(id))
8 | }
9 |
10 | export function resolveCalendarId(id: string): string {
11 | id = id.toLowerCase() // normalize
12 |
13 | if (id !== isoCalendarId && id !== gregoryCalendarId) {
14 | const canonId = queryCalendarIntlFormat(id).resolvedOptions().calendar
15 |
16 | if (computeCalendarIdBase(id) !== computeCalendarIdBase(canonId)) {
17 | throw new RangeError(errorMessages.invalidCalendar(id))
18 | }
19 |
20 | return canonId
21 | }
22 |
23 | return id
24 | }
25 |
26 | export function computeCalendarIdBase(id: string): string {
27 | if (id === 'islamicc') {
28 | id = 'islamic'
29 | }
30 | return id.split('-')[0]
31 | }
32 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/internal/calendarOps.ts:
--------------------------------------------------------------------------------
1 | import { DurationFields } from './durationFields'
2 | import { DateBag, YearMonthBag } from './fields'
3 | import { IsoDateFields } from './isoFields'
4 | import { DiffOptions, OverflowOptions } from './optionsRefine'
5 | import {
6 | PlainDateSlots,
7 | PlainMonthDaySlots,
8 | PlainYearMonthSlots,
9 | } from './slots'
10 | import { DateUnitName, Unit } from './units'
11 |
12 | // Operations for internal use!
13 |
14 | // Function Types
15 | // (Must always be called from a CalendarOps object)
16 | export type DateFromFieldsOp = (
17 | fields: DateBag,
18 | options?: OverflowOptions,
19 | ) => PlainDateSlots
20 |
21 | export type YearMonthFromFieldsOp = (
22 | fields: YearMonthBag,
23 | options?: OverflowOptions,
24 | ) => PlainYearMonthSlots
25 |
26 | export type MonthDayFromFieldsOp = (
27 | fields: DateBag,
28 | options?: OverflowOptions,
29 | ) => PlainMonthDaySlots
30 |
31 | export type FieldsOp = (fieldNames: string[]) => string[]
32 |
33 | export type MergeFieldsOp = (
34 | fields: DateBag,
35 | additionalFields: DateBag,
36 | ) => DateBag
37 |
38 | export type DateAddOp = (
39 | isoFields: IsoDateFields,
40 | durationFields: DurationFields,
41 | options?: OverflowOptions,
42 | ) => IsoDateFields
43 |
44 | export type DateUntilOp = (
45 | isoFields0: IsoDateFields,
46 | isoFields1: IsoDateFields,
47 | largestUnit: Unit,
48 | origOptions?: DiffOptions,
49 | ) => DurationFields
50 |
51 | export type DayOp = (isoFields: IsoDateFields) => number
52 |
53 | // Refine
54 | // (assumes received fields are ALREADY refined)
55 | // TODO: have these functions return the branding too?
56 |
57 | export type DateRefineOps = {
58 | dateFromFields: DateFromFieldsOp
59 | fields: FieldsOp
60 | }
61 | export type YearMonthRefineOps = {
62 | yearMonthFromFields: YearMonthFromFieldsOp
63 | fields: FieldsOp
64 | }
65 | export type MonthDayRefineOps = {
66 | monthDayFromFields: MonthDayFromFieldsOp
67 | fields: FieldsOp
68 | }
69 |
70 | // Mod
71 | // (assumes received fields are ALREADY refined)
72 | export type YearMonthModOps = YearMonthRefineOps & {
73 | mergeFields: MergeFieldsOp
74 | }
75 | export type DateModOps = DateRefineOps & { mergeFields: MergeFieldsOp }
76 | export type MonthDayModOps = MonthDayRefineOps & {
77 | mergeFields: MergeFieldsOp
78 | }
79 |
80 | // Math
81 |
82 | export type MoveOps = { dateAdd: DateAddOp }
83 | export type DiffOps = { dateAdd: DateAddOp; dateUntil: DateUntilOp }
84 |
85 | export type DayOps = { day: DayOp }
86 |
87 | export type YearMonthMoveOps = MoveOps & DayOps
88 | export type YearMonthDiffOps = DiffOps & DayOps
89 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/internal/current.ts:
--------------------------------------------------------------------------------
1 | import { BigNano } from './bigNano'
2 | import { RawDateTimeFormat } from './intlFormatUtils'
3 | import { IsoDateTimeFields } from './isoFields'
4 | import { epochMilliToNano, epochNanoToIso } from './timeMath'
5 | import { TimeZoneOffsetOps } from './timeZoneOps'
6 |
7 | export function getCurrentIsoDateTime(
8 | timeZoneOps: TimeZoneOffsetOps,
9 | ): IsoDateTimeFields {
10 | const epochNano = getCurrentEpochNano()
11 | const offsetNano = timeZoneOps.getOffsetNanosecondsFor(epochNano)
12 | return epochNanoToIso(epochNano, offsetNano)
13 | }
14 |
15 | export function getCurrentEpochNano(): BigNano {
16 | return epochMilliToNano(Date.now())
17 | }
18 |
19 | // -----------------------------------------------------------------------------
20 |
21 | let currentTimeZoneId: string | undefined
22 |
23 | export function getCurrentTimeZoneId(): string {
24 | return currentTimeZoneId || (currentTimeZoneId = computeCurrentTimeZoneId())
25 | }
26 |
27 | function computeCurrentTimeZoneId(): string {
28 | return new RawDateTimeFormat().resolvedOptions().timeZone
29 | }
30 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/internal/durationFields.ts:
--------------------------------------------------------------------------------
1 | import { Unit, unitNamesAsc } from './units'
2 | import {
3 | bindArgs,
4 | mapPropNamesToConstant,
5 | mapPropNamesToIndex,
6 | sortStrings,
7 | zeroOutProps,
8 | } from './utils'
9 |
10 | export interface DurationDateFields {
11 | days: number
12 | weeks: number
13 | months: number
14 | years: number
15 | }
16 |
17 | export interface DurationTimeFields {
18 | nanoseconds: number
19 | microseconds: number
20 | milliseconds: number
21 | seconds: number
22 | minutes: number
23 | hours: number
24 | }
25 |
26 | export type DurationFields = DurationDateFields & DurationTimeFields
27 |
28 | // Field Names
29 | // -----------------------------------------------------------------------------
30 |
31 | export type DurationYearMonthFieldName = 'years' | 'months'
32 | export type DurationDateFieldName =
33 | | DurationYearMonthFieldName
34 | | 'weeks'
35 | | 'days'
36 | export type DurationTimeFieldName =
37 | | 'hours'
38 | | 'minutes'
39 | | 'seconds'
40 | | 'milliseconds'
41 | | 'microseconds'
42 | | 'nanoseconds'
43 | export type DurationDayTimeFieldName = 'day' | DurationTimeFieldName
44 | export type DurationFieldName = DurationDateFieldName | DurationTimeFieldName
45 |
46 | export const durationFieldNamesAsc = unitNamesAsc.map(
47 | (unitName) => unitName + 's',
48 | ) as DurationFieldName[]
49 |
50 | export const durationFieldNamesAlpha = sortStrings(durationFieldNamesAsc)
51 |
52 | export const durationTimeFieldNamesAsc = durationFieldNamesAsc.slice(
53 | 0,
54 | Unit.Day,
55 | ) as DurationTimeFieldName[]
56 |
57 | export const durationDateFieldNamesAsc = durationFieldNamesAsc.slice(Unit.Day)
58 | export const durationCalendarFieldNamesAsc = durationDateFieldNamesAsc.slice(1)
59 |
60 | export const durationFieldIndexes = mapPropNamesToIndex(durationFieldNamesAsc)
61 |
62 | // Field Defaults
63 | // -----------------------------------------------------------------------------
64 |
65 | export const durationFieldDefaults = mapPropNamesToConstant(
66 | durationFieldNamesAsc,
67 | 0,
68 | )
69 | export const durationTimeFieldDefaults = mapPropNamesToConstant(
70 | durationTimeFieldNamesAsc,
71 | 0,
72 | )
73 |
74 | export const clearDurationFields = bindArgs(
75 | zeroOutProps,
76 | durationFieldNamesAsc,
77 | ) as unknown as (unit: Unit, durationFields: DurationFields) => DurationFields
78 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/internal/fields.ts:
--------------------------------------------------------------------------------
1 | import { DurationFields } from './durationFields'
2 | import { Unit, unitNamesAsc } from './units'
3 | import { mapPropNamesToConstant, sortStrings } from './utils'
4 |
5 | // Fields
6 | // -----------------------------------------------------------------------------
7 |
8 | export interface EraYearFields {
9 | era: string
10 | eraYear: number
11 | }
12 |
13 | export type YearFields = Partial & { year: number }
14 |
15 | export interface MonthFields {
16 | monthCode: string
17 | month: number
18 | }
19 |
20 | export interface DayFields {
21 | day: number
22 | }
23 |
24 | export type YearMonthFields = YearFields & MonthFields
25 | export type DateFields = YearMonthFields & DayFields
26 | export type MonthDayFields = MonthFields & DayFields
27 |
28 | export interface TimeFields {
29 | hour: number
30 | microsecond: number
31 | millisecond: number
32 | minute: number
33 | nanosecond: number
34 | second: number
35 | }
36 |
37 | export type DateTimeFields = DateFields & TimeFields
38 |
39 | // Simple Bag (all props optional)
40 | // -----------------------------------------------------------------------------
41 |
42 | export type YearMonthBag = Partial
43 | export type DateBag = Partial
44 | export type MonthDayBag = Partial
45 | export type DurationBag = Partial
46 | export type TimeBag = Partial
47 | export type DateTimeBag = DateBag & TimeBag
48 |
49 | // Strict Bag (with complex expressions)
50 | // -----------------------------------------------------------------------------
51 |
52 | export type EraYearOrYear = EraYearFields | { year: number }
53 | export type MonthCodeOrMonthAndYear =
54 | | { monthCode: string }
55 | | ({ month: number } & EraYearOrYear)
56 | export type MonthCodeOrMonth = { monthCode: string } | { month: number }
57 |
58 | export type YearMonthBagStrict = EraYearOrYear & MonthCodeOrMonth
59 | export type DateBagStrict = EraYearOrYear & MonthCodeOrMonth & DayFields
60 | export type MonthDayBagStrict = MonthCodeOrMonthAndYear & DayFields
61 |
62 | // Stats
63 | // -----------------------------------------------------------------------------
64 |
65 | export interface YearStats {
66 | daysInYear: number
67 | inLeapYear: boolean
68 | monthsInYear: number
69 | }
70 |
71 | export interface YearMonthStats extends YearStats {
72 | daysInMonth: number
73 | }
74 |
75 | export interface DateStats extends YearMonthStats {
76 | dayOfWeek: number
77 | dayOfYear: number
78 | weekOfYear: number
79 | yearOfWeek: number
80 | daysInWeek: number
81 | }
82 |
83 | // Field Names
84 | // -----------------------------------------------------------------------------
85 | // TODO: converge on 'alpha' naming or not
86 |
87 | export const timeFieldNamesAsc = unitNamesAsc.slice(
88 | 0,
89 | Unit.Day,
90 | ) as (keyof TimeFields)[]
91 |
92 | export const timeFieldNamesAlpha = sortStrings(timeFieldNamesAsc)
93 |
94 | export const offsetFieldNames = ['offset']
95 | export const timeZoneFieldNames = ['timeZone']
96 |
97 | export const timeAndOffsetFieldNames = [
98 | ...timeFieldNamesAsc,
99 | ...offsetFieldNames,
100 | ]
101 | export const timeAndZoneFieldNames = [
102 | ...timeAndOffsetFieldNames,
103 | ...timeZoneFieldNames,
104 | ]
105 |
106 | // pre-sorted!!!...
107 |
108 | export const eraYearFieldNames = ['era', 'eraYear']
109 | export const allYearFieldNames = [...eraYearFieldNames, 'year']
110 |
111 | export const yearFieldNames = ['year']
112 | export const monthCodeFieldNames = ['monthCode']
113 | export const monthFieldNames = ['month', ...monthCodeFieldNames] // month/monthCode
114 | export const dayFieldNames = ['day']
115 |
116 | // month/monthCode/year
117 | export const yearMonthFieldNames = [...monthFieldNames, ...yearFieldNames]
118 |
119 | // monthCode/year
120 | export const yearMonthCodeFieldNames = [
121 | ...monthCodeFieldNames,
122 | ...yearFieldNames,
123 | ]
124 |
125 | export const dateFieldNamesAlpha = [...dayFieldNames, ...yearMonthFieldNames]
126 |
127 | export const monthDayFieldNames = [...dayFieldNames, ...monthFieldNames] // day/month/monthCode
128 | export const monthCodeDayFieldNames = [...dayFieldNames, ...monthCodeFieldNames] // day/monthCode
129 |
130 | // Defaults
131 | // -----------------------------------------------------------------------------
132 |
133 | export const timeFieldDefaults = mapPropNamesToConstant(timeFieldNamesAsc, 0)
134 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/internal/intlFormatUtils.ts:
--------------------------------------------------------------------------------
1 | import * as errorMessages from './errorMessages'
2 | import { maxMilli } from './timeMath'
3 |
4 | export type LocalesArg = string | string[]
5 | export type OptionNames = (keyof Intl.DateTimeFormatOptions)[]
6 | export type RawFormattable = Date | number
7 |
8 | export const RawDateTimeFormat = Intl.DateTimeFormat
9 |
10 | export const standardLocaleId = 'en-GB' // 24-hour clock, gregorian by default
11 |
12 | export function hashIntlFormatParts(
13 | intlFormat: Intl.DateTimeFormat,
14 | epochMilli: number,
15 | ): Record {
16 | // TODO: best level for this?
17 | // Probably do it when date is *converted* to epochMilli
18 | if (epochMilli < -maxMilli) {
19 | throw new RangeError(errorMessages.outOfBoundsDate)
20 | }
21 |
22 | const parts = intlFormat.formatToParts(epochMilli)
23 | const hash = {} as Record
24 |
25 | for (const part of parts) {
26 | hash[part.type] = part.value
27 | }
28 |
29 | return hash
30 | }
31 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/internal/isoFields.ts:
--------------------------------------------------------------------------------
1 | import { DayTimeUnit } from './units'
2 | import {
3 | bindArgs,
4 | mapPropNamesToConstant,
5 | sortStrings,
6 | zeroOutProps,
7 | } from './utils'
8 |
9 | export interface IsoDateFields {
10 | isoDay: number
11 | isoMonth: number
12 | isoYear: number
13 | }
14 |
15 | export interface IsoTimeFields {
16 | isoNanosecond: number
17 | isoMicrosecond: number
18 | isoMillisecond: number
19 | isoSecond: number
20 | isoMinute: number
21 | isoHour: number
22 | }
23 |
24 | export type IsoDateTimeFields = IsoDateFields & IsoTimeFields
25 |
26 | // Property Names
27 | // -----------------------------------------------------------------------------
28 |
29 | export const isoTimeFieldNamesAsc: (keyof IsoTimeFields)[] = [
30 | 'isoNanosecond',
31 | 'isoMicrosecond',
32 | 'isoMillisecond',
33 | 'isoSecond',
34 | 'isoMinute',
35 | 'isoHour',
36 | ]
37 |
38 | export const isoDateFieldNamesAsc: (keyof IsoDateFields)[] = [
39 | 'isoDay',
40 | 'isoMonth',
41 | 'isoYear',
42 | ]
43 |
44 | export const isoDateTimeFieldNamesAsc: (keyof IsoDateTimeFields)[] = [
45 | ...isoTimeFieldNamesAsc,
46 | ...isoDateFieldNamesAsc,
47 | ]
48 |
49 | // alphabetical (for getISOFields)
50 | export const isoDateFieldNamesAlpha = sortStrings(isoDateFieldNamesAsc)
51 | export const isoTimeFieldNamesAlpha = sortStrings(isoTimeFieldNamesAsc)
52 | export const isoDateTimeFieldNamesAlpha = sortStrings(isoDateTimeFieldNamesAsc)
53 |
54 | // Defaults
55 | // -----------------------------------------------------------------------------
56 |
57 | export const isoTimeFieldDefaults = mapPropNamesToConstant(
58 | isoTimeFieldNamesAlpha,
59 | 0,
60 | )
61 |
62 | export const clearIsoFields = bindArgs(
63 | zeroOutProps,
64 | isoDateTimeFieldNamesAsc,
65 | ) as unknown as (
66 | unit: DayTimeUnit,
67 | isoFields: IsoDateTimeFields,
68 | ) => IsoDateTimeFields
69 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/internal/markerSystem.ts:
--------------------------------------------------------------------------------
1 | import { BigNano } from './bigNano'
2 | import { DiffOps, MoveOps } from './calendarOps'
3 | import { diffDateTimesExact, diffZonedEpochsExact } from './diff'
4 | import { DurationFields } from './durationFields'
5 | import {
6 | IsoDateFields,
7 | IsoDateTimeFields,
8 | isoTimeFieldDefaults,
9 | } from './isoFields'
10 | import { moveDateTime, moveZonedEpochs } from './move'
11 | import {
12 | DateSlots,
13 | EpochAndZoneSlots,
14 | ZonedEpochSlots,
15 | extractEpochNano,
16 | } from './slots'
17 | import { isoToEpochNano } from './timeMath'
18 | import { TimeZoneOps } from './timeZoneOps'
19 | import { Unit } from './units'
20 | import { Callable, bindArgs } from './utils'
21 |
22 | // the "origin"
23 | export type RelativeToSlots = DateSlots | ZonedEpochSlots
24 |
25 | // the "origin", returned from bag refining
26 | export type RelativeToSlotsNoCalendar = IsoDateFields | EpochAndZoneSlots
27 |
28 | // a date marker that's moved away from the "origin"
29 | export type Marker = IsoDateFields | IsoDateTimeFields | ZonedEpochSlots
30 |
31 | export type MarkerSystem = [Marker, CO, TimeZoneOps?]
32 |
33 | export function createMarkerSystem(
34 | getCalendarOps: (calendarId: string) => CO,
35 | getTimeZoneOps: (timeZoneId: string) => TimeZoneOps,
36 | relativeToSlots: RelativeToSlots,
37 | ): MarkerSystem {
38 | const calendarOps = getCalendarOps(relativeToSlots.calendar)
39 |
40 | if (isZonedEpochSlots(relativeToSlots)) {
41 | return [
42 | relativeToSlots,
43 | calendarOps,
44 | getTimeZoneOps(relativeToSlots.timeZone),
45 | ]
46 | }
47 |
48 | return [
49 | // convert IsoDateFields->IsoDateTimeFields
50 | // because expected in createMoveMarker/createDiffMarkers
51 | { ...relativeToSlots, ...isoTimeFieldDefaults },
52 | calendarOps,
53 | ]
54 | }
55 |
56 | // Atomic Operations
57 | // -----------------------------------------------------------------------------
58 |
59 | export type MarkerToEpochNano = (marker: Marker) => BigNano
60 |
61 | export type MoveMarker = (
62 | calendarOps: MoveOps,
63 | marker: Marker,
64 | durationFields: DurationFields,
65 | ) => Marker
66 |
67 | export type DiffMarkers = (
68 | calendarOps: DiffOps,
69 | marker0: Marker,
70 | marker1: Marker,
71 | largestUnit: Unit,
72 | ) => DurationFields
73 |
74 | export function createMarkerToEpochNano(
75 | timeZoneOps: TimeZoneOps | undefined,
76 | ): MarkerToEpochNano {
77 | return (timeZoneOps ? extractEpochNano : isoToEpochNano) as MarkerToEpochNano
78 | }
79 |
80 | export function createMoveMarker(
81 | timeZoneOps: TimeZoneOps | undefined,
82 | ): MoveMarker {
83 | if (timeZoneOps) {
84 | return bindArgs(moveZonedEpochs, timeZoneOps) as Callable
85 | }
86 | return moveDateTime as Callable
87 | }
88 |
89 | export function createDiffMarkers(
90 | timeZoneOps: TimeZoneOps | undefined,
91 | ): DiffMarkers {
92 | if (timeZoneOps) {
93 | return bindArgs(diffZonedEpochsExact, timeZoneOps) as Callable
94 | }
95 | return diffDateTimesExact as Callable
96 | }
97 |
98 | // Utils
99 | // -----------------------------------------------------------------------------
100 |
101 | export function isZonedEpochSlots(
102 | marker: Marker | undefined,
103 | ): marker is ZonedEpochSlots {
104 | return (marker &&
105 | (marker as ZonedEpochSlots).epochNanoseconds) as unknown as boolean
106 | }
107 |
108 | /*
109 | For PlainDate(Time) markers, days+time are uniform
110 | For ZonedDateTime markers, only time is uniform (days can vary in length)
111 | */
112 | export function isUniformUnit(unit: Unit, marker: Marker | undefined): boolean {
113 | return unit <= Unit.Day - (isZonedEpochSlots(marker) ? 1 : 0)
114 | }
115 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/internal/modify.ts:
--------------------------------------------------------------------------------
1 | import { BigNano } from './bigNano'
2 | import { isoCalendarId } from './calendarConfig'
3 | import * as errorMessages from './errorMessages'
4 | import { IsoTimeFields, isoTimeFieldDefaults } from './isoFields'
5 | import { OffsetDisambig } from './options'
6 | import {
7 | PlainDateSlots,
8 | PlainDateTimeSlots,
9 | ZonedDateTimeSlots,
10 | createPlainDateTimeSlots,
11 | createZonedDateTimeSlots,
12 | } from './slots'
13 | import { checkIsoDateTimeInBounds } from './timeMath'
14 | import {
15 | TimeZoneOps,
16 | getMatchingInstantFor,
17 | getStartOfDayInstantFor,
18 | zonedEpochSlotsToIso,
19 | } from './timeZoneOps'
20 |
21 | // ZonedDateTime with *
22 | // -----------------------------------------------------------------------------
23 |
24 | export function zonedDateTimeWithPlainTime(
25 | getTimeZoneOps: (timeZoneId: string) => TimeZoneOps,
26 | zonedDateTimeSlots: ZonedDateTimeSlots,
27 | plainTimeSlots: IsoTimeFields | undefined,
28 | ): ZonedDateTimeSlots {
29 | const timeZoneId = zonedDateTimeSlots.timeZone
30 | const timeZoneOps = getTimeZoneOps(timeZoneId)
31 |
32 | const isoFields = {
33 | ...zonedEpochSlotsToIso(zonedDateTimeSlots, timeZoneOps),
34 | ...(plainTimeSlots || isoTimeFieldDefaults),
35 | }
36 |
37 | let epochNano: BigNano
38 |
39 | if (plainTimeSlots) {
40 | epochNano = getMatchingInstantFor(
41 | timeZoneOps,
42 | isoFields,
43 | isoFields.offsetNanoseconds,
44 | OffsetDisambig.Prefer, // OffsetDisambig
45 | )
46 | } else {
47 | epochNano = getStartOfDayInstantFor(
48 | timeZoneOps as any, // !!!
49 | isoFields,
50 | )
51 | }
52 |
53 | return createZonedDateTimeSlots(
54 | epochNano,
55 | timeZoneId,
56 | zonedDateTimeSlots.calendar,
57 | )
58 | }
59 |
60 | export function zonedDateTimeWithPlainDate(
61 | getTimeZoneOps: (timeZoneId: string) => TimeZoneOps,
62 | zonedDateTimeSlots: ZonedDateTimeSlots,
63 | plainDateSlots: PlainDateSlots,
64 | ): ZonedDateTimeSlots {
65 | const timeZoneId = zonedDateTimeSlots.timeZone
66 | const timeZoneOps = getTimeZoneOps(timeZoneId)
67 |
68 | const isoFields = {
69 | ...zonedEpochSlotsToIso(zonedDateTimeSlots, timeZoneOps),
70 | ...plainDateSlots,
71 | }
72 | const calendar = getPreferredCalendarId(
73 | zonedDateTimeSlots.calendar,
74 | plainDateSlots.calendar,
75 | )
76 |
77 | const epochNano = getMatchingInstantFor(
78 | timeZoneOps,
79 | isoFields,
80 | isoFields.offsetNanoseconds,
81 | OffsetDisambig.Prefer, // OffsetDisambig
82 | )
83 |
84 | return createZonedDateTimeSlots(epochNano, timeZoneId, calendar)
85 | }
86 |
87 | // PlainDateTime with *
88 | // -----------------------------------------------------------------------------
89 |
90 | export function plainDateTimeWithPlainTime(
91 | plainDateTimeSlots: PlainDateTimeSlots,
92 | plainTimeSlots: IsoTimeFields = isoTimeFieldDefaults,
93 | ): PlainDateTimeSlots {
94 | return createPlainDateTimeSlots(
95 | checkIsoDateTimeInBounds({
96 | ...plainDateTimeSlots,
97 | ...plainTimeSlots,
98 | }),
99 | )
100 | }
101 |
102 | /*
103 | Only used by funcApi
104 | */
105 | export function plainDateTimeWithPlainDate(
106 | plainDateTimeSlots: PlainDateTimeSlots,
107 | plainDateSlots: PlainDateSlots,
108 | ) {
109 | return createPlainDateTimeSlots(
110 | {
111 | ...plainDateTimeSlots,
112 | ...plainDateSlots,
113 | },
114 | getPreferredCalendarId(
115 | plainDateTimeSlots.calendar,
116 | plainDateSlots.calendar,
117 | ),
118 | )
119 | }
120 |
121 | // Anything with calendar/timeZone
122 | // -----------------------------------------------------------------------------
123 |
124 | export function slotsWithCalendarId(
125 | slots: S,
126 | calendarId: string,
127 | ): S {
128 | return { ...slots, calendar: calendarId }
129 | }
130 |
131 | export function slotsWithTimeZoneId(
132 | slots: S,
133 | timeZoneId: string,
134 | ): S {
135 | return { ...slots, timeZone: timeZoneId }
136 | }
137 |
138 | // -----------------------------------------------------------------------------
139 |
140 | function getPreferredCalendarId(a: string, b: string): string {
141 | if (a === b) {
142 | return a
143 | }
144 |
145 | if (a === b || a === isoCalendarId) {
146 | return b
147 | }
148 | if (b === isoCalendarId) {
149 | return a
150 | }
151 |
152 | throw new RangeError(errorMessages.mismatchingCalendars)
153 | }
154 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/internal/options.ts:
--------------------------------------------------------------------------------
1 | import {
2 | roundExpand,
3 | roundHalfCeil,
4 | roundHalfEven,
5 | roundHalfExpand,
6 | roundHalfFloor,
7 | roundHalfTrunc,
8 | } from './utils'
9 |
10 | export const enum Overflow {
11 | Constrain = 0,
12 | Reject = 1,
13 | }
14 |
15 | export const enum EpochDisambig {
16 | Compat = 0,
17 | Reject = 1,
18 | Earlier = 2,
19 | Later = 3,
20 | }
21 |
22 | export const enum OffsetDisambig {
23 | Reject = 0,
24 | Use = 1,
25 | Prefer = 2,
26 | Ignore = 3,
27 | }
28 |
29 | export const enum CalendarDisplay {
30 | Auto = 0,
31 | Never = 1,
32 | Critical = 2,
33 | Always = 3,
34 | }
35 |
36 | export const enum TimeZoneDisplay {
37 | Auto = 0,
38 | Never = 1,
39 | Critical = 2,
40 | }
41 |
42 | export const enum OffsetDisplay {
43 | Auto = 0,
44 | Never = 1,
45 | }
46 |
47 | export const enum RoundingMode {
48 | // modes that get inverted (see invertRoundingMode)
49 | Floor = 0,
50 | HalfFloor = 1,
51 | Ceil = 2,
52 | HalfCeil = 3,
53 | // other modes
54 | Trunc = 4,
55 | HalfTrunc = 5,
56 | Expand = 6,
57 | HalfExpand = 7,
58 | HalfEven = 8,
59 | }
60 |
61 | export const roundingModeFuncs = [
62 | Math.floor,
63 | roundHalfFloor,
64 | Math.ceil,
65 | roundHalfCeil,
66 | Math.trunc,
67 | roundHalfTrunc,
68 | roundExpand,
69 | roundHalfExpand,
70 | roundHalfEven,
71 | ]
72 |
73 | export type SubsecDigits = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
74 |
75 | /*
76 | common SubsecDigits addons:
77 | -1 means hide seconds
78 | undefined means 'auto' (display all digits but no trailing zeros)
79 | */
80 |
81 | export const enum Direction {
82 | Previous = -1, // compatible with internal getTransition
83 | Next = 1, // "
84 | }
85 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/internal/timeZoneConfig.ts:
--------------------------------------------------------------------------------
1 | import { isoArgsToEpochSec } from './timeMath'
2 | import { secInDay } from './units'
3 |
4 | export const utcTimeZoneId = 'UTC'
5 |
6 | export const periodDur = secInDay * 60
7 |
8 | export const minPossibleTransition = isoArgsToEpochSec(1847)
9 | export const maxPossibleTransition = isoArgsToEpochSec(getCurrentYearPlus10())
10 |
11 | function getCurrentYearPlus10() {
12 | const currentDate = /*@__PURE__*/ new Date()
13 | const currentYear = /*@__PURE__*/ currentDate.getUTCFullYear()
14 | return currentYear + 10
15 | }
16 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/internal/timeZoneId.ts:
--------------------------------------------------------------------------------
1 | import { requireString } from './cast'
2 | import * as errorMessages from './errorMessages'
3 | import { RawDateTimeFormat, standardLocaleId } from './intlFormatUtils'
4 | import { formatOffsetNano } from './isoFormat'
5 | import { parseOffsetNanoMaybe } from './isoParse'
6 | import { utcTimeZoneId } from './timeZoneConfig'
7 | import { capitalize, memoize } from './utils'
8 |
9 | export function refineTimeZoneId(id: string): string {
10 | return resolveTimeZoneId(requireString(id))
11 | }
12 |
13 | export function resolveTimeZoneId(id: string): string {
14 | const essence = getTimeZoneEssence(id)
15 | return typeof essence === 'number'
16 | ? formatOffsetNano(essence)
17 | : essence
18 | ? normalizeNamedTimeZoneId(id)
19 | : utcTimeZoneId
20 | }
21 |
22 | export function getTimeZoneAtomic(id: string): string | number {
23 | const essence = getTimeZoneEssence(id)
24 | return typeof essence === 'number'
25 | ? essence
26 | : essence
27 | ? essence.resolvedOptions().timeZone
28 | : utcTimeZoneId
29 | }
30 |
31 | /**
32 | * @returns Undefined means `utcTimeZoneId`
33 | */
34 | export function getTimeZoneEssence(
35 | id: string,
36 | ): number | Intl.DateTimeFormat | undefined {
37 | id = id.toUpperCase()
38 |
39 | const offsetNano = parseOffsetNanoMaybe(id, true) // onlyHourMinute=true
40 | if (offsetNano !== undefined) {
41 | return offsetNano
42 | }
43 |
44 | if (id !== utcTimeZoneId) {
45 | return queryTimeZoneIntlFormat(id)
46 | }
47 | }
48 |
49 | /**
50 | * @param id Expects uppercase
51 | */
52 | const queryTimeZoneIntlFormat = memoize(
53 | (id: string): Intl.DateTimeFormat =>
54 | new RawDateTimeFormat(standardLocaleId, {
55 | timeZone: id,
56 | era: 'short',
57 | year: 'numeric',
58 | month: 'numeric',
59 | day: 'numeric',
60 | hour: 'numeric',
61 | minute: 'numeric',
62 | second: 'numeric',
63 | }),
64 | )
65 |
66 | const icuRegExp =
67 | /^(AC|AE|AG|AR|AS|BE|BS|CA|CN|CS|CT|EA|EC|IE|IS|JS|MI|NE|NS|PL|PN|PR|PS|SS|VS)T$/
68 |
69 | // TODO: move to parsing file?
70 | const badCharactersRegExp = /[^\w\/:+-]+/
71 |
72 | function normalizeNamedTimeZoneId(id: string): string {
73 | if (badCharactersRegExp.test(id)) {
74 | throw new RangeError(errorMessages.invalidTimeZone(id))
75 | }
76 |
77 | if (icuRegExp.test(id)) {
78 | // TODO: give identifier
79 | throw new RangeError(errorMessages.forbiddenIcuTimeZone)
80 | }
81 |
82 | // TODO:^^^ do above rejecting before expensive Intl.DateTimeFormat is created?
83 |
84 | return id
85 | .toLowerCase()
86 | .split('/')
87 | .map((part, partI) => {
88 | // abbreviation-like (big parts, like 'ACT' in 'Australia/ACT')
89 | // OR numeric-offset-like
90 | // OR Pacific/YAP
91 | if ((part.length <= 3 || /\d/.test(part)) && !/etc|yap/.test(part)) {
92 | return part.toUpperCase()
93 | }
94 |
95 | return part.replace(/baja|dumont|[a-z]+/g, (a, i) => {
96 | // abbreviation-like (small parts)
97 | // - starts with 1-or-2-letters?
98 | // - Knox_IN, NZ-CHAT
99 | if ((a.length <= 2 && !partI) || a === 'in' || a === 'chat') {
100 | return a.toUpperCase()
101 | }
102 |
103 | // word-like
104 | if (a.length > 2 || !i) {
105 | return capitalize(a).replace(
106 | /island|noronha|murdo|rivadavia|urville/,
107 | capitalize,
108 | )
109 | }
110 |
111 | // lowercase (au/of/es)
112 | return a
113 | })
114 | })
115 | .join('/')
116 | }
117 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/internal/units.ts:
--------------------------------------------------------------------------------
1 | import { BigNano } from './bigNano'
2 | import {
3 | DurationDateFieldName,
4 | DurationDayTimeFieldName,
5 | DurationFieldName,
6 | DurationTimeFieldName,
7 | DurationYearMonthFieldName,
8 | } from './durationFields'
9 | import { divModTrunc, divTrunc, modTrunc } from './utils'
10 |
11 | export const enum Unit {
12 | Nanosecond = 0,
13 | Microsecond = 1,
14 | Millisecond = 2,
15 | Second = 3,
16 | Minute = 4,
17 | Hour = 5,
18 | Day = 6,
19 | Week = 7,
20 | Month = 8,
21 | Year = 9,
22 | }
23 |
24 | export type TimeUnit =
25 | | Unit.Nanosecond
26 | | Unit.Microsecond
27 | | Unit.Millisecond
28 | | Unit.Second
29 | | Unit.Minute
30 | | Unit.Hour
31 |
32 | export type DayTimeUnit = Unit.Day | TimeUnit
33 |
34 | // Names
35 | // -----------------------------------------------------------------------------
36 |
37 | // singular
38 | export type StrictYearMonthUnitName = 'year' | 'month'
39 | export type StrictDateUnitName = StrictYearMonthUnitName | 'week' | 'day'
40 | export type StrictTimeUnitName =
41 | | 'hour'
42 | | 'minute'
43 | | 'second'
44 | | 'millisecond'
45 | | 'microsecond'
46 | | 'nanosecond'
47 | export type StrictDayTimeUnitName = 'day' | StrictTimeUnitName
48 | export type StrictUnitName = StrictDateUnitName | StrictTimeUnitName
49 |
50 | // singular OR plural
51 | export type YearMonthUnitName =
52 | | StrictYearMonthUnitName
53 | | DurationYearMonthFieldName
54 | export type DateUnitName = StrictDateUnitName | DurationDateFieldName
55 | export type TimeUnitName = StrictTimeUnitName | DurationTimeFieldName
56 | export type DayTimeUnitName = StrictDayTimeUnitName | DurationDayTimeFieldName
57 | export type UnitName = StrictUnitName | DurationFieldName
58 |
59 | export const unitNameMap = {
60 | nanosecond: Unit.Nanosecond,
61 | microsecond: Unit.Microsecond,
62 | millisecond: Unit.Millisecond,
63 | second: Unit.Second,
64 | minute: Unit.Minute,
65 | hour: Unit.Hour,
66 | day: Unit.Day,
67 | week: Unit.Week,
68 | month: Unit.Month,
69 | year: Unit.Year,
70 | }
71 |
72 | export const unitNamesAsc = Object.keys(
73 | unitNameMap,
74 | ) as (keyof typeof unitNameMap)[]
75 |
76 | // Nanoseconds
77 | // -----------------------------------------------------------------------------
78 |
79 | export const secInDay = 86400
80 | export const milliInDay = 86400000
81 | export const milliInSec = 1000
82 |
83 | export const nanoInMicro = 1000 // consolidate with other 1000 units
84 | export const nanoInMilli = 1_000_000
85 | export const nanoInSec = 1_000_000_000
86 | export const nanoInMinute = 60_000_000_000
87 | export const nanoInHour = 3_600_000_000_000
88 | export const nanoInUtcDay = 86_400_000_000_000
89 |
90 | export const unitNanoMap = [
91 | 1, // nano-in-nano
92 | nanoInMicro,
93 | nanoInMilli,
94 | nanoInSec,
95 | nanoInMinute,
96 | nanoInHour,
97 | nanoInUtcDay,
98 | ]
99 |
100 | // Utils
101 | // -----------------------------------------------------------------------------
102 |
103 | /*
104 | When largestUnit=hour, returned `Day` value is "days worth of hours"
105 | */
106 | export function givenFieldsToBigNano(
107 | fields: Record,
108 | largestUnit: DayTimeUnit,
109 | fieldNames: K[],
110 | ): BigNano {
111 | let timeNano = 0
112 | let days = 0
113 |
114 | for (let unit = Unit.Nanosecond; unit <= largestUnit; unit++) {
115 | const fieldVal = fields[fieldNames[unit]]
116 | const unitNano = unitNanoMap[unit]
117 |
118 | // absorb whole-days from current unit, to prevent overflow
119 | const unitInDay = nanoInUtcDay / unitNano
120 | const [unitDays, leftoverUnits] = divModTrunc(fieldVal, unitInDay)
121 |
122 | timeNano += leftoverUnits * unitNano
123 | days += unitDays
124 | }
125 |
126 | // absorb whole-days from timeNano
127 | const [timeDays, leftoverNano] = divModTrunc(timeNano, nanoInUtcDay)
128 | return [days + timeDays, leftoverNano]
129 | }
130 |
131 | export function nanoToGivenFields(
132 | nano: number,
133 | largestUnit: DayTimeUnit, // stops populating at this unit
134 | fieldNames: (keyof F)[],
135 | ): { [Key in keyof F]?: number } {
136 | const fields = {} as { [Key in keyof F]: number }
137 |
138 | for (let unit = largestUnit; unit >= Unit.Nanosecond; unit--) {
139 | const divisor = unitNanoMap[unit]
140 |
141 | fields[fieldNames[unit]] = divTrunc(nano, divisor)
142 | nano = modTrunc(nano, divisor)
143 | }
144 |
145 | return fields
146 | }
147 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/typeOverrides/global.ts:
--------------------------------------------------------------------------------
1 | export * from 'temporal-spec/global'
2 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/typeOverrides/impl.ts:
--------------------------------------------------------------------------------
1 | export * from 'temporal-spec'
2 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/src/typeOverrides/index.ts:
--------------------------------------------------------------------------------
1 | export * from 'temporal-spec'
2 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "target": "ES2018",
5 | "lib": ["ES2017", "ES2021.Intl", "ES2020.BigInt"],
6 | "rootDir": "src",
7 | "outDir": "dist/.tsc",
8 | "tsBuildInfoFile": "dist/.tsbuildinfo",
9 | "noEmit": false,
10 | "sourceMap": true,
11 | "declaration": true,
12 | "declarationMap": true
13 | },
14 | "include": ["src/**/*"],
15 | "exclude": ["src/**/*.test.ts", "**/testUtils.ts"]
16 | }
17 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "include": ["src/**/*"],
4 | "exclude": ["src/typeOverrides/global.ts"]
5 | }
6 |
--------------------------------------------------------------------------------
/packages/temporal-polyfill/vitest.config.js:
--------------------------------------------------------------------------------
1 | import {
2 | dirname,
3 | isAbsolute,
4 | join as joinPaths,
5 | resolve as resolvePath,
6 | } from 'path'
7 | import { readFile } from 'fs/promises'
8 | import { defineConfig } from 'vitest/config'
9 | import { extensions } from './scripts/lib/config'
10 |
11 | export default defineConfig(async () => {
12 | const isDev = /:(dev|debug)$/.test(process.env.npm_lifecycle_event || '')
13 |
14 | return {
15 | test: {
16 | include: ['src/**/*.test.ts'],
17 | },
18 | plugins: [
19 | !isDev && entryPointResolve(JSON.parse(await readFile('./package.json'))),
20 | ],
21 | }
22 | })
23 |
24 | function entryPointResolve(manifest) {
25 | const remaps = {}
26 | const exportConfigMap = manifest.buildConfig.exports
27 |
28 | for (const exportPath in exportConfigMap) {
29 | const exportConfig = exportConfigMap[exportPath]
30 | const exportName =
31 | exportPath === '.' ? 'index' : exportPath.replace(/^\.\//, '')
32 |
33 | const esmExtension =
34 | (exportConfig.iife ? extensions.esmWhenIifePrefix : '') + extensions.esm
35 |
36 | const srcPath = resolvePath('src', (exportConfig.src || exportName) + '.ts')
37 | const distPath = resolvePath('dist', exportName + esmExtension)
38 |
39 | remaps[srcPath] = distPath
40 | }
41 |
42 | return {
43 | name: 'entry-point-resolve',
44 | enforce: 'pre',
45 | resolveId(importId, importerPath, { isEntry }) {
46 | const importPath = !isEntry && computeImportPath(importId, importerPath)
47 | if (importPath) {
48 | return remaps[importPath] || remaps[importPath + '.ts']
49 | }
50 | },
51 | }
52 | }
53 |
54 | function computeImportPath(importId, importerPath) {
55 | if (isAbsolute(importId)) {
56 | return importId
57 | }
58 | if (isImportRelative(importId)) {
59 | return importerPath
60 | ? joinPaths(dirname(importerPath), importId)
61 | : resolvePath(importId) // from CWD
62 | }
63 | // otherwise, probably an external dependency
64 | }
65 |
66 | function isImportRelative(importId) {
67 | return importId.startsWith('./') || importId.startsWith('../')
68 | }
69 |
--------------------------------------------------------------------------------
/packages/temporal-spec/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2017, 2018, 2019, 2020 ECMA International
2 |
3 | Permission to use, copy, modify, and/or distribute this software for any
4 | purpose with or without fee is hereby granted, provided that the above
5 | copyright notice and this permission notice appear in all copies.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
8 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
9 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
10 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
11 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
12 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
13 | PERFORMANCE OF THIS SOFTWARE.
14 |
--------------------------------------------------------------------------------
/packages/temporal-spec/global.cjs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fullcalendar/temporal-polyfill/bc1baf3875392ecd8522d40e7eecb55fa582808c/packages/temporal-spec/global.cjs
--------------------------------------------------------------------------------
/packages/temporal-spec/global.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fullcalendar/temporal-polyfill/bc1baf3875392ecd8522d40e7eecb55fa582808c/packages/temporal-spec/global.js
--------------------------------------------------------------------------------
/packages/temporal-spec/index.cjs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fullcalendar/temporal-polyfill/bc1baf3875392ecd8522d40e7eecb55fa582808c/packages/temporal-spec/index.cjs
--------------------------------------------------------------------------------
/packages/temporal-spec/index.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fullcalendar/temporal-polyfill/bc1baf3875392ecd8522d40e7eecb55fa582808c/packages/temporal-spec/index.js
--------------------------------------------------------------------------------
/packages/temporal-spec/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "temporal-spec",
3 | "version": "0.3.0",
4 | "title": "TypeScript types for Temporal",
5 | "author": {
6 | "name": "Adam Shaw",
7 | "email": "arshaw@users.noreply.github.com",
8 | "url": "http://arshaw.com/"
9 | },
10 | "license": "ISC",
11 | "repository": {
12 | "type": "git",
13 | "url": "https://github.com/fullcalendar/temporal-polyfill.git",
14 | "directory": "packages/temporal-spec"
15 | },
16 | "type": "module",
17 | "main": "./index.cjs",
18 | "types": "./index.d.ts",
19 | "module": "./index.js",
20 | "exports": {
21 | ".": {
22 | "require": "./index.cjs",
23 | "import": "./index.js"
24 | },
25 | "./global": {
26 | "require": "./global.cjs",
27 | "import": "./global.js"
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/packages/temporal-utils/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "temporal-utils",
4 | "version": "0.0.0",
5 | "title": "Temporal Utils",
6 | "description": "Temporal Utils",
7 | "author": {
8 | "name": "Adam Shaw",
9 | "email": "arshaw@users.noreply.github.com",
10 | "url": "http://arshaw.com/"
11 | },
12 | "license": "MIT",
13 | "copyright": "2024 Adam Shaw",
14 | "repository": {
15 | "type": "git",
16 | "url": "https://github.com/fullcalendar/temporal-polyfill.git"
17 | },
18 | "scripts": {
19 | "lint": "biome check .",
20 | "test": "vitest run",
21 | "test:dev": "vitest dev",
22 | "test:debug": "vitest run --no-isolate --no-file-parallelism --test-timeout 3600000"
23 | },
24 | "type": "module",
25 | "dependencies": {
26 | "temporal-spec": "^0.2.0"
27 | },
28 | "devDependencies": {
29 | "@biomejs/biome": "1.5.1",
30 | "temporal-polyfill": "workspace:*",
31 | "vitest": "^1.2.2"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/packages/temporal-utils/src/diff.ts:
--------------------------------------------------------------------------------
1 | import type { Temporal } from 'temporal-spec'
2 | import { DateObj, DateTimeObj, YearMonthObj } from './utils'
3 |
4 | export type DiffFunc = (
5 | date0: T,
6 | date1: T,
7 | options?: Temporal.RoundingMode | DiffOptions,
8 | ) => number
9 |
10 | function createDiffFunc(
11 | unit: Temporal.PluralUnit,
12 | ): DiffFunc {
13 | return (date0, date1, options) => {
14 | const normOptions = normalizeDiffOptions(options)
15 |
16 | if (normOptions.roundingMode) {
17 | return date0.until(date1, {
18 | ...normOptions,
19 | largestUnit: unit,
20 | smallestUnit: unit,
21 | })[unit]
22 | }
23 |
24 | return date0
25 | .until(date1, {
26 | ...normOptions,
27 | largestUnit: unit,
28 | })
29 | .total({
30 | unit,
31 | relativeTo: date0,
32 | })
33 | }
34 | }
35 |
36 | export const diffYears = createDiffFunc('years') as DiffFunc
37 | export const diffMonths = createDiffFunc('months') as DiffFunc
38 | export const diffWeeks = createDiffFunc('weeks') as DiffFunc
39 | export const diffDays = createDiffFunc('days') as DiffFunc
40 | export const diffHours = createDiffFunc('hours') as DiffFunc
41 | export const diffMinutes = createDiffFunc('minutes') as DiffFunc
42 | export const diffSeconds = createDiffFunc('seconds') as DiffFunc
43 | export const diffMilliseconds = createDiffFunc(
44 | 'milliseconds',
45 | ) as DiffFunc
46 | export const diffMicroseconds = createDiffFunc(
47 | 'microseconds',
48 | ) as DiffFunc
49 | export const diffNanoseconds = createDiffFunc(
50 | 'nanoseconds',
51 | ) as DiffFunc
52 |
53 | // Options
54 | // -----------------------------------------------------------------------------
55 |
56 | export type DiffOptions = {
57 | roundingMode?: Temporal.RoundingMode
58 | roundingIncrement?: number
59 | }
60 |
61 | export function normalizeDiffOptions(
62 | options: Temporal.RoundingMode | DiffOptions | undefined,
63 | ): DiffOptions {
64 | return typeof options === 'string' ? { roundingMode: options } : options || {}
65 | }
66 |
--------------------------------------------------------------------------------
/packages/temporal-utils/src/endOf.ts:
--------------------------------------------------------------------------------
1 | import {
2 | startOfHour,
3 | startOfMicrosecond,
4 | startOfMillisecond,
5 | startOfMinute,
6 | startOfMonth,
7 | startOfSecond,
8 | startOfWeek,
9 | startOfYear,
10 | } from './startOf'
11 | import { DateObj, DateTimeObj, YearMonthObj } from './utils'
12 |
13 | const nanoInMicro = 1000
14 | const nanoInMilli = 1000000
15 | const nanoInSec = 1000000000
16 | const nanoInMinute = 60000000000
17 | const nanoInHour = 3600000000000
18 |
19 | export function endOfYear(date: T): T {
20 | return startOfYear(date)
21 | .add({ years: 1 })
22 | .subtract(
23 | (date as DateTimeObj).nanosecond !== undefined
24 | ? { nanoseconds: 1 }
25 | : { days: 1 },
26 | ) as T
27 | }
28 |
29 | export function endOfMonth(date: T): T {
30 | return startOfMonth(date)
31 | .add({ months: 1 })
32 | .subtract(
33 | (date as DateTimeObj).nanosecond !== undefined
34 | ? { nanoseconds: 1 }
35 | : { days: 1 },
36 | ) as T
37 | }
38 |
39 | export function endOfWeek(date: T): T {
40 | return startOfWeek(date)
41 | .add({ weeks: 1 })
42 | .subtract(
43 | (date as DateTimeObj).nanosecond !== undefined
44 | ? { nanoseconds: 1 }
45 | : { days: 1 },
46 | ) as T
47 | }
48 |
49 | export function endOfDay(date: T): T {
50 | if (date.withPlainTime) {
51 | return date
52 | .withPlainTime()
53 | .add({ days: 1 })
54 | .subtract({ nanoseconds: 1 }) as T
55 | }
56 | return date // in case PlainDate passed in, not moved to next day
57 | }
58 |
59 | export function endOfHour(date: T): T {
60 | return startOfHour(date).add({ nanoseconds: nanoInHour - 1 }) as T
61 | }
62 |
63 | export function endOfMinute(date: T): T {
64 | return startOfMinute(date).add({ nanoseconds: nanoInMinute - 1 }) as T
65 | }
66 |
67 | export function endOfSecond(date: T): T {
68 | return startOfSecond(date).add({ nanoseconds: nanoInSec - 1 }) as T
69 | }
70 |
71 | export function endOfMillisecond(date: T): T {
72 | return startOfMillisecond(date).add({ nanoseconds: nanoInMilli - 1 }) as T
73 | }
74 |
75 | export function endOfMicrosecond(date: T): T {
76 | return startOfMicrosecond(date).add({ nanoseconds: nanoInMicro - 1 }) as T
77 | }
78 |
--------------------------------------------------------------------------------
/packages/temporal-utils/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './with'
2 | export * from './round'
3 | export * from './startOf'
4 | export * from './endOf'
5 | export * from './diff'
6 |
--------------------------------------------------------------------------------
/packages/temporal-utils/src/round.test.ts:
--------------------------------------------------------------------------------
1 | import { Temporal } from 'temporal-polyfill'
2 | import { describe, expect, it } from 'vitest'
3 | import { roundToMonth, roundToWeek, roundToYear } from '.'
4 |
5 | describe('roundToYear', () => {
6 | describe('ZonedDateTime', () => {
7 | it('works without options', () => {
8 | const zdt = Temporal.ZonedDateTime.from(
9 | '2024-07-27T12:30:00[America/New_York]',
10 | )
11 | expect(
12 | roundToYear(zdt).equals(
13 | Temporal.ZonedDateTime.from('2025-01-01T00:00:00[America/New_York]'),
14 | ),
15 | ).toBe(true)
16 | })
17 | })
18 |
19 | describe('PlainDateTime', () => {
20 | it('works with single roundingMode arg', () => {
21 | const pdt = Temporal.PlainDateTime.from('2024-07-27T12:30:00')
22 | expect(
23 | roundToYear(pdt, 'floor').equals(
24 | Temporal.PlainDateTime.from('2024-01-01T00:00:00'),
25 | ),
26 | ).toBe(true)
27 | })
28 | })
29 |
30 | describe('PlainDate', () => {
31 | it('works with options', () => {
32 | const pd = Temporal.PlainDate.from('2024-07-27')
33 | expect(
34 | roundToYear(pd, {
35 | roundingMode: 'floor',
36 | roundingIncrement: 1,
37 | }).equals(Temporal.PlainDate.from('2024-01-01')),
38 | ).toBe(true)
39 | expect(() => {
40 | roundToYear(pd, {
41 | roundingMode: 'floor',
42 | roundingIncrement: 2 as any, // not allowed
43 | })
44 | }).toThrowError(RangeError)
45 | })
46 | })
47 | })
48 |
49 | describe('roundToMonth', () => {
50 | describe('ZonedDateTime', () => {
51 | it('works without options', () => {
52 | const zdt = Temporal.ZonedDateTime.from(
53 | '2024-07-27T12:30:00[America/New_York]',
54 | )
55 | expect(
56 | roundToMonth(zdt).equals(
57 | Temporal.ZonedDateTime.from('2024-08-01T00:00:00[America/New_York]'),
58 | ),
59 | ).toBe(true)
60 | })
61 | })
62 |
63 | describe('PlainDateTime', () => {
64 | it('works with single roundingMode arg', () => {
65 | const pdt = Temporal.PlainDateTime.from('2024-07-27T12:30:00')
66 | expect(
67 | roundToMonth(pdt, 'floor').equals(
68 | Temporal.PlainDateTime.from('2024-07-01T00:00:00'),
69 | ),
70 | ).toBe(true)
71 | })
72 | })
73 |
74 | describe('PlainDate', () => {
75 | it('works with options', () => {
76 | const pd = Temporal.PlainDate.from('2024-07-27')
77 | expect(
78 | roundToMonth(pd, {
79 | roundingMode: 'floor',
80 | roundingIncrement: 1,
81 | }).equals(Temporal.PlainDate.from('2024-07-01')),
82 | ).toBe(true)
83 | expect(() => {
84 | roundToMonth(pd, {
85 | roundingMode: 'floor',
86 | roundingIncrement: 2 as any, // not allowed
87 | })
88 | }).toThrowError(RangeError)
89 | })
90 | })
91 | })
92 |
93 | describe('roundToWeek', () => {
94 | describe('ZonedDateTime', () => {
95 | it('works without options', () => {
96 | const zdt = Temporal.ZonedDateTime.from(
97 | '2024-07-20T12:30:00[America/New_York]', // Saturday
98 | )
99 | expect(
100 | roundToWeek(zdt).equals(
101 | Temporal.ZonedDateTime.from(
102 | '2024-07-22T00:00:00[America/New_York]', // next Monday
103 | ),
104 | ),
105 | ).toBe(true)
106 | })
107 | })
108 |
109 | describe('PlainDateTime', () => {
110 | it('works with single roundingMode arg', () => {
111 | const pdt = Temporal.PlainDateTime.from('2024-07-20T12:30:00') // Saturday
112 | expect(
113 | roundToWeek(pdt, 'floor').equals(
114 | Temporal.PlainDateTime.from('2024-07-15T00:00:00'), // this Monday
115 | ),
116 | ).toBe(true)
117 | })
118 | })
119 |
120 | describe('PlainDate', () => {
121 | it('works with options', () => {
122 | const pd = Temporal.PlainDate.from('2024-07-20') // Saturday
123 | expect(
124 | roundToWeek(pd, {
125 | roundingMode: 'floor',
126 | roundingIncrement: 1,
127 | }).equals(
128 | Temporal.PlainDate.from('2024-07-15'), // this Monday
129 | ),
130 | ).toBe(true)
131 | expect(() => {
132 | roundToWeek(pd, {
133 | roundingMode: 'floor',
134 | roundingIncrement: 2 as any, // not allowed
135 | })
136 | }).toThrowError(RangeError)
137 | })
138 | })
139 | })
140 |
--------------------------------------------------------------------------------
/packages/temporal-utils/src/round.ts:
--------------------------------------------------------------------------------
1 | import type { Temporal } from 'temporal-spec'
2 | import { startOfMonth, startOfWeek, startOfYear } from './startOf'
3 | import { DateObj, YearMonthObj } from './utils'
4 |
5 | export function roundToYear(
6 | date: T,
7 | options?: Temporal.RoundingMode | RoundingOptions,
8 | ) {
9 | const start = startOfYear(date)
10 | const duration = start.until(date, normalizeRoundingOptions('year', options))
11 | return start.add(duration)
12 | }
13 |
14 | export function roundToMonth(
15 | date: T,
16 | options?: Temporal.RoundingMode | RoundingOptions,
17 | ) {
18 | const start = startOfMonth(date)
19 | const duration = start.until(date, normalizeRoundingOptions('month', options))
20 | return start.add(duration)
21 | }
22 |
23 | export function roundToWeek(
24 | date: T,
25 | options?: Temporal.RoundingMode | RoundingOptions,
26 | ) {
27 | const start = startOfWeek(date)
28 | const duration = start.until(date, normalizeRoundingOptions('week', options))
29 | return start.add(duration)
30 | }
31 |
32 | // Options
33 | // -----------------------------------------------------------------------------
34 |
35 | // for big units only
36 | export type RoundingOptions = {
37 | roundingMode?: Temporal.RoundingMode
38 | roundingIncrement?: 1
39 | }
40 |
41 | export function normalizeRoundingOptions(
42 | forcedUnit: Temporal.DateTimeUnit,
43 | options: Temporal.RoundingMode | RoundingOptions | undefined,
44 | ): {
45 | roundingMode: Temporal.RoundingMode
46 | smallestUnit: any // HACK
47 | } {
48 | if (typeof options === 'string') {
49 | options = { roundingMode: options }
50 | } else {
51 | options = options || {}
52 | }
53 | if (options.roundingIncrement && options.roundingIncrement !== 1) {
54 | throw new RangeError('Non-1 roundingIncrement not allowed')
55 | }
56 | return {
57 | roundingMode: 'halfExpand',
58 | ...options,
59 | smallestUnit: forcedUnit,
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/packages/temporal-utils/src/startOf.ts:
--------------------------------------------------------------------------------
1 | import { DateObj, DateTimeObj, YearMonthObj } from './utils'
2 | import { withDayOfWeek } from './with'
3 |
4 | const zeroTimeFields = {
5 | hour: 0,
6 | minute: 0,
7 | second: 0,
8 | millisecond: 0,
9 | microsecond: 0,
10 | nanosecond: 0,
11 | }
12 |
13 | export function startOfYear(date: T): T {
14 | return date.with({
15 | month: 1,
16 | day: 1,
17 | ...zeroTimeFields,
18 | }) as T
19 | }
20 |
21 | export function startOfMonth(date: T): T {
22 | return date.with({
23 | day: 1,
24 | ...zeroTimeFields,
25 | }) as T
26 | }
27 |
28 | export function startOfWeek(date: T): T {
29 | const movedDate = withDayOfWeek(date, 1)
30 | return (movedDate as DateTimeObj).withPlainTime
31 | ? ((movedDate as DateTimeObj).withPlainTime() as T)
32 | : movedDate
33 | }
34 |
35 | export function startOfDay(dateTime: T): T {
36 | if (dateTime.withPlainTime) {
37 | return dateTime.withPlainTime() as T
38 | }
39 | return dateTime // in case PlainDate passed in, no error
40 | }
41 |
42 | export function startOfHour(dateTime: T): T {
43 | return dateTime.with({
44 | minute: 0,
45 | second: 0,
46 | millisecond: 0,
47 | microsecond: 0,
48 | nanosecond: 0,
49 | }) as T
50 | }
51 |
52 | export function startOfMinute(dateTime: T): T {
53 | return dateTime.with({
54 | second: 0,
55 | millisecond: 0,
56 | microsecond: 0,
57 | nanosecond: 0,
58 | }) as T
59 | }
60 |
61 | export function startOfSecond(dateTime: T): T {
62 | return dateTime.with({
63 | millisecond: 0,
64 | microsecond: 0,
65 | nanosecond: 0,
66 | }) as T
67 | }
68 |
69 | export function startOfMillisecond(dateTime: T): T {
70 | return dateTime.with({
71 | microsecond: 0,
72 | nanosecond: 0,
73 | }) as T
74 | }
75 |
76 | export function startOfMicrosecond(dateTime: T): T {
77 | return dateTime.with({
78 | nanosecond: 0,
79 | }) as T
80 | }
81 |
--------------------------------------------------------------------------------
/packages/temporal-utils/src/utils.ts:
--------------------------------------------------------------------------------
1 | import type { Temporal } from 'temporal-spec'
2 |
3 | export type DateTimeObj = Temporal.PlainDateTime | Temporal.ZonedDateTime
4 | export type DateObj = Temporal.PlainDate | DateTimeObj
5 | export type YearMonthObj = Temporal.PlainYearMonth | DateObj
6 |
7 | // Input Validation
8 | // -----------------------------------------------------------------------------
9 | // TODO: make DRY with temporal-polyfill somehow!?
10 |
11 | function toNumber(arg: number): number {
12 | if (typeof arg === 'bigint') {
13 | throw new TypeError('Cannot convert bigint to number')
14 | }
15 |
16 | arg = Number(arg)
17 |
18 | if (!Number.isFinite(arg)) {
19 | throw new RangeError('Cannot convert infinity to number')
20 | }
21 |
22 | return arg
23 | }
24 |
25 | export function toInteger(arg: number): number {
26 | return Math.trunc(toNumber(arg)) || 0 // ensure no -0
27 | }
28 |
29 | export function toPositiveInteger(arg: number): number {
30 | return requireNumberIsPositive(toInteger(arg))
31 | }
32 |
33 | /*
34 | Already known to be number
35 | */
36 | function requireNumberIsPositive(num: number): number {
37 | if (num <= 0) {
38 | throw new RangeError('Expected positive number')
39 | }
40 | return num
41 | }
42 |
43 | /*
44 | Already known to be number
45 | */
46 | export function requireNumberInRange(
47 | num: number,
48 | min: number,
49 | max: number, // inclusive
50 | ): number {
51 | if (num < min || num > max) {
52 | throw new RangeError(`Number must be between ${min}-${max}`)
53 | }
54 | return num
55 | }
56 |
--------------------------------------------------------------------------------
/packages/temporal-utils/src/with.ts:
--------------------------------------------------------------------------------
1 | import {
2 | DateObj,
3 | requireNumberInRange,
4 | toInteger,
5 | toPositiveInteger,
6 | } from './utils'
7 |
8 | export function withDayOfYear(
9 | date: T,
10 | dayOfYear: number,
11 | ): T {
12 | const normDayOfYear = requireNumberInRange(
13 | toInteger(dayOfYear),
14 | 1,
15 | date.daysInYear,
16 | )
17 | return date.add({
18 | days: normDayOfYear - date.dayOfYear,
19 | }) as T
20 | }
21 |
22 | export function withDayOfWeek(
23 | date: T,
24 | dayOfWeek: number,
25 | ): T {
26 | const normDayOfWeek = requireNumberInRange(
27 | toInteger(dayOfWeek),
28 | 1,
29 | date.daysInWeek,
30 | )
31 | return date.add({
32 | days: normDayOfWeek - date.dayOfWeek,
33 | }) as T
34 | }
35 |
36 | /*
37 | NOTE: does not check if beyond max number of weeks. allows overflow
38 | */
39 | export function withWeekOfYear(
40 | date: T,
41 | weekOfYear: number,
42 | ): T {
43 | const currentWeekOfYear = date.weekOfYear
44 | if (currentWeekOfYear === undefined) {
45 | throw new RangeError('Week numbers not supported')
46 | }
47 | return date.add({
48 | weeks: toPositiveInteger(weekOfYear) - currentWeekOfYear,
49 | }) as T
50 | }
51 |
--------------------------------------------------------------------------------
/packages/temporal-utils/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "include": ["src/**/*"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/temporal-utils/vitest.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vitest/config'
2 |
3 | export default defineConfig(async () => {
4 | return {
5 | test: {
6 | include: ['src/**/*.test.ts'],
7 | },
8 | }
9 | })
10 |
--------------------------------------------------------------------------------
/patches/rollup-plugin-sourcemaps@0.6.3.patch:
--------------------------------------------------------------------------------
1 | diff --git a/CHANGELOG.md b/CHANGELOG.md
2 | deleted file mode 100644
3 | index ee8381d5054223d9655db6173864595fbed80d32..0000000000000000000000000000000000000000
4 | diff --git a/dist/index.cjs b/dist/index.cjs
5 | index 3690559b27ccffeede30f45998af8e8c3a3fe5c9..6d5c47fcdb850b05604b11638969f44a6bc0d086 100644
6 | --- a/dist/index.cjs
7 | +++ b/dist/index.cjs
8 | @@ -32,6 +32,7 @@ function sourcemaps({ include, exclude, readFile = fs__default['default'].readFi
9 | this.warn('Failed reading file');
10 | return null;
11 | }
12 | + this.addWatchFile(id);
13 | let map;
14 | try {
15 | const result = await promisifiedResolveSourceMap(code, id, readFile);
16 | diff --git a/dist/index.js b/dist/index.js
17 | index 407a3b74d4471fae1c01f5eba7758bfd9e3c7a4c..7d35c6028441d9f7f60aa08a137b2c7635da41a6 100644
18 | --- a/dist/index.js
19 | +++ b/dist/index.js
20 | @@ -24,6 +24,7 @@ function sourcemaps({ include, exclude, readFile = fs.readFile, } = {}) {
21 | this.warn('Failed reading file');
22 | return null;
23 | }
24 | + this.addWatchFile(id);
25 | let map;
26 | try {
27 | const result = await promisifiedResolveSourceMap(code, id, readFile);
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - ./packages/temporal-polyfill
3 | - ./packages/temporal-spec
4 | - ./packages/temporal-utils
5 | - ./packages/export-size
6 | - ./examples/*
7 |
--------------------------------------------------------------------------------
/tsconfig.base.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "module": "ESNext",
5 | "moduleResolution": "Bundler",
6 | "strict": true,
7 | "resolveJsonModule": true,
8 | "composite": true,
9 | "noEmit": true
10 | }
11 | }
12 |
--------------------------------------------------------------------------------