isoperiod 1.2.1__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.
- isoperiod-1.2.1/.editorconfig +33 -0
- isoperiod-1.2.1/.githooks/pre-commit +47 -0
- isoperiod-1.2.1/.github/ISSUE_TEMPLATE/bug_report.yml +61 -0
- isoperiod-1.2.1/.github/ISSUE_TEMPLATE/config.yml +1 -0
- isoperiod-1.2.1/.github/ISSUE_TEMPLATE/feature_request.yml +30 -0
- isoperiod-1.2.1/.github/dependabot.yml +23 -0
- isoperiod-1.2.1/.github/pull_request_template.md +9 -0
- isoperiod-1.2.1/.github/workflows/pipeline.yml +152 -0
- isoperiod-1.2.1/.gitignore +167 -0
- isoperiod-1.2.1/CHANGELOG/1.0.0.md +38 -0
- isoperiod-1.2.1/CHANGELOG/1.1.0.md +48 -0
- isoperiod-1.2.1/CHANGELOG/1.2.0.md +23 -0
- isoperiod-1.2.1/CHANGELOG/1.2.1.md +30 -0
- isoperiod-1.2.1/CITATION.cff +29 -0
- isoperiod-1.2.1/CONTRIBUTING.md +166 -0
- isoperiod-1.2.1/LICENSE +21 -0
- isoperiod-1.2.1/Makefile +82 -0
- isoperiod-1.2.1/PKG-INFO +171 -0
- isoperiod-1.2.1/README.md +129 -0
- isoperiod-1.2.1/conftest.py +6 -0
- isoperiod-1.2.1/docs/Makefile +23 -0
- isoperiod-1.2.1/docs/source/_static/UKCEH_Logo_Master_Black.png +0 -0
- isoperiod-1.2.1/docs/source/_static/UKCEH_Logo_Tagline_Black_Data.png +0 -0
- isoperiod-1.2.1/docs/source/_static/css/custom.css +55 -0
- isoperiod-1.2.1/docs/source/_static/fdri_logo.png +0 -0
- isoperiod-1.2.1/docs/source/_static/fdri_logo_cropped.jpg +0 -0
- isoperiod-1.2.1/docs/source/_templates/autosummary/attribute.rst +7 -0
- isoperiod-1.2.1/docs/source/_templates/autosummary/method.rst +7 -0
- isoperiod-1.2.1/docs/source/_templates/autosummary/property.rst +7 -0
- isoperiod-1.2.1/docs/source/_templates/partials/nav-socials.html +11 -0
- isoperiod-1.2.1/docs/source/api/exceptions.rst +26 -0
- isoperiod-1.2.1/docs/source/api/period.rst +107 -0
- isoperiod-1.2.1/docs/source/conf.py +98 -0
- isoperiod-1.2.1/docs/source/developer/contributing.rst +133 -0
- isoperiod-1.2.1/docs/source/developer/documentation.rst +121 -0
- isoperiod-1.2.1/docs/source/examples/__init__.py +0 -0
- isoperiod-1.2.1/docs/source/examples/comparing.py +102 -0
- isoperiod-1.2.1/docs/source/examples/concepts.py +35 -0
- isoperiod-1.2.1/docs/source/examples/creating.py +91 -0
- isoperiod-1.2.1/docs/source/examples/describing.py +48 -0
- isoperiod-1.2.1/docs/source/examples/interop.py +86 -0
- isoperiod-1.2.1/docs/source/examples/intervals.py +168 -0
- isoperiod-1.2.1/docs/source/examples/offsets.py +102 -0
- isoperiod-1.2.1/docs/source/examples/quick_start.py +85 -0
- isoperiod-1.2.1/docs/source/examples/timezones.py +66 -0
- isoperiod-1.2.1/docs/source/getting_started/concepts.rst +206 -0
- isoperiod-1.2.1/docs/source/getting_started/installation.rst +46 -0
- isoperiod-1.2.1/docs/source/getting_started/quick-start.rst +163 -0
- isoperiod-1.2.1/docs/source/index.rst +135 -0
- isoperiod-1.2.1/docs/source/user_guide/comparing.rst +173 -0
- isoperiod-1.2.1/docs/source/user_guide/creating.rst +196 -0
- isoperiod-1.2.1/docs/source/user_guide/describing.rst +111 -0
- isoperiod-1.2.1/docs/source/user_guide/interop.rst +144 -0
- isoperiod-1.2.1/docs/source/user_guide/intervals.rst +256 -0
- isoperiod-1.2.1/docs/source/user_guide/offsets.rst +192 -0
- isoperiod-1.2.1/docs/source/user_guide/timezones.rst +127 -0
- isoperiod-1.2.1/pyproject.toml +121 -0
- isoperiod-1.2.1/scripts/bump.py +84 -0
- isoperiod-1.2.1/scripts/release.py +98 -0
- isoperiod-1.2.1/src/isoperiod/__init__.py +17 -0
- isoperiod-1.2.1/src/isoperiod/enums.py +13 -0
- isoperiod-1.2.1/src/isoperiod/exceptions.py +25 -0
- isoperiod-1.2.1/src/isoperiod/iso.py +233 -0
- isoperiod-1.2.1/src/isoperiod/parsing.py +256 -0
- isoperiod-1.2.1/src/isoperiod/periods.py +1170 -0
- isoperiod-1.2.1/src/isoperiod/properties.py +805 -0
- isoperiod-1.2.1/src/isoperiod/py.typed +0 -0
- isoperiod-1.2.1/src/isoperiod/timeline.py +124 -0
- isoperiod-1.2.1/src/isoperiod/words.py +181 -0
- isoperiod-1.2.1/tests/isoperiod/test_examples.py +49 -0
- isoperiod-1.2.1/tests/isoperiod/test_invariants.py +159 -0
- isoperiod-1.2.1/tests/isoperiod/test_iso.py +147 -0
- isoperiod-1.2.1/tests/isoperiod/test_parsing.py +320 -0
- isoperiod-1.2.1/tests/isoperiod/test_periods.py +1103 -0
- isoperiod-1.2.1/tests/isoperiod/test_properties.py +562 -0
- isoperiod-1.2.1/tests/isoperiod/test_timeline.py +202 -0
- isoperiod-1.2.1/tests/isoperiod/test_words.py +141 -0
- isoperiod-1.2.1/uv.lock +2043 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# https://editorconfig.org
|
|
2
|
+
root = true
|
|
3
|
+
|
|
4
|
+
[*]
|
|
5
|
+
charset = utf-8
|
|
6
|
+
end_of_line = lf
|
|
7
|
+
indent_style = space
|
|
8
|
+
indent_size = 4
|
|
9
|
+
trim_trailing_whitespace = true
|
|
10
|
+
insert_final_newline = true
|
|
11
|
+
|
|
12
|
+
[*.{html,css,js,json,sh,yml,yaml}]
|
|
13
|
+
indent_size = 2
|
|
14
|
+
|
|
15
|
+
[*.bat]
|
|
16
|
+
indent_style = tab
|
|
17
|
+
end_of_line = crlf
|
|
18
|
+
|
|
19
|
+
[LICENSE]
|
|
20
|
+
insert_final_newline = false
|
|
21
|
+
|
|
22
|
+
# Ignore binary or generated files
|
|
23
|
+
[*.{png,jpg,gif,ico,woff,woff2,ttf,eot,svg,pdf}]
|
|
24
|
+
charset = unset
|
|
25
|
+
end_of_line = unset
|
|
26
|
+
indent_style = unset
|
|
27
|
+
indent_size = unset
|
|
28
|
+
trim_trailing_whitespace = unset
|
|
29
|
+
insert_final_newline = unset
|
|
30
|
+
max_line_length = unset
|
|
31
|
+
|
|
32
|
+
[*.{diff,patch}]
|
|
33
|
+
trim_trailing_whitespace = false
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
#
|
|
3
|
+
# Pre-commit hook: runs ruff format and lint checks via uv.
|
|
4
|
+
# Requires uv on PATH: https://docs.astral.sh/uv/getting-started/installation/
|
|
5
|
+
#
|
|
6
|
+
# Installed automatically by post-generation hook via:
|
|
7
|
+
# git config core.hooksPath .githooks
|
|
8
|
+
#
|
|
9
|
+
# To install manually in an existing clone:
|
|
10
|
+
# make install-hooks
|
|
11
|
+
|
|
12
|
+
# Redirect output to stderr.
|
|
13
|
+
exec 1>&2
|
|
14
|
+
|
|
15
|
+
RED='\033[0;31m'
|
|
16
|
+
NC='\033[0m' # No Color
|
|
17
|
+
format_err=0
|
|
18
|
+
check_err=0
|
|
19
|
+
err=0
|
|
20
|
+
|
|
21
|
+
echo 'Running "uv run ruff format --check"'
|
|
22
|
+
uv run ruff format --check
|
|
23
|
+
|
|
24
|
+
if [ "$?" -eq 1 ]; then
|
|
25
|
+
format_err=1
|
|
26
|
+
err=1
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
echo 'Running "uv run ruff check"'
|
|
30
|
+
uv run ruff check
|
|
31
|
+
|
|
32
|
+
if [ "$?" -eq 1 ]; then
|
|
33
|
+
check_err=1
|
|
34
|
+
err=1
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
if [ "${format_err}" -eq 1 ] ; then
|
|
38
|
+
echo "${RED}Run \"ruff format\" before committing${NC}"
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
if [ "${check_err}" -eq 1 ] ; then
|
|
42
|
+
echo "${RED}Correct ruff errors before committing${NC}"
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
if [ "${err}" -eq 1 ] ; then
|
|
46
|
+
exit 1
|
|
47
|
+
fi
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
name: Bug Report
|
|
2
|
+
description: Something isn't working as expected.
|
|
3
|
+
title: "BUG: "
|
|
4
|
+
labels: [bug]
|
|
5
|
+
body:
|
|
6
|
+
- type: textarea
|
|
7
|
+
id: description
|
|
8
|
+
attributes:
|
|
9
|
+
label: Describe the bug
|
|
10
|
+
description: What happened? What did you expect to happen?
|
|
11
|
+
validations:
|
|
12
|
+
required: true
|
|
13
|
+
- type: textarea
|
|
14
|
+
id: steps
|
|
15
|
+
attributes:
|
|
16
|
+
label: Steps to reproduce
|
|
17
|
+
description: Minimal steps to trigger the bug.
|
|
18
|
+
placeholder: |
|
|
19
|
+
1. Install with `uv pip install isoperiod`
|
|
20
|
+
2. Run `...`
|
|
21
|
+
3. See error
|
|
22
|
+
validations:
|
|
23
|
+
required: true
|
|
24
|
+
- type: textarea
|
|
25
|
+
id: expected
|
|
26
|
+
attributes:
|
|
27
|
+
label: Expected behavior
|
|
28
|
+
validations:
|
|
29
|
+
required: true
|
|
30
|
+
- type: input
|
|
31
|
+
id: version
|
|
32
|
+
attributes:
|
|
33
|
+
label: "isoperiod version"
|
|
34
|
+
placeholder: "0.1.0"
|
|
35
|
+
validations:
|
|
36
|
+
required: true
|
|
37
|
+
- type: input
|
|
38
|
+
id: python-version
|
|
39
|
+
attributes:
|
|
40
|
+
label: Python version
|
|
41
|
+
placeholder: "3.13"
|
|
42
|
+
validations:
|
|
43
|
+
required: true
|
|
44
|
+
- type: dropdown
|
|
45
|
+
id: os
|
|
46
|
+
attributes:
|
|
47
|
+
label: Operating system
|
|
48
|
+
options:
|
|
49
|
+
- macOS
|
|
50
|
+
- Linux
|
|
51
|
+
- Windows
|
|
52
|
+
- Other
|
|
53
|
+
validations:
|
|
54
|
+
required: true
|
|
55
|
+
- type: textarea
|
|
56
|
+
id: context
|
|
57
|
+
attributes:
|
|
58
|
+
label: Additional context
|
|
59
|
+
description: Logs, tracebacks, screenshots, or anything else that helps.
|
|
60
|
+
validations:
|
|
61
|
+
required: false
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
blank_issues_enabled: true
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: Feature Request
|
|
2
|
+
description: Suggest something new.
|
|
3
|
+
title: "FEAT: "
|
|
4
|
+
labels: [enhancement]
|
|
5
|
+
body:
|
|
6
|
+
- type: textarea
|
|
7
|
+
id: description
|
|
8
|
+
attributes:
|
|
9
|
+
label: Describe the feature
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: motivation
|
|
14
|
+
attributes:
|
|
15
|
+
label: Motivation
|
|
16
|
+
description: What problem does this solve? What's the use case?
|
|
17
|
+
validations:
|
|
18
|
+
required: true
|
|
19
|
+
- type: textarea
|
|
20
|
+
id: alternatives
|
|
21
|
+
attributes:
|
|
22
|
+
label: Alternatives considered
|
|
23
|
+
validations:
|
|
24
|
+
required: false
|
|
25
|
+
- type: textarea
|
|
26
|
+
id: context
|
|
27
|
+
attributes:
|
|
28
|
+
label: Additional context
|
|
29
|
+
validations:
|
|
30
|
+
required: false
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: github-actions
|
|
4
|
+
directory: /
|
|
5
|
+
schedule:
|
|
6
|
+
interval: weekly
|
|
7
|
+
cooldown:
|
|
8
|
+
default-days: 7
|
|
9
|
+
open-pull-requests-limit: 5
|
|
10
|
+
groups:
|
|
11
|
+
github-actions:
|
|
12
|
+
patterns:
|
|
13
|
+
- "*"
|
|
14
|
+
|
|
15
|
+
- package-ecosystem: uv
|
|
16
|
+
directory: /
|
|
17
|
+
schedule:
|
|
18
|
+
interval: weekly
|
|
19
|
+
open-pull-requests-limit: 5
|
|
20
|
+
groups:
|
|
21
|
+
all-dependencies:
|
|
22
|
+
patterns:
|
|
23
|
+
- "*"
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
name: Pipeline
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
- develop
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
actions: read
|
|
14
|
+
|
|
15
|
+
pages: write
|
|
16
|
+
id-token: write
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
|
|
21
|
+
test-python:
|
|
22
|
+
uses: NERC-CEH/dri-cicd/.github/workflows/test-python.yml@main
|
|
23
|
+
with:
|
|
24
|
+
use_uv: true
|
|
25
|
+
use_pyright: true
|
|
26
|
+
coverage_threshold: 90
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
build-docs:
|
|
30
|
+
uses: NERC-CEH/dri-cicd/.github/workflows/build-docs.yml@main
|
|
31
|
+
with:
|
|
32
|
+
use_uv: true
|
|
33
|
+
pages_artifact_name: pages-artifact
|
|
34
|
+
|
|
35
|
+
deploy-docs:
|
|
36
|
+
needs: [build-docs]
|
|
37
|
+
if: github.ref == 'refs/heads/main'
|
|
38
|
+
uses: NERC-CEH/dri-cicd/.github/workflows/deploy-docs.yml@main
|
|
39
|
+
with:
|
|
40
|
+
pages_artifact_name: pages-artifact
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
release-ready:
|
|
44
|
+
if: github.event_name == 'pull_request' && github.base_ref == 'main'
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
steps:
|
|
47
|
+
- uses: actions/checkout@v7
|
|
48
|
+
with:
|
|
49
|
+
fetch-depth: 0
|
|
50
|
+
|
|
51
|
+
- name: Check version bump and changelog
|
|
52
|
+
run: |
|
|
53
|
+
BASE_VER=$(git show origin/main:pyproject.toml | grep '^version' | sed 's/.*"\(.*\)"/\1/')
|
|
54
|
+
NEW_VER=$(grep '^version' pyproject.toml | sed 's/.*"\(.*\)"/\1/')
|
|
55
|
+
|
|
56
|
+
if [ "$BASE_VER" = "$NEW_VER" ]; then
|
|
57
|
+
echo "ERROR: merges to main require a version bump (current: $NEW_VER)"
|
|
58
|
+
exit 1
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
echo "Version bumped: $BASE_VER -> $NEW_VER"
|
|
62
|
+
|
|
63
|
+
CHANGELOG="CHANGELOG/${NEW_VER}.md"
|
|
64
|
+
|
|
65
|
+
if [ ! -f "$CHANGELOG" ]; then
|
|
66
|
+
echo "ERROR: $CHANGELOG not found"
|
|
67
|
+
exit 1
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
if grep -q "<!-- Add release notes here -->" "$CHANGELOG"; then
|
|
71
|
+
echo "ERROR: $CHANGELOG still contains the stub placeholder"
|
|
72
|
+
exit 1
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
LINES=$(tail -n +2 "$CHANGELOG" | grep -vc '^$')
|
|
76
|
+
if [ "$LINES" -eq 0 ]; then
|
|
77
|
+
echo "ERROR: $CHANGELOG has no content beyond the heading"
|
|
78
|
+
exit 1
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
echo "Changelog looks good"
|
|
82
|
+
|
|
83
|
+
release:
|
|
84
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
85
|
+
needs: [test-python]
|
|
86
|
+
runs-on: ubuntu-latest
|
|
87
|
+
permissions:
|
|
88
|
+
contents: write
|
|
89
|
+
outputs:
|
|
90
|
+
released: ${{ steps.release.outputs.released }}
|
|
91
|
+
steps:
|
|
92
|
+
- uses: actions/checkout@v7
|
|
93
|
+
|
|
94
|
+
- uses: astral-sh/setup-uv@v7
|
|
95
|
+
|
|
96
|
+
- name: Configure git identity
|
|
97
|
+
run: |
|
|
98
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
99
|
+
git config user.name "github-actions[bot]"
|
|
100
|
+
|
|
101
|
+
- name: Release
|
|
102
|
+
id: release
|
|
103
|
+
env:
|
|
104
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
105
|
+
run: |
|
|
106
|
+
VERSION=$(grep '^version' pyproject.toml | sed 's/.*"\(.*\)"/\1/')
|
|
107
|
+
if git ls-remote --tags origin | grep -q "refs/tags/v$VERSION$"; then
|
|
108
|
+
echo "Tag v$VERSION already exists - skipping release"
|
|
109
|
+
exit 0
|
|
110
|
+
fi
|
|
111
|
+
make release
|
|
112
|
+
echo "released=true" >> "$GITHUB_OUTPUT"
|
|
113
|
+
|
|
114
|
+
build-dist:
|
|
115
|
+
needs: [release]
|
|
116
|
+
if: needs.release.outputs.released == 'true'
|
|
117
|
+
runs-on: ubuntu-latest
|
|
118
|
+
permissions:
|
|
119
|
+
contents: read
|
|
120
|
+
steps:
|
|
121
|
+
- uses: actions/checkout@v7
|
|
122
|
+
with:
|
|
123
|
+
persist-credentials: false
|
|
124
|
+
|
|
125
|
+
- uses: astral-sh/setup-uv@v7
|
|
126
|
+
with:
|
|
127
|
+
enable-cache: false
|
|
128
|
+
|
|
129
|
+
- name: Build
|
|
130
|
+
run: uv build
|
|
131
|
+
|
|
132
|
+
- uses: actions/upload-artifact@v6
|
|
133
|
+
with:
|
|
134
|
+
name: dist
|
|
135
|
+
path: dist/
|
|
136
|
+
|
|
137
|
+
publish-pypi:
|
|
138
|
+
needs: [build-dist]
|
|
139
|
+
runs-on: ubuntu-latest
|
|
140
|
+
permissions:
|
|
141
|
+
id-token: write
|
|
142
|
+
environment:
|
|
143
|
+
name: pypi
|
|
144
|
+
url: https://pypi.org/p/isoperiod
|
|
145
|
+
steps:
|
|
146
|
+
- uses: actions/download-artifact@v6
|
|
147
|
+
with:
|
|
148
|
+
name: dist
|
|
149
|
+
path: dist/
|
|
150
|
+
|
|
151
|
+
- name: Publish to PyPI
|
|
152
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py,cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
.benchmarks/
|
|
54
|
+
benchmark*.svg
|
|
55
|
+
|
|
56
|
+
# Translations
|
|
57
|
+
*.mo
|
|
58
|
+
*.pot
|
|
59
|
+
|
|
60
|
+
# Django stuff:
|
|
61
|
+
*.log
|
|
62
|
+
local_settings.py
|
|
63
|
+
db.sqlite3
|
|
64
|
+
db.sqlite3-journal
|
|
65
|
+
|
|
66
|
+
# Flask stuff:
|
|
67
|
+
instance/
|
|
68
|
+
.webassets-cache
|
|
69
|
+
|
|
70
|
+
# Scrapy stuff:
|
|
71
|
+
.scrapy
|
|
72
|
+
|
|
73
|
+
# Sphinx documentation
|
|
74
|
+
docs/_build/
|
|
75
|
+
# Per-member API pages generated by sphinx.ext.autosummary at build time.
|
|
76
|
+
docs/source/api/_api/
|
|
77
|
+
# Using the plot directive from code in src/examples will copy outputs to docs/src/examples. No need to commit this.
|
|
78
|
+
docs/src
|
|
79
|
+
|
|
80
|
+
# PyBuilder
|
|
81
|
+
.pybuilder/
|
|
82
|
+
target/
|
|
83
|
+
|
|
84
|
+
# Jupyter Notebook
|
|
85
|
+
.ipynb_checkpoints
|
|
86
|
+
|
|
87
|
+
# IPython
|
|
88
|
+
profile_default/
|
|
89
|
+
ipython_config.py
|
|
90
|
+
|
|
91
|
+
# pyenv
|
|
92
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
93
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
94
|
+
# .python-version
|
|
95
|
+
|
|
96
|
+
# pipenv
|
|
97
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
98
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
99
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
100
|
+
# install all needed dependencies.
|
|
101
|
+
#Pipfile.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
#poetry.lock
|
|
109
|
+
|
|
110
|
+
# pdm
|
|
111
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
112
|
+
#pdm.lock
|
|
113
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
114
|
+
# in version control.
|
|
115
|
+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
|
116
|
+
.pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
121
|
+
__pypackages__/
|
|
122
|
+
|
|
123
|
+
# Celery stuff
|
|
124
|
+
celerybeat-schedule
|
|
125
|
+
celerybeat.pid
|
|
126
|
+
|
|
127
|
+
# SageMath parsed files
|
|
128
|
+
*.sage.py
|
|
129
|
+
|
|
130
|
+
# Environments
|
|
131
|
+
.env
|
|
132
|
+
.venv
|
|
133
|
+
env/
|
|
134
|
+
venv/
|
|
135
|
+
ENV/
|
|
136
|
+
env.bak/
|
|
137
|
+
venv.bak/
|
|
138
|
+
|
|
139
|
+
# Spyder project settings
|
|
140
|
+
.spyderproject
|
|
141
|
+
.spyproject
|
|
142
|
+
|
|
143
|
+
# Rope project settings
|
|
144
|
+
.ropeproject
|
|
145
|
+
|
|
146
|
+
# mkdocs documentation
|
|
147
|
+
/site
|
|
148
|
+
|
|
149
|
+
# mypy
|
|
150
|
+
.mypy_cache/
|
|
151
|
+
.dmypy.json
|
|
152
|
+
dmypy.json
|
|
153
|
+
|
|
154
|
+
# Pyre type checker
|
|
155
|
+
.pyre/
|
|
156
|
+
|
|
157
|
+
# pytype static type analyzer
|
|
158
|
+
.pytype/
|
|
159
|
+
|
|
160
|
+
# Cython debug symbols
|
|
161
|
+
cython_debug/
|
|
162
|
+
|
|
163
|
+
# PyCharm
|
|
164
|
+
.idea/
|
|
165
|
+
|
|
166
|
+
# VSCode
|
|
167
|
+
.vscode
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# 1.0.0
|
|
2
|
+
|
|
3
|
+
The first stable, versioned public release of `isoperiod`. Developed within UKCEH's FDRI programme, the library
|
|
4
|
+
provides ISO 8601 period and duration arithmetic for environmental timeseries data, making the grid a timeseries is
|
|
5
|
+
measured on (i.e. its sampling resolution and periodicity) an object that can be compared and validated.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
### Core
|
|
10
|
+
|
|
11
|
+
- **`Period`** - the central object representing a repeating period that splits the timeline into consecutive, numbered
|
|
12
|
+
intervals, covering both fixed-length (microsecond through to multi-day) and calendar-aligned (monthly, annual)
|
|
13
|
+
periods.
|
|
14
|
+
- Built from an ISO 8601 duration string (`"PT15M"`, `"P1D"`, `"P1Y"`), from a named unit (`Period.of_days(1)`), or
|
|
15
|
+
from a `datetime.timedelta`.
|
|
16
|
+
- Immutable, hashable and sortable, so periods work as dictionary keys, in sets, and in order of length.
|
|
17
|
+
|
|
18
|
+
### Offsets and origins
|
|
19
|
+
|
|
20
|
+
- **Offsets** shift every interval boundary off its natural position, written as `<duration>+<offset>` - an
|
|
21
|
+
extension of the ISO 8601 duration format - so a hydrological day (`"P1D+T9H"`) or a water year (`"P1Y+9MT9H"`)
|
|
22
|
+
becomes a value that can be stored and passed around.
|
|
23
|
+
- **Origins** pin the grid to a chosen datetime, with the `"2024-01-01/P7D"` form.
|
|
24
|
+
|
|
25
|
+
### Intervals and ordinals
|
|
26
|
+
|
|
27
|
+
- Every interval on the timeline carries an integer **ordinal**
|
|
28
|
+
- `ordinal()` and `datetime()` map between timestamps and intervals, and are inverses of one another.
|
|
29
|
+
- `floor()` snaps a timestamp to its interval start, `interval()` gives that interval's half-open bounds, and
|
|
30
|
+
`range()` walks every interval overlapping a window of time.
|
|
31
|
+
|
|
32
|
+
### Comparison and alignment
|
|
33
|
+
|
|
34
|
+
- `is_aligned()` checks that a timestamp sits exactly on a period boundary - the check to run on incoming data
|
|
35
|
+
before trusting its claimed resolution.
|
|
36
|
+
- `is_subperiod_of()` reports whether one period's intervals nest cleanly inside another's, and `count()` gives how
|
|
37
|
+
many fit where that number is constant.
|
|
38
|
+
- `is_epoch_agnostic()` identifies periods that split the timeline the same way whatever epoch is used.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# 1.1.0
|
|
2
|
+
|
|
3
|
+
A minor release covering how a `Period` renders itself. `str()` now represents periods that carry an origin,
|
|
4
|
+
and `repr()` reports a time zone accurately.
|
|
5
|
+
|
|
6
|
+
The documentation was also reworked so that every example is executed and its real output published alongside it.
|
|
7
|
+
|
|
8
|
+
## Changed
|
|
9
|
+
|
|
10
|
+
### `str()` format
|
|
11
|
+
|
|
12
|
+
A period built with an origin previously rendered as its duration and derived offset, which discarded the origin and so
|
|
13
|
+
could not be parsed back. It now renders in the `<origin>/<duration>` form it was built from:
|
|
14
|
+
|
|
15
|
+
| | 1.0.0 | 1.1.0 |
|
|
16
|
+
|--------------------------------------------|-------------|-------------------------------|
|
|
17
|
+
| `str(Period.of("2024-01-01/P7D"))` | `"P7D+1D"` | `"2024-01-01/P7D"` |
|
|
18
|
+
| `str(Period.of("1883-01-01T09:00/P1D"))` | `"P1D+T9H"` | `"1883-01-01T09:00:00/P1D"` |
|
|
19
|
+
| `str(Period.of("2024-02-29T09:30/PT15M"))` | `"PT15M"` | `"2024-02-29T09:30:00/PT15M"` |
|
|
20
|
+
|
|
21
|
+
`Period.of(str(period)) == period` now holds for every period.
|
|
22
|
+
|
|
23
|
+
### `repr()` is the string form plus the time zone
|
|
24
|
+
|
|
25
|
+
`repr()` now adds nothing to `str()` beyond the time zone, and only when there is one:
|
|
26
|
+
|
|
27
|
+
| | 1.0.0 | 1.1.0 |
|
|
28
|
+
|----------------|-------------------|------------------|
|
|
29
|
+
| naive | `PT15M[]` | `PT15M` |
|
|
30
|
+
| UTC | `PT1H[Z]` | `PT1H[Z]` |
|
|
31
|
+
| fixed offset | `PT1H[+05:30]` | `PT1H[+05:30]` |
|
|
32
|
+
| with an origin | `P7D+1D[]-105555` | `2024-01-01/P7D` |
|
|
33
|
+
|
|
34
|
+
## Fixed
|
|
35
|
+
|
|
36
|
+
- **A named time zone reported the offset it used in the nineteenth century.** The offset was sampled at
|
|
37
|
+
`datetime.min`, so `repr()` of a period at `Europe/London` gave `P1D[-00:01:15]`, its Local Mean Time before standard
|
|
38
|
+
time was adopted. A `zoneinfo.ZoneInfo` is now identified by its key: `P1D[Europe/London]`.
|
|
39
|
+
- **Two different periods could share a `repr()`.** A `tzinfo` whose `utcoffset()` returns `None` - which
|
|
40
|
+
`datetime` permits - rendered as `PT1S[]`, identical to a naive period. Such a zone is now named after its class, so
|
|
41
|
+
no two unequal periods render alike.
|
|
42
|
+
|
|
43
|
+
## Documentation
|
|
44
|
+
|
|
45
|
+
- Every example in the user guide is now executed when the documentation is built, and the output shown beneath it is
|
|
46
|
+
what that code produced rather than something written by hand. The code lives in `docs/source/examples/`, is linted
|
|
47
|
+
and type checked with the rest of the project, and is run by the test suite.
|
|
48
|
+
- Docstring examples use standard `>>>` doctests, collected by pytest, so a changed return value fails the build.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# 1.2.0
|
|
2
|
+
|
|
3
|
+
A minor release adding two properties for rendering a `Period` in English words.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
### `verbose` and `descriptive`
|
|
8
|
+
|
|
9
|
+
`Period.verbose` spells a period's duration out in full, appending its offset or origin in brackets. `descriptive`
|
|
10
|
+
names the period's frequency with a common word where one exists (e.g. "Daily", "Yearly"), falling back to the plain
|
|
11
|
+
duration otherwise, and recognises two grids by name: the UK Water Day (`P1D+T9H`) and the UK Water Year (`P1Y+9MT9H`).
|
|
12
|
+
|
|
13
|
+
Examples:
|
|
14
|
+
|
|
15
|
+
| | `verbose` | `descriptive` |
|
|
16
|
+
|-------------------------------|------------------------------|------------------------------|
|
|
17
|
+
| `Period.of_years(1)` | `"1 year"` | `"Yearly"` |
|
|
18
|
+
| `Period.of_minutes(15)` | `"15 minutes"` | `"15 minutes"` |
|
|
19
|
+
| `Period.of("P1D+T9H")` | `"1 day (+9 hours)"` | `"Daily (UK Water Day)"` |
|
|
20
|
+
| `Period.of("2024-01-01/P7D")` | `"7 days (from 2024-01-01)"` | `"Weekly (from 2024-01-01)"` |
|
|
21
|
+
|
|
22
|
+
Both are for display only: neither is accepted by `Period.of` or any other constructor, and `str()` remains the
|
|
23
|
+
parseable form.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# 1.2.1
|
|
2
|
+
|
|
3
|
+
A patch release with no changes to the library. It is the first release published to [PyPI](https://pypi.org/project/isoperiod/).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
isoperiod can now be installed from PyPI:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install isoperiod
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
or with uv:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
uv add isoperiod
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Installing from the git repository still works, but is no longer needed.
|
|
20
|
+
|
|
21
|
+
## Packaging
|
|
22
|
+
|
|
23
|
+
- Each release merged to `main` is now built and published to PyPI automatically, using trusted publishing.
|
|
24
|
+
- Package metadata now includes keywords and classifiers for the supported Python versions (3.12 and 3.13) and for
|
|
25
|
+
the bundled type hints (`Typing :: Typed`).
|
|
26
|
+
- The README links to `LICENSE` and `CITATION.cff` are now full URLs, so they work on the PyPI project page.
|
|
27
|
+
|
|
28
|
+
## Documentation
|
|
29
|
+
|
|
30
|
+
- The installation guide now installs from PyPI instead of the git repository.
|