Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 64 additions & 24 deletions nc_time_axis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def pick_format(self, ndays):

def __call__(self, x, pos=0):
format_string = self.pick_format(ndays=self.locator.ndays)
dt = cftime.utime(self.time_units, self.calendar).num2date(x)
dt = cftime.num2date(x, self.time_units, self.calendar)
return dt.strftime(format_string)


Expand Down Expand Up @@ -143,50 +143,84 @@ def tick_values(self, vmin, vmax):

self.ndays = float(abs(vmax - vmin))

utime = cftime.utime(self.date_unit, self.calendar)
lower = utime.num2date(vmin)
upper = utime.num2date(vmax)
lower = cftime.num2date(vmin, self.date_unit, self.calendar)
upper = cftime.num2date(vmax, self.date_unit, self.calendar)

resolution, n = self.compute_resolution(vmin, vmax, lower, upper)

if resolution == 'YEARLY':
# TODO START AT THE BEGINNING OF A DECADE/CENTURY/MILLENIUM as
# appropriate.
years = self._max_n_locator.tick_values(lower.year, upper.year)
ticks = [cftime.datetime(int(year), 1, 1) for year in years]
ticks = []
for year in years:
ticks.append(cftime.datetime(int(year), 1, 1, calendar=None))
elif resolution == 'MONTHLY':
# TODO START AT THE BEGINNING OF A DECADE/CENTURY/MILLENIUM as
# appropriate.
months_offset = self._max_n_locator.tick_values(0, n)
ticks = []
for offset in months_offset:
year = lower.year + np.floor((lower.month + offset) / 12)
month = ((lower.month + offset) % 12) + 1
ticks.append(cftime.datetime(int(year), int(month), 1))
year = int(lower.year + np.floor((lower.month + offset) / 12))
month = int(((lower.month + offset) % 12) + 1)
ticks.append(cftime.datetime(year, month, 1, calendar=None))
elif resolution == 'DAILY':
# TODO: It would be great if this favoured multiples of 7.
days = self._max_n_locator_days.tick_values(vmin, vmax)
ticks = [utime.num2date(dt) for dt in days]
ticks = [
cftime.num2date(
dt,
self.date_unit,
self.calendar
) for dt in days
]
elif resolution == 'HOURLY':
hour_unit = 'hours since 2000-01-01'
hour_utime = cftime.utime(hour_unit, self.calendar)
in_hours = hour_utime.date2num([lower, upper])
in_hours = cftime.date2num(
[lower, upper],
hour_unit,
self.calendar
)
hours = self._max_n_locator.tick_values(in_hours[0], in_hours[1])
ticks = [hour_utime.num2date(dt) for dt in hours]
ticks = [
cftime.num2date(
dt,
hour_unit,
self.calendar
) for dt in hours
]
elif resolution == 'MINUTELY':
minute_unit = 'minutes since 2000-01-01'
minute_utime = cftime.utime(minute_unit, self.calendar)
in_minutes = minute_utime.date2num([lower, upper])
in_minutes = cftime.date2num(
[lower, upper],
minute_unit,
self.calendar
)
minutes = self._max_n_locator.tick_values(in_minutes[0],
in_minutes[1])
ticks = [minute_utime.num2date(dt) for dt in minutes]
ticks = [
cftime.num2date(
dt,
minute_unit,
self.calendar
) for dt in minutes
]
elif resolution == 'SECONDLY':
second_unit = 'seconds since 2000-01-01'
second_utime = cftime.utime(second_unit, self.calendar)
in_seconds = second_utime.date2num([lower, upper])
in_seconds = cftime.date2num(
[lower, upper],
second_unit,
self.calendar
)
seconds = self._max_n_locator.tick_values(in_seconds[0],
in_seconds[1])
ticks = [second_utime.num2date(dt) for dt in seconds]
ticks = [
cftime.num2date(
dt,
second_unit,
self.calendar
) for dt in seconds
]
else:
msg = 'Resolution {} not implemented yet.'.format(resolution)
raise ValueError(msg)
Expand All @@ -199,7 +233,7 @@ def tick_values(self, vmin, vmax):
"standard",
]:
ticks = [t for t in ticks if t.year != 0]
return utime.date2num(ticks)
return cftime.date2num(ticks, self.date_unit, self.calendar)


class NetCDFTimeConverter(mdates.DateConverter):
Expand Down Expand Up @@ -265,7 +299,7 @@ def default_units(cls, sample_point, axis):
def convert(cls, value, unit, axis):
"""
Converts value, if it is not already a number or sequence of numbers,
with :func:`cftime.utime().date2num`.
with :func:`cftime.date2num`.

"""
shape = None
Expand Down Expand Up @@ -293,15 +327,21 @@ def convert(cls, value, unit, axis):
'CalendarDateTime object must be of type '
'`cftime.datetime`.')

ut = cftime.utime(cls.standard_unit, calendar=first_value.calendar)

if isinstance(value, (CalendarDateTime, cftime.datetime)):
value = [value]

if isinstance(first_value, CalendarDateTime):
result = ut.date2num([v.datetime for v in value])
result = cftime.date2num(
[v.datetime for v in value],
cls.standard_unit,
first_value.calendar
)
else:
result = ut.date2num(value)
result = cftime.date2num(
value,
cls.standard_unit,
first_value.calendar
)

if shape is not None:
result = result.reshape(shape)
Expand Down
18 changes: 13 additions & 5 deletions nc_time_axis/tests/unit/test_NetCDFTimeDateLocator.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,12 @@ def check(self, max_n_ticks, num1, num2):
locator = NetCDFTimeDateLocator(max_n_ticks=max_n_ticks,
calendar=self.calendar,
date_unit=self.date_unit)
utime = cftime.utime(self.date_unit, self.calendar)
return locator.compute_resolution(num1, num2, utime.num2date(num1),
utime.num2date(num2))
return locator.compute_resolution(
num1,
num2,
cftime.num2date(num1, self.date_unit, self.calendar),
cftime.num2date(num2, self.date_unit, self.calendar)
)

def test_one_minute(self):
self.assertEqual(self.check(20, 0, 0.0003),
Expand Down Expand Up @@ -146,9 +149,14 @@ def check(self, max_n_ticks, num1, num2, calendar):
def test_yearly_yr0_remove(self):
for calendar in self.all_calendars:
# convert values to dates, check that none of them has year 0
num2date = cftime.utime(self.date_unit, calendar).num2date
ticks = self.check(5, 0, 100 * 365, calendar)
year_ticks = [num2date(t).year for t in ticks]
year_ticks = [
cftime.num2date(
t,
self.date_unit,
calendar
).year for t in ticks
]
if calendar in self.yr0_remove_calendars:
self.assertNotIn(0, year_ticks)
else:
Expand Down