diff --git a/schema/definitions/0.8.0/schema/fmu_meta.json b/schema/definitions/0.8.0/schema/fmu_meta.json index e508038d8..edca4b7fa 100644 --- a/schema/definitions/0.8.0/schema/fmu_meta.json +++ b/schema/definitions/0.8.0/schema/fmu_meta.json @@ -1229,6 +1229,7 @@ "value": { "anyOf": [ { + "format": "date-time", "type": "string" }, { diff --git a/src/fmu/dataio/_utils.py b/src/fmu/dataio/_utils.py index 240a82ed2..e15e81c06 100644 --- a/src/fmu/dataio/_utils.py +++ b/src/fmu/dataio/_utils.py @@ -9,7 +9,6 @@ import shutil import uuid from copy import deepcopy -from datetime import datetime from pathlib import Path from typing import Any, Final, Literal @@ -441,70 +440,3 @@ def glue_metadata_preprocessed( meta["tracklog"].extend(newmeta["tracklog"]) return meta - - -def parse_timedata( - datablock: dict, - isoformat: bool = True, -) -> tuple[str | None, str | None]: - """The time section under datablock has variants to parse. - - Formats:: - - "time": { - "t0": { - "value": "2022-08-02T00:00:00", - "label": "base" - } - } - # with or without t1 - - # or legacy format: - "time": [ - { - "value": "2030-01-01T00:00:00", - "label": "moni" - }, - { - "value": "2010-02-03T00:00:00", - "label": "base" - } - ], - - In addition, need to parse the dates on isoformat string format to YYYMMDD - - Args: - datablock: The data block section from a metadata record - - Returns - (t0, t1) where t0 is e.g. "20220907" as string objects and/or None if not - isoformat, while t0 is on form "2030-01-23T00:00:00" if isoformat is True - - - """ - date0 = None - date1 = None - if "time" not in datablock: - return (None, None) - - if isinstance(datablock["time"], list): - date0 = datablock["time"][0]["value"] - - if len(datablock["time"]) == 2: - date1 = datablock["time"][1]["value"] - - elif isinstance(datablock["time"], dict): - date0 = datablock["time"]["t0"].get("value") - if "t1" in datablock["time"]: - date1 = datablock["time"]["t1"].get("value") - - if not isoformat: - if date0: - tdate0 = datetime.strptime(date0, "%Y-%m-%dT%H:%M:%S") - date0 = tdate0.strftime("%Y%m%d") - - if date1: - tdate1 = datetime.strptime(date1, "%Y-%m-%dT%H:%M:%S") - date1 = tdate1.strftime("%Y%m%d") - - return (date0, date1) diff --git a/src/fmu/dataio/datastructure/meta/content.py b/src/fmu/dataio/datastructure/meta/content.py index 586d97ac8..56e7c1ff9 100644 --- a/src/fmu/dataio/datastructure/meta/content.py +++ b/src/fmu/dataio/datastructure/meta/content.py @@ -2,7 +2,15 @@ from typing import Any, Dict, List, Literal, Optional, Union -from pydantic import BaseModel, Field, GetJsonSchemaHandler, RootModel, model_validator +from pydantic import ( + AwareDatetime, + BaseModel, + Field, + GetJsonSchemaHandler, + NaiveDatetime, + RootModel, + model_validator, +) from pydantic_core import CoreSchema from typing_extensions import Annotated @@ -18,7 +26,7 @@ class FMUTimeObject(BaseModel): default=None, examples=["base", "monitor", "mylabel"], ) - value: Optional[str] = Field( + value: Optional[Union[NaiveDatetime, AwareDatetime]] = Field( default=None, examples=["2020-10-28T14:28:02"], ) diff --git a/src/fmu/dataio/providers/objectdata/_base.py b/src/fmu/dataio/providers/objectdata/_base.py index 9e6e6f235..0569439f9 100644 --- a/src/fmu/dataio/providers/objectdata/_base.py +++ b/src/fmu/dataio/providers/objectdata/_base.py @@ -9,7 +9,7 @@ from fmu.dataio._definitions import ConfigurationError from fmu.dataio._logging import null_logger -from fmu.dataio._utils import generate_description, parse_timedata +from fmu.dataio._utils import generate_description from fmu.dataio.datastructure._internal.internal import AllowedContent from fmu.dataio.datastructure.meta import content @@ -65,6 +65,22 @@ def derive_name( return "" +def get_timedata_from_existing(meta_timedata: dict) -> tuple[datetime, datetime]: + if isinstance(meta_timedata, list): + date0 = meta_timedata[0]["value"] + if len(meta_timedata) == 2: + date1 = meta_timedata[1]["value"] + elif isinstance(meta_timedata, dict): + date0 = meta_timedata["t0"].get("value") + if "t1" in meta_timedata: + date1 = meta_timedata["t1"].get("value") + + return ( + datetime.strptime(date0, "%Y-%m-%dT%H:%M:%S"), + datetime.strptime(date1, "%Y-%m-%dT%H:%M:%S"), + ) + + def get_fmu_time_object(timedata_item: list[str]) -> content.FMUTimeObject: """ Returns a FMUTimeObject from a timedata item on list @@ -73,7 +89,7 @@ def get_fmu_time_object(timedata_item: list[str]) -> content.FMUTimeObject: """ value, *label = timedata_item return content.FMUTimeObject( - value=datetime.strptime(str(value), "%Y%m%d").isoformat(), + value=datetime.strptime(str(value), "%Y%m%d"), label=label[0] if label else None, ) @@ -103,8 +119,8 @@ class ObjectDataProvider(ABC): efolder: str = field(default="") extension: str = field(default="") fmt: str = field(default="") - time0: str | None = field(default=None) - time1: str | None = field(default=None) + time0: datetime | None = field(default=None) + time1: datetime | None = field(default=None) @staticmethod def _validate_get_ext(fmt: str, subtype: str, validator: dict[str, V]) -> V: @@ -205,7 +221,7 @@ def _derive_timedata(self) -> dict[str, str] | None: if stop: assert start and start.value is not None # for mypy assert stop and stop.value is not None # for mypy - if datetime.fromisoformat(start.value) > datetime.fromisoformat(stop.value): + if start.value > stop.value: start, stop = stop, start self.time0, self.time1 = start.value, stop.value if stop else None @@ -294,7 +310,10 @@ def from_metadata_dict( """Instantiate from existing metadata.""" relpath = Path(meta_existing["file"]["relative_path"]) - time0, time1 = parse_timedata(meta_existing["data"]) + + time0, time1 = None, None + if "time" in meta_existing["data"]: + time0, time1 = get_timedata_from_existing(meta_existing["data"]["time"]) return cls( obj=obj, diff --git a/tests/test_units/test_utils.py b/tests/test_units/test_utils.py index 25caff155..a17bf9540 100644 --- a/tests/test_units/test_utils.py +++ b/tests/test_units/test_utils.py @@ -34,55 +34,6 @@ def test_check_if_number(value, result): assert utils.check_if_number(value) == result -@pytest.mark.parametrize( - "given, expected, isoformat", - ( - ( - {}, - (None, None), - True, - ), - ( - {"time": {"t0": {"value": "2022-08-02T00:00:00", "label": "base"}}}, - ("2022-08-02T00:00:00", None), - True, - ), - ( - { - "time": [ - {"value": "2030-01-01T00:00:00", "label": "moni"}, - {"value": "2010-02-03T00:00:00", "label": "base"}, - ] - }, - ("2030-01-01T00:00:00", "2010-02-03T00:00:00"), - True, - ), - ( - {}, - (None, None), - False, - ), - ( - {"time": {"t0": {"value": "2022-08-02T00:00:00", "label": "base"}}}, - ("20220802", None), - False, - ), - ( - { - "time": [ - {"value": "2030-01-01T00:00:00", "label": "moni"}, - {"value": "2010-02-03T00:00:00", "label": "base"}, - ] - }, - ("20300101", "20100203"), - False, - ), - ), -) -def test_parse_timedata(given: dict, expected: tuple, isoformat: bool): - assert utils.parse_timedata(given, isoformat) == expected - - def test_get_object_name(): assert utils.get_object_name(object()) is None