timevec 0.3.2__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- timevec-0.4.0/.coveragerc +2 -0
- {timevec-0.3.2 → timevec-0.4.0}/.github/workflows/gitignore-in.yml +1 -6
- timevec-0.4.0/.github/workflows/happy.yml +10 -0
- {timevec-0.3.2 → timevec-0.4.0}/.github/workflows/octocov.yml +11 -4
- timevec-0.4.0/.github/workflows/pypi-publish.yml +59 -0
- {timevec-0.3.2 → timevec-0.4.0}/.github/workflows/python-test.yml +14 -16
- timevec-0.4.0/.github/workflows/spellcheck.yml +8 -0
- timevec-0.4.0/CHANGELOG.md +94 -0
- {timevec-0.3.2/timevec.egg-info → timevec-0.4.0}/PKG-INFO +55 -3
- {timevec-0.3.2 → timevec-0.4.0}/README.md +54 -2
- timevec-0.4.0/examples/plot.py +58 -0
- timevec-0.4.0/lefthook.yml +13 -0
- {timevec-0.3.2 → timevec-0.4.0}/pyproject.toml +18 -0
- {timevec-0.3.2 → timevec-0.4.0}/tests/test_builtin_math.py +32 -2
- {timevec-0.3.2 → timevec-0.4.0}/tests/test_many_dates.py +11 -2
- {timevec-0.3.2 → timevec-0.4.0}/tests/test_numpy.py +18 -1
- timevec-0.4.0/tests/test_numpy_datetime64.py +147 -0
- {timevec-0.3.2 → timevec-0.4.0}/tests/test_same_result.py +47 -7
- timevec-0.4.0/tests/test_util.py +187 -0
- {timevec-0.3.2 → timevec-0.4.0}/timevec/__init__.py +28 -8
- {timevec-0.3.2 → timevec-0.4.0}/timevec/_version.py +3 -3
- {timevec-0.3.2 → timevec-0.4.0}/timevec/builtin_math.py +43 -22
- {timevec-0.3.2 → timevec-0.4.0}/timevec/numpy.py +65 -28
- {timevec-0.3.2 → timevec-0.4.0}/timevec/numpy_datetime64.py +63 -19
- {timevec-0.3.2 → timevec-0.4.0}/timevec/util.py +42 -48
- {timevec-0.3.2 → timevec-0.4.0/timevec.egg-info}/PKG-INFO +55 -3
- {timevec-0.3.2 → timevec-0.4.0}/timevec.egg-info/SOURCES.txt +5 -1
- timevec-0.4.0/timevec.egg-info/scm_file_list.json +38 -0
- timevec-0.4.0/timevec.egg-info/scm_version.json +8 -0
- {timevec-0.3.2 → timevec-0.4.0}/uv.lock +96 -100
- timevec-0.3.2/.github/workflows/happy.yml +0 -18
- timevec-0.3.2/.github/workflows/pypi-publish.yml +0 -49
- timevec-0.3.2/.github/workflows/spellcheck.yml +0 -16
- timevec-0.3.2/examples/plot.py +0 -39
- timevec-0.3.2/mypy.ini +0 -19
- timevec-0.3.2/tests/test_numpy_datetime64.py +0 -60
- timevec-0.3.2/tests/test_util.py +0 -110
- {timevec-0.3.2 → timevec-0.4.0}/.github/renovate.json5 +0 -0
- {timevec-0.3.2 → timevec-0.4.0}/.gitignore +0 -0
- {timevec-0.3.2 → timevec-0.4.0}/.gitignore.in +0 -0
- {timevec-0.3.2 → timevec-0.4.0}/.octocov.yml +0 -0
- {timevec-0.3.2 → timevec-0.4.0}/LICENSE +0 -0
- {timevec-0.3.2 → timevec-0.4.0}/examples/day_vec.svg +0 -0
- {timevec-0.3.2 → timevec-0.4.0}/examples/month_vec.svg +0 -0
- {timevec-0.3.2 → timevec-0.4.0}/examples/week_vec.svg +0 -0
- {timevec-0.3.2 → timevec-0.4.0}/examples/year_vec.svg +0 -0
- {timevec-0.3.2 → timevec-0.4.0}/setup.cfg +0 -0
- {timevec-0.3.2 → timevec-0.4.0}/timevec/py.typed +0 -0
- {timevec-0.3.2 → timevec-0.4.0}/timevec.egg-info/dependency_links.txt +0 -0
- {timevec-0.3.2 → timevec-0.4.0}/timevec.egg-info/requires.txt +0 -0
- {timevec-0.3.2 → timevec-0.4.0}/timevec.egg-info/top_level.txt +0 -0
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
name: Update gitignore file
|
|
2
|
-
|
|
3
2
|
on:
|
|
4
3
|
schedule:
|
|
5
4
|
# daily at 00:00
|
|
6
5
|
- cron: '0 0 * * *'
|
|
7
|
-
|
|
8
6
|
permissions:
|
|
9
7
|
contents: write
|
|
10
8
|
pull-requests: write
|
|
11
|
-
|
|
12
9
|
jobs:
|
|
13
10
|
update-gitignore:
|
|
14
|
-
|
|
15
|
-
steps:
|
|
16
|
-
- uses: gitignore-in/gh-action@v0.2.3
|
|
11
|
+
uses: kitsuyui/gh-actions-workflows/.github/workflows/gitignore-in.yml@3717c78af3a5e594b7a81b063276e15fac7e08fc
|
|
@@ -5,6 +5,10 @@ on:
|
|
|
5
5
|
branches:
|
|
6
6
|
- main
|
|
7
7
|
|
|
8
|
+
concurrency:
|
|
9
|
+
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }}
|
|
10
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
11
|
+
|
|
8
12
|
permissions:
|
|
9
13
|
contents: read
|
|
10
14
|
pull-requests: write
|
|
@@ -20,16 +24,19 @@ jobs:
|
|
|
20
24
|
|
|
21
25
|
steps:
|
|
22
26
|
- name: Checkout
|
|
23
|
-
uses: actions/checkout@v6
|
|
27
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
24
28
|
|
|
25
29
|
- name: Set up Python
|
|
26
30
|
id: setup-python
|
|
27
|
-
uses: actions/setup-python@v6
|
|
31
|
+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
|
28
32
|
with:
|
|
29
33
|
python-version: "${{ matrix.python }}"
|
|
30
34
|
|
|
31
35
|
- name: Set up uv
|
|
32
|
-
uses: astral-sh/setup-uv@v7
|
|
36
|
+
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7
|
|
37
|
+
with:
|
|
38
|
+
enable-cache: true
|
|
39
|
+
cache-dependency-glob: uv.lock
|
|
33
40
|
|
|
34
41
|
- name: Install dependencies
|
|
35
42
|
run: |
|
|
@@ -44,7 +51,7 @@ jobs:
|
|
|
44
51
|
|
|
45
52
|
# See also: https://github.com/k1LoW/octocov-action
|
|
46
53
|
- name: Report coverage with octocov
|
|
47
|
-
uses: k1LoW/octocov-action@v1
|
|
54
|
+
uses: k1LoW/octocov-action@b3b6ee60482a667950f87553abf1df63217235d9 # v1
|
|
48
55
|
with:
|
|
49
56
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
50
57
|
config: .octocov.yml
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
name: Packaging
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
types: [opened, synchronize, reopened]
|
|
6
|
+
release:
|
|
7
|
+
# "released" events are emitted either when directly be released or be edited from pre-released.
|
|
8
|
+
types: [prereleased, released]
|
|
9
|
+
|
|
10
|
+
concurrency:
|
|
11
|
+
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.event.release.id || github.ref }}
|
|
12
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
deploy:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
permissions:
|
|
21
|
+
contents: read
|
|
22
|
+
id-token: write
|
|
23
|
+
attestations: write
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
26
|
+
|
|
27
|
+
- name: Set up Python
|
|
28
|
+
id: setup-python
|
|
29
|
+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
|
30
|
+
with:
|
|
31
|
+
python-version: "3.14"
|
|
32
|
+
|
|
33
|
+
- name: Set up uv
|
|
34
|
+
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7
|
|
35
|
+
with:
|
|
36
|
+
enable-cache: true
|
|
37
|
+
cache-dependency-glob: uv.lock
|
|
38
|
+
|
|
39
|
+
- name: Install dependencies
|
|
40
|
+
run: uv sync
|
|
41
|
+
|
|
42
|
+
- name: Build
|
|
43
|
+
run: uv build
|
|
44
|
+
|
|
45
|
+
- name: Attest build provenance
|
|
46
|
+
if: github.event_name == 'release' && !github.event.release.prerelease
|
|
47
|
+
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
|
|
48
|
+
with:
|
|
49
|
+
subject-path: dist/*
|
|
50
|
+
|
|
51
|
+
- name: Publish distribution to Test PyPI
|
|
52
|
+
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1
|
|
53
|
+
if: github.event_name == 'release' && github.event.release.prerelease
|
|
54
|
+
with:
|
|
55
|
+
repository-url: https://test.pypi.org/legacy/
|
|
56
|
+
|
|
57
|
+
- name: Publish distribution to PyPI
|
|
58
|
+
if: github.event_name == 'release' && !github.event.release.prerelease
|
|
59
|
+
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1
|
|
@@ -8,21 +8,16 @@ on:
|
|
|
8
8
|
branches:
|
|
9
9
|
- main
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
steps:
|
|
15
|
-
- name: Checkout
|
|
16
|
-
uses: actions/checkout@v6
|
|
11
|
+
concurrency:
|
|
12
|
+
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }}
|
|
13
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
17
14
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
with:
|
|
21
|
-
go-version: stable
|
|
22
|
-
|
|
23
|
-
- name: Run actionlint
|
|
24
|
-
run: go run github.com/rhysd/actionlint/cmd/actionlint@v1.7.12
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
25
17
|
|
|
18
|
+
jobs:
|
|
19
|
+
actionlint:
|
|
20
|
+
uses: kitsuyui/gh-actions-workflows/.github/workflows/actionlint.yml@3717c78af3a5e594b7a81b063276e15fac7e08fc # main
|
|
26
21
|
test:
|
|
27
22
|
runs-on: ubuntu-latest
|
|
28
23
|
strategy:
|
|
@@ -31,16 +26,19 @@ jobs:
|
|
|
31
26
|
python: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
32
27
|
steps:
|
|
33
28
|
- name: Checkout
|
|
34
|
-
uses: actions/checkout@v6
|
|
29
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
35
30
|
|
|
36
31
|
- name: Set up Python
|
|
37
32
|
id: setup-python
|
|
38
|
-
uses: actions/setup-python@v6
|
|
33
|
+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
|
39
34
|
with:
|
|
40
35
|
python-version: "${{ matrix.python }}"
|
|
41
36
|
|
|
42
37
|
- name: Set up uv
|
|
43
|
-
uses: astral-sh/setup-uv@v7
|
|
38
|
+
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7
|
|
39
|
+
with:
|
|
40
|
+
enable-cache: true
|
|
41
|
+
cache-dependency-glob: uv.lock
|
|
44
42
|
|
|
45
43
|
- name: Install dependencies
|
|
46
44
|
run: |
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.3.2] - 2026-05-03
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- Add workflow concurrency groups to CI
|
|
14
|
+
|
|
15
|
+
## [0.3.1] - 2025-02-01
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
- Migrate package manager from Poetry to uv
|
|
19
|
+
- Set minimal permissions in CI publish workflow
|
|
20
|
+
- Add actionlint check to CI
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
- Fix decade range near datetime minimum
|
|
24
|
+
|
|
25
|
+
## [0.3.0] - 2024-10-16
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
- Add Python 3.13 support
|
|
29
|
+
|
|
30
|
+
## [0.2.0] - 2023-10-06
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
- Add Python 3.12 support
|
|
34
|
+
|
|
35
|
+
## [0.1.7] - 2023-07-17
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
- Minor refactor: convert comments to docstrings
|
|
39
|
+
|
|
40
|
+
## [0.1.6] - 2023-02-18
|
|
41
|
+
|
|
42
|
+
### Added
|
|
43
|
+
- Add `py.typed` marker for PEP 561 compliance
|
|
44
|
+
|
|
45
|
+
## [0.1.5] - 2023-02-08
|
|
46
|
+
|
|
47
|
+
### Changed
|
|
48
|
+
- Rewrite vector ↔ time conversion logic
|
|
49
|
+
- Add additional tests for time/vector round-trips
|
|
50
|
+
|
|
51
|
+
## [0.1.4] - 2023-01-26
|
|
52
|
+
|
|
53
|
+
### Added
|
|
54
|
+
- Reverse conversion: create datetime from time vector
|
|
55
|
+
- Add tests for many dates
|
|
56
|
+
|
|
57
|
+
## [0.1.3] - 2023-01-24
|
|
58
|
+
|
|
59
|
+
### Added
|
|
60
|
+
- Add `numpy.datetime64` vector support
|
|
61
|
+
- Unify date conversion between `builtin_math` and `numpy` submodules
|
|
62
|
+
|
|
63
|
+
## [0.1.2] - 2023-01-23
|
|
64
|
+
|
|
65
|
+
### Fixed
|
|
66
|
+
- Fix boundary value bug in edge cases
|
|
67
|
+
|
|
68
|
+
## [0.1.1] - 2023-01-23
|
|
69
|
+
|
|
70
|
+
### Changed
|
|
71
|
+
- Abstract common logic across `year_vec`, `month_vec`, `day_vec`, `week_vec`
|
|
72
|
+
- Add figure to README illustrating vector representation
|
|
73
|
+
|
|
74
|
+
## [0.1.0] - 2023-01-21
|
|
75
|
+
|
|
76
|
+
### Added
|
|
77
|
+
- Initial release
|
|
78
|
+
- Time vector representation using cos/sin for periodicity
|
|
79
|
+
- `builtin_math` submodule (no NumPy dependency)
|
|
80
|
+
- `numpy` submodule (optional NumPy support)
|
|
81
|
+
|
|
82
|
+
[Unreleased]: https://github.com/kitsuyui/python-timevec/compare/v0.3.2...HEAD
|
|
83
|
+
[0.3.2]: https://github.com/kitsuyui/python-timevec/compare/v0.3.1...v0.3.2
|
|
84
|
+
[0.3.1]: https://github.com/kitsuyui/python-timevec/compare/v0.3.0...v0.3.1
|
|
85
|
+
[0.3.0]: https://github.com/kitsuyui/python-timevec/compare/v0.2.0...v0.3.0
|
|
86
|
+
[0.2.0]: https://github.com/kitsuyui/python-timevec/compare/v0.1.7...v0.2.0
|
|
87
|
+
[0.1.7]: https://github.com/kitsuyui/python-timevec/compare/v0.1.6...v0.1.7
|
|
88
|
+
[0.1.6]: https://github.com/kitsuyui/python-timevec/compare/v0.1.5...v0.1.6
|
|
89
|
+
[0.1.5]: https://github.com/kitsuyui/python-timevec/compare/v0.1.4...v0.1.5
|
|
90
|
+
[0.1.4]: https://github.com/kitsuyui/python-timevec/compare/v0.1.3...v0.1.4
|
|
91
|
+
[0.1.3]: https://github.com/kitsuyui/python-timevec/compare/v0.1.2...v0.1.3
|
|
92
|
+
[0.1.2]: https://github.com/kitsuyui/python-timevec/compare/v0.1.1...v0.1.2
|
|
93
|
+
[0.1.1]: https://github.com/kitsuyui/python-timevec/compare/v0.1.0...v0.1.1
|
|
94
|
+
[0.1.0]: https://github.com/kitsuyui/python-timevec/releases/tag/v0.1.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: timevec
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Get some usable vector representations of datetime.
|
|
5
5
|
Author-email: Yui KITSU <kitsuyui+github@kitsuyui.com>
|
|
6
6
|
License: Copyright 2023-2025 Kitsu Yui
|
|
@@ -56,15 +56,39 @@ This is called a time vector.
|
|
|
56
56
|
|
|
57
57
|
# Installation
|
|
58
58
|
|
|
59
|
+
The base package has no NumPy dependency and supports
|
|
60
|
+
`timevec.builtin_math`:
|
|
61
|
+
|
|
59
62
|
```sh
|
|
60
63
|
pip install timevec
|
|
61
64
|
```
|
|
62
65
|
|
|
66
|
+
Install the `numpy` extra when you use `timevec.numpy` or
|
|
67
|
+
`timevec.numpy_datetime64`:
|
|
68
|
+
|
|
69
|
+
```sh
|
|
70
|
+
pip install "timevec[numpy]"
|
|
71
|
+
```
|
|
72
|
+
|
|
63
73
|
# Usage
|
|
64
74
|
|
|
75
|
+
With the base install:
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
import datetime
|
|
79
|
+
import timevec.builtin_math as tv
|
|
80
|
+
|
|
81
|
+
dt = datetime.datetime(2020, 1, 1, 0, 0, 0)
|
|
82
|
+
vec = tv.year_vec(dt)
|
|
83
|
+
# (1.0, 0.0)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
With the `numpy` extra:
|
|
87
|
+
|
|
65
88
|
```python
|
|
66
|
-
import timevec.numpy as tv
|
|
67
89
|
import datetime
|
|
90
|
+
import timevec.numpy as tv
|
|
91
|
+
|
|
68
92
|
dt = datetime.datetime(2020, 1, 1, 0, 0, 0)
|
|
69
93
|
vec = tv.year_vec(dt)
|
|
70
94
|
# array([1., 0.])
|
|
@@ -130,11 +154,39 @@ You can express periodicity such as morning, noon, and night.
|
|
|
130
154
|
|
|
131
155
|
## Others
|
|
132
156
|
|
|
133
|
-
- `long_time_vec` ...
|
|
157
|
+
- `long_time_vec` ... `datetime.min` to `datetime.max` period
|
|
134
158
|
- `millennium_vec` ... one millennium (1000 years) period
|
|
135
159
|
- `century_vec` ... one century (100 years) period
|
|
136
160
|
- `decade_vec` ... one decade (10 years) period
|
|
137
161
|
|
|
162
|
+
## Development
|
|
163
|
+
|
|
164
|
+
This repository uses [lefthook](https://lefthook.dev/) to run the same checks as CI
|
|
165
|
+
locally, so problems surface before they reach CI.
|
|
166
|
+
|
|
167
|
+
```sh
|
|
168
|
+
# Install dependencies
|
|
169
|
+
uv sync
|
|
170
|
+
|
|
171
|
+
# Install the Git hooks (once; requires lefthook on your PATH)
|
|
172
|
+
lefthook install
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Once installed, the hooks run automatically:
|
|
176
|
+
|
|
177
|
+
- **pre-commit**: `uv run poe check`
|
|
178
|
+
- **pre-push**: `uv run poe check` and `uv run poe test`
|
|
179
|
+
|
|
180
|
+
You can also run the checks manually:
|
|
181
|
+
|
|
182
|
+
```sh
|
|
183
|
+
uv run poe check
|
|
184
|
+
uv run poe test
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
CI still runs the full matrix (see `.github/workflows/`); the hooks only bring that
|
|
188
|
+
feedback earlier on your machine.
|
|
189
|
+
|
|
138
190
|
# License
|
|
139
191
|
|
|
140
192
|
BSD 3-Clause License
|
|
@@ -22,15 +22,39 @@ This is called a time vector.
|
|
|
22
22
|
|
|
23
23
|
# Installation
|
|
24
24
|
|
|
25
|
+
The base package has no NumPy dependency and supports
|
|
26
|
+
`timevec.builtin_math`:
|
|
27
|
+
|
|
25
28
|
```sh
|
|
26
29
|
pip install timevec
|
|
27
30
|
```
|
|
28
31
|
|
|
32
|
+
Install the `numpy` extra when you use `timevec.numpy` or
|
|
33
|
+
`timevec.numpy_datetime64`:
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
pip install "timevec[numpy]"
|
|
37
|
+
```
|
|
38
|
+
|
|
29
39
|
# Usage
|
|
30
40
|
|
|
41
|
+
With the base install:
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
import datetime
|
|
45
|
+
import timevec.builtin_math as tv
|
|
46
|
+
|
|
47
|
+
dt = datetime.datetime(2020, 1, 1, 0, 0, 0)
|
|
48
|
+
vec = tv.year_vec(dt)
|
|
49
|
+
# (1.0, 0.0)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
With the `numpy` extra:
|
|
53
|
+
|
|
31
54
|
```python
|
|
32
|
-
import timevec.numpy as tv
|
|
33
55
|
import datetime
|
|
56
|
+
import timevec.numpy as tv
|
|
57
|
+
|
|
34
58
|
dt = datetime.datetime(2020, 1, 1, 0, 0, 0)
|
|
35
59
|
vec = tv.year_vec(dt)
|
|
36
60
|
# array([1., 0.])
|
|
@@ -96,11 +120,39 @@ You can express periodicity such as morning, noon, and night.
|
|
|
96
120
|
|
|
97
121
|
## Others
|
|
98
122
|
|
|
99
|
-
- `long_time_vec` ...
|
|
123
|
+
- `long_time_vec` ... `datetime.min` to `datetime.max` period
|
|
100
124
|
- `millennium_vec` ... one millennium (1000 years) period
|
|
101
125
|
- `century_vec` ... one century (100 years) period
|
|
102
126
|
- `decade_vec` ... one decade (10 years) period
|
|
103
127
|
|
|
128
|
+
## Development
|
|
129
|
+
|
|
130
|
+
This repository uses [lefthook](https://lefthook.dev/) to run the same checks as CI
|
|
131
|
+
locally, so problems surface before they reach CI.
|
|
132
|
+
|
|
133
|
+
```sh
|
|
134
|
+
# Install dependencies
|
|
135
|
+
uv sync
|
|
136
|
+
|
|
137
|
+
# Install the Git hooks (once; requires lefthook on your PATH)
|
|
138
|
+
lefthook install
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Once installed, the hooks run automatically:
|
|
142
|
+
|
|
143
|
+
- **pre-commit**: `uv run poe check`
|
|
144
|
+
- **pre-push**: `uv run poe check` and `uv run poe test`
|
|
145
|
+
|
|
146
|
+
You can also run the checks manually:
|
|
147
|
+
|
|
148
|
+
```sh
|
|
149
|
+
uv run poe check
|
|
150
|
+
uv run poe test
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
CI still runs the full matrix (see `.github/workflows/`); the hooks only bring that
|
|
154
|
+
feedback earlier on your machine.
|
|
155
|
+
|
|
104
156
|
# License
|
|
105
157
|
|
|
106
158
|
BSD 3-Clause License
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# ja: 実際に year_vec がどのような値を返すかのグラフを描き、わかりやすくします。
|
|
2
|
+
|
|
3
|
+
import datetime
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
|
|
6
|
+
import matplotlib.pyplot as plt
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
import timevec.numpy as tv
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def plot_func_vec(
|
|
13
|
+
func: Callable[[datetime.datetime], np.ndarray],
|
|
14
|
+
dates: list[datetime.datetime],
|
|
15
|
+
) -> None:
|
|
16
|
+
vecs = np.array([func(dt) for dt in dates])
|
|
17
|
+
_fig, _ax = plt.subplots()
|
|
18
|
+
plt.plot(vecs)
|
|
19
|
+
plt.savefig(f"examples/{func.__name__}.svg")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def main() -> None:
|
|
23
|
+
plot_func_vec(
|
|
24
|
+
tv.year_vec,
|
|
25
|
+
dates=[
|
|
26
|
+
datetime.datetime(2023, 1, 1, 0, 0, 0)
|
|
27
|
+
+ datetime.timedelta(days=day)
|
|
28
|
+
for day in range(365 * 2)
|
|
29
|
+
],
|
|
30
|
+
)
|
|
31
|
+
plot_func_vec(
|
|
32
|
+
tv.month_vec,
|
|
33
|
+
dates=[
|
|
34
|
+
datetime.datetime(2023, 1, 1, 0, 0, 0)
|
|
35
|
+
+ datetime.timedelta(days=day)
|
|
36
|
+
for day in range(31 * 2)
|
|
37
|
+
],
|
|
38
|
+
)
|
|
39
|
+
plot_func_vec(
|
|
40
|
+
tv.week_vec,
|
|
41
|
+
dates=[
|
|
42
|
+
datetime.datetime(2023, 1, 1, 0, 0, 0)
|
|
43
|
+
+ datetime.timedelta(hours=h)
|
|
44
|
+
for h in range(24 * 7 * 2)
|
|
45
|
+
],
|
|
46
|
+
)
|
|
47
|
+
plot_func_vec(
|
|
48
|
+
tv.day_vec,
|
|
49
|
+
dates=[
|
|
50
|
+
datetime.datetime(2023, 1, 1, 0, 0, 0)
|
|
51
|
+
+ datetime.timedelta(hours=h)
|
|
52
|
+
for h in range(24 * 2)
|
|
53
|
+
],
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
if __name__ == "__main__":
|
|
58
|
+
main()
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Clear Git hook environment variables before running checks so nested or sibling
|
|
2
|
+
# repositories are not affected: https://git-scm.com/docs/githooks
|
|
3
|
+
pre-commit:
|
|
4
|
+
jobs:
|
|
5
|
+
- name: check
|
|
6
|
+
run: for name in $(env | sed -n 's/^\(GIT_[A-Za-z0-9_]*\)=.*/\1/p'); do unset "$name"; done; uv run poe check
|
|
7
|
+
|
|
8
|
+
pre-push:
|
|
9
|
+
jobs:
|
|
10
|
+
- name: check
|
|
11
|
+
run: for name in $(env | sed -n 's/^\(GIT_[A-Za-z0-9_]*\)=.*/\1/p'); do unset "$name"; done; uv run poe check
|
|
12
|
+
- name: test
|
|
13
|
+
run: for name in $(env | sed -n 's/^\(GIT_[A-Za-z0-9_]*\)=.*/\1/p'); do unset "$name"; done; uv run poe test
|
|
@@ -131,3 +131,21 @@ max-complexity = 4
|
|
|
131
131
|
[tool.mypy]
|
|
132
132
|
strict = true
|
|
133
133
|
ignore_missing_imports = false
|
|
134
|
+
|
|
135
|
+
[[tool.mypy.overrides]]
|
|
136
|
+
module = "tests.*"
|
|
137
|
+
disallow_untyped_defs = false
|
|
138
|
+
warn_return_any = false
|
|
139
|
+
ignore_missing_imports = true
|
|
140
|
+
|
|
141
|
+
[[tool.mypy.overrides]]
|
|
142
|
+
module = "numpy"
|
|
143
|
+
ignore_missing_imports = true
|
|
144
|
+
|
|
145
|
+
[[tool.mypy.overrides]]
|
|
146
|
+
module = "scipy.*"
|
|
147
|
+
ignore_missing_imports = true
|
|
148
|
+
|
|
149
|
+
[[tool.mypy.overrides]]
|
|
150
|
+
module = "sympy"
|
|
151
|
+
ignore_missing_imports = true
|
|
@@ -48,8 +48,14 @@ def assert_range_vector_value(
|
|
|
48
48
|
) -> None:
|
|
49
49
|
assert fn(range.begin) == pytest.approx((1.0, 0.0), abs=abs)
|
|
50
50
|
assert fn(range.end_of_first_quarter) == pytest.approx((0.0, 1.0), abs=abs)
|
|
51
|
-
assert fn(range.end_of_second_quarter) == pytest.approx(
|
|
52
|
-
|
|
51
|
+
assert fn(range.end_of_second_quarter) == pytest.approx(
|
|
52
|
+
(-1.0, 0.0),
|
|
53
|
+
abs=abs,
|
|
54
|
+
)
|
|
55
|
+
assert fn(range.end_of_third_quarter) == pytest.approx(
|
|
56
|
+
(0.0, -1.0),
|
|
57
|
+
abs=abs,
|
|
58
|
+
)
|
|
53
59
|
assert fn(range.end) == pytest.approx((1.0, 0.0), abs=abs)
|
|
54
60
|
|
|
55
61
|
|
|
@@ -156,6 +162,15 @@ def test_day_vec() -> None:
|
|
|
156
162
|
assert (x, y) == pytest.approx((1.0, 0.0), abs=1e-6)
|
|
157
163
|
|
|
158
164
|
|
|
165
|
+
def test_ratio_to_vec_rejects_nan_and_inf() -> None:
|
|
166
|
+
with pytest.raises(ValueError, match="finite"):
|
|
167
|
+
tv.ratio_to_vec(float("nan"))
|
|
168
|
+
with pytest.raises(ValueError, match="finite"):
|
|
169
|
+
tv.ratio_to_vec(float("inf"))
|
|
170
|
+
with pytest.raises(ValueError, match="finite"):
|
|
171
|
+
tv.ratio_to_vec(float("-inf"))
|
|
172
|
+
|
|
173
|
+
|
|
159
174
|
def test_edge_cases() -> None:
|
|
160
175
|
# beginning of year
|
|
161
176
|
dt = datetime.datetime(2023, 1, 1, 0, 0, 0)
|
|
@@ -178,3 +193,18 @@ def test_edge_cases() -> None:
|
|
|
178
193
|
dt = datetime.datetime(2023, 12, 31, 23, 59, 59, 999999)
|
|
179
194
|
x, y = tv.year_vec(dt)
|
|
180
195
|
assert (x, y) == pytest.approx((1.0, 0.0), abs=1e-6)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def test_vec_to_ratio_zero_vector_raises() -> None:
|
|
199
|
+
with pytest.raises(ValueError):
|
|
200
|
+
tv.vec_to_ratio(0.0, 0.0)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def test_datetime_from_vecs_empty_raises() -> None:
|
|
204
|
+
with pytest.raises(ValueError, match="No recognized time targets"):
|
|
205
|
+
tv.datetime_from_vecs({})
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def test_datetime_from_vecs_unrecognized_keys_raises() -> None:
|
|
209
|
+
with pytest.raises(ValueError, match="No recognized time targets"):
|
|
210
|
+
tv.datetime_from_vecs({"unknown_key": (0.5, 0.5)}) # type: ignore[dict-item]
|
|
@@ -23,7 +23,16 @@ def test_many_dates() -> None:
|
|
|
23
23
|
"week",
|
|
24
24
|
"day",
|
|
25
25
|
]
|
|
26
|
-
] = [
|
|
26
|
+
] = [
|
|
27
|
+
"long_time",
|
|
28
|
+
"millennium",
|
|
29
|
+
"century",
|
|
30
|
+
"decade",
|
|
31
|
+
"year",
|
|
32
|
+
"month",
|
|
33
|
+
"week",
|
|
34
|
+
"day",
|
|
35
|
+
]
|
|
27
36
|
minimal: Iterable[Literal["long_time", "century", "year", "day"]] = [
|
|
28
37
|
"long_time",
|
|
29
38
|
"century",
|
|
@@ -57,5 +66,5 @@ def test_many_dates() -> None:
|
|
|
57
66
|
vecs7 = tv64.datetime64_to_vecs(np.datetime64(dt), minimal)
|
|
58
67
|
tv64.datetime64_from_vecs(vecs7)
|
|
59
68
|
|
|
60
|
-
# tested
|
|
69
|
+
# tested beyond 2400-01-01
|
|
61
70
|
assert dt >= datetime.datetime(2400, 1, 1, 0, 0, 0)
|
|
@@ -11,7 +11,7 @@ def test_long_time_vec() -> None:
|
|
|
11
11
|
vec = tvn.long_time_vec(dt)
|
|
12
12
|
assert vec == pytest.approx(np.array([1.0, 0.0]), abs=1e-6)
|
|
13
13
|
|
|
14
|
-
dt = datetime.datetime
|
|
14
|
+
dt = datetime.datetime.max
|
|
15
15
|
vec = tvn.long_time_vec(dt)
|
|
16
16
|
assert vec == pytest.approx(np.array([1.0, 0.0]), abs=1e-6)
|
|
17
17
|
|
|
@@ -139,3 +139,20 @@ def test_edge_cases() -> None:
|
|
|
139
139
|
dt = datetime.datetime(2023, 1, 31, 23, 59, 59, 999999)
|
|
140
140
|
vec = tvn.month_vec(dt)
|
|
141
141
|
assert vec == pytest.approx(np.array([1.0, 0.0]), abs=1e-5)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def test_vec_to_ratio_zero_vector_raises() -> None:
|
|
145
|
+
with pytest.raises(ValueError):
|
|
146
|
+
tvn.vec_to_ratio(np.array([0.0, 0.0]))
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def test_datetime_from_vecs_empty_raises() -> None:
|
|
150
|
+
with pytest.raises(ValueError, match="No recognized time targets"):
|
|
151
|
+
tvn.datetime_from_vecs({})
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def test_datetime_from_vecs_unrecognized_keys_raises() -> None:
|
|
155
|
+
with pytest.raises(ValueError, match="No recognized time targets"):
|
|
156
|
+
tvn.datetime_from_vecs(
|
|
157
|
+
{"unknown_key": np.array([0.5, 0.5])}, # type: ignore[dict-item]
|
|
158
|
+
)
|