seecr-stdlib 0.6.4__py3-none-any.whl

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.
@@ -0,0 +1,147 @@
1
+ ## begin license ##
2
+ #
3
+ # Seecr StdLib provides convenient tools for Seecr developers
4
+ #
5
+ # Copyright (C) 2025-2026 Seecr (Seek You Too B.V.) https://seecr.nl
6
+ #
7
+ # This file is part of "Seecr Standard Library"
8
+ #
9
+ # "Seecr Standard Library" is free software: you can redistribute it and/or modify
10
+ # it under the terms of the GNU General Public License as published by
11
+ # the Free Software Foundation, either version 3 of the License, or
12
+ # (at your option) any later version.
13
+ #
14
+ # "Seecr Standard Library" is distributed in the hope that it will be useful,
15
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ # GNU General Public License for more details.
18
+ #
19
+ # You should have received a copy of the GNU General Public License
20
+ # along with "Seecr Standard Library". If not, see <http://www.gnu.org/licenses/>.
21
+ #
22
+ ## end license ##
23
+
24
+ import pytest
25
+
26
+ from seecr_stdlib.format import format_date
27
+ from datetime import datetime, timezone, date
28
+
29
+
30
+ @pytest.mark.parametrize(
31
+ "date,expected",
32
+ [
33
+ (None, ""),
34
+ (datetime(2024, 6, 15, 13, 45, 30), "2024-06-15"),
35
+ (datetime(2023, 1, 1), "2023-01-01"),
36
+ (datetime(2022, 12, 31, 23, 59, 59), "2022-12-31"),
37
+ (date(2022, 12, 31), "2022-12-31"),
38
+ ],
39
+ )
40
+ def test_format_date(date, expected):
41
+ assert format_date(date) == expected
42
+
43
+
44
+ @pytest.mark.parametrize(
45
+ "date,expected",
46
+ [
47
+ (None, ""),
48
+ (datetime(2024, 6, 15, 13, 45, 30), "15-06-2024"),
49
+ (datetime(2023, 1, 1), "01-01-2023"),
50
+ (date(2023, 1, 1), "01-01-2023"),
51
+ (datetime(2022, 12, 31, 23, 59, 59), "31-12-2022"),
52
+ ],
53
+ )
54
+ def test_format_date_nl(date, expected):
55
+ assert format_date(date, "nl") == expected
56
+
57
+
58
+ @pytest.mark.parametrize(
59
+ "date,expected",
60
+ [
61
+ (None, ""),
62
+ (datetime(2024, 6, 15, 13, 45, 30), "15-06-2024 13:45"),
63
+ (datetime(2023, 1, 1), "01-01-2023 00:00"),
64
+ (datetime(2022, 12, 31, 23, 59, 59), "31-12-2022 23:59"),
65
+ (
66
+ datetime(2026, 7, 13, 13, 40, 48, 727405, tzinfo=timezone.utc),
67
+ "13-07-2026 15:40",
68
+ ),
69
+ ],
70
+ )
71
+ def test_format_date_nl_tijd(date, expected):
72
+ assert format_date(date, "nl_tijd") == expected
73
+ assert format_date(date, "nl-t") == expected
74
+
75
+
76
+ @pytest.mark.parametrize(
77
+ "date,expected",
78
+ [
79
+ (None, ""),
80
+ (datetime(2024, 6, 15, 13, 45, 30), "zaterdag 15 juni 2024"),
81
+ (datetime(2023, 1, 1), "zondag 1 januari 2023"),
82
+ (date(2023, 1, 1), "zondag 1 januari 2023"),
83
+ (datetime(2022, 12, 31, 23, 59, 59), "zaterdag 31 december 2022"),
84
+ (datetime(2025, 12, 9), "dinsdag 9 december 2025"),
85
+ ],
86
+ )
87
+ def test_format_date_nllang(date, expected):
88
+ assert format_date(date, "nllang") == expected
89
+
90
+
91
+ @pytest.mark.parametrize(
92
+ "date,expected",
93
+ [
94
+ (None, ""),
95
+ (datetime(2024, 6, 15, 13, 45, 30), "za 15 jun 2024"),
96
+ (datetime(2023, 1, 1), "zo 1 jan 2023"),
97
+ (datetime(2022, 12, 31, 23, 59, 59), "za 31 dec 2022"),
98
+ (datetime(2025, 12, 9), "di 9 dec 2025"),
99
+ ],
100
+ )
101
+ def test_format_date_nlkort(date, expected):
102
+ assert format_date(date, "nlkort") == expected
103
+
104
+
105
+ @pytest.mark.parametrize(
106
+ "date,expected",
107
+ [
108
+ (None, ""),
109
+ (datetime(2024, 6, 15, 13, 45, 30), "zaterdag 15 juni 2024 13:45"),
110
+ (datetime(2023, 1, 1, 0, 0), "zondag 1 januari 2023 00:00"),
111
+ ],
112
+ )
113
+ def test_format_date_nllang_tijd(date, expected):
114
+ assert format_date(date, "nllang_tijd") == expected
115
+ assert format_date(date, "nl-lt") == expected
116
+
117
+
118
+ @pytest.mark.parametrize(
119
+ "string_date,style,expected",
120
+ [
121
+ ("", "iso", ""),
122
+ ("2024-06-15", "iso", "2024-06-15"),
123
+ ("invalid-date", "iso", "invalid-date"),
124
+ ("2024-06-15", "nllang", "zaterdag 15 juni 2024"),
125
+ ("2023-01-01", "nlkort", "zo 1 jan 2023"),
126
+ ("gewoon tekst", "nlkort", "gewoon tekst"),
127
+ ("2023-01-01", "unknown-style", "2023-01-01"),
128
+ ("2026-07-30 09:53:55", "nllang_tijd", "donderdag 30 juli 2026 09:53"),
129
+ ],
130
+ )
131
+ def test_format_date_with_string_input(string_date, style, expected):
132
+ assert format_date(string_date, style) == expected
133
+
134
+
135
+ @pytest.mark.parametrize(
136
+ "nr,style,expected",
137
+ [
138
+ (0, "iso", "1970-01-01"),
139
+ (0, "nllang", "donderdag 1 januari 1970"),
140
+ (0, "nlkort", "do 1 jan 1970"),
141
+ (1789395323, "iso", "2026-09-14"),
142
+ (1789395323, "nllang_tijd", "maandag 14 september 2026 16:15"),
143
+ (1789395323, "zulu", "2026-09-14T14:15:23Z"),
144
+ ],
145
+ )
146
+ def test_format_date_from_timestamp(nr, style, expected):
147
+ assert format_date(nr, style) == expected
seecr_stdlib/time.py ADDED
@@ -0,0 +1,73 @@
1
+ ## begin license ##
2
+ #
3
+ # Seecr StdLib provides convenient tools for Seecr developers
4
+ #
5
+ # Copyright (C) 2026 Seecr (Seek You Too B.V.) https://seecr.nl
6
+ #
7
+ # This file is part of "Seecr Standard Library"
8
+ #
9
+ # "Seecr Standard Library" is free software: you can redistribute it and/or modify
10
+ # it under the terms of the GNU General Public License as published by
11
+ # the Free Software Foundation, either version 3 of the License, or
12
+ # (at your option) any later version.
13
+ #
14
+ # "Seecr Standard Library" is distributed in the hope that it will be useful,
15
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ # GNU General Public License for more details.
18
+ #
19
+ # You should have received a copy of the GNU General Public License
20
+ # along with "Seecr Standard Library". If not, see <http://www.gnu.org/licenses/>.
21
+ #
22
+ ## end license ##
23
+
24
+ from datetime import datetime, timedelta, timezone
25
+ from dateutil import tz
26
+ from uuid import UUID
27
+
28
+ tz_utc = timezone.utc
29
+
30
+
31
+ def now_utc():
32
+ return datetime.now(tz=tz_utc)
33
+
34
+
35
+ def now_local():
36
+ return datetime.now(tz=tz.tzlocal())
37
+
38
+
39
+ def datetime_as_float(dt):
40
+ return dt.timestamp()
41
+
42
+
43
+ def isoformat_as_datetime_utc(isoformat, timezone=None):
44
+ dt = datetime.fromisoformat(isoformat)
45
+ tzinfo = tz_utc
46
+ if timezone:
47
+ tzinfo = tz.gettz(timezone) or tz_utc
48
+ dt = dt.replace(tzinfo=tzinfo)
49
+ return dt.astimezone(tz=tz_utc)
50
+
51
+
52
+ def timestamp_local_to_utc(t):
53
+ local_time = datetime.fromtimestamp(t, tz.tzlocal())
54
+ utc_time = datetime_local_to_utc(local_time)
55
+ return datetime_as_float(utc_time)
56
+
57
+
58
+ def datetime_local_to_utc(dt):
59
+ return dt.replace(tzinfo=tz.tzlocal()).astimezone(tz=tz_utc)
60
+
61
+
62
+ def datetime_utc_to_local(dt):
63
+ return dt.replace(tzinfo=tz_utc).astimezone(tz=tz.tzlocal())
64
+
65
+
66
+ def uuid_as_datetime_utc(uuid):
67
+ tz_unaware = datetime(1582, 10, 15) + timedelta(microseconds=UUID(uuid).time // 10)
68
+
69
+ return tz_unaware.replace(tzinfo=tz_utc)
70
+
71
+
72
+ def float_as_datetime(timestamp):
73
+ return datetime.fromtimestamp(timestamp, tz=tz_utc)
@@ -0,0 +1,84 @@
1
+ ## begin license ##
2
+ #
3
+ # Seecr StdLib provides convenient tools for Seecr developers
4
+ #
5
+ # Copyright (C) 2026 Seecr (Seek You Too B.V.) https://seecr.nl
6
+ #
7
+ # This file is part of "Seecr Standard Library"
8
+ #
9
+ # "Seecr Standard Library" is free software: you can redistribute it and/or modify
10
+ # it under the terms of the GNU General Public License as published by
11
+ # the Free Software Foundation, either version 3 of the License, or
12
+ # (at your option) any later version.
13
+ #
14
+ # "Seecr Standard Library" is distributed in the hope that it will be useful,
15
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ # GNU General Public License for more details.
18
+ #
19
+ # You should have received a copy of the GNU General Public License
20
+ # along with "Seecr Standard Library". If not, see <http://www.gnu.org/licenses/>.
21
+ #
22
+ ## end license ##
23
+
24
+ from .time import (
25
+ now_utc,
26
+ now_local,
27
+ datetime_as_float,
28
+ float_as_datetime,
29
+ isoformat_as_datetime_utc,
30
+ )
31
+ from datetime import timezone, datetime
32
+
33
+ from dateutil import tz
34
+ import pytest
35
+
36
+
37
+ def test_now_utc():
38
+ now = now_utc()
39
+ assert now.strftime("%Z") == "UTC"
40
+ assert now.tzinfo == timezone.utc
41
+
42
+
43
+ def test_now_local():
44
+ now = now_local()
45
+ assert isinstance(now.tzinfo, tz.tzlocal)
46
+ assert now.utcoffset() == datetime.now().astimezone().utcoffset()
47
+
48
+
49
+ def test_datetime_as_float():
50
+ t = datetime(1970, 1, 1, 0, 0, 0, tzinfo=timezone.utc)
51
+ assert t.timestamp() == 0.0
52
+ assert datetime_as_float(t) == 0.0
53
+
54
+
55
+ def test_timestamp_as_datetime():
56
+ now = now_utc()
57
+ now_dt = float_as_datetime(datetime_as_float(now))
58
+ assert now_dt == now
59
+
60
+
61
+ @pytest.mark.parametrize(
62
+ "isoformat, timezone_str, expected_timestamp",
63
+ [
64
+ ("2026-09-16 10:47:54", "Europe/Amsterdam", 1789548474.0),
65
+ ("2026-09-16 05:47:54", "America/Sao_Paulo", 1789548474.0),
66
+ ("2026-09-16 08:47:54", "UTC", 1789548474.0),
67
+ ("1970-01-01 12:00", "UTC", 43200.0),
68
+ ("1970-01-01 12:00", "Europe/Amsterdam", 43200.0 - 3600.0),
69
+ ("1970-01-01 12:00", "America/Sao_Paulo", 43200.0 + 3 * 3600.0),
70
+ ("1970-01-01 12:00", None, 43200.0),
71
+ ("1970-01-01 12:00", "From/Nowhere", 43200.0),
72
+ ],
73
+ )
74
+ def test_fromisoformat(isoformat, timezone_str, expected_timestamp):
75
+ dt = isoformat_as_datetime_utc(isoformat, timezone_str)
76
+ assert dt.timestamp() == expected_timestamp
77
+ assert dt.tzinfo == timezone.utc
78
+
79
+
80
+ def test_isformat_as_datetime_utc_compared_to_datetime_fromisoformat():
81
+ isoformat = "2026-09-16 10:47:54"
82
+ dt1 = isoformat_as_datetime_utc(isoformat)
83
+ dt2 = datetime.fromisoformat(isoformat + "+00:00")
84
+ assert dt1.timestamp() == dt2.timestamp()
@@ -0,0 +1,132 @@
1
+ Metadata-Version: 2.4
2
+ Name: seecr-stdlib
3
+ Version: 0.6.4
4
+ Summary: Standard tools for seecr
5
+ Author-email: Thijs Janssen <thijs@seecr.nl>, Johan Jonkers <johan@seecr.nl>
6
+ License-Expression: GPL-3.0-or-later
7
+ Project-URL: Homepage, https://github.com/seecr/seecr-stdlib
8
+ Project-URL: Source, https://github.com/seecr/seecr-stdlib
9
+ Project-URL: Issues, https://github.com/seecr/seecr-stdlib/issues
10
+ Keywords: tools
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Intended Audience :: Developers
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Requires-Python: >=3.11
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: pytest<10,>=9.0.1
24
+ Requires-Dist: python-dateutil<3,>=2.9.0
25
+ Dynamic: license-file
26
+
27
+ # Seecr Standard Library
28
+
29
+ Small, dependency-light Python utilities shared across [Seecr](https://seecr.nl) projects.
30
+
31
+ [![Tests](https://github.com/seecr/seecr-stdlib/actions/workflows/test.yml/badge.svg)](https://github.com/seecr/seecr-stdlib/actions/workflows/test.yml)
32
+ [![PyPI](https://img.shields.io/pypi/v/seecr-stdlib.svg)](https://pypi.org/project/seecr-stdlib/)
33
+
34
+ ## Installation
35
+
36
+ ```sh
37
+ pip install seecr-stdlib
38
+ ```
39
+
40
+ Requires Python 3.11 or newer.
41
+
42
+ ## What's inside
43
+
44
+ ### `Dict`: attribute access for dicts
45
+
46
+ ```python
47
+ from seecr_stdlib import Dict
48
+
49
+ d = Dict(answer=42)
50
+ d.answer # 42
51
+ d.question = "?" # same as d["question"] = "?"
52
+ ```
53
+
54
+ Keys never shadow dict methods: `Dict(items=1).items` is still the method, and
55
+ assigning `d.items = 1` raises `AttributeError`.
56
+
57
+ ### `first`
58
+
59
+ ```python
60
+ from seecr_stdlib import first
61
+
62
+ first([1, 2, 3]) # 1
63
+ first([]) # None
64
+ ```
65
+
66
+ ### `Config`: dotted-path access to JSON configuration
67
+
68
+ ```python
69
+ from pathlib import Path
70
+ from seecr_stdlib.config import Config
71
+
72
+ config = Config.read(Path("config.json"), required=["db.host"])
73
+ config.get("db.host")
74
+ config.get("db.ports.0") # list indices work too
75
+ config.get("db.user", default="postgres") # default for missing keys
76
+
77
+ # Fill in {placeholders} in all string values
78
+ config = config.expand_with({"host": "localhost"})
79
+ ```
80
+
81
+ ### `seecr_stdlib.time`: timezone-aware helpers
82
+
83
+ `now_utc()`, `now_local()`, `datetime_as_float()`, `float_as_datetime()`,
84
+ `isoformat_as_datetime_utc()`, `datetime_local_to_utc()`,
85
+ `datetime_utc_to_local()`, `timestamp_local_to_utc()` and
86
+ `uuid_as_datetime_utc()` (the timestamp of a version 1 UUID).
87
+
88
+ ### `format_date`: uniform date formatting
89
+
90
+ Accepts `date`/`datetime` objects, ISO 8601 strings and Unix timestamps.
91
+ Dutch styles that show time convert to `Europe/Amsterdam`.
92
+
93
+ ```python
94
+ from datetime import datetime, timezone
95
+ from seecr_stdlib.format import format_date
96
+
97
+ d = datetime(2026, 10, 29, 13, 45, tzinfo=timezone.utc)
98
+ format_date(d) # '2026-10-29'
99
+ format_date(d, "nl") # '29-10-2026'
100
+ format_date(d, "nl_tijd") # '29-10-2026 14:45'
101
+ format_date(d, "nllang") # 'donderdag 29 oktober 2026'
102
+ format_date(d, "nlkort_tijd") # 'do 29 okt 2026 14:45'
103
+ format_date(d, "zulu") # '2026-10-29T13:45:00Z'
104
+ ```
105
+
106
+ Short aliases: `nl-t`, `nl-l`, `nl-k`, `nl-lt`, `nl-kt`. It also works as a
107
+ Jinja filter:
108
+
109
+ ```python
110
+ env.filters["format_date"] = format_date
111
+ ```
112
+
113
+ ```jinja
114
+ {{ created | format_date('nl') }}
115
+ ```
116
+
117
+ ## Development
118
+
119
+ ```sh
120
+ python -m venv env --prompt seecr-stdlib
121
+ source env/bin/activate
122
+ pip install -e .
123
+ pytest
124
+ ```
125
+
126
+ Tests live next to the code as `<module>_test.py`.
127
+
128
+ ## License
129
+
130
+ Copyright (C) 2025-2026 Seecr (Seek You Too B.V.)
131
+
132
+ Licensed under the [GNU General Public License v3.0 or later](LICENSE).
@@ -0,0 +1,19 @@
1
+ seecr_stdlib/.gitignore,sha256=tWvEr3sBkY2ODntRp4_IUz3cidH-xu1efiOQK9DfK04,12
2
+ seecr_stdlib/__init__.py,sha256=t5dSC6iiDADZBqVm2fGSY-E7u_4JbeU_NXEiTjuncR8,946
3
+ seecr_stdlib/_version.py,sha256=l1_a_8HzaDJqVbw1kdSMo2iFfWIhkl-b6lAgabjQrx0,528
4
+ seecr_stdlib/config.py,sha256=vjXOJbxwYWthGgthsX0LlwIS5tD4r3rGuMiR7f_j70Q,2675
5
+ seecr_stdlib/config_test.py,sha256=CSXYWSWeEZbq_BXwDMBGNMVRW1ge2wbsYQFJ66SgmXM,3889
6
+ seecr_stdlib/dict.py,sha256=Pep3gJ1m3Bgi2UbLynj3-INBvYO7Q2XUggjBEEJf9FM,2526
7
+ seecr_stdlib/dict_test.py,sha256=Eab1LjPAxzA5FmHpLkSd8zAIm0ZEfVeGaBqE3kyukIk,2834
8
+ seecr_stdlib/first.py,sha256=ENhw5Hm4imQs3zfmzDU0wM7p4-uMo9KiOLW286ER0XU,979
9
+ seecr_stdlib/first_test.py,sha256=HU52jc_FQv3Lcc0kDgs8x8WyNmfEvviF4bdCytZo8to,999
10
+ seecr_stdlib/time.py,sha256=frFfdJSqiXU9XMvMoggHoOu60xgzVg4daWtin_jM4Ro,2070
11
+ seecr_stdlib/time_test.py,sha256=JGwSAus1YEVebhT0hxDvVt42T_EoBE4yPWjL5mFVKwU,2722
12
+ seecr_stdlib/format/__init__.py,sha256=tzPzQ3jnZMqXYsmDFrpB70a24HttoyytbjSfDpm92t4,913
13
+ seecr_stdlib/format/date.py,sha256=eo39JcEHELaJG8GI99PFRi6Zpk4aYuHf0jTeM4pDwxY,4081
14
+ seecr_stdlib/format/date_test.py,sha256=B6-NzlQp2gdCu7bXRN6_Uq-QVAR8bTlP3ULG6cqM40Y,4667
15
+ seecr_stdlib-0.6.4.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
16
+ seecr_stdlib-0.6.4.dist-info/METADATA,sha256=eECHJ7VdHlvNne2jDo4gWO7W7daAD2LFbLB-3CbRHdQ,3870
17
+ seecr_stdlib-0.6.4.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
18
+ seecr_stdlib-0.6.4.dist-info/top_level.txt,sha256=DVSY49-2DFcrvvqtYe4o8WvwttWLdGrKyrQhyBKmCRY,13
19
+ seecr_stdlib-0.6.4.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+