From 73ccc43b2c5d05686e0a7627230807601dcb3ff2 Mon Sep 17 00:00:00 2001 From: Victor Ramirez Date: Mon, 16 Dec 2024 17:38:56 +0000 Subject: [PATCH] PLAT-1361: Allow for pandas timestamps to be passed in API --- coinmetrics/_data_collection.py | 8 +++++++- coinmetrics/_utils.py | 15 +++++++++++++-- test/test_to_dataframe.py | 17 +++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/coinmetrics/_data_collection.py b/coinmetrics/_data_collection.py index d8cfff4..95644bd 100644 --- a/coinmetrics/_data_collection.py +++ b/coinmetrics/_data_collection.py @@ -983,20 +983,26 @@ def _helper_to_list(data_collection: DataCollection) -> List[Dict[str, Any]]: return data_collection.to_list() @staticmethod - def parse_date(date_input: Union[datetime, date, str]) -> datetime: + def parse_date(date_input: Union[datetime, date, str, pd.Timestamp]) -> datetime: """ Parses a datetime object or datetime string into a datetime object. Datetime string must be a valid ISO 8601 format. Timezone aware objects are converted to UTC :param date_input: Union[datetime, date, str] date to parse into datetime :return: datetime """ + # pd.Timestamp is a subset of datetime + if isinstance(date_input, pd.Timestamp): + date_input = date_input.to_pydatetime() + if isinstance(date_input, datetime): if date_input.tzname() is None: return date_input else: return date_input.astimezone(timezone.utc).replace(tzinfo=None) + if isinstance(date_input, date): return datetime(date_input.year, date_input.month, date_input.day) + formats = [ "%Y-%m-%dT%H:%M:%S", "%Y-%m-%dT%H%M%S", diff --git a/coinmetrics/_utils.py b/coinmetrics/_utils.py index 14d7916..bd098ed 100644 --- a/coinmetrics/_utils.py +++ b/coinmetrics/_utils.py @@ -1,5 +1,5 @@ import pathlib -from datetime import date, datetime +from datetime import date, datetime, timezone from enum import Enum from functools import wraps from logging import getLogger @@ -7,6 +7,7 @@ from time import sleep from typing import Any, Callable, Dict, List, Optional, Tuple, Union, Set from coinmetrics._typing import FilePathOrBuffer, UrlParamTypes +from pandas import Timestamp logger = getLogger("cm_client_utils") @@ -18,7 +19,17 @@ def transform_url_params_values_to_str( for param_name, param_value in params.items(): if param_value is None: continue - if isinstance(param_value, (datetime, date)): + if isinstance(param_value, (datetime, date, Timestamp)): + if isinstance(param_value, Timestamp): + param_value = param_value.to_pydatetime() + + if isinstance(param_value, datetime): + param_value = param_value.astimezone(timezone.utc).replace(tzinfo=None) + + if isinstance(param_value, date) and not isinstance(param_value, datetime): + param_value = datetime(param_value.year, param_value.month, param_value.day) + + assert isinstance(param_value, datetime) if param_name.endswith("_time"): processed_params[param_name] = param_value.isoformat() else: diff --git a/test/test_to_dataframe.py b/test/test_to_dataframe.py index 9a19d85..4289166 100644 --- a/test/test_to_dataframe.py +++ b/test/test_to_dataframe.py @@ -34,5 +34,22 @@ def test_transaction_tracker_catalog() -> None: assert len(data) >= 2 +@pytest.mark.skipif(not cm_api_key_set, reason=REASON_TO_SKIP) +def test_pandas_timestamp() -> None: + list_timestamp_objects = [ + pd.Timestamp(2024, 1, 1), + pd.Timestamp(2024, 1, 1, 12, 0, 0), + pd.Timestamp(2024, 1, 1, 12, 0, 0).tz_localize('US/Eastern'), + pd.Timestamp(2024, 1, 1, 0, 0, 0).tz_localize('UTC') + ] + for ts in list_timestamp_objects: + data = client.get_asset_metrics( + 'btc', + 'ReferenceRateUSD', + start_time=ts, + limit_per_asset=1 + ).to_dataframe() + + if __name__ == "__main__": pytest.main()