diff --git a/.gitignore b/.gitignore index 8cd0df3..d24a0d7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ .vscode -.idea \ No newline at end of file +.idea +.magic +*.mojopkg \ No newline at end of file diff --git a/Makefile b/Makefile index e73b204..1e3f86c 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,11 @@ +install: + magic install + test: - mojo run test.mojo + magic run mojo run test.mojo format: - mojo format . + magic run mojo format morrow test.mojo build: - mojo package morrow -o morrow.mojopkg \ No newline at end of file + magic run mojo package morrow -o morrow.mojopkg \ No newline at end of file diff --git a/magic.lock b/magic.lock new file mode 100644 index 0000000..8b7f0e6 --- /dev/null +++ b/magic.lock @@ -0,0 +1,809 @@ +version: 5 +environments: + default: + channels: + - url: https://conda.anaconda.org/conda-forge/ + - url: https://conda.modular.com/max/ + packages: + osx-arm64: + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h99b78c6_7.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ca-certificates-2024.8.30-hf0a4a13_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.7.2-pyh31011fe_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.9.0-24_osxarm64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.9.0-24_osxarm64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-19.1.0-ha82da77_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20191231-hc8eb9b7_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.6.3-hf9b8971_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.4.2-h3422bc3_5.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-5.0.0-13_2_0_hd922786_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-13.2.0-hf226fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.9.0-24_osxarm64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.27-openmp_h517c56d_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.20-h99b78c6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.46.1-hc14010f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-hfb2fe0b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-18.1.8-hde57baf_1.conda + - conda: https://conda.modular.com/max/noarch/max-24.5.0-release.conda + - conda: https://conda.modular.com/max/osx-arm64/max-core-24.5.0-release.conda + - conda: https://conda.modular.com/max/osx-arm64/max-python-24.5.0-3.12release.conda + - conda: https://conda.modular.com/max/noarch/mblack-24.5.0-release.conda + - conda: https://conda.modular.com/max/noarch/mojo-jupyter-24.5.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h7bae524_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.26.4-py312h8442bc7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.3.2-h8359307_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.6-h739c21a_1_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python_abi-3.12-5_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-26.2.0-py312hc6335d2_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.2-h92ec313_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h5083fa2_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.4.1-py312h024a12e_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024a-h8827d51_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xz-5.2.6-h57fd34a_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zeromq-4.3.5-h64debc3_5.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.20.2-pyhd8ed1ab_0.conda +packages: +- kind: conda + name: bzip2 + version: 1.0.8 + build: h99b78c6_7 + build_number: 7 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h99b78c6_7.conda + sha256: adfa71f158cbd872a36394c56c3568e6034aa55c623634b37a4836bd036e6b91 + md5: fc6948412dbbbe9a4c9ddbbcfe0a79ab + depends: + - __osx >=11.0 + license: bzip2-1.0.6 + license_family: BSD + size: 122909 + timestamp: 1720974522888 +- kind: conda + name: ca-certificates + version: 2024.8.30 + build: hf0a4a13_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/ca-certificates-2024.8.30-hf0a4a13_0.conda + sha256: 2db1733f4b644575dbbdd7994a8f338e6ef937f5ebdb74acd557e9dda0211709 + md5: 40dec13fd8348dbe303e57be74bd3d35 + license: ISC + size: 158482 + timestamp: 1725019034582 +- kind: conda + name: click + version: 8.1.7 + build: unix_pyh707e725_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_0.conda + sha256: f0016cbab6ac4138a429e28dbcb904a90305b34b3fe41a9b89d697c90401caec + md5: f3ad426304898027fc619827ff428eca + depends: + - __unix + - python >=3.8 + license: BSD-3-Clause + license_family: BSD + size: 84437 + timestamp: 1692311973840 +- kind: conda + name: importlib-metadata + version: 8.5.0 + build: pyha770c72_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_0.conda + sha256: 7194700ce1a5ad2621fd68e894dd8c1ceaff9a38723e6e0e5298fdef13017b1c + md5: 54198435fce4d64d8a89af22573012a8 + depends: + - python >=3.8 + - zipp >=0.5 + license: Apache-2.0 + license_family: APACHE + size: 28646 + timestamp: 1726082927916 +- kind: conda + name: jupyter_client + version: 8.6.3 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_0.conda + sha256: 4419c85e209a715f551a5c9bead746f29ee9d0fc41e772a76db3868622795671 + md5: a14218cfb29662b4a19ceb04e93e298e + depends: + - importlib-metadata >=4.8.3 + - jupyter_core >=4.12,!=5.0.* + - python >=3.8 + - python-dateutil >=2.8.2 + - pyzmq >=23.0 + - tornado >=6.2 + - traitlets >=5.3 + license: BSD-3-Clause + license_family: BSD + size: 106055 + timestamp: 1726610805505 +- kind: conda + name: jupyter_core + version: 5.7.2 + build: pyh31011fe_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.7.2-pyh31011fe_1.conda + sha256: 732b1e8536bc22a5a174baa79842d79db2f4956d90293dd82dc1b3f6099bcccd + md5: 0a2980dada0dd7fd0998f0342308b1b1 + depends: + - __unix + - platformdirs >=2.5 + - python >=3.8 + - traitlets >=5.3 + license: BSD-3-Clause + license_family: BSD + size: 57671 + timestamp: 1727163547058 +- kind: conda + name: krb5 + version: 1.21.3 + build: h237132a_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda + sha256: 4442f957c3c77d69d9da3521268cad5d54c9033f1a73f99cde0a3658937b159b + md5: c6dc8a0fdec13a0565936655c33069a1 + depends: + - __osx >=11.0 + - libcxx >=16 + - libedit >=3.1.20191231,<3.2.0a0 + - libedit >=3.1.20191231,<4.0a0 + - openssl >=3.3.1,<4.0a0 + license: MIT + license_family: MIT + size: 1155530 + timestamp: 1719463474401 +- kind: conda + name: libblas + version: 3.9.0 + build: 24_osxarm64_openblas + build_number: 24 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.9.0-24_osxarm64_openblas.conda + sha256: 4739f7463efb12e6d71536d8b0285a8de5aaadcc442bfedb9d92d1b4cbc47847 + md5: 35cb711e7bc46ee5f3dd67af99ad1986 + depends: + - libopenblas >=0.3.27,<0.3.28.0a0 + - libopenblas >=0.3.27,<1.0a0 + constrains: + - liblapack 3.9.0 24_osxarm64_openblas + - blas * openblas + - liblapacke 3.9.0 24_osxarm64_openblas + - libcblas 3.9.0 24_osxarm64_openblas + license: BSD-3-Clause + license_family: BSD + size: 15144 + timestamp: 1726668802976 +- kind: conda + name: libcblas + version: 3.9.0 + build: 24_osxarm64_openblas + build_number: 24 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.9.0-24_osxarm64_openblas.conda + sha256: 40dc3f7c44af5cd5a2020386cb30f92943a9d8f7f54321b4d6ae32b2e54af9a4 + md5: c8977086a19233153e454bb2b332a920 + depends: + - libblas 3.9.0 24_osxarm64_openblas + constrains: + - liblapack 3.9.0 24_osxarm64_openblas + - blas * openblas + - liblapacke 3.9.0 24_osxarm64_openblas + license: BSD-3-Clause + license_family: BSD + size: 15062 + timestamp: 1726668809379 +- kind: conda + name: libcxx + version: 19.1.0 + build: ha82da77_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-19.1.0-ha82da77_0.conda + sha256: b71167d9b7c8e598b12bbdafefd0139e3c70c6eb258cbda3de3fb422d0098025 + md5: a4c66c0d5b0f268fd27a956145004d27 + depends: + - __osx >=11.0 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + size: 520766 + timestamp: 1726782571130 +- kind: conda + name: libedit + version: 3.1.20191231 + build: hc8eb9b7_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20191231-hc8eb9b7_2.tar.bz2 + sha256: 3912636197933ecfe4692634119e8644904b41a58f30cad9d1fc02f6ba4d9fca + md5: 30e4362988a2623e9eb34337b83e01f9 + depends: + - ncurses >=6.2,<7.0.0a0 + license: BSD-2-Clause + license_family: BSD + size: 96607 + timestamp: 1597616630749 +- kind: conda + name: libexpat + version: 2.6.3 + build: hf9b8971_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.6.3-hf9b8971_0.conda + sha256: 5cbe5a199fba14ade55457a468ce663aac0b54832c39aa54470b3889b4c75c4a + md5: 5f22f07c2ab2dea8c66fe9585a062c96 + depends: + - __osx >=11.0 + constrains: + - expat 2.6.3.* + license: MIT + license_family: MIT + size: 63895 + timestamp: 1725568783033 +- kind: conda + name: libffi + version: 3.4.2 + build: h3422bc3_5 + build_number: 5 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.4.2-h3422bc3_5.tar.bz2 + sha256: 41b3d13efb775e340e4dba549ab5c029611ea6918703096b2eaa9c015c0750ca + md5: 086914b672be056eb70fd4285b6783b6 + license: MIT + license_family: MIT + size: 39020 + timestamp: 1636488587153 +- kind: conda + name: libgfortran + version: 5.0.0 + build: 13_2_0_hd922786_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-5.0.0-13_2_0_hd922786_3.conda + sha256: 44e541b4821c96b28b27fef5630883a60ce4fee91fd9c79f25a199f8f73f337b + md5: 4a55d9e169114b2b90d3ec4604cd7bbf + depends: + - libgfortran5 13.2.0 hf226fd6_3 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 110233 + timestamp: 1707330749033 +- kind: conda + name: libgfortran5 + version: 13.2.0 + build: hf226fd6_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-13.2.0-hf226fd6_3.conda + sha256: bafc679eedb468a86aa4636061c55966186399ee0a04b605920d208d97ac579a + md5: 66ac81d54e95c534ae488726c1f698ea + depends: + - llvm-openmp >=8.0.0 + constrains: + - libgfortran 5.0.0 13_2_0_*_3 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 997381 + timestamp: 1707330687590 +- kind: conda + name: liblapack + version: 3.9.0 + build: 24_osxarm64_openblas + build_number: 24 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.9.0-24_osxarm64_openblas.conda + sha256: 67fbfd0466eee443cda9596ed22daabedc96b7b4d1b31f49b1c1b0983dd1dd2c + md5: 49a3241f76cdbe705e346204a328f66c + depends: + - libblas 3.9.0 24_osxarm64_openblas + constrains: + - blas * openblas + - liblapacke 3.9.0 24_osxarm64_openblas + - libcblas 3.9.0 24_osxarm64_openblas + license: BSD-3-Clause + license_family: BSD + size: 15063 + timestamp: 1726668815824 +- kind: conda + name: libopenblas + version: 0.3.27 + build: openmp_h517c56d_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.27-openmp_h517c56d_1.conda + sha256: 46cfcc592b5255262f567cd098be3c61da6bca6c24d640e878dc8342b0f6d069 + md5: 71b8a34d70aa567a990162f327e81505 + depends: + - __osx >=11.0 + - libgfortran 5.* + - libgfortran5 >=12.3.0 + - llvm-openmp >=16.0.6 + constrains: + - openblas >=0.3.27,<0.3.28.0a0 + license: BSD-3-Clause + license_family: BSD + size: 2925328 + timestamp: 1720425811743 +- kind: conda + name: libsodium + version: 1.0.20 + build: h99b78c6_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.20-h99b78c6_0.conda + sha256: fade8223e1e1004367d7101dd17261003b60aa576df6d7802191f8972f7470b1 + md5: a7ce36e284c5faaf93c220dfc39e3abd + depends: + - __osx >=11.0 + license: ISC + size: 164972 + timestamp: 1716828607917 +- kind: conda + name: libsqlite + version: 3.46.1 + build: hc14010f_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.46.1-hc14010f_0.conda + sha256: 3725f962f490c5d44dae326d5f5b2e3c97f71a6322d914ccc85b5ddc2e50d120 + md5: 58050ec1724e58668d0126a1615553fa + depends: + - __osx >=11.0 + - libzlib >=1.3.1,<2.0a0 + license: Unlicense + size: 829500 + timestamp: 1725353720793 +- kind: conda + name: libzlib + version: 1.3.1 + build: hfb2fe0b_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-hfb2fe0b_1.conda + sha256: c34365dd37b0eab27b9693af32a1f7f284955517c2cc91f1b88a7ef4738ff03e + md5: 636077128927cf79fd933276dc3aed47 + depends: + - __osx >=11.0 + constrains: + - zlib 1.3.1 *_1 + license: Zlib + license_family: Other + size: 46921 + timestamp: 1716874262512 +- kind: conda + name: llvm-openmp + version: 18.1.8 + build: hde57baf_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-18.1.8-hde57baf_1.conda + sha256: 7a76e2932ac77e6314bfa1c4ff83f617c8260313bfed1b8401b508ed3e9d70ba + md5: fe89757e3cd14bb1c6ebd68dac591363 + depends: + - __osx >=11.0 + constrains: + - openmp 18.1.8|18.1.8.* + license: Apache-2.0 WITH LLVM-exception + license_family: APACHE + size: 276263 + timestamp: 1723605341828 +- kind: conda + name: max + version: 24.5.0 + build: release + subdir: noarch + noarch: python + url: https://conda.modular.com/max/noarch/max-24.5.0-release.conda + sha256: 3050d7885a304944afbf93ca9786e56e6df20f0685e1705f88fab045fb5aae70 + md5: 662a61803cd141e857d3b9f821c7bd66 + depends: + - max-core ==24.5.0 release + - max-python >=24.5.0,<25.0a0 + - mojo-jupyter ==24.5.0 release + - mblack ==24.5.0 release + size: 9642 + timestamp: 1726172475909 +- kind: conda + name: max-core + version: 24.5.0 + build: release + subdir: osx-arm64 + url: https://conda.modular.com/max/osx-arm64/max-core-24.5.0-release.conda + sha256: 8848071dde1f98a4da8e39c90f9210098e7c3c4aaddd0e2255fd9fe1f01df0b7 + md5: fba502bf5142da57735a593ccf35a255 + depends: + - mblack ==24.5.0 release + arch: arm64 + platform: osx + size: 244231803 + timestamp: 1726175523753 +- kind: conda + name: max-python + version: 24.5.0 + build: 3.12release + subdir: osx-arm64 + url: https://conda.modular.com/max/osx-arm64/max-python-24.5.0-3.12release.conda + sha256: e6cdd0477236d49d4f6586d4a66ffe1c5e5cb188535a8ec09ed742eda12cbf5f + md5: f33d8f4cc5c17d893fdb5d6e162c08c6 + depends: + - max-core ==24.5.0 release + - python 3.12.* + - numpy >=1.18,<2.0 + - python_abi 3.12.* *_cp312 + arch: arm64 + platform: osx + size: 125388933 + timestamp: 1726175523755 +- kind: conda + name: mblack + version: 24.5.0 + build: release + subdir: noarch + noarch: python + url: https://conda.modular.com/max/noarch/mblack-24.5.0-release.conda + sha256: 913881fc3aa19db447ed82e898f261a413be9129dc43b9ea600e06030f76dbd5 + md5: 2bc6ce9f257235686dc1b2509cc7198d + depends: + - python >=3.9,<3.13 + - click >=8.0.0 + - mypy_extensions >=0.4.3 + - packaging >=22.0 + - pathspec >=0.9.0 + - platformdirs >=2 + - python + license: MIT + size: 130435 + timestamp: 1726172475910 +- kind: conda + name: mojo-jupyter + version: 24.5.0 + build: release + subdir: noarch + noarch: python + url: https://conda.modular.com/max/noarch/mojo-jupyter-24.5.0-release.conda + sha256: dff2e857eae32ce92fde12a712756d647f0aa312aeb5d79b350b2acbc71a2f96 + md5: 3b7be5cbff5b8015b095e950506be4b3 + depends: + - max-core ==24.5.0 release + - python >=3.9,<3.13 + - jupyter_client >=8.6.2,<8.7 + - python + size: 21595 + timestamp: 1726172475911 +- kind: conda + name: mypy_extensions + version: 1.0.0 + build: pyha770c72_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda + sha256: f240217476e148e825420c6bc3a0c0efb08c0718b7042fae960400c02af858a3 + md5: 4eccaeba205f0aed9ac3a9ea58568ca3 + depends: + - python >=3.5 + license: MIT + license_family: MIT + size: 10492 + timestamp: 1675543414256 +- kind: conda + name: ncurses + version: '6.5' + build: h7bae524_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h7bae524_1.conda + sha256: 27d0b9ff78ad46e1f3a6c96c479ab44beda5f96def88e2fe626e0a49429d8afc + md5: cb2b0ea909b97b3d70cd3921d1445e1a + depends: + - __osx >=11.0 + license: X11 AND BSD-3-Clause + size: 802321 + timestamp: 1724658775723 +- kind: conda + name: numpy + version: 1.26.4 + build: py312h8442bc7_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.26.4-py312h8442bc7_0.conda + sha256: c8841d6d6f61fd70ca80682efbab6bdb8606dc77c68d8acabfbd7c222054f518 + md5: d83fc83d589e2625a3451c9a7e21047c + depends: + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libcxx >=16 + - liblapack >=3.9.0,<4.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + size: 6073136 + timestamp: 1707226249608 +- kind: conda + name: openssl + version: 3.3.2 + build: h8359307_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.3.2-h8359307_0.conda + sha256: 940fa01c4dc6152158fe8943e05e55a1544cab639df0994e3b35937839e4f4d1 + md5: 1773ebccdc13ec603356e8ff1db9e958 + depends: + - __osx >=11.0 + - ca-certificates + license: Apache-2.0 + license_family: Apache + size: 2882450 + timestamp: 1725410638874 +- kind: conda + name: packaging + version: '24.1' + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda + sha256: 36aca948219e2c9fdd6d80728bcc657519e02f06c2703d8db3446aec67f51d81 + md5: cbe1bb1f21567018ce595d9c2be0f0db + depends: + - python >=3.8 + license: Apache-2.0 + license_family: APACHE + size: 50290 + timestamp: 1718189540074 +- kind: conda + name: pathspec + version: 0.12.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_0.conda + sha256: 4e534e66bfe8b1e035d2169d0e5b185450546b17e36764272863e22e0370be4d + md5: 17064acba08d3686f1135b5ec1b32b12 + depends: + - python >=3.7 + license: MPL-2.0 + license_family: MOZILLA + size: 41173 + timestamp: 1702250135032 +- kind: conda + name: platformdirs + version: 4.3.6 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_0.conda + sha256: c81bdeadc4adcda216b2c7b373f0335f5c78cc480d1d55d10f21823590d7e46f + md5: fd8f2b18b65bbf62e8f653100690c8d2 + depends: + - python >=3.8 + license: MIT + license_family: MIT + size: 20625 + timestamp: 1726613611845 +- kind: conda + name: python + version: 3.12.6 + build: h739c21a_1_cpython + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.6-h739c21a_1_cpython.conda + sha256: 99e0b806062b2a4be3016d9a6d253d85e25b5f9ee6bebe442dec6fd6759288f3 + md5: 5beefd0212cdea661f999f0ec29a2e3a + depends: + - __osx >=11.0 + - bzip2 >=1.0.8,<2.0a0 + - libexpat >=2.6.3,<3.0a0 + - libffi >=3.4,<4.0a0 + - libsqlite >=3.46.1,<4.0a0 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.3.2,<4.0a0 + - readline >=8.2,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + - xz >=5.2.6,<6.0a0 + constrains: + - python_abi 3.12.* *_cp312 + license: Python-2.0 + size: 12852860 + timestamp: 1727014294263 +- kind: conda + name: python-dateutil + version: 2.9.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda + sha256: f3ceef02ac164a8d3a080d0d32f8e2ebe10dd29e3a685d240e38b3599e146320 + md5: 2cf4264fffb9e6eff6031c5b6884d61c + depends: + - python >=3.7 + - six >=1.5 + license: Apache-2.0 + license_family: APACHE + size: 222742 + timestamp: 1709299922152 +- kind: conda + name: python_abi + version: '3.12' + build: 5_cp312 + build_number: 5 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/python_abi-3.12-5_cp312.conda + sha256: 49d624e4b809c799d2bf257b22c23cf3fc4460f5570d9a58e7ad86350aeaa1f4 + md5: b76f9b1c862128e56ac7aa8cd2333de9 + constrains: + - python 3.12.* *_cpython + license: BSD-3-Clause + license_family: BSD + size: 6278 + timestamp: 1723823099686 +- kind: conda + name: pyzmq + version: 26.2.0 + build: py312hc6335d2_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-26.2.0-py312hc6335d2_2.conda + sha256: 8d46c0f1af50989f308b9da68e6123bc3560f3a3a741b4e7cb8867c603b5a9f1 + md5: ca61d76f24d66c2938af62e882c9a02d + depends: + - __osx >=11.0 + - libcxx >=17 + - libsodium >=1.0.20,<1.0.21.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - zeromq >=4.3.5,<4.4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 359594 + timestamp: 1725449428595 +- kind: conda + name: readline + version: '8.2' + build: h92ec313_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.2-h92ec313_1.conda + sha256: a1dfa679ac3f6007362386576a704ad2d0d7a02e98f5d0b115f207a2da63e884 + md5: 8cbb776a2f641b943d413b3e19df71f4 + depends: + - ncurses >=6.3,<7.0a0 + license: GPL-3.0-only + license_family: GPL + size: 250351 + timestamp: 1679532511311 +- kind: conda + name: six + version: 1.16.0 + build: pyh6c4a22f_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2 + sha256: a85c38227b446f42c5b90d9b642f2c0567880c15d72492d8da074a59c8f91dd6 + md5: e5f25f8dbc060e9a8d912e432202afc2 + depends: + - python + license: MIT + license_family: MIT + size: 14259 + timestamp: 1620240338595 +- kind: conda + name: tk + version: 8.6.13 + build: h5083fa2_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h5083fa2_1.conda + sha256: 72457ad031b4c048e5891f3f6cb27a53cb479db68a52d965f796910e71a403a8 + md5: b50a57ba89c32b62428b71a875291c9b + depends: + - libzlib >=1.2.13,<2.0.0a0 + license: TCL + license_family: BSD + size: 3145523 + timestamp: 1699202432999 +- kind: conda + name: tornado + version: 6.4.1 + build: py312h024a12e_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.4.1-py312h024a12e_1.conda + sha256: 5eefede1d8a2f55892bc582dbcb574b1806f19bc1e3939ce56b79721b9406db7 + md5: 967bc97bb9e258993289546479af971f + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + size: 841722 + timestamp: 1724956439106 +- kind: conda + name: traitlets + version: 5.14.3 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_0.conda + sha256: 8a64fa0f19022828513667c2c7176cfd125001f3f4b9bc00d33732e627dd2592 + md5: 3df84416a021220d8b5700c613af2dc5 + depends: + - python >=3.8 + license: BSD-3-Clause + license_family: BSD + size: 110187 + timestamp: 1713535244513 +- kind: conda + name: tzdata + version: 2024a + build: h8827d51_1 + build_number: 1 + subdir: noarch + noarch: generic + url: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024a-h8827d51_1.conda + sha256: 7d21c95f61319dba9209ca17d1935e6128af4235a67ee4e57a00908a1450081e + md5: 8bfdead4e0fff0383ae4c9c50d0531bd + license: LicenseRef-Public-Domain + size: 124164 + timestamp: 1724736371498 +- kind: conda + name: xz + version: 5.2.6 + build: h57fd34a_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/xz-5.2.6-h57fd34a_0.tar.bz2 + sha256: 59d78af0c3e071021cfe82dc40134c19dab8cdf804324b62940f5c8cd71803ec + md5: 39c6b54e94014701dd157f4f576ed211 + license: LGPL-2.1 and GPL-2.0 + size: 235693 + timestamp: 1660346961024 +- kind: conda + name: zeromq + version: 4.3.5 + build: h64debc3_5 + build_number: 5 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/zeromq-4.3.5-h64debc3_5.conda + sha256: b4ba544a04129472651a5df3b8906ed68e7f43bf23e724fd0e368218083c920c + md5: c29dbe9343a0b55b027fa645644c59d9 + depends: + - __osx >=11.0 + - krb5 >=1.21.3,<1.22.0a0 + - libcxx >=17 + - libsodium >=1.0.20,<1.0.21.0a0 + license: MPL-2.0 + license_family: MOZILLA + size: 296355 + timestamp: 1725430145243 +- kind: conda + name: zipp + version: 3.20.2 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/zipp-3.20.2-pyhd8ed1ab_0.conda + sha256: 1e84fcfa41e0afdd87ff41e6fbb719c96a0e098c1f79be342293ab0bd8dea322 + md5: 4daaed111c05672ae669f7036ee5bba3 + depends: + - python >=3.8 + license: MIT + license_family: MIT + size: 21409 + timestamp: 1726248679175 diff --git a/mojoproject.toml b/mojoproject.toml new file mode 100644 index 0000000..d7cc270 --- /dev/null +++ b/mojoproject.toml @@ -0,0 +1,12 @@ +[project] +authors = ["Prodesire ", "Shalei "] +channels = ["conda-forge", "https://conda.modular.com/max"] +description = "Morrow provides human-friendly method for managing, formatting, and transforming dates, times, and timestamps." +name = "morrow.mojo" +platforms = ["osx-arm64"] +version = "0.1.0" + +[tasks] + +[dependencies] +max = ">=24.5.0,<25" diff --git a/morrow/_libc.mojo b/morrow/_libc.mojo index f9af241..a70b0e3 100644 --- a/morrow/_libc.mojo +++ b/morrow/_libc.mojo @@ -1,6 +1,7 @@ -from memory.unsafe import Pointer - +from memory import UnsafePointer +from sys.ffi import external_call +# C type aliases alias c_void = UInt8 alias c_char = UInt8 alias c_schar = Int8 @@ -18,77 +19,86 @@ alias c_double = Float64 @value @register_passable("trivial") struct CTimeval: + """Represents the C struct timeval.""" + var tv_sec: Int # Seconds var tv_usec: Int # Microseconds - fn __init__(tv_sec: Int = 0, tv_usec: Int = 0) -> Self: - return Self {tv_sec: tv_sec, tv_usec: tv_usec} + fn __init__(inout self, tv_sec: Int = 0, tv_usec: Int = 0): + self.tv_sec = tv_sec + self.tv_usec = tv_usec @value @register_passable("trivial") struct CTm: - var tm_sec: Int32 # Seconds - var tm_min: Int32 # Minutes - var tm_hour: Int32 # Hour - var tm_mday: Int32 # Day of the month - var tm_mon: Int32 # Month - var tm_year: Int32 # Year minus 1900 - var tm_wday: Int32 # Day of the week - var tm_yday: Int32 # Day of the year - var tm_isdst: Int32 # Daylight savings flag - var tm_gmtoff: Int64 # localtime zone offset seconds - - fn __init__() -> Self: - return Self { - tm_sec: 0, - tm_min: 0, - tm_hour: 0, - tm_mday: 0, - tm_mon: 0, - tm_year: 0, - tm_wday: 0, - tm_yday: 0, - tm_isdst: 0, - tm_gmtoff: 0, - } + """Represents the C struct tm for date and time information.""" + + var tm_sec: c_int # Seconds + var tm_min: c_int # Minutes + var tm_hour: c_int # Hour + var tm_mday: c_int # Day of the month + var tm_mon: c_int # Month + var tm_year: c_int # Year minus 1900 + var tm_wday: c_int # Day of the week + var tm_yday: c_int # Day of the year + var tm_isdst: c_int # Daylight savings flag + var tm_gmtoff: c_long # localtime zone offset seconds + + fn __init__(inout self): + self.tm_sec = 0 + self.tm_min = 0 + self.tm_hour = 0 + self.tm_mday = 0 + self.tm_mon = 0 + self.tm_year = 0 + self.tm_wday = 0 + self.tm_yday = 0 + self.tm_isdst = 0 + self.tm_gmtoff = 0 @always_inline fn c_gettimeofday() -> CTimeval: + """Wrapper for the C function gettimeofday.""" var tv = CTimeval() - var p_tv = Pointer[CTimeval].address_of(tv) - external_call["gettimeofday", NoneType, Pointer[CTimeval], Int32](p_tv, 0) + external_call["gettimeofday", NoneType](Reference(tv), 0) return tv @always_inline fn c_localtime(owned tv_sec: Int) -> CTm: - var p_tv_sec = Pointer[Int].address_of(tv_sec) - var tm = external_call["localtime", Pointer[CTm], Pointer[Int]](p_tv_sec).load() + """Wrapper for the C function localtime.""" + var tm = CTm() + _ = external_call["localtime_r", Reference[CTm]]( + Reference(tv_sec), Reference(tm) + ) return tm @always_inline fn c_strptime(time_str: String, time_format: String) -> CTm: + """Wrapper for the C function strptime.""" var tm = CTm() - var p_tm = Pointer[CTm].address_of(tm) - external_call["strptime", NoneType, Pointer[c_char], Pointer[c_char], Pointer[CTm]]( - to_char_ptr(time_str), to_char_ptr(time_format), p_tm + _ = external_call["strptime", Reference[String]]( + time_str.unsafe_ptr(), time_format.unsafe_ptr(), Reference(tm) ) return tm @always_inline fn c_gmtime(owned tv_sec: Int) -> CTm: - var p_tv_sec = Pointer[Int].address_of(tv_sec) - var tm = external_call["gmtime", Pointer[CTm], Pointer[Int]](p_tv_sec).load() + """Wrapper for the C function gmtime.""" + var tm = CTm() + _ = external_call["gmtime_r", Reference[CTm]]( + Reference(tv_sec), Reference(tm) + ) return tm -fn to_char_ptr(s: String) -> Pointer[c_char]: +fn to_char_ptr(s: String) -> UnsafePointer[c_char]: """Only ASCII-based strings.""" - var ptr = Pointer[c_char]().alloc(len(s)) + var ptr = UnsafePointer[c_char]().alloc(len(s)) for i in range(len(s)): ptr.store(i, ord(s[i])) return ptr diff --git a/morrow/_py.mojo b/morrow/_py.mojo index 1e02f8e..471909a 100644 --- a/morrow/_py.mojo +++ b/morrow/_py.mojo @@ -1,4 +1,4 @@ -from python import Python +from python import Python, PythonObject fn py_dt_datetime() raises -> PythonObject: diff --git a/morrow/formatter.mojo b/morrow/formatter.mojo index 4a35054..b72cdfe 100644 --- a/morrow/formatter.mojo +++ b/morrow/formatter.mojo @@ -1,7 +1,11 @@ from collections.vector import InlinedFixedVector from utils.static_tuple import StaticTuple -from .util import rjust -from .constants import MONTH_NAMES, MONTH_ABBREVIATIONS, DAY_NAMES, DAY_ABBREVIATIONS +from .constants import ( + MONTH_NAMES, + MONTH_ABBREVIATIONS, + DAY_NAMES, + DAY_ABBREVIATIONS, +) from .timezone import UTC_TZ alias formatter = _Formatter() @@ -88,71 +92,73 @@ struct _Formatter: ret += self.replace_token(m, match_chr_ord, match_count) return ret - fn replace_token(self, m: Morrow, token: Int, token_count: Int) raises -> String: + fn replace_token( + self, m: Morrow, token: Int, token_count: Int + ) raises -> String: if token == _Y: if token_count == 1: return "Y" if token_count == 2: - return rjust(m.year, 4, "0")[2:4] + return str(m.year).rjust(4, "0")[2:4] if token_count == 4: - return rjust(m.year, 4, "0") + return str(m.year).rjust(4, "0") elif token == _M: if token_count == 1: - return String(m.month) + return str(m.month) if token_count == 2: - return rjust(m.month, 2, "0") + return str(m.month).rjust(2, "0") if token_count == 3: - return String(MONTH_ABBREVIATIONS[m.month]) + return str(MONTH_ABBREVIATIONS[m.month]) if token_count == 4: - return String(MONTH_NAMES[m.month]) + return str(MONTH_NAMES[m.month]) elif token == _D: if token_count == 1: - return String(m.day) + return str(m.day) if token_count == 2: - return rjust(m.day, 2, "0") + return str(m.day).rjust(2, "0") elif token == _H: if token_count == 1: - return String(m.hour) + return str(m.hour) if token_count == 2: - return rjust(m.hour, 2, "0") + return str(m.hour).rjust(2, "0") elif token == _h: var h_12 = m.hour if m.hour > 12: h_12 -= 12 if token_count == 1: - return String(h_12) + return str(h_12) if token_count == 2: - return rjust(h_12, 2, "0") + return str(h_12).rjust(2, "0") elif token == _m: if token_count == 1: - return String(m.minute) + return str(m.minute) if token_count == 2: - return rjust(m.minute, 2, "0") + return str(m.minute).rjust(2, "0") elif token == _s: if token_count == 1: - return String(m.second) + return str(m.second) if token_count == 2: - return rjust(m.second, 2, "0") + return str(m.second).rjust(2, "0") elif token == _S: if token_count == 1: - return String(m.microsecond // 100000) + return str(m.microsecond // 100000) if token_count == 2: - return rjust(m.microsecond // 10000, 2, "0") + return str(m.microsecond // 10000).rjust(2, "0") if token_count == 3: - return rjust(m.microsecond // 1000, 3, "0") + return str(m.microsecond // 1000).rjust(3, "0") if token_count == 4: - return rjust(m.microsecond // 100, 4, "0") + return str(m.microsecond // 100).rjust(4, "0") if token_count == 5: - return rjust(m.microsecond // 10, 5, "0") + return str(m.microsecond // 10).rjust(5, "0") if token_count == 6: - return rjust(m.microsecond, 6, "0") + return str(m.microsecond).rjust(6, "0") elif token == _d: if token_count == 1: - return String(m.isoweekday()) + return str(m.isoweekday()) if token_count == 3: - return String(DAY_ABBREVIATIONS[m.isoweekday()]) + return str(DAY_ABBREVIATIONS[m.isoweekday()]) if token_count == 4: - return String(DAY_NAMES[m.isoweekday()]) + return str(DAY_NAMES[m.isoweekday()]) elif token == _Z: if token_count == 3: return UTC_TZ.name if m.tz.is_none() else m.tz.name diff --git a/morrow/morrow.mojo b/morrow/morrow.mojo index cda506a..6efad37 100644 --- a/morrow/morrow.mojo +++ b/morrow/morrow.mojo @@ -1,12 +1,12 @@ from ._py import py_dt_datetime -from .util import normalize_timestamp, rjust, _ymd2ord, _days_before_year +from .util import normalize_timestamp, _ymd2ord, _days_before_year from ._libc import c_gettimeofday, c_localtime, c_gmtime, c_strptime from ._libc import CTimeval, CTm from .timezone import TimeZone from .timedelta import TimeDelta from .formatter import formatter from .constants import _DAYS_BEFORE_MONTH, _DAYS_IN_MONTH -from python.object import PythonObject +from python import PythonObject from python import Python @@ -36,7 +36,7 @@ struct Morrow(StringableRaising): second: Int = 0, microsecond: Int = 0, tz: TimeZone = TimeZone.none(), - ) raises: + ): self.year = year self.month = month self.day = day @@ -47,17 +47,17 @@ struct Morrow(StringableRaising): self.tz = tz @staticmethod - fn now() raises -> Self: + fn now() -> Self: var t = c_gettimeofday() return Self._fromtimestamp(t, False) @staticmethod - fn utcnow() raises -> Self: + fn utcnow() -> Self: var t = c_gettimeofday() return Self._fromtimestamp(t, True) @staticmethod - fn _fromtimestamp(t: CTimeval, utc: Bool) raises -> Self: + fn _fromtimestamp(t: CTimeval, utc: Bool) -> Self: var tm: CTm var tz: TimeZone if utc: @@ -94,7 +94,7 @@ struct Morrow(StringableRaising): @staticmethod fn strptime( date_str: String, fmt: String, tzinfo: TimeZone = TimeZone.none() - ) raises -> Self: + ) -> Self: """ Create a Morrow instance from a date string and format, in the style of ``datetime.strptime``. Optionally replaces the parsed TimeZone. @@ -168,45 +168,49 @@ struct Morrow(StringableRaising): 'minutes', 'seconds', 'milliseconds' and 'microseconds'. """ var date_str = ( - rjust(self.year, 4, "0") + str(self.year).rjust(4, "0") + "-" - + rjust(self.month, 2, "0") + + str(self.month).rjust(2, "0") + "-" - + rjust(self.day, 2, "0") + + str(self.day).rjust(2, "0") ) var time_str = String("") if timespec == "auto" or timespec == "microseconds": time_str = ( - rjust(self.hour, 2, "0") + str(self.hour).rjust(2, "0") + ":" - + rjust(self.minute, 2, "0") + + str(self.minute).rjust(2, "0") + ":" - + rjust(self.second, 2, "0") + + str(self.second).rjust(2, "0") + "." - + rjust(self.microsecond, 6, "0") + + str(self.microsecond).rjust(6, "0") ) elif timespec == "milliseconds": time_str = ( - rjust(self.hour, 2, "0") + str(self.hour).rjust(2, "0") + ":" - + rjust(self.minute, 2, "0") + + str(self.minute).rjust(2, "0") + ":" - + rjust(self.second, 2, "0") + + str(self.second).rjust(2, "0") + "." - + rjust(self.microsecond // 1000, 3, "0") + + str(self.microsecond // 1000).rjust(3, "0") ) elif timespec == "seconds": time_str = ( - rjust(self.hour, 2, "0") + str(self.hour).rjust(2, "0") + ":" - + rjust(self.minute, 2, "0") + + str(self.minute).rjust(2, "0") + ":" - + rjust(self.second, 2, "0") + + str(self.second).rjust(2, "0") ) elif timespec == "minutes": - time_str = rjust(self.hour, 2, "0") + ":" + rjust(self.minute, 2, "0") + time_str = ( + str(self.hour).rjust(2, "0") + + ":" + + str(self.minute).rjust(2, "0") + ) elif timespec == "hours": - time_str = rjust(self.hour, 2, "0") + time_str = str(self.hour).rjust(2, "0") else: raise Error() if self.tz.is_none(): @@ -349,5 +353,6 @@ struct Morrow(StringableRaising): ) else: raise Error( - "invalid python object, only support py builtin datetime or date" + "invalid python object, only support py builtin datetime or" + " date" ) diff --git a/morrow/timedelta.mojo b/morrow/timedelta.mojo index ddd41b1..88b45a7 100644 --- a/morrow/timedelta.mojo +++ b/morrow/timedelta.mojo @@ -1,9 +1,7 @@ -from .util import rjust - alias SECONDS_OF_DAY = 24 * 3600 -struct TimeDelta(Stringable): +struct TimeDelta(Stringable, Formattable): var days: Int var seconds: Int var microseconds: Int @@ -57,19 +55,22 @@ struct TimeDelta(Stringable): self.microseconds = other.microseconds fn __str__(self) -> String: + return String.format_sequence(self) + + fn format_to(self: Self, inout writer: Formatter): var mm = self.seconds // 60 var ss = self.seconds % 60 var hh = mm // 60 mm = mm % 60 - var s = String(hh) + ":" + rjust(mm, 2, "0") + ":" + rjust(ss, 2, "0") if self.days: if abs(self.days) != 1: - s = String(self.days) + " days, " + s + writer.write(self.days, " days, ") else: - s = String(self.days) + " day, " + s + writer.write(self.days, " day, ") + + writer.write(hh, ":", str(mm).rjust(2, "0"), ":", str(ss).rjust(2, "0")) if self.microseconds: - s = s + rjust(self.microseconds, 6, "0") - return s + writer.write(str(self.microseconds).rjust(6, "0")) fn total_seconds(self) -> Float64: """Total seconds in the duration.""" @@ -126,7 +127,9 @@ struct TimeDelta(Stringable): return self.__mul__(other) fn _to_microseconds(self) -> Int: - return (self.days * SECONDS_OF_DAY + self.seconds) * 1000000 + self.microseconds + return ( + self.days * SECONDS_OF_DAY + self.seconds + ) * 1000000 + self.microseconds fn __mod__(self, other: Self) -> Self: var r = self._to_microseconds() % other._to_microseconds() @@ -161,7 +164,8 @@ struct TimeDelta(Stringable): if self.seconds < other.seconds: return True elif ( - self.seconds == other.seconds and self.microseconds < other.microseconds + self.seconds == other.seconds + and self.microseconds < other.microseconds ): return True return False diff --git a/morrow/timezone.mojo b/morrow/timezone.mojo index 4da9916..1c6bd92 100644 --- a/morrow/timezone.mojo +++ b/morrow/timezone.mojo @@ -1,4 +1,3 @@ -from .util import rjust from ._libc import c_localtime alias UTC_TZ = TimeZone(0, "UTC") @@ -26,7 +25,7 @@ struct TimeZone(Stringable): @staticmethod fn local() -> TimeZone: var local_t = c_localtime(0) - return TimeZone(local_t.tm_gmtoff.to_int(), "local") + return TimeZone(local_t.tm_gmtoff.value, "local") @staticmethod fn from_utc(utc_str: String) raises -> TimeZone: @@ -73,4 +72,4 @@ struct TimeZone(Stringable): offset_abs = self.offset var hh = offset_abs // 3600 var mm = offset_abs % 3600 - return sign + rjust(hh, 2, "0") + sep + rjust(mm, 2, "0") + return sign + str(hh).rjust(2, "0") + sep + str(mm).rjust(2, "0") diff --git a/morrow/util.mojo b/morrow/util.mojo index e6a5718..504f75a 100644 --- a/morrow/util.mojo +++ b/morrow/util.mojo @@ -1,5 +1,3 @@ -from collections.vector import DynamicVector - from .constants import MAX_TIMESTAMP, MAX_TIMESTAMP_MS, MAX_TIMESTAMP_US from .constants import _DAYS_IN_MONTH, _DAYS_BEFORE_MONTH @@ -37,7 +35,8 @@ def _ymd2ord(year: Int, month: Int, day: Int) -> Int: def normalize_timestamp(timestamp: Float64) -> Float64: - """Normalize millisecond and microsecond timestamps into normal timestamps.""" + """Normalize millisecond and microsecond timestamps into normal timestamps. + """ if timestamp > MAX_TIMESTAMP: if timestamp < MAX_TIMESTAMP_MS: timestamp /= 1000 @@ -45,22 +44,6 @@ def normalize_timestamp(timestamp: Float64) -> Float64: timestamp /= 1_000_000 else: raise Error( - "The specified timestamp " + String(timestamp) + "is too large." + "The specified timestamp " + str(timestamp) + "is too large." ) return timestamp - - -fn _repeat_string(string: String, n: Int) -> String: - var result: String = "" - for _ in range(n): - result += string - return result - - -fn rjust(string: String, width: Int, fillchar: String = " ") -> String: - var extra = width - len(string) - return _repeat_string(fillchar, extra) + string - - -fn rjust(string: Int, width: Int, fillchar: String = " ") -> String: - return rjust(String(string), width, fillchar) diff --git a/test.mojo b/test.mojo index fc27f63..8edf628 100644 --- a/test.mojo +++ b/test.mojo @@ -1,6 +1,7 @@ from testing import assert_equal, assert_true +from python import PythonObject -from morrow._libc import c_gettimeofday +from morrow._libc import c_gettimeofday, c_localtime, c_gmtime from morrow._py import py_dt_datetime, py_time from morrow import Morrow from morrow import TimeZone @@ -49,7 +50,9 @@ def test_iso_format(): var d0 = Morrow(2023, 10, 1, 0, 0, 0, 1234) assert_equal(d0.isoformat(), "2023-10-01T00:00:00.001234") assert_equal(d0.isoformat(timespec="seconds"), "2023-10-01T00:00:00") - assert_equal(d0.isoformat(timespec="milliseconds"), "2023-10-01T00:00:00.001") + assert_equal( + d0.isoformat(timespec="milliseconds"), "2023-10-01T00:00:00.001" + ) # with TimeZone var d1 = Morrow(2023, 10, 1, 0, 0, 0, 1234, TimeZone(28800, "Beijing")) @@ -69,7 +72,9 @@ def test_time_zone(): def test_strptime(): print("Running test_strptime()") - m = Morrow.strptime("20-01-2023 15:49:10", "%d-%m-%Y %H:%M:%S", TimeZone.none()) + m = Morrow.strptime( + "20-01-2023 15:49:10", "%d-%m-%Y %H:%M:%S", TimeZone.none() + ) assert_equal(str(m), "2023-01-20T15:49:10.000000+00:00") m = Morrow.strptime("2023-10-18 15:49:10 +0800", "%Y-%m-%d %H:%M:%S %z") @@ -93,7 +98,9 @@ def test_ordinal(): def test_sub(): print("Running test_sub()") - var result = Morrow(2023, 10, 1, 10, 0, 0, 1) - Morrow(2023, 10, 1, 10, 0, 0) + var result = Morrow(2023, 10, 1, 10, 0, 0, 1) - Morrow( + 2023, 10, 1, 10, 0, 0 + ) assert_equal(result.microseconds, 1) assert_equal(str(result), "0:00:00000001") @@ -118,10 +125,14 @@ def test_timedelta(): print("Running test_timedelta()") assert_equal(TimeDelta(3, 2, 100).total_seconds(), 259202.0001) assert_true( - TimeDelta(2, 1, 50).__add__(TimeDelta(1, 1, 50)).__eq__(TimeDelta(3, 2, 100)) + TimeDelta(2, 1, 50) + .__add__(TimeDelta(1, 1, 50)) + .__eq__(TimeDelta(3, 2, 100)) ) assert_true( - TimeDelta(3, 2, 100).__sub__(TimeDelta(2, 1, 50)).__eq__(TimeDelta(1, 1, 50)) + TimeDelta(3, 2, 100) + .__sub__(TimeDelta(2, 1, 50)) + .__eq__(TimeDelta(1, 1, 50)) ) assert_true(TimeDelta(3, 2, 100).__neg__().__eq__(TimeDelta(-3, -2, -100))) assert_true(TimeDelta(-3, -2, -100).__abs__().__eq__(TimeDelta(3, 2, 100))) @@ -167,7 +178,9 @@ def test_format(): ) assert_equal(m.format("Y-YY-YYY-YYYY M-MM D-DD"), "Y-24--2024 2-02 1-01") assert_equal(m.format("H-HH-h-hh m-mm s-ss"), "3-03-3-03 4-04 5-05") - assert_equal(m.format("S-SS-SSS-SSSS-SSSSS-SSSSSS"), "1-12-123-1234-12345-123456") + assert_equal( + m.format("S-SS-SSS-SSSS-SSSSS-SSSSSS"), "1-12-123-1234-12345-123456" + ) assert_equal(m.format("d-dd-ddd-dddd"), "4--Thu-Thursday") assert_equal(m.format("YYYY[Y] [[]MM[]][M]"), "2024Y [02]M")