minimindslab-datetime-span 0.1.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.
- minimindslab_datetime_span-0.1.0/LICENSE +21 -0
- minimindslab_datetime_span-0.1.0/PKG-INFO +58 -0
- minimindslab_datetime_span-0.1.0/README.md +38 -0
- minimindslab_datetime_span-0.1.0/pyproject.toml +33 -0
- minimindslab_datetime_span-0.1.0/setup.cfg +4 -0
- minimindslab_datetime_span-0.1.0/src/minimindslab_datetime_span/__init__.py +17 -0
- minimindslab_datetime_span-0.1.0/src/minimindslab_datetime_span/__main__.py +37 -0
- minimindslab_datetime_span-0.1.0/src/minimindslab_datetime_span/core.py +84 -0
- minimindslab_datetime_span-0.1.0/src/minimindslab_datetime_span.egg-info/PKG-INFO +58 -0
- minimindslab_datetime_span-0.1.0/src/minimindslab_datetime_span.egg-info/SOURCES.txt +12 -0
- minimindslab_datetime_span-0.1.0/src/minimindslab_datetime_span.egg-info/dependency_links.txt +1 -0
- minimindslab_datetime_span-0.1.0/src/minimindslab_datetime_span.egg-info/entry_points.txt +2 -0
- minimindslab_datetime_span-0.1.0/src/minimindslab_datetime_span.egg-info/top_level.txt +1 -0
- minimindslab_datetime_span-0.1.0/tests/test_datetime_span.py +71 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 MiniMindsLab
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: minimindslab-datetime-span
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Parse, normalize, and compare simple UTC datetime spans with deterministic formatting.
|
|
5
|
+
Author: MiniMindsLab
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: datetime,timespan,utc,iso8601,cli
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
15
|
+
Classifier: Topic :: Utilities
|
|
16
|
+
Requires-Python: >=3.11
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# MiniMindsLab DateTime Span
|
|
22
|
+
|
|
23
|
+
`minimindslab-datetime-span` is a tiny, pure-Python utility for parsing, validating, formatting, and comparing UTC datetime spans.
|
|
24
|
+
|
|
25
|
+
## Features
|
|
26
|
+
|
|
27
|
+
- Parse UTC ISO-8601 datetime strings (`Z` or `+00:00`).
|
|
28
|
+
- Model spans with inclusive/exclusive boundaries.
|
|
29
|
+
- Deterministic canonical formatting.
|
|
30
|
+
- Compare spans for overlap, containment, and adjacency.
|
|
31
|
+
- CLI that reads from a file or stdin and prints canonicalized spans.
|
|
32
|
+
|
|
33
|
+
## Span Format
|
|
34
|
+
|
|
35
|
+
Library and CLI accept bracketed span lines:
|
|
36
|
+
|
|
37
|
+
- `[START,END]` inclusive start and end
|
|
38
|
+
- `(START,END)` exclusive start and end
|
|
39
|
+
- `[START,END)` inclusive start, exclusive end
|
|
40
|
+
- `(START,END]` exclusive start, inclusive end
|
|
41
|
+
|
|
42
|
+
Datetimes must be UTC and ISO-like (e.g. `2024-01-02T03:04:05Z`).
|
|
43
|
+
|
|
44
|
+
Canonical output uses second precision and `Z`, e.g.:
|
|
45
|
+
|
|
46
|
+
`[2024-01-02T03:04:05Z,2024-01-02T04:04:05Z)`
|
|
47
|
+
|
|
48
|
+
## CLI
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# From a file
|
|
52
|
+
miniminds-datetime-span --file spans.txt
|
|
53
|
+
|
|
54
|
+
# From stdin
|
|
55
|
+
printf '[2024-01-01T00:00:00Z,2024-01-01T01:00:00Z)\n' | miniminds-datetime-span
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Each non-empty, non-comment line (`# ...`) is parsed and canonicalized.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# MiniMindsLab DateTime Span
|
|
2
|
+
|
|
3
|
+
`minimindslab-datetime-span` is a tiny, pure-Python utility for parsing, validating, formatting, and comparing UTC datetime spans.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Parse UTC ISO-8601 datetime strings (`Z` or `+00:00`).
|
|
8
|
+
- Model spans with inclusive/exclusive boundaries.
|
|
9
|
+
- Deterministic canonical formatting.
|
|
10
|
+
- Compare spans for overlap, containment, and adjacency.
|
|
11
|
+
- CLI that reads from a file or stdin and prints canonicalized spans.
|
|
12
|
+
|
|
13
|
+
## Span Format
|
|
14
|
+
|
|
15
|
+
Library and CLI accept bracketed span lines:
|
|
16
|
+
|
|
17
|
+
- `[START,END]` inclusive start and end
|
|
18
|
+
- `(START,END)` exclusive start and end
|
|
19
|
+
- `[START,END)` inclusive start, exclusive end
|
|
20
|
+
- `(START,END]` exclusive start, inclusive end
|
|
21
|
+
|
|
22
|
+
Datetimes must be UTC and ISO-like (e.g. `2024-01-02T03:04:05Z`).
|
|
23
|
+
|
|
24
|
+
Canonical output uses second precision and `Z`, e.g.:
|
|
25
|
+
|
|
26
|
+
`[2024-01-02T03:04:05Z,2024-01-02T04:04:05Z)`
|
|
27
|
+
|
|
28
|
+
## CLI
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# From a file
|
|
32
|
+
miniminds-datetime-span --file spans.txt
|
|
33
|
+
|
|
34
|
+
# From stdin
|
|
35
|
+
printf '[2024-01-01T00:00:00Z,2024-01-01T01:00:00Z)\n' | miniminds-datetime-span
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Each non-empty, non-comment line (`# ...`) is parsed and canonicalized.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77.0.3"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "minimindslab-datetime-span"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Parse, normalize, and compare simple UTC datetime spans with deterministic formatting."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "MiniMindsLab" }]
|
|
13
|
+
dependencies = []
|
|
14
|
+
keywords = ["datetime", "timespan", "utc", "iso8601", "cli"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Topic :: Software Development :: Libraries",
|
|
23
|
+
"Topic :: Utilities"
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.scripts]
|
|
27
|
+
miniminds-datetime-span = "minimindslab_datetime_span.__main__:main"
|
|
28
|
+
|
|
29
|
+
[tool.setuptools]
|
|
30
|
+
package-dir = {"" = "src"}
|
|
31
|
+
|
|
32
|
+
[tool.setuptools.packages.find]
|
|
33
|
+
where = ["src"]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""MiniMindsLab DateTime Span library."""
|
|
2
|
+
|
|
3
|
+
from .core import (
|
|
4
|
+
DateTimeSpan,
|
|
5
|
+
SpanParseError,
|
|
6
|
+
parse_utc_datetime,
|
|
7
|
+
parse_span,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"DateTimeSpan",
|
|
12
|
+
"SpanParseError",
|
|
13
|
+
"parse_utc_datetime",
|
|
14
|
+
"parse_span",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
from .core import SpanParseError, parse_span
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _iter_lines(path: str | None):
|
|
10
|
+
if path:
|
|
11
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
12
|
+
for line in f:
|
|
13
|
+
yield line
|
|
14
|
+
else:
|
|
15
|
+
for line in sys.stdin:
|
|
16
|
+
yield line
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def main(argv: list[str] | None = None) -> int:
|
|
20
|
+
parser = argparse.ArgumentParser(description="Canonicalize UTC datetime spans.")
|
|
21
|
+
parser.add_argument("--file", "-f", dest="file", help="Input text file; defaults to stdin")
|
|
22
|
+
args = parser.parse_args(argv)
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
for raw in _iter_lines(args.file):
|
|
26
|
+
line = raw.strip()
|
|
27
|
+
if not line or line.startswith("#"):
|
|
28
|
+
continue
|
|
29
|
+
print(parse_span(line).canonical())
|
|
30
|
+
except (OSError, SpanParseError) as exc:
|
|
31
|
+
print(f"error: {exc}", file=sys.stderr)
|
|
32
|
+
return 2
|
|
33
|
+
return 0
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
if __name__ == "__main__":
|
|
37
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from datetime import datetime, timedelta, timezone
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class SpanParseError(ValueError):
|
|
8
|
+
"""Raised when datetime or span parsing/validation fails."""
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def parse_utc_datetime(value: str) -> datetime:
|
|
12
|
+
s = value.strip()
|
|
13
|
+
if s.endswith("Z"):
|
|
14
|
+
s = s[:-1] + "+00:00"
|
|
15
|
+
try:
|
|
16
|
+
dt = datetime.fromisoformat(s)
|
|
17
|
+
except ValueError as exc:
|
|
18
|
+
raise SpanParseError(f"Invalid ISO-8601 datetime: {value!r}") from exc
|
|
19
|
+
if dt.tzinfo is None:
|
|
20
|
+
raise SpanParseError(f"Datetime must include UTC timezone: {value!r}")
|
|
21
|
+
if dt.utcoffset() != timedelta(0):
|
|
22
|
+
raise SpanParseError(f"Datetime must be UTC (+00:00 or Z): {value!r}")
|
|
23
|
+
return dt.astimezone(timezone.utc)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _fmt_utc(dt: datetime) -> str:
|
|
27
|
+
return dt.astimezone(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True)
|
|
31
|
+
class DateTimeSpan:
|
|
32
|
+
start: datetime
|
|
33
|
+
end: datetime
|
|
34
|
+
start_inclusive: bool = True
|
|
35
|
+
end_inclusive: bool = False
|
|
36
|
+
|
|
37
|
+
def __post_init__(self) -> None:
|
|
38
|
+
s = self.start.astimezone(timezone.utc)
|
|
39
|
+
e = self.end.astimezone(timezone.utc)
|
|
40
|
+
object.__setattr__(self, "start", s)
|
|
41
|
+
object.__setattr__(self, "end", e)
|
|
42
|
+
if e < s:
|
|
43
|
+
raise SpanParseError("Invalid span: end precedes start")
|
|
44
|
+
|
|
45
|
+
def canonical(self) -> str:
|
|
46
|
+
l = "[" if self.start_inclusive else "("
|
|
47
|
+
r = "]" if self.end_inclusive else ")"
|
|
48
|
+
return f"{l}{_fmt_utc(self.start)},{_fmt_utc(self.end)}{r}"
|
|
49
|
+
|
|
50
|
+
def overlaps(self, other: "DateTimeSpan") -> bool:
|
|
51
|
+
if self.end < other.start or other.end < self.start:
|
|
52
|
+
return False
|
|
53
|
+
if self.end == other.start:
|
|
54
|
+
return self.end_inclusive and other.start_inclusive
|
|
55
|
+
if other.end == self.start:
|
|
56
|
+
return other.end_inclusive and self.start_inclusive
|
|
57
|
+
return True
|
|
58
|
+
|
|
59
|
+
def contains(self, other: "DateTimeSpan") -> bool:
|
|
60
|
+
left_ok = self.start < other.start or (self.start == other.start and (self.start_inclusive or not other.start_inclusive))
|
|
61
|
+
right_ok = self.end > other.end or (self.end == other.end and (self.end_inclusive or not other.end_inclusive))
|
|
62
|
+
return left_ok and right_ok
|
|
63
|
+
|
|
64
|
+
def is_adjacent(self, other: "DateTimeSpan") -> bool:
|
|
65
|
+
if self.end == other.start:
|
|
66
|
+
return not (self.end_inclusive and other.start_inclusive)
|
|
67
|
+
if other.end == self.start:
|
|
68
|
+
return not (other.end_inclusive and self.start_inclusive)
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def parse_span(text: str) -> DateTimeSpan:
|
|
73
|
+
s = text.strip()
|
|
74
|
+
if len(s) < 5:
|
|
75
|
+
raise SpanParseError("Invalid span syntax")
|
|
76
|
+
if s[0] not in "([" or s[-1] not in ")]":
|
|
77
|
+
raise SpanParseError("Span must start with '(' or '[' and end with ')' or ']'")
|
|
78
|
+
body = s[1:-1]
|
|
79
|
+
parts = body.split(",")
|
|
80
|
+
if len(parts) != 2:
|
|
81
|
+
raise SpanParseError("Span must contain exactly one comma separating start and end")
|
|
82
|
+
start = parse_utc_datetime(parts[0])
|
|
83
|
+
end = parse_utc_datetime(parts[1])
|
|
84
|
+
return DateTimeSpan(start, end, start_inclusive=(s[0] == "["), end_inclusive=(s[-1] == "]"))
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: minimindslab-datetime-span
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Parse, normalize, and compare simple UTC datetime spans with deterministic formatting.
|
|
5
|
+
Author: MiniMindsLab
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: datetime,timespan,utc,iso8601,cli
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
15
|
+
Classifier: Topic :: Utilities
|
|
16
|
+
Requires-Python: >=3.11
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# MiniMindsLab DateTime Span
|
|
22
|
+
|
|
23
|
+
`minimindslab-datetime-span` is a tiny, pure-Python utility for parsing, validating, formatting, and comparing UTC datetime spans.
|
|
24
|
+
|
|
25
|
+
## Features
|
|
26
|
+
|
|
27
|
+
- Parse UTC ISO-8601 datetime strings (`Z` or `+00:00`).
|
|
28
|
+
- Model spans with inclusive/exclusive boundaries.
|
|
29
|
+
- Deterministic canonical formatting.
|
|
30
|
+
- Compare spans for overlap, containment, and adjacency.
|
|
31
|
+
- CLI that reads from a file or stdin and prints canonicalized spans.
|
|
32
|
+
|
|
33
|
+
## Span Format
|
|
34
|
+
|
|
35
|
+
Library and CLI accept bracketed span lines:
|
|
36
|
+
|
|
37
|
+
- `[START,END]` inclusive start and end
|
|
38
|
+
- `(START,END)` exclusive start and end
|
|
39
|
+
- `[START,END)` inclusive start, exclusive end
|
|
40
|
+
- `(START,END]` exclusive start, inclusive end
|
|
41
|
+
|
|
42
|
+
Datetimes must be UTC and ISO-like (e.g. `2024-01-02T03:04:05Z`).
|
|
43
|
+
|
|
44
|
+
Canonical output uses second precision and `Z`, e.g.:
|
|
45
|
+
|
|
46
|
+
`[2024-01-02T03:04:05Z,2024-01-02T04:04:05Z)`
|
|
47
|
+
|
|
48
|
+
## CLI
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# From a file
|
|
52
|
+
miniminds-datetime-span --file spans.txt
|
|
53
|
+
|
|
54
|
+
# From stdin
|
|
55
|
+
printf '[2024-01-01T00:00:00Z,2024-01-01T01:00:00Z)\n' | miniminds-datetime-span
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Each non-empty, non-comment line (`# ...`) is parsed and canonicalized.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/minimindslab_datetime_span/__init__.py
|
|
5
|
+
src/minimindslab_datetime_span/__main__.py
|
|
6
|
+
src/minimindslab_datetime_span/core.py
|
|
7
|
+
src/minimindslab_datetime_span.egg-info/PKG-INFO
|
|
8
|
+
src/minimindslab_datetime_span.egg-info/SOURCES.txt
|
|
9
|
+
src/minimindslab_datetime_span.egg-info/dependency_links.txt
|
|
10
|
+
src/minimindslab_datetime_span.egg-info/entry_points.txt
|
|
11
|
+
src/minimindslab_datetime_span.egg-info/top_level.txt
|
|
12
|
+
tests/test_datetime_span.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
minimindslab_datetime_span
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import io
|
|
2
|
+
import os
|
|
3
|
+
import tempfile
|
|
4
|
+
import unittest
|
|
5
|
+
from contextlib import redirect_stderr, redirect_stdout
|
|
6
|
+
|
|
7
|
+
from minimindslab_datetime_span import DateTimeSpan, SpanParseError, parse_span
|
|
8
|
+
from minimindslab_datetime_span.__main__ import main
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DateTimeSpanTests(unittest.TestCase):
|
|
12
|
+
def test_acceptance_parse_and_canonical(self):
|
|
13
|
+
span = parse_span("[2024-01-01T00:00:00Z,2024-01-01T01:02:03+00:00)")
|
|
14
|
+
self.assertEqual(span.canonical(), "[2024-01-01T00:00:00Z,2024-01-01T01:02:03Z)")
|
|
15
|
+
|
|
16
|
+
def test_acceptance_end_precedes_start(self):
|
|
17
|
+
with self.assertRaisesRegex(SpanParseError, "end precedes start"):
|
|
18
|
+
parse_span("[2024-01-01T02:00:00Z,2024-01-01T01:00:00Z)")
|
|
19
|
+
|
|
20
|
+
def test_acceptance_comparison(self):
|
|
21
|
+
a = parse_span("[2024-01-01T00:00:00Z,2024-01-01T01:00:00Z)")
|
|
22
|
+
b = parse_span("[2024-01-01T00:30:00Z,2024-01-01T02:00:00Z)")
|
|
23
|
+
c = parse_span("[2024-01-01T01:00:00Z,2024-01-01T02:00:00Z)")
|
|
24
|
+
d = parse_span("[2024-01-01T00:10:00Z,2024-01-01T00:20:00Z)")
|
|
25
|
+
|
|
26
|
+
self.assertTrue(a.overlaps(b))
|
|
27
|
+
self.assertTrue(a.contains(d))
|
|
28
|
+
self.assertTrue(a.is_adjacent(c))
|
|
29
|
+
self.assertFalse(a.overlaps(c))
|
|
30
|
+
|
|
31
|
+
def test_cli_stdin(self):
|
|
32
|
+
in_data = "# comment\n\n[2024-01-01T00:00:00Z,2024-01-01T01:00:00Z)\n"
|
|
33
|
+
out = io.StringIO()
|
|
34
|
+
err = io.StringIO()
|
|
35
|
+
with redirect_stdout(out), redirect_stderr(err):
|
|
36
|
+
import sys
|
|
37
|
+
old_stdin = sys.stdin
|
|
38
|
+
sys.stdin = io.StringIO(in_data)
|
|
39
|
+
try:
|
|
40
|
+
rc = main([])
|
|
41
|
+
finally:
|
|
42
|
+
sys.stdin = old_stdin
|
|
43
|
+
self.assertEqual(rc, 0)
|
|
44
|
+
self.assertEqual(out.getvalue().strip(), "[2024-01-01T00:00:00Z,2024-01-01T01:00:00Z)")
|
|
45
|
+
self.assertEqual(err.getvalue(), "")
|
|
46
|
+
|
|
47
|
+
def test_cli_file(self):
|
|
48
|
+
fd, path = tempfile.mkstemp(text=True)
|
|
49
|
+
os.close(fd)
|
|
50
|
+
try:
|
|
51
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
52
|
+
f.write("[2024-01-01T00:00:00Z,2024-01-01T00:00:01Z]\n")
|
|
53
|
+
out = io.StringIO()
|
|
54
|
+
with redirect_stdout(out):
|
|
55
|
+
rc = main(["--file", path])
|
|
56
|
+
self.assertEqual(rc, 0)
|
|
57
|
+
self.assertEqual(out.getvalue().strip(), "[2024-01-01T00:00:00Z,2024-01-01T00:00:01Z]")
|
|
58
|
+
finally:
|
|
59
|
+
os.unlink(path)
|
|
60
|
+
|
|
61
|
+
def test_negative_invalid_syntax(self):
|
|
62
|
+
with self.assertRaises(SpanParseError):
|
|
63
|
+
parse_span("2024-01-01T00:00:00Z,2024-01-01T01:00:00Z")
|
|
64
|
+
|
|
65
|
+
def test_negative_non_utc_rejected(self):
|
|
66
|
+
with self.assertRaisesRegex(SpanParseError, "UTC"):
|
|
67
|
+
parse_span("[2024-01-01T00:00:00+01:00,2024-01-01T01:00:00+01:00)")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
if __name__ == "__main__":
|
|
71
|
+
unittest.main()
|