Skip to content

Conversation

seisman
Copy link
Member

@seisman seisman commented Sep 17, 2025

In the sequence_join function, we use str(v) to convert any value into a string that can be passed to the GMT API. However, str(v) can't handle some datetime-like objects. For example, datetime.datetime(2010, 2, 1) is converted to 2010-02-01 00:00:00 with a whitespace. Below is an example showing the issue:

>>> from pygmt.helpers import sequence_join
>>> import pandas as pd
>>> import numpy as np
>>> import xarray as xr
>>> import datetime
>>> sequence_join(
...     [
...         datetime.date(2010, 1, 1),
...         np.datetime64("2010-01-01T16:00:00"),
...         np.array("2010-03-01T00:00:00", dtype=np.datetime64),
...         datetime.datetime(2010, 3, 1),
...         pd.Timestamp("2015-01-01T12:00:00.123456789"),
...         xr.DataArray(data=np.datetime64("2005-01-01T08:00:00", "ns")),
...     ],
...     sep="/",
... )
"2010-01-01/2010-01-01T16:00:00/2010-03-01T00:00:00/2010-03-01 00:00:00/2015-01-01 12:00:00.123456789/<xarray.DataArray ()> Size: 8B\narray('2005-01-01T08:00:00.000000000', dtype='datetime64[ns]')"

This PR fixes the issue by adapting the existing codes in the kwargs_to_strings decorator:

for index, item in enumerate(value):
if " " in str(item):
# Check if there is a space " " when converting
# a pandas.Timestamp/xr.DataArray to a string.
# If so, use np.datetime_as_string instead.
# Convert datetime-like item to ISO 8601
# string format like YYYY-MM-DDThh:mm:ss.ffffff.
value[index] = np.datetime_as_string(
np.asarray(item, dtype=np.datetime64)
)

With the changes, the above code produces the following output:

'2010-01-01/2010-01-01T16:00:00/2010-03-01T00:00:00/2010-03-01T00:00:00.000000/2015-01-01T12:00:00.123456/2005-01-01T08:00:00.000000000

The changes are necessary to support arguments like

region=[datetime.date(2010, 1, 1), pd.Timestamp("2015-01-01T12:00:00.123456789"), 0, 10]

in the new alias system.

@seisman seisman added this to the 0.17.0 milestone Sep 17, 2025
@seisman seisman added enhancement Improving an existing feature needs review This PR has higher priority and needs review. labels Sep 17, 2025
@seisman seisman requested a review from Copilot September 17, 2025 13:43
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes the handling of datetime-like objects in the sequence_join function to ensure they are properly converted to ISO 8601 string format instead of containing spaces that break GMT API compatibility. The fix adapts existing code from the kwargs_to_strings decorator to detect datetime objects with spaces in their string representation and use np.datetime_as_string for proper formatting.

  • Adds logic to detect datetime-like objects that contain spaces when converted to strings
  • Uses np.datetime_as_string to convert them to proper ISO 8601 format
  • Updates documentation with examples showing the new datetime handling capabilities

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
pygmt/helpers/utils.py Adds numpy import and datetime conversion logic to sequence_join function with updated docstring examples
pygmt/alias.py Updates docstring with datetime handling examples for the _to_string function

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

# If so, use np.datetime_as_string to convert it to ISO 8601 string format
# YYYY-MM-DDThh:mm:ss.ffffff.
for idx, item in enumerate(value):
if " " in str(item):
Copy link
Preview

Copilot AI Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using ' ' in str(item) as a heuristic to detect datetime objects is fragile and could produce false positives for non-datetime objects that happen to contain spaces in their string representation. Consider using isinstance checks or hasattr to detect datetime-like objects more reliably.

Copilot uses AI. Check for mistakes.

@seisman seisman added the run/benchmark Trigger the benchmark workflow in PRs label Sep 18, 2025
@seisman seisman removed the run/benchmark Trigger the benchmark workflow in PRs label Sep 18, 2025
@seisman seisman mentioned this pull request Sep 18, 2025
39 tasks
Comment on lines 876 to 886
# 'str(v)' produces a string like '2024-01-01 00:00:00' for some datetime-like
# objects (e.g., datetime.datetime, pandas.Timestamp), which contains a space.
# If so, use np.datetime_as_string to convert it to ISO 8601 string format
# YYYY-MM-DDThh:mm:ss.ffffff.
_value = [
np.datetime_as_string(np.asarray(item, dtype=np.datetime64))
if " " in str(item)
else item
for item in value
]
return sep.join(str(v) for v in _value)
Copy link
Member

@weiji14 weiji14 Sep 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have some similar code from #2885/#562 here:

for index, item in enumerate(value):
if " " in str(item):
# Check if there is a space " " when converting
# a pandas.Timestamp/xr.DataArray to a string.
# If so, use np.datetime_as_string instead.
# Convert datetime-like item to ISO 8601
# string format like YYYY-MM-DDThh:mm:ss.ffffff.
value[index] = np.datetime_as_string(
np.asarray(item, dtype=np.datetime64)
)
newvalue = separators[fmt].join(f"{item}" for item in value)

should there be a common function maybe to avoid duplication?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the new codes are modified from the one you posted. I prefer to keep the kwargs_to_strings decorator unchanged, since we're working towards removing this decorator.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm ok. I'm just trying to think of how to remove the double str() call somehow. Something like this, but not exactly perfect:

Suggested change
# 'str(v)' produces a string like '2024-01-01 00:00:00' for some datetime-like
# objects (e.g., datetime.datetime, pandas.Timestamp), which contains a space.
# If so, use np.datetime_as_string to convert it to ISO 8601 string format
# YYYY-MM-DDThh:mm:ss.ffffff.
_value = [
np.datetime_as_string(np.asarray(item, dtype=np.datetime64))
if " " in str(item)
else item
for item in value
]
return sep.join(str(v) for v in _value)
# 'str(v)' produces a string like '2024-01-01 00:00:00' for some datetime-like
# objects (e.g., datetime.datetime, pandas.Timestamp), which contains a space.
# If so, use np.datetime_as_string to convert it to ISO 8601 string format
# YYYY-MM-DDThh:mm:ss.ffffff.
_values = [
np.datetime_as_string(np.asarray(item, dtype=np.datetime64))
if " " in str(item)
else str(item)
for item in value
]
return sep.join(_values)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed it to:

        _values = [
            np.datetime_as_string(np.asarray(item, dtype="datetime64"))
            if " " in (s := str(item))
            else s
            for item in value
        ]
        return sep.join(_values)  # type: ignore[arg-type]

@seisman seisman removed the needs review This PR has higher priority and needs review. label Sep 23, 2025
@seisman seisman merged commit 9b8d5ae into main Sep 23, 2025
23 of 24 checks passed
@seisman seisman deleted the sequence_join/datetime branch September 23, 2025 05:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Improving an existing feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants