├── .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 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
ChromeFirefoxOperaEdgeSafari
110
(Feb 2023)
108
(Dec 2022)
96
(Feb 2023)
110
(Feb 2023)
16.5
(May 2023)
30 | 31 | ### Partial Support 32 | 33 | Supports unis `year`/`month`/`week`/`day`/`hour`/`minute`/`second`/`millisecond`: 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 |
ChromeFirefoxOperaEdgeSafari
77
(Sep 2019)
78
(Jun 2020)
64
(Oct 2019)
79
(Jan 2020)
14.1
(Apr 2021)
55 | 56 | ### If Leveraging `Intl.RelativeTimeFormat` Instead 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 |
ChromeFirefoxOperaEdgeSafari
71
(Dec 2018)
65
(Jan 2019)
58
(Jan 2019)
79
(Jan 2020)
14
(Sep 2020)
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 | --------------------------------------------------------------------------------