Skip to content

Commit 702a5f1

Browse files
committed
Replace pendulum with whenever
1 parent 1a6f3cd commit 702a5f1

File tree

8 files changed

+185
-159
lines changed

8 files changed

+185
-159
lines changed

infrahub_sdk/ctl/branch.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
from rich.table import Table
66

77
from ..async_typer import AsyncTyper
8-
from ..ctl.client import initialize_client
9-
from ..ctl.utils import calculate_time_diff, catch_exception
8+
from ..utils import calculate_time_diff
9+
from .client import initialize_client
1010
from .parameters import CONFIG_PARAM
11+
from .utils import catch_exception
1112

1213
app = AsyncTyper()
1314
console = Console()

infrahub_sdk/ctl/utils.py

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,9 @@
88
from pathlib import Path
99
from typing import TYPE_CHECKING, Any, Callable, NoReturn, Optional, TypeVar
1010

11-
import pendulum
1211
import typer
1312
from click.exceptions import Exit
1413
from httpx import HTTPError
15-
from pendulum.datetime import DateTime
1614
from rich.console import Console
1715
from rich.logging import RichHandler
1816
from rich.markup import escape
@@ -152,20 +150,6 @@ def parse_cli_vars(variables: Optional[list[str]]) -> dict[str, str]:
152150
return {var.split("=")[0]: var.split("=")[1] for var in variables if "=" in var}
153151

154152

155-
def calculate_time_diff(value: str) -> str | None:
156-
"""Calculate the time in human format between a timedate in string format and now."""
157-
try:
158-
time_value = pendulum.parse(value)
159-
except pendulum.parsing.exceptions.ParserError:
160-
return None
161-
162-
if not isinstance(time_value, DateTime):
163-
return None
164-
165-
pendulum.set_locale("en")
166-
return time_value.diff_for_humans(other=pendulum.now(), absolute=True)
167-
168-
169153
def find_graphql_query(name: str, directory: str | Path = ".") -> str:
170154
if isinstance(directory, str):
171155
directory = Path(directory)

infrahub_sdk/timestamp.py

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
from __future__ import annotations
22

33
import re
4+
from datetime import UTC, datetime
45

5-
import pendulum
6-
from pendulum.datetime import DateTime
6+
from whenever import Date, Instant, Time, ZonedDateTime
77

88
REGEX_MAPPING = {
99
"seconds": r"(\d+)(s|sec|second|seconds)",
@@ -16,47 +16,67 @@ class TimestampFormatError(ValueError): ...
1616

1717

1818
class Timestamp:
19-
def __init__(self, value: str | DateTime | Timestamp | None = None):
20-
if value and isinstance(value, DateTime):
19+
obj: ZonedDateTime
20+
21+
def __init__(self, value: str | ZonedDateTime | Timestamp | None = None):
22+
if value and isinstance(value, ZonedDateTime):
2123
self.obj = value
2224
elif value and isinstance(value, self.__class__):
2325
self.obj = value.obj
2426
elif isinstance(value, str):
2527
self.obj = self._parse_string(value)
2628
else:
27-
self.obj = DateTime.now(tz="UTC")
29+
self.obj = ZonedDateTime.now("UTC")
2830

2931
@classmethod
30-
def _parse_string(cls, value: str) -> DateTime:
32+
def _parse_string(cls, value: str) -> ZonedDateTime:
33+
try:
34+
zoned_date = ZonedDateTime.parse_common_iso(value)
35+
return zoned_date
36+
except ValueError:
37+
pass
38+
3139
try:
32-
parsed_date = pendulum.parse(value)
33-
if isinstance(parsed_date, DateTime):
34-
return parsed_date
35-
except (pendulum.parsing.exceptions.ParserError, ValueError):
40+
instant_date_ = Instant.parse_common_iso(value)
41+
return instant_date_.to_tz("UTC")
42+
except ValueError:
3643
pass
3744

38-
params = {}
45+
try:
46+
date = Date.parse_common_iso(value)
47+
local_date = date.at(Time(12, 00))
48+
return local_date.assume_tz("UTC")
49+
except ValueError:
50+
pass
51+
52+
params: dict[str, float] = {}
3953
for key, regex in REGEX_MAPPING.items():
4054
match = re.search(regex, value)
4155
if match:
42-
params[key] = int(match.group(1))
56+
params[key] = float(match.group(1))
4357

44-
if not params:
45-
raise TimestampFormatError(f"Invalid time format for {value}")
58+
if params:
59+
return ZonedDateTime.now("UTC").subtract(**params) # type: ignore[call-overload]
4660

47-
return DateTime.now(tz="UTC").subtract(**params)
61+
raise TimestampFormatError(f"Invalid time format for {value}")
4862

4963
def __repr__(self) -> str:
5064
return f"Timestamp: {self.to_string()}"
5165

5266
def to_string(self, with_z: bool = True) -> str:
53-
iso8601_string = self.obj.to_iso8601_string()
54-
if not with_z and iso8601_string[-1] == "Z":
67+
if with_z:
68+
return self.obj.instant().format_common_iso()
69+
iso8601_string = self.obj.format_common_iso()
70+
if iso8601_string[-1] == "Z":
5571
iso8601_string = iso8601_string[:-1] + "+00:00"
5672
return iso8601_string
5773

5874
def to_timestamp(self) -> int:
59-
return self.obj.int_timestamp
75+
return self.obj.timestamp()
76+
77+
def to_datetime(self) -> datetime:
78+
time_str = self.to_string()
79+
return datetime.strptime(time_str[:-1], "%Y-%m-%dT%H:%M:%S.%f").replace(tzinfo=UTC)
6080

6181
def __eq__(self, other: object) -> bool:
6282
if not isinstance(other, Timestamp):

infrahub_sdk/utils.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818
from infrahub_sdk.repository import GitRepoManager
1919

2020
from .exceptions import FileNotValidError, JsonDecodeError
21+
from .timestamp import Timestamp, TimestampFormatError
2122

2223
if TYPE_CHECKING:
2324
from graphql import GraphQLResolveInfo
25+
from whenever import TimeDelta
2426

2527

2628
def base36encode(number: int) -> str:
@@ -367,3 +369,26 @@ def get_user_permissions(data: list[dict]) -> dict:
367369
groups[group_name] = permissions
368370

369371
return groups
372+
373+
374+
def calculate_time_diff(value: str) -> str | None:
375+
"""Calculate the time in human format between a timedate in string format and now."""
376+
try:
377+
time_value = Timestamp(value)
378+
except TimestampFormatError:
379+
return None
380+
381+
delta: TimeDelta = Timestamp().obj.difference(time_value.obj)
382+
(hrs, mins, secs, _) = delta.in_hrs_mins_secs_nanos()
383+
384+
if hrs and hrs < 24 and mins:
385+
return f"{hrs}h {mins}m and {secs}s ago"
386+
if hrs and hrs > 24:
387+
remaining_hrs = hrs % 24
388+
days = int((hrs - remaining_hrs) / 24)
389+
return f"{days}d and {remaining_hrs}h ago"
390+
if hrs == 0 and mins and secs:
391+
return f"{mins}m and {secs}s ago"
392+
if hrs == 0 and mins == 0 and secs:
393+
return f"{secs}s ago"
394+
return "now"

0 commit comments

Comments
 (0)