Skip to content

Commit

Permalink
Merge pull request #79 from kalaspuff/feature/modifier
Browse files Browse the repository at this point in the history
Added optional modifier argument to the timestamp functions
  • Loading branch information
kalaspuff authored Oct 17, 2022
2 parents d30b149 + 726cba8 commit 7e4b059
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 13 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ pip install utcnow-cli
code ~$ utcnow
2022-10-17T14:25:04.481821Z
code ~$ utcnow +365d
2023-10-17T14:25:04.481821Z
code ~$ utcnow "2022-02-28 10:10:59.100+02:00" "1984-08-01 15:00"
2022-02-28T08:10:59.100000Z
1984-08-01T15:00:00.000000Z
Expand Down Expand Up @@ -319,6 +322,14 @@ utcnow.utcnow()
# 7. datetime.datetime.utcnow().isoformat() + "Z"
```

```python
# Getting the current server time in UTC but with a modifier

import utcnow
utcnow.utcnow("now", "+10d")
# "2021-02-28T08:24:48.382262Z"
```

```python
# Or getting the current time in UTC as a datetime object

Expand Down
24 changes: 24 additions & 0 deletions tests/test_modifier.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import utcnow


def test_rfc3339_modifier() -> None:
assert utcnow.rfc3339_timestamp("2022-10-17T15:15:22.556084Z", "+365d") == "2023-10-17T15:15:22.556084Z"
assert utcnow.rfc3339_timestamp("2022-10-17T15:15:22.556084Z", ".4") == "2022-10-17T15:15:22.956084Z"
assert utcnow.rfc3339_timestamp(1666019834.321119, "-10s") == "2022-10-17T15:17:04.321119Z"


def test_unxitime_modifier() -> None:
assert utcnow.unixtime(0, "+10s") == 10.0
assert utcnow.unixtime(0, "+24h") == 86400.0
assert utcnow.unixtime("now", None) < utcnow.unixtime("+1s")
assert (
utcnow.unixtime("2022-10-17T15:15:22.556084Z", None)
== utcnow.unixtime("2022-10-17T15:15:22.556084Z", 0)
== utcnow.unixtime("2022-10-17T15:15:22.556084Z")
)


def test_unixtime_modifier() -> None:
assert utcnow.as_datetime("now", "+60s") > utcnow.as_datetime("now")
assert utcnow.as_datetime("+60s") > utcnow.as_datetime("now")
assert utcnow.as_datetime("-60s") < utcnow.as_datetime("now")
99 changes: 86 additions & 13 deletions utcnow/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@
r"^[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt ]([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9].[0-9]{6}([Zz]|[+-]00:00|)$"
)

MODIFIER_MUL = {
"s": 1,
"m": 60,
"h": 3600,
"d": 86400,
}

utc = UTC = timezone_.utc

Expand All @@ -56,7 +62,46 @@ def _is_numeric(value: str_) -> bool:


@functools.lru_cache(maxsize=128, typed=True)
def _transform_value(value: Union[str_, datetime_, object, int, float, Decimal, Real]) -> str_:
def _init_modifier(
value: Union[str_, datetime_, object, int, float, Decimal, Real], modifier: Optional[Union[str_, int, float]] = 0
) -> Tuple[Union[str_, datetime_, object, int, float, Decimal, Real], Union[int, float]]:
if (
value is not _SENTINEL
and value
and str_(value)[0] in ("+", "-")
and not modifier
and isinstance(value, str_)
and value[-1] in MODIFIER_MUL
):
modifier = value
value = _SENTINEL

if modifier is None:
modifier = 0

if isinstance(modifier, str_):
modifier_mul: int = 1
modifier_mul_str = modifier[-1]

if len(modifier) > 1 and modifier_mul_str in MODIFIER_MUL and (modifier[-2].isdigit() or modifier[-2] == "."):
modifier = modifier[:-1]
modifier_mul = MODIFIER_MUL[modifier_mul_str]

if "." in modifier:
modifier = float(modifier) * modifier_mul
else:
modifier = int(modifier) * modifier_mul

if value == "now":
value = _SENTINEL

return value, modifier


@functools.lru_cache(maxsize=128, typed=True)
def _transform_value(
value: Union[str_, datetime_, object, int, float, Decimal, Real], modifier: Optional[Union[str_, int, float]] = 0
) -> str_:
str_value: str_
try:
if isinstance(value, str_):
Expand Down Expand Up @@ -176,10 +221,18 @@ class _baseclass(metaclass=_metaclass):
def __init__(self) -> None:
pass

def __call__(self, value: Union[str_, datetime_, object, int, float, Decimal, Real] = _SENTINEL) -> str_:
def __call__(
self,
value: Union[str_, datetime_, object, int, float, Decimal, Real] = _SENTINEL,
modifier: Optional[Union[str_, int, float]] = 0,
) -> str_:
value, modifier = _init_modifier(value, modifier)

if value is _SENTINEL:
return datetime_.utcnow().isoformat(timespec="microseconds") + "Z"
return _transform_value(value)
return (
datetime_.utcnow() if not modifier else datetime_.utcnow() + timedelta_(seconds=modifier)
).isoformat(timespec="microseconds") + "Z"
return _transform_value(value) if not modifier else _transform_value(_timestamp_to_unixtime(value) + modifier)


class now_(_baseclass):
Expand All @@ -203,21 +256,41 @@ def __new__(cls, *args: Any) -> utcnow_:

return result

def as_string(self, value: Union[str_, datetime_, object, int, float, Decimal, Real] = _SENTINEL) -> str_:
def as_string(
self,
value: Union[str_, datetime_, object, int, float, Decimal, Real] = _SENTINEL,
modifier: Optional[Union[str_, int, float]] = 0,
) -> str_:
value, modifier = _init_modifier(value, modifier)

if value is _SENTINEL:
return datetime_.utcnow().isoformat(timespec="microseconds") + "Z"
return _transform_value(value)
return (
datetime_.utcnow() if not modifier else datetime_.utcnow() + timedelta_(seconds=modifier)
).isoformat(timespec="microseconds") + "Z"
return _transform_value(value) if not modifier else _transform_value(_timestamp_to_unixtime(value) + modifier)

def as_datetime(
self,
value: Union[str_, datetime_, object, int, float, Decimal, Real] = _SENTINEL,
modifier: Optional[Union[str_, int, float]] = 0,
) -> datetime_:
value, modifier = _init_modifier(value, modifier)

def as_datetime(self, value: Union[str_, datetime_, object, int, float, Decimal, Real] = _SENTINEL) -> datetime_:
if value is _SENTINEL:
# 'datetime.datetime.now(UTC)' is faster than 'datetime.datetime.utcnow().replace(tzinfo=UTC)'
return datetime_.now(UTC)
return _timestamp_to_datetime(value)
return datetime_.now(UTC) if not modifier else datetime_.now(UTC) + timedelta_(seconds=modifier)
return _timestamp_to_datetime(value) if not modifier else datetime_.now(UTC) + timedelta_(seconds=modifier)

def as_unixtime(
self,
value: Union[str_, datetime_, object, int, float, Decimal, Real] = _SENTINEL,
modifier: Optional[Union[str_, int, float]] = 0,
) -> float:
value, modifier = _init_modifier(value, modifier)

def as_unixtime(self, value: Union[str_, datetime_, object, int, float, Decimal, Real] = _SENTINEL) -> float:
if value is _SENTINEL:
return time_.time()
return _timestamp_to_unixtime(value)
return time_.time() + modifier
return _timestamp_to_unixtime(value) + modifier

def timediff(
self,
Expand Down

0 comments on commit 7e4b059

Please sign in to comment.