diff --git a/.editorconfig b/.editorconfig index 14c1d8c19..d4d73cf57 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,3 +4,4 @@ root = true charset = utf-8 end_of_line = lf insert_final_newline = true +indent_size = 2 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..34cbdac9b --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,50 @@ +name: Release +on: + workflow_run: + workflows: ['Lint & Unit Test'] + branches: [master] + types: + - completed + +permissions: + contents: read # for checkout + +jobs: + release: + env: + GITHUB_TRIGGER_REF: ${{ github.event.workflow_run.head_branch }} # For workflow_run the GITHUB_REF is the default branch, as mentioned in the docs + if: ${{ github.event.workflow_run.conclusion == 'success' }} + name: Release + runs-on: ubuntu-latest + permissions: + contents: write # to be able to publish a GitHub release + issues: write # to be able to comment on released issues + pull-requests: write # to be able to comment on released pull requests + id-token: write # to enable use of OIDC for npm provenance + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ env.GITHUB_TRIGGER_REF }} + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 'lts/*' # semantic-release requires Node >= 18 + - name: Install dependencies + run: npm clean-install + - name: Build + env: # temporary workaround for "Error: error:0308010C:digital envelope routines::unsupported" in Node lts / 18 + NODE_OPTIONS: --openssl-legacy-provider + run: npm run build && npm run babel + - name: Verify the integrity of provenance attestations and registry signatures for installed dependencies + run: npm audit signatures + - name: Release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + run: | + npm install -g @semantic-release/changelog @semantic-release/git semantic-release + GITHUB_REF=${{ env.GITHUB_TRIGGER_REF }} semantic-release + echo release success + curl -s ${{ secrets.GITEE_SYNC_URL }} diff --git a/.npmignore b/.npmignore index f342d6304..4577b0a50 100644 --- a/.npmignore +++ b/.npmignore @@ -29,3 +29,4 @@ docs .travis.yml karma.sauce.conf.js .github +.releaserc diff --git a/.releaserc b/.releaserc new file mode 100644 index 000000000..8c9fced18 --- /dev/null +++ b/.releaserc @@ -0,0 +1,16 @@ +{ + "branches": ["master"], + "prepare": [ + { + "path": "@semantic-release/changelog" + }, + [ + "@semantic-release/git", + { + "assets": [ + "CHANGELOG.md" + ] + } + ] + ] +} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b8aba28b2..000000000 --- a/.travis.yml +++ /dev/null @@ -1,24 +0,0 @@ -language: node_js -cache: - directories: - - ~/.npm - - node_modules -node_js: - - '18' - - '16' - - '14' - - '12' -install: - - npm install -g codecov - - npm install -script: - - npm run lint - - npm test - - codecov -after_success: - - if [ "$TRAVIS_BRANCH" == "master" ] && [ "${TRAVIS_PULL_REQUEST}" == "false" ]; then - npx travis-deploy-once --pro && npm run build && npm run babel && npm install -g @semantic-release/changelog @semantic-release/git semantic-release && semantic-release && echo release success && curl ${TriggerUrl} && npm run test:sauce; - fi -branches: - only: - - master diff --git a/CHANGELOG.md b/CHANGELOG.md index b0857bc7b..144780498 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,81 @@ +## [1.11.10](https://github.com/iamkun/dayjs/compare/v1.11.9...v1.11.10) (2023-09-19) + + +### Bug Fixes + +* Add Korean Day of Month with ordinal ([#2395](https://github.com/iamkun/dayjs/issues/2395)) ([dd55ee2](https://github.com/iamkun/dayjs/commit/dd55ee2aadd1009242235e47d558bbf028827896)) +* change back fa locale to the Gregorian calendar equivalent ([#2411](https://github.com/iamkun/dayjs/issues/2411)) ([95e9458](https://github.com/iamkun/dayjs/commit/95e9458b221fe35e59ee4a160a5db247313a68fb)) +* duration plugin - MILLISECONDS_A_MONTH const calculation ([#2362](https://github.com/iamkun/dayjs/issues/2362)) ([f0a0b54](https://github.com/iamkun/dayjs/commit/f0a0b546b074b3b511c2319a1ce83d412894b91f)) +* duration plugin getter get result 0 instead of undefined ([#2369](https://github.com/iamkun/dayjs/issues/2369)) ([061aa7e](https://github.com/iamkun/dayjs/commit/061aa7ed6c31696974665fc9b11a74d30841ebed)) +* fix isDayjs check logic ([#2383](https://github.com/iamkun/dayjs/issues/2383)) ([5f3f878](https://github.com/iamkun/dayjs/commit/5f3f8786c796cd432fe6bcb6966a810daea89203)) +* fix timezone plugin to get correct locale setting ([#2420](https://github.com/iamkun/dayjs/issues/2420)) ([4f45012](https://github.com/iamkun/dayjs/commit/4f4501256fa1bc72128aae1d841bbd782df86aed)) +* **locale:** add meridiem in `ar` locale ([#2418](https://github.com/iamkun/dayjs/issues/2418)) ([361be5c](https://github.com/iamkun/dayjs/commit/361be5c7c628614ee833d710acbe154a598b904d)) +* round durations to millisecond precision for ISO string ([#2367](https://github.com/iamkun/dayjs/issues/2367)) ([890a17a](https://github.com/iamkun/dayjs/commit/890a17a8d8ddd43c7c8b806e3afc7b27f3288d27)) +* sub-second precisions need to be rounded at the seconds field to avoid adding floats ([#2377](https://github.com/iamkun/dayjs/issues/2377)) ([a9d7d03](https://github.com/iamkun/dayjs/commit/a9d7d0398d22ebd4bfc3812ca0134a97606d54d9)) +* update $x logic to avoid plugin error ([#2429](https://github.com/iamkun/dayjs/issues/2429)) ([2254635](https://github.com/iamkun/dayjs/commit/22546357f30924fcff3b3ffa14fd04be21f97a5e)) +* Update Slovenian locale for relative time ([#2396](https://github.com/iamkun/dayjs/issues/2396)) ([5470a15](https://github.com/iamkun/dayjs/commit/5470a15e437fac803797363063b24f3ba3bd5299)) +* update uzbek language translation ([#2327](https://github.com/iamkun/dayjs/issues/2327)) ([0a91056](https://github.com/iamkun/dayjs/commit/0a910564d76dc7c128da8e0d85d8e11ebdb5660b)) + +## [1.11.9](https://github.com/iamkun/dayjs/compare/v1.11.8...v1.11.9) (2023-07-01) + + +### Bug Fixes + +* Add null to min and max plugin return type ([#2355](https://github.com/iamkun/dayjs/issues/2355)) ([62d9042](https://github.com/iamkun/dayjs/commit/62d9042eb84b78d78324694ccbeaad1679d37e68)) +* check if null passed to objectSupport parser ([#2175](https://github.com/iamkun/dayjs/issues/2175)) ([013968f](https://github.com/iamkun/dayjs/commit/013968f609c32e2269df69b4dd1feb2e8e1e035a)) +* dayjs.diff improve performance ([#2244](https://github.com/iamkun/dayjs/issues/2244)) ([33c80e1](https://github.com/iamkun/dayjs/commit/33c80e14cf14f70ceb4f54639e266cd70a3c3996)) +* dayjs(null) throws error, not return dayjs object as invalid date ([#2334](https://github.com/iamkun/dayjs/issues/2334)) ([c79e2f5](https://github.com/iamkun/dayjs/commit/c79e2f5d03eef5660b1f13385b69c0c9668d2f98)) +* objectSupport plugin causes an error when null is passed to dayjs function (closes [#2277](https://github.com/iamkun/dayjs/issues/2277)) ([#2342](https://github.com/iamkun/dayjs/issues/2342)) ([89bf31c](https://github.com/iamkun/dayjs/commit/89bf31ce0a36dcfc892029dc019d85d3654cf5fb)) +* Optimize format method ([#2313](https://github.com/iamkun/dayjs/issues/2313)) ([1fe1b1d](https://github.com/iamkun/dayjs/commit/1fe1b1d9a214d3b8c9f267b432801424a493f1c4)) +* update Duration plugin add/subtract take into account days in month ([#2337](https://github.com/iamkun/dayjs/issues/2337)) ([3b1060f](https://github.com/iamkun/dayjs/commit/3b1060f92183ab3a3c49289c2d87fbdd34c1eacc)) +* update MinMax plugin 1. ignore the 'null' in args 2. return the only one arg ([#2330](https://github.com/iamkun/dayjs/issues/2330)) ([3c2c6ee](https://github.com/iamkun/dayjs/commit/3c2c6ee4db00bbb43a7a3bb0b56bc0d0f03daddc)) + +## [1.11.8](https://github.com/iamkun/dayjs/compare/v1.11.7...v1.11.8) (2023-06-02) + + +### Bug Fixes + +* .format add padding to 'YYYY' ([#2231](https://github.com/iamkun/dayjs/issues/2231)) ([00c223b](https://github.com/iamkun/dayjs/commit/00c223b7e92970d07557133994fcb225a6d4c960)) +* Added .valueOf method to Duration class ([#2226](https://github.com/iamkun/dayjs/issues/2226)) ([9b4fcfd](https://github.com/iamkun/dayjs/commit/9b4fcfde35b39693894be1821b6c7222fac98657)) +* timezone type mark `date` parameter as optional ([#2222](https://github.com/iamkun/dayjs/issues/2222)) ([b87aa0e](https://github.com/iamkun/dayjs/commit/b87aa0ed9a748c478a66ef48230cd1d6350d7b8a)) +* type file first parameter date is optional in isSame(), isBefore(), isAfter() ([#2272](https://github.com/iamkun/dayjs/issues/2272)) ([4d56f3e](https://github.com/iamkun/dayjs/commit/4d56f3eb2b3770879d60f824590bf1b32f237d47)) + +## [1.11.7](https://github.com/iamkun/dayjs/compare/v1.11.6...v1.11.7) (2022-12-06) + + +### Bug Fixes + +* Add locale (zh-tw) meridiem ([#2149](https://github.com/iamkun/dayjs/issues/2149)) ([1e9ba76](https://github.com/iamkun/dayjs/commit/1e9ba761ff4e3f2759106dfe1aa9054d5826451c)) +* update fa locale ([#2151](https://github.com/iamkun/dayjs/issues/2151)) ([1c26732](https://github.com/iamkun/dayjs/commit/1c267321a1a01b4947e1482bac67d67ebc7c3dfa)) + +## [1.11.6](https://github.com/iamkun/dayjs/compare/v1.11.5...v1.11.6) (2022-10-21) + + +### Bug Fixes + +* add BigIntSupport plugin ([#2087](https://github.com/iamkun/dayjs/issues/2087)) ([f6dce48](https://github.com/iamkun/dayjs/commit/f6dce48a9e39677718b087867d9fd901d5078155)) +* Fix objectSupport collides with Duration plugin - issue [#2027](https://github.com/iamkun/dayjs/issues/2027) ([#2038](https://github.com/iamkun/dayjs/issues/2038)) ([c9370ea](https://github.com/iamkun/dayjs/commit/c9370ea96bf420439ee7eaa4146e8ed643160312)) + +## [1.11.5](https://github.com/iamkun/dayjs/compare/v1.11.4...v1.11.5) (2022-08-12) + + +### Bug Fixes + +* ordinal for nl not working ([#2011](https://github.com/iamkun/dayjs/issues/2011)) ([c93c85e](https://github.com/iamkun/dayjs/commit/c93c85eaa11564a1aae2d823480a417812c01bf4)) +* wrong ordinal for french locale ([#2010](https://github.com/iamkun/dayjs/issues/2010)) ([dd192a7](https://github.com/iamkun/dayjs/commit/dd192a72fc5d26ce56481e89b0c1ccf5f939be0c)) + +## [1.11.4](https://github.com/iamkun/dayjs/compare/v1.11.3...v1.11.4) (2022-07-19) + + +### Bug Fixes + +* correct past property in ku (kurdish) locale ([#1916](https://github.com/iamkun/dayjs/issues/1916)) ([74e82b9](https://github.com/iamkun/dayjs/commit/74e82b9da5ec8b90361fc27ac7c8b63faf354502)) +* fix French [fr] local ordinal ([#1932](https://github.com/iamkun/dayjs/issues/1932)) ([8f09834](https://github.com/iamkun/dayjs/commit/8f09834a88b8e7f8353c6e7473d4711596890a8c)) +* fix objectSupport plugin ConfigTypeMap type ([#1441](https://github.com/iamkun/dayjs/issues/1441)) ([#1990](https://github.com/iamkun/dayjs/issues/1990)) ([fd51fe4](https://github.com/iamkun/dayjs/commit/fd51fe4f7fa799d8c598343e71fa59299ec4cf93)) +* fix type error to add ordianl property in InstanceLocaleDataReturn and GlobalLocaleDataReturn types ([#1931](https://github.com/iamkun/dayjs/issues/1931)) ([526f0ae](https://github.com/iamkun/dayjs/commit/526f0ae549ffbeeb9ef1099ca23964791fc59743)) +* update locale ar-* meridiem function ([#1954](https://github.com/iamkun/dayjs/issues/1954)) ([3d31611](https://github.com/iamkun/dayjs/commit/3d316117f04362d31f4e8bd349620b8414ce5d0c)) +* zh-tw / zh-hk locale ordinal error ([#1976](https://github.com/iamkun/dayjs/issues/1976)) ([0a1bd08](https://github.com/iamkun/dayjs/commit/0a1bd08e736be7d4e378aaca280caa6543f8066d)) + ## [1.11.3](https://github.com/iamkun/dayjs/compare/v1.11.2...v1.11.3) (2022-06-06) diff --git a/README.md b/README.md index 728dadfbe..8423d7765 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -English | [简体中文](./docs/zh-cn/README.zh-CN.md) | [日本語](./docs/ja/README-ja.md) | [Português Brasileiro](./docs/pt-br/README-pt-br.md) | [한국어](./docs/ko/README-ko.md) | [Español (España)](./docs/es-es/README-es-es.md) | [Русский](./docs/ru/README-ru.md) | [Türkçe](./docs/tr/README-tr.md) +English | [简体中文](./docs/zh-cn/README.zh-CN.md) | [日本語](./docs/ja/README-ja.md) | [Português Brasileiro](./docs/pt-br/README-pt-br.md) | [한국어](./docs/ko/README-ko.md) | [Español (España)](./docs/es-es/README-es-es.md) | [Русский](./docs/ru/README-ru.md) | [Türkçe](./docs/tr/README-tr.md) | [සිංහල](./docs/si/README-si.md) | [עברית](./docs/he/README-he.md)
+ alt="Day.js" />Fast 2kB alternative to Moment.js with the same modern API
-
+
+
+
+
## Sponsors
-Support this project by becoming a sponsor. Your logo will show up here with a link to your website.
+Support this project by becoming a sponsor. Your logo will show up here with a link to your website.
[[Become a sponsor via Github](https://github.com/sponsors/iamkun/)] [[Become a sponsor via OpenCollective](https://opencollective.com/dayjs#sponsor)]
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
@@ -124,10 +143,11 @@ Please give us a 💖 star 💖 to support us. Thank you.
And thank you to all our backers! 🙏
-
-
-
-
+
+
+
+
+
## License
diff --git a/docs/es-es/README-es-es.md b/docs/es-es/README-es-es.md
index ae190e846..d9b54c794 100644
--- a/docs/es-es/README-es-es.md
+++ b/docs/es-es/README-es-es.md
@@ -1,4 +1,4 @@
-Español (España) | [English](../../README.md) | [简体中文](../zh-cn/README.zh-CN.md) | [日本語](../ja/README-ja.md) | [Português Brasileiro](../pt-br/README-pt-br.md) | [한국어](../ko/README-ko.md) | [Русский](../ru/README-ru.md)
+Español (España) | [English](../../README.md) | [简体中文](../zh-cn/README.zh-CN.md) | [日本語](../ja/README-ja.md) | [Português Brasileiro](../pt-br/README-pt-br.md) | [한국어](../ko/README-ko.md) | [Русский](../ru/README-ru.md) | [עברית](./docs/he/README-he.md)
- + + + + ## Patrocinadores Apoya a este proyecto convirtiéndote en un patrocinador. Tu logo aparecerá aquí, enlazado a tu sitio web. [[Conviértete en un patrocinador](https://opencollective.com/dayjs#sponsor)] diff --git a/docs/he/README-he.md b/docs/he/README-he.md new file mode 100644 index 000000000..f410269ef --- /dev/null +++ b/docs/he/README-he.md @@ -0,0 +1,176 @@ +
אלטרנטיבה מהירה ל-Moment.js ששוקלת רק 2kB עם אותן יכולות מודרניות
+
+
+
+
+
+
+
+
+
+
+
- + + + + ## ライセンス Day.js は [MIT License](../../LICENSE) のもとで利用を許諾します。 diff --git a/docs/ko/README-ko.md b/docs/ko/README-ko.md index 7d6228d28..3b1b985b5 100644 --- a/docs/ko/README-ko.md +++ b/docs/ko/README-ko.md @@ -1,4 +1,4 @@ -한국어 | [English](../../README.md) | [简体中文](../zh-cn/README.zh-CN.md) | [日本語](../ja/README-ja.md) | [Português Brasileiro](../pt-br/README-pt-br.md) | [Español (España)](../es-es/README-es-es.md) | [Русский](../ru/README-ru.md) +한국어 | [English](../../README.md) | [简体中文](../zh-cn/README.zh-CN.md) | [日本語](../ja/README-ja.md) | [Português Brasileiro](../pt-br/README-pt-br.md) | [Español (España)](../es-es/README-es-es.md) | [Русский](../ru/README-ru.md)| [עברית](./docs/he/README-he.md)
-> Day.js는 Moment.js와 호환되는 대부분의 API를 사용하며, 최신 브라우저에서 날짜와 시간에 대한 구문 분석, 유효성 검사, 조작, 출력하는 경량 JavaScript 라이브러리입니다. Moment.js를 사용하고 있다면, Day.js는 껌입니다. +> Day.js는 대부분의 API가 Moment.js와 호환되며 최신 브라우저에서 날짜와 시간에 대한 구문 분석, 유효성 검사, 조작, 출력을 간편하게 처리하는 경량 JavaScript 라이브러리 입니다. Moment.js를 사용해본 경험이 있다면, Day.js도 쉽게 사용하실 수 있습니다. ```js dayjs() @@ -46,7 +46,7 @@ dayjs() ### 문서 -더 많은 세부 사항과 API, 그리고 다른 문서를 [day.js.org](https://day.js.org/) 웹사이트에서 볼 수 있습니다. +더 많은 세부 사항과 API 및 다른 문서는 [day.js.org](https://day.js.org/) 웹사이트에서 확인하실 수 있습니다. ### 설치 @@ -58,7 +58,7 @@ npm install dayjs --save ### API -Day.js API를 사용해서 날짜와 시간에 대한 구문 분석, 유효성 검사, 조작, 출력을 쉽게 할 수 있습니다. +Day.js API를 사용하여 날짜와 시간의 구문 분석, 검증, 조작, 출력을 간편하게 처리할 수 있습니다. ```javascript dayjs('2018-08-08') // parse @@ -78,9 +78,9 @@ dayjs().isBefore(dayjs()) // query ### I18n -Day.js는 국제화에 대해 많은 지원을 합니다. +Day.js는 국제화에 대한 많은 지원을 제공하고 있습니다. -그러나 그것을 사용하지 않는다면, 그 누구도 당신의 빌드에 포함되지 않습니다. +하지만 이 기능을 사용하지 않는 경우, 빌드에 포함되지 않습니다. ```javascript import 'dayjs/locale/es' // load on demand @@ -96,7 +96,7 @@ dayjs('2018-05-05') ### Plugin -플러그인은 기능을 확장하거나 새로운 기능을 추가하기 위해 Day.js에 추가할 수 있는 독립적인 모듈입니다. +플러그인은 Day.js의 기능을 확장하거나 새로운 기능을 도입하기 위한 독립적인 모듈입니다. ```javascript import advancedFormat from 'dayjs/plugin/advancedFormat' // load on demand @@ -108,6 +108,12 @@ dayjs().format('Q Do k kk X x') // more available formats 📚[플러그인 목록](https://day.js.org/docs/en/plugin/plugin) +### 사용 트렌드 + + + + + ## License Day.js는 [MIT License](./LICENSE)를 사용합니다. diff --git a/docs/pt-br/README-pt-br.md b/docs/pt-br/README-pt-br.md index 4923fa58d..31d0bdbc4 100644 --- a/docs/pt-br/README-pt-br.md +++ b/docs/pt-br/README-pt-br.md @@ -1,4 +1,4 @@ -Português Brasileiro | [English](../../README.md) | [简体中文](../zh-cn/README.zh-CN.md) | [日本語](../ja/README-ja.md) | [한국어](../ko/README-ko.md) | [Español (España)](../es-es/README-es-es.md) | [Русский](../ru/README-ru.md) +Português Brasileiro | [English](../../README.md) | [简体中文](../zh-cn/README.zh-CN.md) | [日本語](../ja/README-ja.md) | [한국어](../ko/README-ko.md) | [Español (España)](../es-es/README-es-es.md) | [Русский](../ru/README-ru.md)| [עברית](./docs/he/README-he.md)- + + + + ## Patrocinadores Ajude este projeto se tornando um patrocinador. O seu logo será exibido aqui, com um link para o seu site. [[Tornar-se um Patrocinador](https://opencollective.com/dayjs#sponsor)]. diff --git a/docs/ru/README-ru.md b/docs/ru/README-ru.md index c8f9b83ca..065af14ba 100644 --- a/docs/ru/README-ru.md +++ b/docs/ru/README-ru.md @@ -1,4 +1,4 @@ -Русский | [English](../../README.md) | [简体中文](../zh-cn/README.zh-CN.md) | [日本語](../ja/README-ja.md) | [Português Brasileiro](../pt-br/README-pt-br.md) | [한국어](../ko/README-ko.md) | [Español (España)](../es-es/README-es-es.md) +Русский | [English](../../README.md) | [简体中文](../zh-cn/README.zh-CN.md) | [日本語](../ja/README-ja.md) | [Português Brasileiro](../pt-br/README-pt-br.md) | [한국어](../ko/README-ko.md) | [Español (España)](../es-es/README-es-es.md)| [עברית](./docs/he/README-he.md)
- + + + + ## Спонсоры Поддержите этот проект, став спонсором. Ваш логотип будет показан здесь с ссылкой на ваш веб-сайт. [[Стать спонсором](https://opencollective.com/dayjs#sponsor)] diff --git a/docs/si/README-si.md b/docs/si/README-si.md new file mode 100644 index 000000000..e5f7fb92b --- /dev/null +++ b/docs/si/README-si.md @@ -0,0 +1,141 @@ +සිංහල | [English](../../README.md) | [简体中文](./docs/zh-cn/README.zh-CN.md) | [日本語](./docs/ja/README-ja.md) | [Português Brasileiro](./docs/pt-br/README-pt-br.md) | [한국어](./docs/ko/README-ko.md) | [Español (España)](./docs/es-es/README-es-es.md) | [Русский](./docs/ru/README-ru.md) | [Türkçe](./docs/tr/README-tr.md)| [עברית](./docs/he/README-he.md) + +
+නවීන යෙ.ක්ර.මු. සමඟින් Moment.js සඳහා කි.බ. 2 ක වේගවත් විකල්පයකි
+
+
+
+
+
+
+
+
+
+
+
- + + + + ## Sponsorlar Sponsor olarak bu projeye destek olun. Logonuz, web sayfanızın linki ile birlikte burada görünür. [[Sponsor Ol](https://opencollective.com/dayjs#sponsor)] @@ -133,4 +139,4 @@ Ve tüm destekçilerimize teşekkür ederiz! 🙏 ## Lisans -Day.js, bir [MIT Lisansı](../../LICENSE) altında lisanslanmıştır. \ No newline at end of file +Day.js, bir [MIT Lisansı](../../LICENSE) altında lisanslanmıştır. diff --git a/docs/zh-cn/README.zh-CN.md b/docs/zh-cn/README.zh-CN.md index b543a80f1..d7bb1ecbc 100644 --- a/docs/zh-cn/README.zh-CN.md +++ b/docs/zh-cn/README.zh-CN.md @@ -11,8 +11,8 @@ alt="Gzip Size"> - + + + + ## 开源协议 Day.js 遵循 [MIT 开源协议](../../LICENSE). diff --git a/package.json b/package.json index 44b293c48..c76e01780 100644 --- a/package.json +++ b/package.json @@ -36,21 +36,6 @@ "src/**/*" ] }, - "release": { - "prepare": [ - { - "path": "@semantic-release/changelog" - }, - [ - "@semantic-release/git", - { - "assets": [ - "CHANGELOG.md" - ] - } - ] - ] - }, "keywords": [ "dayjs", "date", diff --git a/src/index.js b/src/index.js index 19c409078..061ade178 100644 --- a/src/index.js +++ b/src/index.js @@ -6,7 +6,10 @@ let L = 'en' // global locale const Ls = {} // global loaded locale Ls[L] = en -const isDayjs = d => d instanceof Dayjs // eslint-disable-line no-use-before-define +const IS_DAYJS = '$isDayjsObject' + +// eslint-disable-next-line no-use-before-define +const isDayjs = d => d instanceof Dayjs || !!(d && d[IS_DAYJS]) const parseLocale = (preset, object, isLocal) => { let l @@ -72,7 +75,7 @@ const parseDate = (cfg) => { || 1, d[4] || 0, d[5] || 0, d[6] || 0, ms)) } return new Date(d[1], m, d[3] - || 1, d[4] || 0, d[5] || 0, d[6] || 0, ms) + || 1, d[4] || 0, d[5] || 0, d[6] || 0, ms) } } @@ -83,11 +86,12 @@ class Dayjs { constructor(cfg) { this.$L = parseLocale(cfg.locale, null, true) this.parse(cfg) // for plugin + this.$x = this.$x || cfg.x || {} + this[IS_DAYJS] = true } parse(cfg) { this.$d = parseDate(cfg) - this.$x = cfg.x || {} this.init() } @@ -278,34 +282,63 @@ class Dayjs { return isLowercase ? m.toLowerCase() : m }) - const matches = { - YY: String(this.$y).slice(-2), - YYYY: this.$y, - M: $M + 1, - MM: Utils.s($M + 1, 2, '0'), - MMM: getShort(locale.monthsShort, $M, months, 3), - MMMM: getShort(months, $M), - D: this.$D, - DD: Utils.s(this.$D, 2, '0'), - d: String(this.$W), - dd: getShort(locale.weekdaysMin, this.$W, weekdays, 2), - ddd: getShort(locale.weekdaysShort, this.$W, weekdays, 3), - dddd: weekdays[this.$W], - H: String($H), - HH: Utils.s($H, 2, '0'), - h: get$H(1), - hh: get$H(2), - a: meridiemFunc($H, $m, true), - A: meridiemFunc($H, $m, false), - m: String($m), - mm: Utils.s($m, 2, '0'), - s: String(this.$s), - ss: Utils.s(this.$s, 2, '0'), - SSS: Utils.s(this.$ms, 3, '0'), - Z: zoneStr // 'ZZ' logic below + const matches = (match) => { + switch (match) { + case 'YY': + return String(this.$y).slice(-2) + case 'YYYY': + return Utils.s(this.$y, 4, '0') + case 'M': + return $M + 1 + case 'MM': + return Utils.s($M + 1, 2, '0') + case 'MMM': + return getShort(locale.monthsShort, $M, months, 3) + case 'MMMM': + return getShort(months, $M) + case 'D': + return this.$D + case 'DD': + return Utils.s(this.$D, 2, '0') + case 'd': + return String(this.$W) + case 'dd': + return getShort(locale.weekdaysMin, this.$W, weekdays, 2) + case 'ddd': + return getShort(locale.weekdaysShort, this.$W, weekdays, 3) + case 'dddd': + return weekdays[this.$W] + case 'H': + return String($H) + case 'HH': + return Utils.s($H, 2, '0') + case 'h': + return get$H(1) + case 'hh': + return get$H(2) + case 'a': + return meridiemFunc($H, $m, true) + case 'A': + return meridiemFunc($H, $m, false) + case 'm': + return String($m) + case 'mm': + return Utils.s($m, 2, '0') + case 's': + return String(this.$s) + case 'ss': + return Utils.s(this.$s, 2, '0') + case 'SSS': + return Utils.s(this.$ms, 3, '0') + case 'Z': + return zoneStr // 'ZZ' logic below + default: + break + } + return null } - return str.replace(C.REGEX_FORMAT, (match, $1) => $1 || matches[match] || zoneStr.replace(':', '')) // 'ZZ' + return str.replace(C.REGEX_FORMAT, (match, $1) => $1 || matches(match) || zoneStr.replace(':', '')) // 'ZZ' } utcOffset() { @@ -319,18 +352,38 @@ class Dayjs { const that = dayjs(input) const zoneDelta = (that.utcOffset() - this.utcOffset()) * C.MILLISECONDS_A_MINUTE const diff = this - that - let result = Utils.m(this, that) - - result = { - [C.Y]: result / 12, - [C.M]: result, - [C.Q]: result / 3, - [C.W]: (diff - zoneDelta) / C.MILLISECONDS_A_WEEK, - [C.D]: (diff - zoneDelta) / C.MILLISECONDS_A_DAY, - [C.H]: diff / C.MILLISECONDS_A_HOUR, - [C.MIN]: diff / C.MILLISECONDS_A_MINUTE, - [C.S]: diff / C.MILLISECONDS_A_SECOND - }[unit] || diff // milliseconds + const getMonth = () => Utils.m(this, that) + + let result + switch (unit) { + case C.Y: + result = getMonth() / 12 + break + case C.M: + result = getMonth() + break + case C.Q: + result = getMonth() / 3 + break + case C.W: + result = (diff - zoneDelta) / C.MILLISECONDS_A_WEEK + break + case C.D: + result = (diff - zoneDelta) / C.MILLISECONDS_A_DAY + break + case C.H: + result = diff / C.MILLISECONDS_A_HOUR + break + case C.MIN: + result = diff / C.MILLISECONDS_A_MINUTE + break + case C.S: + result = diff / C.MILLISECONDS_A_SECOND + break + default: + result = diff // milliseconds + break + } return float ? result : Utils.a(result) } diff --git a/src/locale/ar-dz.js b/src/locale/ar-dz.js index fdc5e0b86..cd076f7d7 100644 --- a/src/locale/ar-dz.js +++ b/src/locale/ar-dz.js @@ -17,7 +17,7 @@ const locale = { LLL: 'D MMMM YYYY HH:mm', LLLL: 'dddd D MMMM YYYY HH:mm' }, - meridiem: hour => (hour > 12 ? 'ص' : 'م'), + meridiem: hour => (hour > 12 ? 'م' : 'ص'), relativeTime: { future: 'في %s', past: 'منذ %s', diff --git a/src/locale/ar-iq.js b/src/locale/ar-iq.js index 1040db80d..7878f6481 100644 --- a/src/locale/ar-iq.js +++ b/src/locale/ar-iq.js @@ -18,7 +18,7 @@ const locale = { LLL: 'D MMMM YYYY HH:mm', LLLL: 'dddd D MMMM YYYY HH:mm' }, - meridiem: hour => (hour > 12 ? 'ص' : 'م'), + meridiem: hour => (hour > 12 ? 'م' : 'ص'), relativeTime: { future: 'في %s', past: 'منذ %s', diff --git a/src/locale/ar-kw.js b/src/locale/ar-kw.js index 23ad4be6c..465ff9936 100644 --- a/src/locale/ar-kw.js +++ b/src/locale/ar-kw.js @@ -17,7 +17,7 @@ const locale = { LLL: 'D MMMM YYYY HH:mm', LLLL: 'dddd D MMMM YYYY HH:mm' }, - meridiem: hour => (hour > 12 ? 'ص' : 'م'), + meridiem: hour => (hour > 12 ? 'م' : 'ص'), relativeTime: { future: 'في %s', past: 'منذ %s', diff --git a/src/locale/ar-ly.js b/src/locale/ar-ly.js index 6db397056..316627099 100644 --- a/src/locale/ar-ly.js +++ b/src/locale/ar-ly.js @@ -10,7 +10,7 @@ const locale = { monthsShort: 'يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split('_'), weekdaysMin: 'ح_ن_ث_ر_خ_ج_س'.split('_'), ordinal: n => n, - meridiem: hour => (hour > 12 ? 'ص' : 'م'), + meridiem: hour => (hour > 12 ? 'م' : 'ص'), formats: { LT: 'HH:mm', LTS: 'HH:mm:ss', diff --git a/src/locale/ar-ma.js b/src/locale/ar-ma.js index 802b71684..5b1e70aed 100644 --- a/src/locale/ar-ma.js +++ b/src/locale/ar-ma.js @@ -18,7 +18,7 @@ const locale = { LLL: 'D MMMM YYYY HH:mm', LLLL: 'dddd D MMMM YYYY HH:mm' }, - meridiem: hour => (hour > 12 ? 'ص' : 'م'), + meridiem: hour => (hour > 12 ? 'م' : 'ص'), relativeTime: { future: 'في %s', past: 'منذ %s', diff --git a/src/locale/ar-sa.js b/src/locale/ar-sa.js index 9f908ade1..f543bac22 100644 --- a/src/locale/ar-sa.js +++ b/src/locale/ar-sa.js @@ -17,7 +17,7 @@ const locale = { LLL: 'D MMMM YYYY HH:mm', LLLL: 'dddd D MMMM YYYY HH:mm' }, - meridiem: hour => (hour > 12 ? 'ص' : 'م'), + meridiem: hour => (hour > 12 ? 'م' : 'ص'), relativeTime: { future: 'في %s', past: 'منذ %s', diff --git a/src/locale/ar-tn.js b/src/locale/ar-tn.js index 54c4e3e2d..c44cf66c1 100644 --- a/src/locale/ar-tn.js +++ b/src/locale/ar-tn.js @@ -18,7 +18,7 @@ const locale = { LLL: 'D MMMM YYYY HH:mm', LLLL: 'dddd D MMMM YYYY HH:mm' }, - meridiem: hour => (hour > 12 ? 'ص' : 'م'), + meridiem: hour => (hour > 12 ? 'م' : 'ص'), relativeTime: { future: 'في %s', past: 'منذ %s', diff --git a/src/locale/ar.js b/src/locale/ar.js index a7025c442..f418379c8 100644 --- a/src/locale/ar.js +++ b/src/locale/ar.js @@ -2,6 +2,7 @@ import dayjs from 'dayjs' const months = 'يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split('_') + const symbolMap = { 1: '١', 2: '٢', @@ -36,6 +37,7 @@ const locale = { months, monthsShort: months, weekStart: 6, + meridiem: hour => (hour > 12 ? 'م' : 'ص'), relativeTime: { future: 'بعد %s', past: 'منذ %s', diff --git a/src/locale/en.js b/src/locale/en.js index 71c2f226e..2913d5772 100644 --- a/src/locale/en.js +++ b/src/locale/en.js @@ -3,5 +3,10 @@ export default { name: 'en', weekdays: 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), - months: 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_') + months: 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), + ordinal: (n) => { + const s = ['th', 'st', 'nd', 'rd'] + const v = n % 100 + return `[${n}${(s[(v - 20) % 10] || s[v] || s[0])}]` + } } diff --git a/src/locale/fr.js b/src/locale/fr.js index e913b4aa7..be06169f6 100644 --- a/src/locale/fr.js +++ b/src/locale/fr.js @@ -34,7 +34,7 @@ const locale = { yy: '%d ans' }, ordinal: (n) => { - const o = n === 1 ? 'er' : 'e' + const o = n === 1 ? 'er' : '' return `${n}${o}` } } diff --git a/src/locale/ko.js b/src/locale/ko.js index 8a8e8ea9b..7053e83f3 100644 --- a/src/locale/ko.js +++ b/src/locale/ko.js @@ -8,7 +8,7 @@ const locale = { weekdaysMin: '일_월_화_수_목_금_토'.split('_'), months: '1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월'.split('_'), monthsShort: '1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월'.split('_'), - ordinal: n => n, + ordinal: n => `${n}일`, formats: { LT: 'A h:mm', LTS: 'A h:mm:ss', diff --git a/src/locale/ku.js b/src/locale/ku.js index 1d7a449d8..c87c5d88c 100644 --- a/src/locale/ku.js +++ b/src/locale/ku.js @@ -72,7 +72,7 @@ const locale = { meridiem: hour => (hour < 12 ? 'پ.ن' : 'د.ن'), relativeTime: { future: 'لە %s', - past: '%s', + past: 'لەمەوپێش %s', s: 'چەند چرکەیەک', m: 'یەک خولەک', mm: '%d خولەک', diff --git a/src/locale/nl.js b/src/locale/nl.js index 6d9ff7b04..49dc6bbcc 100644 --- a/src/locale/nl.js +++ b/src/locale/nl.js @@ -8,7 +8,7 @@ const locale = { weekdaysMin: 'zo_ma_di_wo_do_vr_za'.split('_'), months: 'januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december'.split('_'), monthsShort: 'jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec'.split('_'), - ordinal: n => `${n}${n === 1 || n === 8 || n >= 20 ? 'ste' : 'de'}`, + ordinal: n => `[${n}${n === 1 || n === 8 || n >= 20 ? 'ste' : 'de'}]`, weekStart: 1, yearStart: 4, formats: { diff --git a/src/locale/sl.js b/src/locale/sl.js index f09e3bfce..1e0e4f885 100644 --- a/src/locale/sl.js +++ b/src/locale/sl.js @@ -1,6 +1,71 @@ // Slovenian [sl] import dayjs from 'dayjs' +function dual(n) { + return (n % 100) == 2 // eslint-disable-line +} + +function threeFour(n) { + return (n % 100) == 3 || (n % 100) == 4 // eslint-disable-line +} + +/* eslint-disable */ +function translate(number, withoutSuffix, key, isFuture) { + const result = `${number} ` + switch (key) { + case 's': // a few seconds / in a few seconds / a few seconds ago + return (withoutSuffix || isFuture) ? 'nekaj sekund' : 'nekaj sekundami' + case 'm': // a minute / in a minute / a minute ago + return withoutSuffix ? 'ena minuta' : 'eno minuto' + case 'mm': // 9 minutes / in 9 minutes / 9 minutes ago + if (dual(number)) { + return result + ((withoutSuffix || isFuture) ? 'minuti' : 'minutama') + } + if (threeFour(number)) { + return result + ((withoutSuffix || isFuture) ? 'minute' : 'minutami') + } + return result + ((withoutSuffix || isFuture) ? 'minut' : 'minutami') + case 'h': // an hour / in an hour / an hour ago + return withoutSuffix ? 'ena ura' : (isFuture ? 'eno uro' : 'eno uro') + case 'hh': // 9 hours / in 9 hours / 9 hours ago + if (dual(number)) { + return result + ((withoutSuffix || isFuture) ? 'uri' : 'urama') + } + if (threeFour(number)) { + return result + ((withoutSuffix || isFuture) ? 'ure' : 'urami') + } + return result + ((withoutSuffix || isFuture) ? 'ur' : 'urami') + case 'd': // a day / in a day / a day ago + return (withoutSuffix || isFuture) ? 'en dan' : 'enim dnem' + case 'dd': // 9 days / in 9 days / 9 days ago + if (dual(number)) { + return result + ((withoutSuffix || isFuture) ? 'dneva' : 'dnevoma') + } + return result + ((withoutSuffix || isFuture) ? 'dni' : 'dnevi') + case 'M': // a month / in a month / a month ago + return (withoutSuffix || isFuture) ? 'en mesec' : 'enim mesecem' + case 'MM': // 9 months / in 9 months / 9 months ago + if (dual(number)) { // 2 minutes / in 2 minutes + return result + ((withoutSuffix || isFuture) ? 'meseca' : 'mesecema') + } + if (threeFour(number)) { + return result + ((withoutSuffix || isFuture) ? 'mesece' : 'meseci') + } + return result + ((withoutSuffix || isFuture) ? 'mesecev' : 'meseci') + case 'y': // a year / in a year / a year ago + return (withoutSuffix || isFuture) ? 'eno leto' : 'enim letom' + case 'yy': // 9 years / in 9 years / 9 years ago + if (dual(number)) { // 2 minutes / in 2 minutes + return result + ((withoutSuffix || isFuture) ? 'leti' : 'letoma') + } + if (threeFour(number)) { + return result + ((withoutSuffix || isFuture) ? 'leta' : 'leti') + } + return result + ((withoutSuffix || isFuture) ? 'let' : 'leti') + } +} + +/* eslint-enable */ const locale = { name: 'sl', weekdays: 'nedelja_ponedeljek_torek_sreda_četrtek_petek_sobota'.split('_'), @@ -16,26 +81,26 @@ const locale = { L: 'DD.MM.YYYY', LL: 'D. MMMM YYYY', LLL: 'D. MMMM YYYY H:mm', - LLLL: 'dddd, D. MMMM YYYY H:mm' + LLLL: 'dddd, D. MMMM YYYY H:mm', + l: 'D. M. YYYY' }, relativeTime: { future: 'čez %s', past: 'pred %s', - s: 'nekaj sekund', - m: 'minuta', - mm: '%d minut', - h: 'ura', - hh: '%d ur', - d: 'dan', - dd: '%d dni', - M: 'mesec', - MM: '%d mesecev', - y: 'leto', - yy: '%d let' + s: translate, + m: translate, + mm: translate, + h: translate, + hh: translate, + d: translate, + dd: translate, + M: translate, + MM: translate, + y: translate, + yy: translate } } dayjs.locale(locale, null, true) export default locale - diff --git a/src/locale/uz-latn.js b/src/locale/uz-latn.js index 220d5d44c..7d6021921 100644 --- a/src/locale/uz-latn.js +++ b/src/locale/uz-latn.js @@ -20,7 +20,7 @@ const locale = { }, relativeTime: { future: 'Yaqin %s ichida', - past: 'Bir necha %s oldin', + past: '%s oldin', s: 'soniya', m: 'bir daqiqa', mm: '%d daqiqa', diff --git a/src/locale/uz.js b/src/locale/uz.js index 23f4042eb..e705f68bd 100644 --- a/src/locale/uz.js +++ b/src/locale/uz.js @@ -20,7 +20,7 @@ const locale = { }, relativeTime: { future: 'Якин %s ичида', - past: 'Бир неча %s олдин', + past: '%s олдин', s: 'фурсат', m: 'бир дакика', mm: '%d дакика', diff --git a/src/locale/zh-hk.js b/src/locale/zh-hk.js index 3c38d0756..193f2e2d8 100644 --- a/src/locale/zh-hk.js +++ b/src/locale/zh-hk.js @@ -8,14 +8,25 @@ const locale = { weekdays: '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'), weekdaysShort: '週日_週一_週二_週三_週四_週五_週六'.split('_'), weekdaysMin: '日_一_二_三_四_五_六'.split('_'), - ordinal: n => `${n}日`, + ordinal: (number, period) => { + switch (period) { + case 'W': + return `${number}週` + default: + return `${number}日` + } + }, formats: { LT: 'HH:mm', LTS: 'HH:mm:ss', L: 'YYYY/MM/DD', LL: 'YYYY年M月D日', LLL: 'YYYY年M月D日 HH:mm', - LLLL: 'YYYY年M月D日dddd HH:mm' + LLLL: 'YYYY年M月D日dddd HH:mm', + l: 'YYYY/M/D', + ll: 'YYYY年M月D日', + lll: 'YYYY年M月D日 HH:mm', + llll: 'YYYY年M月D日dddd HH:mm' }, relativeTime: { future: '%s內', @@ -31,6 +42,21 @@ const locale = { MM: '%d 個月', y: '一年', yy: '%d 年' + }, + meridiem: (hour, minute) => { + const hm = (hour * 100) + minute + if (hm < 600) { + return '凌晨' + } else if (hm < 900) { + return '早上' + } else if (hm < 1100) { + return '上午' + } else if (hm < 1300) { + return '中午' + } else if (hm < 1800) { + return '下午' + } + return '晚上' } } diff --git a/src/locale/zh-tw.js b/src/locale/zh-tw.js index b7880674d..709f70cff 100644 --- a/src/locale/zh-tw.js +++ b/src/locale/zh-tw.js @@ -8,7 +8,14 @@ const locale = { weekdaysMin: '日_一_二_三_四_五_六'.split('_'), months: '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split('_'), monthsShort: '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'), - ordinal: n => `${n}日`, + ordinal: (number, period) => { + switch (period) { + case 'W': + return `${number}週` + default: + return `${number}日` + } + }, formats: { LT: 'HH:mm', LTS: 'HH:mm:ss', @@ -35,10 +42,24 @@ const locale = { MM: '%d 個月', y: '1 年', yy: '%d 年' + }, + meridiem: (hour, minute) => { + const hm = (hour * 100) + minute + if (hm < 600) { + return '凌晨' + } else if (hm < 900) { + return '早上' + } else if (hm < 1100) { + return '上午' + } else if (hm < 1300) { + return '中午' + } else if (hm < 1800) { + return '下午' + } + return '晚上' } } dayjs.locale(locale, null, true) export default locale - diff --git a/src/plugin/advancedFormat/index.js b/src/plugin/advancedFormat/index.js index a78296f7a..7794af987 100644 --- a/src/plugin/advancedFormat/index.js +++ b/src/plugin/advancedFormat/index.js @@ -1,14 +1,8 @@ import { FORMAT_DEFAULT } from '../../constant' -export default (o, c, d) => { // locale needed later +export default (o, c) => { // locale needed later const proto = c.prototype const oldFormat = proto.format - d.en.ordinal = (number) => { - const s = ['th', 'st', 'nd', 'rd'] - const v = number % 100 - return `[${number}${(s[(v - 20) % 10] || s[v] || s[0])}]` - } - // extend en locale here proto.format = function (formatStr) { const locale = this.$locale() diff --git a/src/plugin/bigIntSupport/index.js b/src/plugin/bigIntSupport/index.js new file mode 100644 index 000000000..a90bae1fe --- /dev/null +++ b/src/plugin/bigIntSupport/index.js @@ -0,0 +1,25 @@ +// eslint-disable-next-line valid-typeof +const isBigInt = num => typeof num === 'bigint' +export default (o, c, dayjs) => { + const proto = c.prototype + const parseDate = (cfg) => { + const { date } = cfg + if (isBigInt(date)) { + return Number(date) + } + return date + } + + const oldParse = proto.parse + proto.parse = function (cfg) { + cfg.date = parseDate.bind(this)(cfg) + oldParse.bind(this)(cfg) + } + + + const oldUnix = dayjs.unix + dayjs.unix = function (timestamp) { + const ts = isBigInt(timestamp) ? Number(timestamp) : timestamp + return oldUnix(ts) + } +} diff --git a/src/plugin/duration/index.js b/src/plugin/duration/index.js index 10ebb4ca2..e2060c344 100644 --- a/src/plugin/duration/index.js +++ b/src/plugin/duration/index.js @@ -8,7 +8,7 @@ import { } from '../../constant' const MILLISECONDS_A_YEAR = MILLISECONDS_A_DAY * 365 -const MILLISECONDS_A_MONTH = MILLISECONDS_A_DAY * 30 +const MILLISECONDS_A_MONTH = MILLISECONDS_A_YEAR / 12 const durationRegex = /^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/ @@ -140,6 +140,7 @@ class Duration { let seconds = this.$d.seconds || 0 if (this.$d.milliseconds) { seconds += this.$d.milliseconds / 1000 + seconds = Math.round(seconds * 1000) / 1000 } const S = getNumberUnitFormat(seconds, 'S') @@ -198,7 +199,7 @@ class Duration { } else { base = this.$d[pUnit] } - return base === 0 ? 0 : base // a === 0 will be true on both 0 and -0 + return base || 0 // a === 0 will be true on both 0 and -0 } add(input, unit, isSubtract) { @@ -235,6 +236,10 @@ class Duration { .fromNow(!withSuffix) } + valueOf() { + return this.asMilliseconds() + } + milliseconds() { return this.get('milliseconds') } asMilliseconds() { return this.as('milliseconds') } seconds() { return this.get('seconds') } @@ -253,6 +258,15 @@ class Duration { asYears() { return this.as('years') } } +const manipulateDuration = (date, duration, k) => + date.add(duration.years() * k, 'y') + .add(duration.months() * k, 'M') + .add(duration.days() * k, 'd') + .add(duration.hours() * k, 'h') + .add(duration.minutes() * k, 'm') + .add(duration.seconds() * k, 's') + .add(duration.milliseconds() * k, 'ms') + export default (option, Dayjs, dayjs) => { $d = dayjs $u = dayjs().$utils() @@ -264,12 +278,20 @@ export default (option, Dayjs, dayjs) => { const oldAdd = Dayjs.prototype.add const oldSubtract = Dayjs.prototype.subtract + Dayjs.prototype.add = function (value, unit) { - if (isDuration(value)) value = value.asMilliseconds() + if (isDuration(value)) { + return manipulateDuration(this, value, 1) + } + return oldAdd.bind(this)(value, unit) } + Dayjs.prototype.subtract = function (value, unit) { - if (isDuration(value)) value = value.asMilliseconds() + if (isDuration(value)) { + return manipulateDuration(this, value, -1) + } + return oldSubtract.bind(this)(value, unit) } } diff --git a/src/plugin/minMax/index.js b/src/plugin/minMax/index.js index f515c60d9..8b3e2d3fe 100644 --- a/src/plugin/minMax/index.js +++ b/src/plugin/minMax/index.js @@ -1,12 +1,18 @@ export default (o, c, d) => { const sortBy = (method, dates) => { - if (!dates || !dates.length || !dates[0] || (dates.length === 1 && !dates[0].length)) { + if ( + !dates || + !dates.length || + (dates.length === 1 && !dates[0]) || + (dates.length === 1 && Array.isArray(dates[0]) && !dates[0].length) + ) { return null } if (dates.length === 1 && dates[0].length > 0) { [dates] = dates } - let result + dates = dates.filter(date => date) + let result; [result] = dates for (let i = 1; i < dates.length; i += 1) { if (!dates[i].isValid() || dates[i][method](result)) { diff --git a/src/plugin/objectSupport/index.js b/src/plugin/objectSupport/index.js index 4344dc402..bd32358e9 100644 --- a/src/plugin/objectSupport/index.js +++ b/src/plugin/objectSupport/index.js @@ -1,6 +1,7 @@ export default (o, c, dayjs) => { const proto = c.prototype - const isObject = obj => !(obj instanceof Date) && !(obj instanceof Array) && obj instanceof Object + const isObject = obj => obj !== null && !(obj instanceof Date) && !(obj instanceof Array) + && !proto.$utils().u(obj) && (obj.constructor.name === 'Object') const prettyUnit = (u) => { const unit = proto.$utils().p(u) return unit === 'date' ? 'day' : unit @@ -39,29 +40,36 @@ export default (o, c, dayjs) => { const oldSet = proto.set const oldAdd = proto.add + const oldSubtract = proto.subtract const callObject = function (call, argument, string, offset = 1) { - if (argument instanceof Object) { - const keys = Object.keys(argument) - let chain = this - keys.forEach((key) => { - chain = call.bind(chain)(argument[key] * offset, key) - }) - return chain - } - return call.bind(this)(argument * offset, string) + const keys = Object.keys(argument) + let chain = this + keys.forEach((key) => { + chain = call.bind(chain)(argument[key] * offset, key) + }) + return chain } - proto.set = function (string, int) { - int = int === undefined ? string : int - return callObject.bind(this)(function (i, s) { - return oldSet.bind(this)(s, i) - }, int, string) + proto.set = function (unit, value) { + value = value === undefined ? unit : value + if (unit.constructor.name === 'Object') { + return callObject.bind(this)(function (i, s) { + return oldSet.bind(this)(s, i) + }, value, unit) + } + return oldSet.bind(this)(unit, value) } - proto.add = function (number, string) { - return callObject.bind(this)(oldAdd, number, string) + proto.add = function (value, unit) { + if (value.constructor.name === 'Object') { + return callObject.bind(this)(oldAdd, value, unit) + } + return oldAdd.bind(this)(value, unit) } - proto.subtract = function (number, string) { - return callObject.bind(this)(oldAdd, number, string, -1) + proto.subtract = function (value, unit) { + if (value.constructor.name === 'Object') { + return callObject.bind(this)(oldAdd, value, unit, -1) + } + return oldSubtract.bind(this)(value, unit) } } diff --git a/src/plugin/timezone/index.js b/src/plugin/timezone/index.js index c8f8a4675..832fb4a73 100644 --- a/src/plugin/timezone/index.js +++ b/src/plugin/timezone/index.js @@ -97,7 +97,7 @@ export default (o, c, d) => { const date = this.toDate() const target = date.toLocaleString('en-US', { timeZone: timezone }) const diff = Math.round((date - new Date(target)) / 1000 / 60) - let ins = d(target).$set(MS, this.$ms) + let ins = d(target, { locale: this.$L }).$set(MS, this.$ms) .utcOffset((-Math.round(date.getTimezoneOffset() / 15) * 15) - diff, true) if (keepLocalTime) { const newOffset = ins.utcOffset() @@ -120,7 +120,7 @@ export default (o, c, d) => { return oldStartOf.call(this, units, startOf) } - const withoutTz = d(this.format('YYYY-MM-DD HH:mm:ss:SSS')) + const withoutTz = d(this.format('YYYY-MM-DD HH:mm:ss:SSS'), { locale: this.$L }) const startOfWithoutTz = oldStartOf.call(withoutTz, units, startOf) return startOfWithoutTz.tz(this.$x.$timezone, true) } diff --git a/test/constructor.test.js b/test/constructor.test.js index b14b2fec4..66cdbfc68 100644 --- a/test/constructor.test.js +++ b/test/constructor.test.js @@ -13,6 +13,14 @@ it('supports instanceof dayjs', () => { expect(dayjs() instanceof dayjs).toBeTruthy() }) +it('$isDayjsObject', () => { + const mockOtherVersionDayjsObj = { + $isDayjsObject: true + } + expect(dayjs.isDayjs(mockOtherVersionDayjsObj)).toBeTruthy() +}) + it('does not break isDayjs', () => { expect(dayjs.isDayjs(dayjs())).toBeTruthy() + expect(dayjs.isDayjs(new Date())).toBeFalsy() }) diff --git a/test/display.test.js b/test/display.test.js index 7eb48fe82..ce1f47893 100644 --- a/test/display.test.js +++ b/test/display.test.js @@ -260,3 +260,11 @@ it('As JSON -> toJSON', () => { it('As ISO 8601 String -> toISOString e.g. 2013-02-04T22:44:30.652Z', () => { expect(dayjs().toISOString()).toBe(moment().toISOString()) }) + +it('Year 1 formatted with YYYY should pad with zeroes', () => { + const date = new Date(1, 0, 1) + date.setUTCFullYear(1) // Required because 0-99 are parsed as 19xx in JS: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date#year + const res = dayjs(date).format('YYYY') + expect(res.slice(0, 3)).toBe('000') // because of timezone, the result might be 0000 0001 or 0002 + expect(res).toBe(moment(date).format('YYYY')) +}) diff --git a/test/issues/issue2027.correct-order.test.js b/test/issues/issue2027.correct-order.test.js new file mode 100644 index 000000000..3d9d6300c --- /dev/null +++ b/test/issues/issue2027.correct-order.test.js @@ -0,0 +1,78 @@ +import MockDate from 'mockdate' +import dayjs from '../../src' +import duration from '../../src/plugin/duration' +import objectSupport from '../../src/plugin/objectSupport' + +dayjs.extend(objectSupport) +dayjs.extend(duration) + +beforeEach(() => { + MockDate.set(new Date()) +}) + +afterEach(() => { + MockDate.reset() +}) + +// issue 2027 +describe('issue 2027 - order objectSupport > Duration', () => { + it('add Duration object returns correct date', () => { + const baseDate = dayjs('2022-06-26T14:01:02.003') + const durationToAdd = dayjs.duration(6, 'hours') + const testDate = baseDate.add(durationToAdd) + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 20:01:02.003') + }) + it('subtract Duration object returns correct date', () => { + const baseDate = dayjs('2022-06-26T14:01:02.003') + const durationToAdd = dayjs.duration(6, 'hours') + const testDate = baseDate.subtract(durationToAdd) + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 08:01:02.003') + }) + + it('add number with unit returns correct date', () => { + const baseDate = dayjs('2022-06-26T14:01:02.003') + const testDate = baseDate.add(6, 'hours') + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 20:01:02.003') + }) + it('subtract number with unit returns correct date', () => { + const baseDate = dayjs('2022-06-26T14:01:02.003') + const testDate = baseDate.subtract(6, 'hours') + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 08:01:02.003') + }) + + it('parse string returns correct date', () => { + const testDate = dayjs('2022-06-26T14:01:02.003') + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 14:01:02.003') + }) + it('parse object returns correct date', () => { + const testDate = dayjs({ + year: '2022', + month: '05', + day: '26', + hour: '14', + minute: '01', + second: '02', + millisecond: '003' + }) + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 14:01:02.003') + }) + + it('set hour with number returns correct date', () => { + const baseDate = dayjs('2022-06-26T14:01:02.003') + const testDate = baseDate.hour(10) + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 10:01:02.003') + }) + it('set hour with object returns correct date', () => { + const baseDate = dayjs('2022-06-26T14:01:02.003') + const testDate = baseDate.set({ hour: '10' }) + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 10:01:02.003') + }) +}) diff --git a/test/issues/issue2027.swapped-order.test.js b/test/issues/issue2027.swapped-order.test.js new file mode 100644 index 000000000..62a969e49 --- /dev/null +++ b/test/issues/issue2027.swapped-order.test.js @@ -0,0 +1,79 @@ +import MockDate from 'mockdate' +import dayjs from '../../src' +import duration from '../../src/plugin/duration' +import objectSupport from '../../src/plugin/objectSupport' + +dayjs.extend(duration) +dayjs.extend(objectSupport) + +beforeEach(() => { + MockDate.set(new Date()) +}) + +afterEach(() => { + MockDate.reset() +}) + +// issue 2027 +describe('issue 2027 - order objectSupport > Duration', () => { + it('add Duration object returns correct date', () => { + const baseDate = dayjs('2022-06-26T14:01:02.003') + const durationToAdd = dayjs.duration(6, 'hours') + const testDate = baseDate.add(durationToAdd) + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 20:01:02.003') + }) + + it('subtract Duration object returns correct date', () => { + const baseDate = dayjs('2022-06-26T14:01:02.003') + const durationToAdd = dayjs.duration(6, 'hours') + const testDate = baseDate.subtract(durationToAdd) + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 08:01:02.003') + }) + + it('add number with unit returns correct date', () => { + const baseDate = dayjs('2022-06-26T14:01:02.003') + const testDate = baseDate.add(6, 'hours') + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 20:01:02.003') + }) + it('subtract number with unit returns correct date', () => { + const baseDate = dayjs('2022-06-26T14:01:02.003') + const testDate = baseDate.subtract(6, 'hours') + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 08:01:02.003') + }) + + it('parse string returns correct date', () => { + const testDate = dayjs('2022-06-26T14:01:02.003') + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 14:01:02.003') + }) + it('parse object returns correct date', () => { + const testDate = dayjs({ + year: '2022', + month: '05', + day: '26', + hour: '14', + minute: '01', + second: '02', + millisecond: '003' + }) + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 14:01:02.003') + }) + + it('set hour with number returns correct date', () => { + const baseDate = dayjs('2022-06-26T14:01:02.003') + const testDate = baseDate.hour(10) + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 10:01:02.003') + }) + it('set hour with object returns correct date', () => { + const baseDate = dayjs('2022-06-26T14:01:02.003') + const testDate = baseDate.set({ hour: '10' }) + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 10:01:02.003') + }) +}) diff --git a/test/locale/ar-dz.test.js b/test/locale/ar-dz.test.js new file mode 100644 index 000000000..532e5f8eb --- /dev/null +++ b/test/locale/ar-dz.test.js @@ -0,0 +1,23 @@ +import MockDate from 'mockdate' +import dayjs from '../../src' +import relativeTime from '../../src/plugin/relativeTime' +import '../../src/locale/ru' +import locale from '../../src/locale/ar-dz' + +dayjs.extend(relativeTime) + +beforeEach(() => { + MockDate.set(new Date()) +}) + +afterEach(() => { + MockDate.reset() +}) + +it('Meridiem', () => { + dayjs.locale(locale) + expect(dayjs('2020-01-01 03:00:00').locale('ar-dz').format('A')).toEqual('ص') + expect(dayjs('2020-01-01 11:00:00').locale('ar-dz').format('A')).toEqual('ص') + expect(dayjs('2020-01-01 16:00:00').locale('ar-dz').format('A')).toEqual('م') + expect(dayjs('2020-01-01 20:00:00').locale('ar-dz').format('A')).toEqual('م') +}) diff --git a/test/locale/ar-iq.test.js b/test/locale/ar-iq.test.js new file mode 100644 index 000000000..810723da3 --- /dev/null +++ b/test/locale/ar-iq.test.js @@ -0,0 +1,23 @@ +import MockDate from 'mockdate' +import dayjs from '../../src' +import relativeTime from '../../src/plugin/relativeTime' +import '../../src/locale/ru' +import locale from '../../src/locale/ar-iq' + +dayjs.extend(relativeTime) + +beforeEach(() => { + MockDate.set(new Date()) +}) + +afterEach(() => { + MockDate.reset() +}) + +it('Meridiem', () => { + dayjs.locale(locale) + expect(dayjs('2020-01-01 03:00:00').locale('ar-iq').format('A')).toEqual('ص') + expect(dayjs('2020-01-01 11:00:00').locale('ar-iq').format('A')).toEqual('ص') + expect(dayjs('2020-01-01 16:00:00').locale('ar-iq').format('A')).toEqual('م') + expect(dayjs('2020-01-01 20:00:00').locale('ar-iq').format('A')).toEqual('م') +}) diff --git a/test/locale/ar-kw.test.js b/test/locale/ar-kw.test.js new file mode 100644 index 000000000..8a8e14933 --- /dev/null +++ b/test/locale/ar-kw.test.js @@ -0,0 +1,23 @@ +import MockDate from 'mockdate' +import dayjs from '../../src' +import relativeTime from '../../src/plugin/relativeTime' +import '../../src/locale/ru' +import locale from '../../src/locale/ar-kw' + +dayjs.extend(relativeTime) + +beforeEach(() => { + MockDate.set(new Date()) +}) + +afterEach(() => { + MockDate.reset() +}) + +it('Meridiem', () => { + dayjs.locale(locale) + expect(dayjs('2020-01-01 03:00:00').locale('ar-kw').format('A')).toEqual('ص') + expect(dayjs('2020-01-01 11:00:00').locale('ar-kw').format('A')).toEqual('ص') + expect(dayjs('2020-01-01 16:00:00').locale('ar-kw').format('A')).toEqual('م') + expect(dayjs('2020-01-01 20:00:00').locale('ar-kw').format('A')).toEqual('م') +}) diff --git a/test/locale/ar-ly.test.js b/test/locale/ar-ly.test.js new file mode 100644 index 000000000..927675136 --- /dev/null +++ b/test/locale/ar-ly.test.js @@ -0,0 +1,23 @@ +import MockDate from 'mockdate' +import dayjs from '../../src' +import relativeTime from '../../src/plugin/relativeTime' +import '../../src/locale/ru' +import locale from '../../src/locale/ar-ly' + +dayjs.extend(relativeTime) + +beforeEach(() => { + MockDate.set(new Date()) +}) + +afterEach(() => { + MockDate.reset() +}) + +it('Meridiem', () => { + dayjs.locale(locale) + expect(dayjs('2020-01-01 03:00:00').locale('ar-ly').format('A')).toEqual('ص') + expect(dayjs('2020-01-01 11:00:00').locale('ar-ly').format('A')).toEqual('ص') + expect(dayjs('2020-01-01 16:00:00').locale('ar-ly').format('A')).toEqual('م') + expect(dayjs('2020-01-01 20:00:00').locale('ar-ly').format('A')).toEqual('م') +}) diff --git a/test/locale/ar-ma.test.js b/test/locale/ar-ma.test.js new file mode 100644 index 000000000..e08ab86d1 --- /dev/null +++ b/test/locale/ar-ma.test.js @@ -0,0 +1,23 @@ +import MockDate from 'mockdate' +import dayjs from '../../src' +import relativeTime from '../../src/plugin/relativeTime' +import '../../src/locale/ru' +import locale from '../../src/locale/ar-ma' + +dayjs.extend(relativeTime) + +beforeEach(() => { + MockDate.set(new Date()) +}) + +afterEach(() => { + MockDate.reset() +}) + +it('Meridiem', () => { + dayjs.locale(locale) + expect(dayjs('2020-01-01 03:00:00').locale('ar-ma').format('A')).toEqual('ص') + expect(dayjs('2020-01-01 11:00:00').locale('ar-ma').format('A')).toEqual('ص') + expect(dayjs('2020-01-01 16:00:00').locale('ar-ma').format('A')).toEqual('م') + expect(dayjs('2020-01-01 20:00:00').locale('ar-ma').format('A')).toEqual('م') +}) diff --git a/test/locale/ar-sa.test.js b/test/locale/ar-sa.test.js new file mode 100644 index 000000000..1936f5086 --- /dev/null +++ b/test/locale/ar-sa.test.js @@ -0,0 +1,23 @@ +import MockDate from 'mockdate' +import dayjs from '../../src' +import relativeTime from '../../src/plugin/relativeTime' +import '../../src/locale/ru' +import locale from '../../src/locale/ar-sa' + +dayjs.extend(relativeTime) + +beforeEach(() => { + MockDate.set(new Date()) +}) + +afterEach(() => { + MockDate.reset() +}) + +it('Meridiem', () => { + dayjs.locale(locale) + expect(dayjs('2020-01-01 03:00:00').locale('ar-sa').format('A')).toEqual('ص') + expect(dayjs('2020-01-01 11:00:00').locale('ar-sa').format('A')).toEqual('ص') + expect(dayjs('2020-01-01 16:00:00').locale('ar-sa').format('A')).toEqual('م') + expect(dayjs('2020-01-01 20:00:00').locale('ar-sa').format('A')).toEqual('م') +}) diff --git a/test/locale/ar-tn.test.js b/test/locale/ar-tn.test.js new file mode 100644 index 000000000..7c73dfcd9 --- /dev/null +++ b/test/locale/ar-tn.test.js @@ -0,0 +1,23 @@ +import MockDate from 'mockdate' +import dayjs from '../../src' +import relativeTime from '../../src/plugin/relativeTime' +import '../../src/locale/ru' +import locale from '../../src/locale/ar-tn' + +dayjs.extend(relativeTime) + +beforeEach(() => { + MockDate.set(new Date()) +}) + +afterEach(() => { + MockDate.reset() +}) + +it('Meridiem', () => { + dayjs.locale(locale) + expect(dayjs('2020-01-01 03:00:00').locale('ar-tn').format('A')).toEqual('ص') + expect(dayjs('2020-01-01 11:00:00').locale('ar-tn').format('A')).toEqual('ص') + expect(dayjs('2020-01-01 16:00:00').locale('ar-tn').format('A')).toEqual('م') + expect(dayjs('2020-01-01 20:00:00').locale('ar-tn').format('A')).toEqual('م') +}) diff --git a/test/locale/ar.test.js b/test/locale/ar.test.js index 3f9f60f0c..2b5a57d29 100644 --- a/test/locale/ar.test.js +++ b/test/locale/ar.test.js @@ -50,3 +50,13 @@ it('RelativeTime: Time from X gets formatted', () => { .toBe(t[2]) }) }) + +it('Format meridiem with locale function', () => { + for (let i = 0; i <= 23; i += 1) { + const hour = dayjs() + .startOf('day') + .add(i, 'hour') + const meridiem = i > 12 ? 'م' : 'ص' + expect(hour.locale('ar').format('A')).toBe(`${meridiem}`) + } +}) diff --git a/test/locale/sl.test.js b/test/locale/sl.test.js new file mode 100644 index 000000000..672d6064b --- /dev/null +++ b/test/locale/sl.test.js @@ -0,0 +1,82 @@ +import MockDate from 'mockdate' +import dayjs from '../../src' +import relativeTime from '../../src/plugin/relativeTime' +import '../../src/locale/sl' + +dayjs.extend(relativeTime) + +beforeEach(() => { + MockDate.set(new Date()) +}) + +afterEach(() => { + MockDate.reset() +}) + +it('Slovenian locale relative time in past and future', () => { + const cases = [ + [1, 's', 'čez nekaj sekund', 'nekaj sekund'], + [-1, 's', 'pred nekaj sekundami', 'nekaj sekund'], + [1, 'm', 'čez eno minuto', 'ena minuta'], + [-1, 'm', 'pred eno minuto', 'ena minuta'], + [2, 'm', 'čez 2 minuti', '2 minuti'], + [-2, 'm', 'pred 2 minutama', '2 minuti'], + [3, 'm', 'čez 3 minute', '3 minute'], + [-3, 'm', 'pred 3 minutami', '3 minute'], + [5, 'm', 'čez 5 minut', '5 minut'], + [-5, 'm', 'pred 5 minutami', '5 minut'], + [1, 'h', 'čez eno uro', 'ena ura'], + [-1, 'h', 'pred eno uro', 'ena ura'], + [2, 'h', 'čez 2 uri', '2 uri'], + [-2, 'h', 'pred 2 urama', '2 uri'], + [3, 'h', 'čez 3 ure', '3 ure'], + [-3, 'h', 'pred 3 urami', '3 ure'], + [5, 'h', 'čez 5 ur', '5 ur'], + [-5, 'h', 'pred 5 urami', '5 ur'], + [1, 'd', 'čez en dan', 'en dan'], + [-1, 'd', 'pred enim dnem', 'en dan'], + [2, 'd', 'čez 2 dneva', '2 dneva'], + [-2, 'd', 'pred 2 dnevoma', '2 dneva'], + [3, 'd', 'čez 3 dni', '3 dni'], + [-3, 'd', 'pred 3 dnevi', '3 dni'], + [5, 'd', 'čez 5 dni', '5 dni'], + [-5, 'd', 'pred 5 dnevi', '5 dni'], + [1, 'M', 'čez en mesec', 'en mesec'], + [-1, 'M', 'pred enim mesecem', 'en mesec'], + [2, 'M', 'čez 2 meseca', '2 meseca'], + [-2, 'M', 'pred 2 mesecema', '2 meseca'], + [3, 'M', 'čez 3 mesece', '3 mesece'], + [-3, 'M', 'pred 3 meseci', '3 mesece'], + [5, 'M', 'čez 5 mesecev', '5 mesecev'], + [-5, 'M', 'pred 5 meseci', '5 mesecev'], + [1, 'y', 'čez eno leto', 'eno leto'], + [-1, 'y', 'pred enim letom', 'eno leto'], + [2, 'y', 'čez 2 leti', '2 leti'], + [-2, 'y', 'pred 2 letoma', '2 leti'], + [3, 'y', 'čez 3 leta', '3 leta'], + [-3, 'y', 'pred 3 leti', '3 leta'], + [5, 'y', 'čez 5 let', '5 let'], + [-5, 'y', 'pred 5 leti', '5 let'] + // these are rounded + // if user decides to change rounding then it would be good to test them also + // [102, 's', 'čez 102 sekundi', '102 sekundi'], + // [-102, 's', 'pred 102 sekundama', '102 sekundi'], + // [103, 's', 'čez 103 sekunde', '103 sekunde'], + // [-103, 's', 'pred 103 sekundami', '103 sekunde'], + // [114, 's', 'čez 114 sekund', '114 sekund'], + // [-114, 's', 'pred 114 sekundami', '114 sekund'], + // [-102, 'm', 'čez 102 minuti', '102 minuti'], + // [-102, 'm', 'pred 102 minutama', '102 minuti'], + // [103, 'm', 'čez 103 minute', '103 minute'], + // [-103, 'm', 'pred 103 minutami', '103 minute'], + // [114, 'm', 'čez 114 minut', '114 minut'], + // [-114, 'm', 'pred 114 minutami', '114 minut'] + ] + + cases.forEach((c) => { + // With suffix + expect(dayjs().add(c[0], c[1]).locale('sl').fromNow()).toBe(c[2]) + // Without suffix + expect(dayjs().add(c[0], c[1]).locale('sl').fromNow(true)).toBe(c[3]) + }) +}) diff --git a/test/locale/zh-hk.test.js b/test/locale/zh-hk.test.js new file mode 100644 index 000000000..5b65c6cfc --- /dev/null +++ b/test/locale/zh-hk.test.js @@ -0,0 +1,27 @@ +import dayjs from '../../src' +import advancedFormat from '../../src/plugin/advancedFormat' +import weekOfYear from '../../src/plugin/weekOfYear' +import '../../src/locale/zh' +import '../../src/locale/zh-cn' +import '../../src/locale/zh-hk' +import '../../src/locale/zh-tw' + +dayjs.extend(advancedFormat).extend(weekOfYear) + +const zh = dayjs().locale('zh') +const zhCN = dayjs().locale('zh-cn') +const zhHK = dayjs().locale('zh-hk') +const zhTW = dayjs().locale('zh-tw') + +test('ordinal', () => { + expect(zh.format('wo')).toEqual(`${zh.format('w')}周`) + expect(zhCN.format('wo')).toEqual(`${zhCN.format('w')}周`) + expect(zhHK.format('wo')).toEqual(`${zhHK.format('w')}週`) + expect(zhTW.format('wo')).toEqual(`${zhTW.format('w')}週`) +}) + +test('Meridiem', () => { + for (let i = 0; i <= 24; i += 1) { + expect(zh.add(i, 'hour').format('A')).toBe(zhCN.add(i, 'hour').format('A')) + } +}) diff --git a/test/locale/zh-tw.test.js b/test/locale/zh-tw.test.js new file mode 100644 index 000000000..57a3f1079 --- /dev/null +++ b/test/locale/zh-tw.test.js @@ -0,0 +1,21 @@ +import dayjs from '../../src' +import advancedFormat from '../../src/plugin/advancedFormat' +import weekOfYear from '../../src/plugin/weekOfYear' +import '../../src/locale/zh' +import '../../src/locale/zh-tw' + +dayjs.extend(advancedFormat).extend(weekOfYear) + +const zh = dayjs().locale('zh') +const zhTW = dayjs().locale('zh-tw') + +test('ordinal', () => { + expect(zh.format('wo')).toEqual(`${zh.format('w')}周`) + expect(zhTW.format('wo')).toEqual(`${zhTW.format('w')}週`) +}) + +test('Meridiem', () => { + for (let i = 0; i <= 24; i += 1) { + expect(zh.add(i, 'hour').format('A')).toBe(zhTW.add(i, 'hour').format('A')) + } +}) diff --git a/test/plugin/advancedFormat.test.js b/test/plugin/advancedFormat.test.js index 955f94032..2905538f6 100644 --- a/test/plugin/advancedFormat.test.js +++ b/test/plugin/advancedFormat.test.js @@ -8,6 +8,7 @@ import weekYear from '../../src/plugin/weekYear' import timezone from '../../src/plugin/timezone' import utc from '../../src/plugin/utc' import '../../src/locale/zh-cn' +import '../../src/locale/nl' dayjs.extend(utc) dayjs.extend(timezone) @@ -51,12 +52,16 @@ it('Format Day of Month Do 1 - 31', () => { expect(dayjs(d).format('Do')).toBe(moment(d).format('Do')) d = '2018-05-04 00:00:00.000' expect(dayjs(d).format('Do')).toBe(moment(d).format('Do')) + d = '2018-05-08 00:00:00.000' + expect(dayjs(d).locale('nl').format('Do')).toBe(moment(d).locale('nl').format('Do')) d = '2018-05-11' expect(dayjs(d).format('Do')).toBe(moment(d).format('Do')) d = '2018-05-12' expect(dayjs(d).format('Do')).toBe(moment(d).format('Do')) d = '2018-05-13' expect(dayjs(d).format('Do')).toBe(moment(d).format('Do')) + d = '2018-05-19 00:00:00.000' + expect(dayjs(d).locale('nl').format('Do')).toBe(moment(d).locale('nl').format('Do')) d = '2018-05-22' expect(dayjs(d).format('Do')).toBe(moment(d).format('Do')) }) diff --git a/test/plugin/bigIntSupport.test.js b/test/plugin/bigIntSupport.test.js new file mode 100644 index 000000000..b2e70866f --- /dev/null +++ b/test/plugin/bigIntSupport.test.js @@ -0,0 +1,30 @@ +import MockDate from 'mockdate' +import moment from 'moment' +import dayjs from '../../src' +import bigIntSupport from '../../src/plugin/bigIntSupport' + +dayjs.extend(bigIntSupport) + +beforeEach(() => { + MockDate.set(new Date()) +}) + +afterEach(() => { + MockDate.reset() +}) + +/* global BigInt */ + +it('Parse BigInt ts and tsms', () => { + const tsms = 1666310421101 + const tsmsBig = BigInt(tsms) + const ts = 1666311003 + const tsBig = BigInt(ts) + const momentTsms = moment(tsms) + const momentTs = moment.unix(ts) + expect(dayjs(tsms).valueOf()).toBe(momentTsms.valueOf()) + expect(dayjs(tsms).valueOf()).toBe(dayjs(tsmsBig).valueOf()) + expect(dayjs.unix(ts).valueOf()).toBe(momentTs.valueOf()) + expect(dayjs.unix(tsBig).valueOf()).toBe(dayjs.unix(tsBig).valueOf()) +}) + diff --git a/test/plugin/duration.test.js b/test/plugin/duration.test.js index 2e804691c..3e726af91 100644 --- a/test/plugin/duration.test.js +++ b/test/plugin/duration.test.js @@ -67,6 +67,25 @@ describe('Creating', () => { ms: -1 }).toISOString()).toBe('-PT0.001S') }) + it('convert to milliseconds', () => { + expect(+dayjs.duration(100)).toBe(100) + }) + it('handles rounding to millisecond precision', () => { + expect(dayjs.duration(2 / 3).toISOString()).toBe('PT0.001S') + }) + it('should handle round with millisecond precision when negative', () => { + expect(dayjs.duration(1000.5).toISOString()).toBe('PT1.001S') + expect(dayjs.duration(-1000.5).toISOString()).toBe('-PT1S') + }) + it('should handle floating point rounding errors', () => { + // An example of this is when adding 2 to 0.812 seconds, which is how + // the seconds component is calculated in .toISOString(). + // > 2 + 0.812 + // 2.8120000000000003 + expect(dayjs.duration(-2812).toISOString()).toBe('-PT2.812S') // was -PT2.8120000000000003S + expect(dayjs.duration(3121632.27382247).toISOString()).toBe('PT52M1.632S') // was PT52M1.6320000000000001S + expect(dayjs.duration(7647826.525774224).toISOString()).toBe('PT2H7M27.827S') // was PT2H7M27.826999999999998S + }) }) describe('Parse ISO string', () => { @@ -82,8 +101,9 @@ describe('Parse ISO string', () => { it('ISO string with week', () => { const d = dayjs.duration('P2M3W4D') expect(d.toISOString()).toBe('P2M25D') - expect(d.asDays()).toBe(85) // moment 85, count 2M as 61 days - expect(d.asWeeks()).toBe(12.142857142857142) // moment 12.285714285714286 + expect(d.asDays()).toBe(85.83333333333333) // moment 86, count 2M as 61 days + expect(d.asWeeks()).toBe(12.261904761904763) // moment 12.285714285714286 + expect(d.asMonths()).toBe(2.8219178082191783) // moment 2.8213721020965523 }) it('Invalid ISO string', () => { expect(dayjs.duration('Invalid').toISOString()).toBe('P0D') @@ -175,10 +195,20 @@ describe('Add', () => { expect(a.add({ days: 5 }).days()).toBe(6) }) +describe('Add to a dayjs()', () => { + const a = dayjs() + const b = dayjs.duration({ hours: 7, minutes: 10 }) + expect(a.add(b)).toEqual(a.add(7, 'hours').add(10, 'minutes')) +}) + test('Add duration', () => { const a = dayjs('2020-10-01') const days = dayjs.duration(2, 'days') expect(a.add(days).format('YYYY-MM-DD')).toBe('2020-10-03') + + const b = dayjs('2023-02-01 00:00:00') + const p = dayjs.duration('P1Y1M1DT1H1M1S') + expect(b.add(p).format('YYYY-MM-DD HH:mm:ss')).toBe('2024-03-02 01:01:01') }) describe('Subtract', () => { @@ -191,6 +221,10 @@ test('Subtract duration', () => { const a = dayjs('2020-10-20') const days = dayjs.duration(2, 'days') expect(a.subtract(days).format('YYYY-MM-DD')).toBe('2020-10-18') + + const b = dayjs('2023-03-02 02:02:02') + const p = dayjs.duration('P1Y1M1DT1H1M1S') + expect(b.subtract(p).format('YYYY-MM-DD HH:mm:ss')).toBe('2022-02-01 01:01:01') }) describe('Seconds', () => { diff --git a/test/plugin/isSameOrAfter.test.js b/test/plugin/isSameOrAfter.test.js index fd504c2f8..f5533d763 100644 --- a/test/plugin/isSameOrAfter.test.js +++ b/test/plugin/isSameOrAfter.test.js @@ -49,6 +49,13 @@ test('is same or after without units', () => { expect(+m).toEqual(+mCopy, 'isSameOrAfter second should not change moment') }) +test('is same or after without date', () => { + const past = dayjs().subtract(1, 'day') + const future = dayjs().add(1, 'day') + expect(past.isSameOrAfter()).toBe(false, 'past is before now') + expect(future.isSameOrAfter()).toBe(true, 'future is not before now') +}) + test('is same or after month', () => { const m = dayjs(new Date(2011, 2, 3, 4, 5, 6, 7)) const mCopy = dayjs(m) diff --git a/test/plugin/isSameOrBefore.test.js b/test/plugin/isSameOrBefore.test.js index a8e18215a..3ceff55fa 100644 --- a/test/plugin/isSameOrBefore.test.js +++ b/test/plugin/isSameOrBefore.test.js @@ -34,6 +34,13 @@ test('is same or before without units', () => { expect(+m).toEqual(+mCopy, 'isSameOrBefore second should not change moment') }) +test('is same or before without date', () => { + const past = dayjs().subtract(1, 'day') + const future = dayjs().add(1, 'day') + expect(past.isSameOrBefore()).toBe(true, 'past is before now') + expect(future.isSameOrBefore()).toBe(false, 'future is not before now') +}) + test('is same or before year', () => { const m = dayjs(new Date(2011, 1, 2, 3, 4, 5, 6)) const mCopy = dayjs(m) diff --git a/test/plugin/minMax.test.js b/test/plugin/minMax.test.js index eeef92063..f8a508001 100644 --- a/test/plugin/minMax.test.js +++ b/test/plugin/minMax.test.js @@ -55,3 +55,17 @@ it('If Invalid Date return Invalid Date', () => { expect(dayjs.min([arg1, arg2, arg3, arg4]).format()) .toBe(arg4.format()) }) + +it('Ignore if exists an "null" argument', () => { + expect(dayjs.max(null, null, arg1, arg2, null, arg3).format()) + .toBe(arg1.format()) + expect(dayjs.min([null, null, arg1, arg2, null, arg3]).format()) + .toBe(arg3.format()) +}) + +it('Return the only date if just provided one argument', () => { + expect(dayjs.max(arg1).format()) + .toBe(arg1.format()) + expect(dayjs.min([arg1]).format()) + .toBe(arg1.format()) +}) diff --git a/test/plugin/objectSupport.test.js b/test/plugin/objectSupport.test.js index 06b10f9dc..abf046d34 100755 --- a/test/plugin/objectSupport.test.js +++ b/test/plugin/objectSupport.test.js @@ -140,6 +140,12 @@ it('Constructor from Object UTC', () => { expect(moment.utc(tests[i][0]).format(fmt)).toBe(result) } }) + +it('Constructor from null should return Invalid Date', () => { + expect(dayjs(null).isValid()).toBe(false) + expect(moment(null).isValid()).toBe(false) +}) + it('Set from Object', () => { for (let i = 0; i < tests.length; i += 1) { expect(dayjs(now).set(tests[i][0]).format(fmt)).toBe(moment(now).set(tests[i][0]).format(fmt)) @@ -384,3 +390,11 @@ it('add decimal values of days and months', () => { expect(dayjs([2016, 7, 1]).add(1.6, 'years').format('YYYY-MM-DD')).toBe('2017-07-01') expect(dayjs([2016, 1, 1]).add(1.1, 'quarters').format('YYYY-MM-DD')).toBe('2016-04-01') }) + +it('returns valid date on undefined', () => { + expect(dayjs().isValid()).toBe(true) +}) + +it('returns invalid date on null', () => { + expect(dayjs(null).isValid()).toBe(false) +}) diff --git a/test/plugin/timezone.test.js b/test/plugin/timezone.test.js index 2b28cc309..c4529c6c8 100644 --- a/test/plugin/timezone.test.js +++ b/test/plugin/timezone.test.js @@ -318,4 +318,15 @@ describe('startOf and endOf', () => { const endOfDay = originalDay.endOf('day') expect(endOfDay.valueOf()).toEqual(originalDay.valueOf()) }) + + it('preserves locality when tz is called', () => { + const tzWithoutLocality = dayjs.tz('2023-02-17 00:00:00', NY) + const tzWithLocality = dayjs.tz('2023-02-17 00:00:00', NY).locale({ + name: 'locale_test', + weekStart: 3 + }) + + expect(tzWithoutLocality.startOf('week').format('YYYY-MM-DD')).toEqual('2023-02-12') + expect(tzWithLocality.startOf('week').format('YYYY-MM-DD')).toEqual('2023-02-15') + }) }) diff --git a/types/index.d.ts b/types/index.d.ts index 1ac6c8dac..766bd7943 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -382,7 +382,7 @@ declare namespace dayjs { * * Docs: https://day.js.org/docs/en/query/is-before */ - isBefore(date: ConfigType, unit?: OpUnitType): boolean + isBefore(date?: ConfigType, unit?: OpUnitType): boolean /** * This indicates whether the Day.js object is the same as the other supplied date-time. * ``` @@ -394,7 +394,7 @@ declare namespace dayjs { * ``` * Docs: https://day.js.org/docs/en/query/is-same */ - isSame(date: ConfigType, unit?: OpUnitType): boolean + isSame(date?: ConfigType, unit?: OpUnitType): boolean /** * This indicates whether the Day.js object is after the other supplied date-time. * ``` @@ -408,7 +408,7 @@ declare namespace dayjs { * * Docs: https://day.js.org/docs/en/query/is-after */ - isAfter(date: ConfigType, unit?: OpUnitType): boolean + isAfter(date?: ConfigType, unit?: OpUnitType): boolean locale(): string diff --git a/types/plugin/bigIntSupport.d.ts b/types/plugin/bigIntSupport.d.ts new file mode 100644 index 000000000..d9f2f394e --- /dev/null +++ b/types/plugin/bigIntSupport.d.ts @@ -0,0 +1,11 @@ +import { PluginFunc } from 'dayjs' + +declare module 'dayjs' { + interface ConfigTypeMap { + bigIntSupport: BigInt + } + export function unix(t: BigInt): Dayjs +} + +declare const plugin: PluginFunc +export = plugin diff --git a/types/plugin/isSameOrAfter.d.ts b/types/plugin/isSameOrAfter.d.ts index 1c8c26497..916bc801c 100644 --- a/types/plugin/isSameOrAfter.d.ts +++ b/types/plugin/isSameOrAfter.d.ts @@ -5,6 +5,6 @@ export = plugin declare module 'dayjs' { interface Dayjs { - isSameOrAfter(date: ConfigType, unit?: OpUnitType): boolean + isSameOrAfter(date?: ConfigType, unit?: OpUnitType): boolean } } diff --git a/types/plugin/isSameOrBefore.d.ts b/types/plugin/isSameOrBefore.d.ts index 1df5492c0..d52b0955c 100644 --- a/types/plugin/isSameOrBefore.d.ts +++ b/types/plugin/isSameOrBefore.d.ts @@ -5,6 +5,6 @@ export = plugin declare module 'dayjs' { interface Dayjs { - isSameOrBefore(date: ConfigType, unit?: OpUnitType): boolean + isSameOrBefore(date?: ConfigType, unit?: OpUnitType): boolean } } diff --git a/types/plugin/isoWeek.d.ts b/types/plugin/isoWeek.d.ts index 26c591ba6..3f4d88f62 100644 --- a/types/plugin/isoWeek.d.ts +++ b/types/plugin/isoWeek.d.ts @@ -18,10 +18,10 @@ declare module 'dayjs' { endOf(unit: ISOUnitType): Dayjs - isSame(date: ConfigType, unit?: ISOUnitType): boolean + isSame(date?: ConfigType, unit?: ISOUnitType): boolean - isBefore(date: ConfigType, unit?: ISOUnitType): boolean + isBefore(date?: ConfigType, unit?: ISOUnitType): boolean - isAfter(date: ConfigType, unit?: ISOUnitType): boolean + isAfter(date?: ConfigType, unit?: ISOUnitType): boolean } } diff --git a/types/plugin/minMax.d.ts b/types/plugin/minMax.d.ts index f1673500c..4c5f6dcd7 100644 --- a/types/plugin/minMax.d.ts +++ b/types/plugin/minMax.d.ts @@ -4,8 +4,8 @@ declare const plugin: PluginFunc export = plugin declare module 'dayjs' { - export function max(dayjs: Dayjs[]): Dayjs - export function max(...dayjs: Dayjs[]): Dayjs - export function min(dayjs: Dayjs[]): Dayjs - export function min(...dayjs: Dayjs[]): Dayjs + export function max(dayjs: Dayjs[]): Dayjs | null + export function max(...dayjs: Dayjs[]): Dayjs | null + export function min(dayjs: Dayjs[]): Dayjs | null + export function min(...dayjs: Dayjs[]): Dayjs | null } diff --git a/types/plugin/objectSupport.d.ts b/types/plugin/objectSupport.d.ts index 0d3cf05b4..ad0e1ffea 100755 --- a/types/plugin/objectSupport.d.ts +++ b/types/plugin/objectSupport.d.ts @@ -9,4 +9,40 @@ declare module 'dayjs' { add(argument: object): Dayjs subtract(argument: object): Dayjs } + + interface ConfigTypeMap { + objectSupport: { + years?: number | string; + year?: number | string; + y?: number | string; + + months?: number | string; + month?: number | string; + M?: number | string; + + days?: number | string; + day?: number | string; + d?: number | string; + + dates?: number | string; + date?: number | string; + D?: number | string; + + hours?: number | string; + hour?: number | string; + h?: number | string; + + minutes?: number | string; + minute?: number | string; + m?: number | string; + + seconds?: number | string; + second?: number | string; + s?: number | string; + + milliseconds?: number | string; + millisecond?: number | string; + ms?: number | string; + } + } } diff --git a/types/plugin/quarterOfYear.d.ts b/types/plugin/quarterOfYear.d.ts index 8a9cc1e3d..317e0ad2d 100644 --- a/types/plugin/quarterOfYear.d.ts +++ b/types/plugin/quarterOfYear.d.ts @@ -17,10 +17,10 @@ declare module 'dayjs' { endOf(unit: QUnitType | OpUnitType): Dayjs - isSame(date: ConfigType, unit?: QUnitType): boolean + isSame(date?: ConfigType, unit?: QUnitType): boolean - isBefore(date: ConfigType, unit?: QUnitType): boolean + isBefore(date?: ConfigType, unit?: QUnitType): boolean - isAfter(date: ConfigType, unit?: QUnitType): boolean + isAfter(date?: ConfigType, unit?: QUnitType): boolean } } diff --git a/types/plugin/timezone.d.ts b/types/plugin/timezone.d.ts index d504f6927..049bb0870 100644 --- a/types/plugin/timezone.d.ts +++ b/types/plugin/timezone.d.ts @@ -10,7 +10,7 @@ declare module 'dayjs' { } interface DayjsTimezone { - (date: ConfigType, timezone?: string): Dayjs + (date?: ConfigType, timezone?: string): Dayjs (date: ConfigType, format: string, timezone?: string): Dayjs guess(): string setDefault(timezone?: string): void