@@ -484,7 +484,7 @@ def _discard_microsecond(date):
484484 dates = np .asarray (date )
485485 shape = dates .shape
486486 dates = dates .ravel ()
487- # Create date objects of the same type returned by utime .num2date()
487+ # Create date objects of the same type returned by cftime .num2date()
488488 # (either datetime.datetime or cftime.datetime), discarding the
489489 # microseconds
490490 dates = np .array ([d and d .__class__ (d .year , d .month , d .day ,
@@ -494,7 +494,7 @@ def _discard_microsecond(date):
494494 return result
495495
496496
497- def num2date (time_value , unit , calendar ):
497+ def num2date (time_value , unit , calendar , only_use_cftime_datetimes = False ):
498498 """
499499 Return datetime encoding of numeric time value (resolution of 1 second).
500500
@@ -508,7 +508,7 @@ def num2date(time_value, unit, calendar):
508508 unit = 'days since 001-01-01 00:00:00'}
509509 calendar = 'proleptic_gregorian'.
510510
511- The datetime instances returned are 'real' python datetime
511+ By default, the datetime instances returned are 'real' python datetime
512512 objects if the date falls in the Gregorian calendar (i.e.
513513 calendar='proleptic_gregorian', or calendar = 'standard' or 'gregorian'
514514 and the date is after 1582-10-15). Otherwise, they are 'phony' datetime
@@ -535,6 +535,13 @@ def num2date(time_value, unit, calendar):
535535 * calendar (string):
536536 Name of the calendar, see cf_units.CALENDARS.
537537
538+ Kwargs:
539+
540+ * only_use_cftime_datetimes (bool):
541+ If True, will always return cftime datetime objects, regardless of
542+ calendar. If False, returns datetime.datetime instances where
543+ possible. Defaults to False.
544+
538545 Returns:
539546 datetime, or numpy.ndarray of datetime object.
540547
@@ -561,10 +568,12 @@ def num2date(time_value, unit, calendar):
561568 if unit_string .endswith (" since epoch" ):
562569 unit_string = unit_string .replace ("epoch" , EPOCH )
563570 unit_inst = Unit (unit_string , calendar = calendar )
564- return unit_inst .num2date (time_value )
571+ return unit_inst .num2date (
572+ time_value , only_use_cftime_datetimes = only_use_cftime_datetimes )
565573
566574
567- def _num2date_to_nearest_second (time_value , utime ):
575+ def _num2date_to_nearest_second (time_value , utime ,
576+ only_use_cftime_datetimes = False ):
568577 """
569578 Return datetime encoding of numeric time value with respect to the given
570579 time reference units, with a resolution of 1 second.
@@ -574,6 +583,11 @@ def _num2date_to_nearest_second(time_value, utime):
574583 * utime (cftime.utime):
575584 cftime.utime object with which to perform the conversion/s.
576585
586+ * only_use_cftime_datetimes (bool):
587+ If True, will always return cftime datetime objects, regardless of
588+ calendar. If False, returns datetime.datetime instances where
589+ possible. Defaults to False.
590+
577591 Returns:
578592 datetime, or numpy.ndarray of datetime object.
579593 """
@@ -582,7 +596,7 @@ def _num2date_to_nearest_second(time_value, utime):
582596 time_values = time_values .ravel ()
583597
584598 # We account for the edge case where the time is in seconds and has a
585- # half second: utime .num2date() may produce a date that would round
599+ # half second: cftime .num2date() may produce a date that would round
586600 # down.
587601 #
588602 # Note that this behaviour is different to the num2date function in version
@@ -592,7 +606,9 @@ def _num2date_to_nearest_second(time_value, utime):
592606 # later versions, if one wished to do so for the sake of consistency.
593607 has_half_seconds = np .logical_and (utime .units == 'seconds' ,
594608 time_values % 1. == 0.5 )
595- dates = utime .num2date (time_values )
609+ dates = cftime .num2date (
610+ time_values , utime .unit_string , calendar = utime .calendar ,
611+ only_use_cftime_datetimes = only_use_cftime_datetimes )
596612 try :
597613 # We can assume all or none of the dates have a microsecond attribute
598614 microseconds = np .array ([d .microsecond if d else 0 for d in dates ])
@@ -603,7 +619,10 @@ def _num2date_to_nearest_second(time_value, utime):
603619 if time_values [ceil_mask ].size > 0 :
604620 useconds = Unit ('second' )
605621 second_frac = useconds .convert (0.75 , utime .units )
606- dates [ceil_mask ] = utime .num2date (time_values [ceil_mask ] + second_frac )
622+ dates [ceil_mask ] = cftime .num2date (
623+ time_values [ceil_mask ] + second_frac , utime .unit_string ,
624+ calendar = utime .calendar ,
625+ only_use_cftime_datetimes = only_use_cftime_datetimes )
607626 dates [round_mask ] = _discard_microsecond (dates [round_mask ])
608627 result = dates [0 ] if shape is () else dates .reshape (shape )
609628 return result
@@ -1878,7 +1897,7 @@ def convert(self, value, other, ctype=FLOAT64, inplace=False):
18781897 raise ValueError ("Unable to convert from '%r' to '%r'." %
18791898 (self , other ))
18801899
1881- def utime (self ):
1900+ def utime (self , only_use_cftime_datetimes = False ):
18821901 """
18831902 Returns a cftime.utime object which performs conversions of
18841903 numeric time values to/from datetime objects given the current
@@ -1888,6 +1907,13 @@ def utime(self):
18881907 '<time-unit> since <time-origin>'
18891908 i.e. 'hours since 1970-01-01 00:00:00'
18901909
1910+ Kwargs:
1911+
1912+ * only_use_cftime_datetimes (bool):
1913+ If True, num2date method will always return cftime datetime
1914+ objects, regardless of calendar. If False, returns
1915+ datetime.datetime instances where possible. Defaults to False.
1916+
18911917 Returns:
18921918 cftime.utime.
18931919
@@ -1915,7 +1941,9 @@ def utime(self):
19151941 # ensure to strip out non-parsable 'UTC' postfix, which
19161942 # is generated by UDUNITS-2 formatted output
19171943 #
1918- return cftime .utime (str (self ).rstrip (" UTC" ), self .calendar )
1944+ return cftime .utime (
1945+ str (self ).rstrip (" UTC" ), self .calendar ,
1946+ only_use_cftime_datetimes = only_use_cftime_datetimes )
19191947
19201948 def date2num (self , date ):
19211949 """
@@ -1956,7 +1984,7 @@ def date2num(self, date):
19561984 date = _discard_microsecond (date )
19571985 return cdf_utime .date2num (date )
19581986
1959- def num2date (self , time_value ):
1987+ def num2date (self , time_value , only_use_cftime_datetimes = False ):
19601988 """
19611989 Returns a datetime-like object calculated from the numeric time
19621990 value using the current calendar and the unit time reference.
@@ -1965,8 +1993,8 @@ def num2date(self, time_value):
19651993 '<time-unit> since <time-origin>'
19661994 i.e. 'hours since 1970-01-01 00:00:00'
19671995
1968- The datetime objects returned are 'real' Python datetime objects
1969- if the date falls in the Gregorian calendar (i.e. the calendar
1996+ By default, the datetime objects returned are 'real' Python datetime
1997+ objects if the date falls in the Gregorian calendar (i.e. the calendar
19701998 is 'standard', 'gregorian', or 'proleptic_gregorian' and the
19711999 date is after 1582-10-15). Otherwise a 'phoney' datetime-like
19722000 object (cftime.datetime) is returned which can handle dates
@@ -1977,8 +2005,15 @@ def num2date(self, time_value):
19772005
19782006 Args:
19792007
1980- * time_value (float): Numeric time value/s. Maximum resolution
1981- is 1 second.
2008+ * time_value (float):
2009+ Numeric time value/s. Maximum resolution is 1 second.
2010+
2011+ Kwargs:
2012+
2013+ * only_use_cftime_datetimes (bool):
2014+ If True, will always return cftime datetime objects, regardless of
2015+ calendar. If False, returns datetime.datetime instances where
2016+ possible. Defaults to False.
19822017
19832018 Returns:
19842019 datetime, or numpy.ndarray of datetime object.
@@ -1995,5 +2030,9 @@ def num2date(self, time_value):
19952030 ['1970-01-01 06:00:00', '1970-01-01 07:00:00']
19962031
19972032 """
1998- cdf_utime = self .utime ()
1999- return _num2date_to_nearest_second (time_value , cdf_utime )
2033+ cdf_utime = self .utime (
2034+ only_use_cftime_datetimes = only_use_cftime_datetimes )
2035+
2036+ return _num2date_to_nearest_second (
2037+ time_value , cdf_utime ,
2038+ only_use_cftime_datetimes = only_use_cftime_datetimes )
0 commit comments