workclock 0.1.0__cp39-abi3-win_amd64.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.
workclock/__init__.py ADDED
@@ -0,0 +1,23 @@
1
+ """Fast, correct business-time arithmetic: working hours, SLAs and shifts."""
2
+
3
+ from workclock._errors import (
4
+ NoWorkingTimeError,
5
+ OutOfRangeError,
6
+ UnknownZoneError,
7
+ WorkclockError,
8
+ )
9
+ from workclock._schedule import Break, Holidays, Rule, Schedule, WorkDay
10
+ from workclock._workclock import __version__
11
+
12
+ __all__ = [
13
+ "Break",
14
+ "Holidays",
15
+ "NoWorkingTimeError",
16
+ "OutOfRangeError",
17
+ "Rule",
18
+ "Schedule",
19
+ "UnknownZoneError",
20
+ "WorkDay",
21
+ "WorkclockError",
22
+ "__version__",
23
+ ]
workclock/_arrays.py ADDED
@@ -0,0 +1,84 @@
1
+ """NumPy support for the batch API, without depending on NumPy.
2
+
3
+ NumPy is imported only when an array is passed in. ``datetime64`` values are
4
+ naive, so they are read and returned as UTC.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import TYPE_CHECKING, Any
10
+
11
+ if TYPE_CHECKING:
12
+ import numpy as np
13
+ from numpy.typing import NDArray
14
+ from typing_extensions import TypeGuard
15
+
16
+ _FINER_UNITS = ("ns", "ps", "fs", "as")
17
+
18
+
19
+ def is_array(value: object) -> TypeGuard[NDArray[Any]]:
20
+ """Return whether ``value`` is a NumPy array."""
21
+ kind = type(value)
22
+ return kind.__module__ == "numpy" and kind.__name__ == "ndarray"
23
+
24
+
25
+ def broadcast_shape(a: object, b: object) -> tuple[int, ...]:
26
+ """Return the shape that ``a`` and ``b`` broadcast to under NumPy's rules.
27
+
28
+ Raises ``ValueError`` naming both shapes if they do not broadcast.
29
+ """
30
+ import numpy as np
31
+
32
+ shapes = np.asarray(a).shape, np.asarray(b).shape
33
+ try:
34
+ return np.broadcast_shapes(*shapes)
35
+ except ValueError:
36
+ raise ValueError(
37
+ f"batch inputs of shapes {shapes[0]} and {shapes[1]} do not "
38
+ "broadcast together"
39
+ ) from None
40
+
41
+
42
+ def to_micros(values: object, unit: str, shape: tuple[int, ...]) -> list[int]:
43
+ """Return ``values`` as integer microseconds, broadcast to ``shape``.
44
+
45
+ Raises ``TypeError`` for numbers, which carry no unit, ``ValueError`` for
46
+ NaT, and for values with digits below a microsecond, which would otherwise
47
+ be rounded.
48
+ """
49
+ import numpy as np
50
+
51
+ given = np.asarray(values)
52
+ if given.dtype.kind in "biufc":
53
+ raise TypeError(f"expected {unit} values, got {given.dtype} numbers")
54
+ converted: NDArray[Any] = np.asarray(values, dtype=f"{unit}[us]")
55
+ array = np.broadcast_to(converted, shape)
56
+ nat = np.flatnonzero(np.isnat(array))
57
+ if nat.size:
58
+ raise ValueError(f"item {nat[0]}: batch inputs must not contain NaT")
59
+ finer = (
60
+ given.dtype.kind in "mM" and np.datetime_data(given.dtype)[0] in _FINER_UNITS
61
+ )
62
+ if finer:
63
+ lossy = np.broadcast_to(converted.astype(given.dtype) != given, shape)
64
+ if lossy.any():
65
+ i = int(np.flatnonzero(lossy)[0])
66
+ value = np.broadcast_to(given, shape).flat[i]
67
+ raise ValueError(f"item {i}: {value!r} has digits below a microsecond")
68
+ result: list[int] = array.astype(np.int64).ravel().tolist()
69
+ return result
70
+
71
+
72
+ def item(values: object, shape: tuple[int, ...], i: int) -> object:
73
+ """Return item ``i`` of ``values`` broadcast to ``shape`` and flattened."""
74
+ import numpy as np
75
+
76
+ return np.broadcast_to(np.asarray(values), shape).flat[i]
77
+
78
+
79
+ def from_micros(micros: list[int], unit: str, shape: tuple[int, ...]) -> NDArray[Any]:
80
+ """Return integer microseconds as an array of ``unit`` with ``shape``."""
81
+ import numpy as np
82
+
83
+ array: NDArray[np.int64] = np.array(micros, dtype=np.int64).reshape(shape)
84
+ return array.astype(f"{unit}[us]")
workclock/_errors.py ADDED
@@ -0,0 +1,20 @@
1
+ """Exceptions raised by workclock."""
2
+
3
+
4
+ class WorkclockError(Exception):
5
+ """Base class for workclock errors."""
6
+
7
+
8
+ class NoWorkingTimeError(WorkclockError, ValueError):
9
+ """The schedule has no working time, or a search found none within its bound.
10
+
11
+ The bound is 366 days, plus seven days per holiday date.
12
+ """
13
+
14
+
15
+ class OutOfRangeError(WorkclockError, OverflowError):
16
+ """An instant, amount or result is outside the supported range."""
17
+
18
+
19
+ class UnknownZoneError(WorkclockError, ValueError):
20
+ """A time zone name is not in the IANA time zone database."""