Skip to content

Commit

Permalink
feat: add two new time functions to utils.py
Browse files Browse the repository at this point in the history
Includes some extensive tests.

Co-authored-by: Pete Gadomski <[email protected]>
  • Loading branch information
Phil Varner and gadomski committed Feb 10, 2023
1 parent 2d01225 commit bab2f10
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Added

- Additional util methods `now_in_utc` and `now_to_rfc3339_str` ([#760](https://github.com/stac-utils/pystac/pull/760))
- Add `media_type` and `role` filtering to Item and Collection `get_assets()` method ([#936](https://github.com/stac-utils/pystac/pull/936))
- `Asset.has_role` ([#936](https://github.com/stac-utils/pystac/pull/936))
- Enum MediaType entry for flatgeobuf ([discussion](https://github.com/flatgeobuf/flatgeobuf/discussions/112#discussioncomment-4606721)) ([#938](https://github.com/stac-utils/pystac/pull/938))
Expand Down
16 changes: 15 additions & 1 deletion pystac/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,14 +329,28 @@ def datetime_to_str(dt: datetime, timespec: str = "auto") -> str:
def str_to_datetime(s: str) -> datetime:
"""Converts a string timestamp to a :class:`datetime.datetime` instance using
:meth:`dateutil.parser.parse` under the hood. The input string may be in any
format :std:doc:`supported by the parser <parser>`.
format :std:doc:`supported by the parser <parser>`. This includes many formats
including ISO 8601 and RFC 3339.
Args:
s (str) : The string to convert to :class:`datetime.datetime`.
Returns:
str: The :class:`datetime.datetime` represented the by the string.
"""
return dateutil.parser.isoparse(s)


def now_in_utc() -> datetime:
"""Returns a datetime value of now with the UTC timezone applied"""
return datetime.now(timezone.utc)


def now_to_rfc3339_str() -> str:
"""Returns an RFC 3339 string representing now"""
return datetime_to_str(now_in_utc())


def geometry_to_bbox(geometry: Dict[str, Any]) -> List[float]:
"""Extract the bounding box from a geojson geometry
Expand Down
79 changes: 79 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@
from datetime import datetime, timedelta, timezone
from typing import Optional

import pytest
from dateutil import tz

from pystac import utils
from pystac.utils import (
is_absolute_href,
make_absolute_href,
make_relative_href,
now_in_utc,
now_to_rfc3339_str,
str_to_datetime,
)
from tests.utils import TestCases
Expand Down Expand Up @@ -349,3 +352,79 @@ def test_geojson_bbox(self) -> None:
for geom in geom_dicts:
got = utils.geometry_to_bbox(geom)
self.assertNotEqual(got, None)


@pytest.mark.parametrize(
"datetime",
[
"37-01-01T12:00:27.87Z", # invalid year, must be 4 digits
"21985-12-12T23:20:50.52Z", # year must be 4 digits
"1985-13-12T23:20:50.52Z", # month > 12
"1985-12-32T23:20:50.52Z", # day > 31
"1985-12-01T25:20:50.52Z", # hour > 24
"1985-12-01T00:60:50.52Z", # minute > 59
"1985-12-01T00:06:61.52Z", # second > 60
"1985-04-12T23:20:50.Z", # fractional sec . but no frac secs
"1985-04-12T23:20:50,Z", # fractional sec , but no frac secs
"1990-12-31T23:59:61Z", # second > 60 w/o fractional seconds
],
)
def test_parse_invalid_rfc3339_str_to_datetime(datetime: str) -> None:
with pytest.raises(ValueError):
str_to_datetime(datetime)


@pytest.mark.parametrize(
"datetime",
[
"1985-04-12", # date only
"1937-01-01T12:00:27.87+0100", # invalid TZ format, no sep :
"1985-12-12T23:20:50.52", # no TZ
"1985-04-12T23:20:50,52Z", # comma as frac sec sep disallowed in RFC3339
],
)
def test_parse_invalid_rfc3339_but_valid_iso8601_str_to_datetime(datetime: str) -> None:
assert str_to_datetime(datetime)


@pytest.mark.parametrize(
"datetime",
[
"1985-04-12T23:20:50.52Z",
"1996-12-19T16:39:57-00:00",
"1996-12-19T16:39:57+00:00",
"1996-12-19T16:39:57-08:00",
"1996-12-19T16:39:57+08:00",
"1937-01-01T12:00:27.87+01:00",
"1985-04-12T23:20:50.52Z",
"1937-01-01T12:00:27.8710+01:00",
"1937-01-01T12:00:27.8+01:00",
"1937-01-01T12:00:27.8Z",
"2020-07-23T00:00:00.000+03:00",
"2020-07-23T00:00:00+03:00",
"1985-04-12t23:20:50.000z",
"2020-07-23T00:00:00Z",
"2020-07-23T00:00:00.0Z",
"2020-07-23T00:00:00.01Z",
"2020-07-23T00:00:00.012Z",
"2020-07-23T00:00:00.0123Z",
"2020-07-23T00:00:00.01234Z",
"2020-07-23T00:00:00.012345Z",
"2020-07-23T00:00:00.0123456Z",
"2020-07-23T00:00:00.01234567Z",
"2020-07-23T00:00:00.012345678Z",
],
)
def test_parse_valid_iso8601_str_to_datetime(datetime: str) -> None:
assert str_to_datetime(datetime)


def test_now_functions() -> None:
now1 = now_in_utc()
time.sleep(1)
now2 = now_in_utc()

assert now1 < now2
assert now1.tzinfo == timezone.utc

assert str_to_datetime(now_to_rfc3339_str())

0 comments on commit bab2f10

Please sign in to comment.