diff --git a/.github/workflows/schedule-update-actions.yml b/.github/workflows/schedule-update-actions.yml index 2010cb8..c026447 100644 --- a/.github/workflows/schedule-update-actions.yml +++ b/.github/workflows/schedule-update-actions.yml @@ -12,14 +12,14 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4.2.0 + - uses: actions/checkout@v4.2.1 with: # [Required] Access token with `workflow` scope. - token: ${{ secrets.GITHUB_TOKEN }} + token: ${{ secrets.WORKFLOW_SECRET_ODSBOX }} - name: Run GitHub Actions Version Updater uses: saadmk11/github-actions-version-updater@v0.8.1 with: # [Required] Access token with `workflow` scope. - token: ${{ secrets.GITHUB_TOKEN }} + token: ${{ secrets.WORKFLOW_SECRET_ODSBOX }} pull_request_title: "ci: Update GitHub Actions to Latest Version" diff --git a/.github/workflows/template-sync.yml b/.github/workflows/template-sync.yml index 2ec662b..d886da2 100644 --- a/.github/workflows/template-sync.yml +++ b/.github/workflows/template-sync.yml @@ -5,7 +5,7 @@ jobs: sync: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4.2.0 # important! + - uses: actions/checkout@v4.2.1 # important! - uses: euphoricsystems/action-sync-template-repository@v2.5.1 with: github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/pyproject.toml b/pyproject.toml index 1c19109..c04a210 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ classifiers = [ "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Scientific/Engineering :: Information Analysis", ] -requires-python = ">=3.10.14" +requires-python = ">=3.10.12" dynamic = ["version"] dependencies = [ "protobuf>=5.27.0,<6.0.0", @@ -47,7 +47,7 @@ test = [ "flake8-formatter_junit_xml", "flake8", "flake8-pyproject", - "pre-commit==3.8.0", + "pre-commit==4.0.0", "pylint==3.3.1", "pylint_junit", "pytest-cov==5.0.0", diff --git a/src/odsbox/__init__.py b/src/odsbox/__init__.py index 42dcd31..a70c35c 100644 --- a/src/odsbox/__init__.py +++ b/src/odsbox/__init__.py @@ -2,4 +2,4 @@ from __future__ import annotations -__version__ = "1.0.0" +__version__ = "1.0.1" diff --git a/src/odsbox/asam_time.py b/src/odsbox/asam_time.py index 9c40013..981cadc 100644 --- a/src/odsbox/asam_time.py +++ b/src/odsbox/asam_time.py @@ -1,4 +1,4 @@ -"""datetime is represented as string in ASAM ODS. Here you find some helpers.""" +"""datetime is represented as string in ASAM ODS formatted using YYYYMMDDHHMMSSFFF. Here you find some helpers.""" from __future__ import annotations @@ -22,7 +22,8 @@ def to_pd_timestamp(asam_time: str) -> pd.Timestamp: """ Convert ASAM ODS datetime string to pandas Timestamp. - :param str asam_time: ASAM ODS datetime string to be converted + :param str asam_time: ASAM ODS datetime string to be converted. formatted like `YYYYMMDDHHMMSSFFF`. + It must at least contain `YYYYMMDD`. :raises requests.SyntaxError: If content is invalid. :return pd.Timestamp: Corresponding pandas Timestamp value. For empty string `pd.NaT` is returned. """ @@ -42,3 +43,24 @@ def to_pd_timestamp(asam_time: str) -> pd.Timestamp: microsecond=int(asam_time_normalized[14:20]) if asam_time_len >= 20 else 0, nanosecond=int(asam_time_normalized[20:23]) if asam_time_len >= 23 else 0, ) + + +def from_pd_timestamp(timestamp: pd.Timestamp, length: int = 17) -> str: + """ + Convert a pandas Timestamp to a string formatted as asamtime (`YYYYMMDDHHMMSSFFF`). + + :param pd.Timestamp timestamp: The pandas Timestamp to convert. The timezone + information given in timestamp is ignored. + :param int length: The desired length of the output string. The final string will + be truncated to the specified length. The maximum is 23 including + nanoseconds. + :return str: The asam time representation of the timestamp. For `None` or `pd.NaT` + an empty string is returned. + """ + if timestamp is None or pd.isna(timestamp): + return "" + + asam_time_str = timestamp.strftime("%Y%m%d%H%M%S%f") + if length > 20: + asam_time_str += f"{timestamp.nanosecond:03d}" + return asam_time_str[: min(length, len(asam_time_str))] diff --git a/tests/test_asam_time.py b/tests/test_asam_time.py index 5d47cd2..4bcdeb3 100644 --- a/tests/test_asam_time.py +++ b/tests/test_asam_time.py @@ -8,6 +8,68 @@ import pandas as pd +def test_from_pd_timestamp(): + dates = [ + ("20241211", "2024-12-11T00:00:00"), + ("20241211133310", "2024-12-11T13:33:10"), + ("2024121113331056", "2024-12-11T13:33:10.560000"), + ("20241211133310561234567", "2024-12-11T13:33:10.561234567"), + ("2024121113", "2024-12-11T13:00:00"), + ("202412111333", "2024-12-11T13:33:00"), + ("20241211133356", "2024-12-11T13:33:56"), + ("202412111333561", "2024-12-11T13:33:56.100000"), + ("2024121113335612", "2024-12-11T13:33:56.120000"), + ("20241211133356123", "2024-12-11T13:33:56.123000"), + ("202412111333561234", "2024-12-11T13:33:56.123400"), + ("2024121113335612345", "2024-12-11T13:33:56.123450"), + ("20241211133356123456", "2024-12-11T13:33:56.123456"), + ("202412111333561234567", "2024-12-11T13:33:56.123456700"), + ("2024121113335612345678", "2024-12-11T13:33:56.123456780"), + ("20241211133356123456789", "2024-12-11T13:33:56.123456789"), + ("20241211133356123456789", "2024-12-11T13:33:56.123456789"), + ("19700101", "1970-01-01T00:00:00"), + ("1970010100", "1970-01-01T00:00:00"), + ("197001010000", "1970-01-01T00:00:00"), + ("19700101000000", "1970-01-01T00:00:00"), + ] + + for date in dates: + pd_timestamp_in = pd.Timestamp(date[1]) + result = asam_time.from_pd_timestamp(pd_timestamp_in, length=len(date[0])) + assert result == date[0] + # lets do the roundtrip + pd_timestamp_out = asam_time.to_pd_timestamp(result) + assert pd_timestamp_out == pd_timestamp_in + + +def test_from_pd_timestamp_special(): + assert "20241211000000000000000" == asam_time.from_pd_timestamp(pd.Timestamp("2024-12-11T00:00:00"), 23) + assert "20241211000000000000" == asam_time.from_pd_timestamp(pd.Timestamp("2024-12-11T00:00:00"), 20) + assert "20241211000000000" == asam_time.from_pd_timestamp(pd.Timestamp("2024-12-11T00:00:00"), 17) + assert "20241211000000000" == asam_time.from_pd_timestamp(pd.Timestamp("2024-12-11T00:00:00")) + + +def test_from_pd_timestamp_z(): + assert "20241211133310000" == asam_time.from_pd_timestamp(pd.Timestamp("2024-12-11T13:33:10Z")) + assert "20240711133310000" == asam_time.from_pd_timestamp(pd.Timestamp("2024-07-11T13:33:10Z")) + assert "20241211133310000" == asam_time.from_pd_timestamp(pd.Timestamp("2024-12-11T13:33:10+02")) + assert "20240711133310000" == asam_time.from_pd_timestamp(pd.Timestamp("2024-07-11T13:33:10+02")) + + +def test_from_pd_timestamp_special2(): + assert "20241211133356123456789" == asam_time.from_pd_timestamp(pd.Timestamp("2024-12-11T13:33:56.123456789"), 23) + assert "20241211133356123" == asam_time.from_pd_timestamp(pd.Timestamp("2024-12-11T13:33:56.123456789")) + assert "20241211133356123456789" == asam_time.from_pd_timestamp(pd.Timestamp("2024-12-11T13:33:56.123456789"), 26) + assert "20241211133356123456" == asam_time.from_pd_timestamp(pd.Timestamp("2024-12-11T13:33:56.123456789"), 20) + assert "20241211133356123" == asam_time.from_pd_timestamp(pd.Timestamp("2024-12-11T13:33:56.123456789"), 17) + assert "20241211133356" == asam_time.from_pd_timestamp(pd.Timestamp("2024-12-11T13:33:56.123456789"), 14) + + +def test_from_pd_timestamp_none(): + assert "" == asam_time.from_pd_timestamp(pd.NaT) + assert "" == asam_time.from_pd_timestamp(None) + + def test_to_pd_timestamp(): dates = [ ("20241211", "2024-12-11T00:00:00"),