diff --git a/pygmt/src/solar.py b/pygmt/src/solar.py index 6aa59dd4527..372e168d0c7 100644 --- a/pygmt/src/solar.py +++ b/pygmt/src/solar.py @@ -32,7 +32,7 @@ def solar( r""" Plot day-night terminators and other sunlight parameters. - This function plots the day-night terminator. Alternatively, it can plot the + This method plots the day-night terminator. Alternatively, it can plot the terminators for civil twilight, nautical twilight, or astronomical twilight. Full GMT docs at :gmt-docs:`solar.html`. @@ -40,7 +40,7 @@ def solar( {aliases} - G = fill - J = projection - - T = terminator, **+d**: terminator_datetime + - T = terminator, **+d**/**+z**: terminator_datetime - V = verbose - W = pen - c = panel @@ -49,8 +49,7 @@ def solar( Parameters ---------- terminator - Set the type of terminator displayed, which can be set with either the full name - or the first letter of the name. Available options are: + Set the type of terminator. Choose one of the following: - ``"astronomical"``: Astronomical twilight - ``"civil"``: Civil twilight @@ -60,8 +59,12 @@ def solar( Refer to https://en.wikipedia.org/wiki/Twilight for the definitions of different types of twilight. terminator_datetime : str or datetime object - Set the UTC date and time of the displayed terminator [Default is the current - UTC date and time]. It can be passed as a string or Python datetime object. + Set the date and time for the terminator calculation. It can be provided as a + string or any datetime-like object recognized by :func:`pandas.to_datetime`. The + time can be specified in UTC or using a UTC offset. The offset must be an + integer number of hours (e.g., -8 or +5); fractional hours are truncated + towards zero (e.g., -8.5 becomes -8 and +5.5 becomes +5). [Default is the + current UTC date and time]. {region} {projection} {frame} @@ -102,12 +105,16 @@ def solar( """ self._activate_figure() - datetime_string = None + datetime_string, datetime_timezone = None, None if terminator_datetime: try: - datetime_string = pd.to_datetime(terminator_datetime).strftime( - "%Y-%m-%dT%H:%M:%S.%f" - ) + _datetime = pd.to_datetime(terminator_datetime) + datetime_string = _datetime.strftime("%Y-%m-%dT%H:%M:%S.%f") + # GMT's solar module uses the C 'atoi' function to parse the timezone + # offset. Ensure the offset is an integer number of hours (e.g., -8 or +5). + # Fractional hours (e.g., -8.5 or +5.5) are truncated towards zero. + if utcoffset := _datetime.utcoffset(): + datetime_timezone = int(utcoffset.total_seconds() / 3600) except ValueError as verr: raise GMTValueError(terminator_datetime, description="datetime") from verr @@ -125,6 +132,7 @@ def solar( }, ), Alias(datetime_string, name="terminator_datetime", prefix="+d"), + Alias(datetime_timezone, name="terminator_timezone", prefix="+z"), ], W=Alias(pen, name="pen"), ).add_common( diff --git a/pygmt/tests/baseline/test_solar_terminator_datetime_timezone.png.dvc b/pygmt/tests/baseline/test_solar_terminator_datetime_timezone.png.dvc new file mode 100644 index 00000000000..b9b496f70f7 --- /dev/null +++ b/pygmt/tests/baseline/test_solar_terminator_datetime_timezone.png.dvc @@ -0,0 +1,5 @@ +outs: +- md5: 10c5d3cadeb50764177bf49cbefeecb1 + size: 48753 + hash: md5 + path: test_solar_terminator_datetime_timezone.png diff --git a/pygmt/tests/test_solar.py b/pygmt/tests/test_solar.py index a1b716bd7d0..b63b6b40899 100644 --- a/pygmt/tests/test_solar.py +++ b/pygmt/tests/test_solar.py @@ -119,3 +119,29 @@ def test_solar_default_terminator(): terminator_datetime="1990-02-17 04:25:00", ) return fig + + +@pytest.mark.mpl_image_compare +def test_solar_terminator_datetime_timezone(): + """ + Test passing the terminator_datetime argument with a time string that includes a + timezone. + """ + fig = Figure() + fig.basemap(region="d", projection="W0/15c", frame=True) + fig.solar(terminator_datetime="2020-01-01T01:02:03", pen="1p,black") + fig.solar(terminator_datetime="2020-01-01T01:02:03+01:00", pen="1p,red") + fig.solar(terminator_datetime="2020-01-01T01:02:03-01:00", pen="1p,blue") + fig.solar( + terminator_datetime=datetime.datetime( + 2020, 1, 1, 1, 2, 3, tzinfo=datetime.timezone(datetime.timedelta(hours=2)) + ), + pen="1p,lightred", + ) + fig.solar( + terminator_datetime=datetime.datetime( + 2020, 1, 1, 1, 2, 3, tzinfo=datetime.timezone(datetime.timedelta(hours=-2)) + ), + pen="1p,lightblue", + ) + return fig