U
    Cvf5                     @  s   d dl mZ d dlZd dlZd dlmZmZ d dl	m
Z
 d dlmZmZ d dlmZmZ zd dlZW n ek
r|   dZY nX ddd	d
gZdddZdddZdd Zdd ZdddZdddZdS )    )annotationsN)date_range_likeget_date_type)CFTimeIndex)_should_cftime_be_usedconvert_times)_contains_datetime_like_objectsis_np_datetime_likeZ	gregorianZproleptic_gregorianZjulianstandardTc                 C  s\   t ||d}| dkr:|tkr:|| d dd|| dd }n|| d dd|| dd }|jS )zLReturn the number of days in the input year according to the input calendar.
use_cftime      )r   _CALENDARS_WITHOUT_YEAR_ZEROdays)yearcalendarr   Z	date_type
difference r   >/tmp/pip-unpacked-wheel-h316xyqg/xarray/coding/calendar_ops.py_days_in_year   s
    r   timec                   s  ddl m} | | }t|s,td| dt| |jj}| krZt|jA rZ| S |jj	dk
 r tkrtd  d|dks dkr|dkrtd	|dkr dkrd
}|  }	|dkr6|| djt d}
| fddt|jjj|
D |f|d|	|< |	|tj|	| ddd i}	n@|d
krvt|jt ddd}||	|< |	j|	|  dd}	|dk	rt| d}|	j||i|d}	|	| j|j |	| j dd |	S )a&  Transform a time-indexed Dataset or DataArray to one that uses another calendar.

    This function only converts the individual timestamps; it does not modify any
    data except in dropping invalid/surplus dates, or inserting values for missing dates.

    If the source and target calendars are both from a standard type, only the
    type of the time array is modified. When converting to a calendar with a
    leap year from to a calendar without a leap year, the 29th of February will
    be removed from the array. In the other direction the 29th of February will
    be missing in the output, unless `missing` is specified, in which case that
    value is inserted. For conversions involving the `360_day` calendar, see Notes.

    This method is safe to use with sub-daily data as it doesn't touch the time
    part of the timestamps.

    Parameters
    ----------
    obj : DataArray or Dataset
      Input DataArray or Dataset with a time coordinate of a valid dtype
      (:py:class:`numpy.datetime64`  or :py:class:`cftime.datetime`).
    calendar : str
      The target calendar name.
    dim : str
      Name of the time coordinate in the input DataArray or Dataset.
    align_on : {None, 'date', 'year'}
      Must be specified when either the source or target is a `"360_day"`
      calendar; ignored otherwise. See Notes.
    missing : any, optional
      By default, i.e. if the value is None, this method will simply attempt
      to convert the dates in the source calendar to the same dates in the
      target calendar, and drop any of those that are not possible to
      represent.  If a value is provided, a new time coordinate will be
      created in the target calendar with the same frequency as the original
      time coordinate; for any dates that are not present in the source, the
      data will be filled with this value.  Note that using this mode requires
      that the source data have an inferable frequency; for more information
      see :py:func:`xarray.infer_freq`.  For certain frequency, source, and
      target calendar combinations, this could result in many missing values, see notes.
    use_cftime : bool, optional
      Whether to use cftime objects in the output, only used if `calendar` is
      one of {"proleptic_gregorian", "gregorian" or "standard"}.
      If True, the new time axis uses cftime objects.
      If None (default), it uses :py:class:`numpy.datetime64` values if the date
          range permits it, and :py:class:`cftime.datetime` objects if not.
      If False, it uses :py:class:`numpy.datetime64`  or fails.

    Returns
    -------
      Copy of source with the time coordinate converted to the target calendar.
      If `missing` was None (default), invalid dates in the new calendar are
      dropped, but missing dates are not inserted.
      If `missing` was given, the new data is reindexed to have a time axis
      with the same frequency as the source, but in the new calendar; any
      missing datapoints are filled with `missing`.

    Notes
    -----
    Passing a value to `missing` is only usable if the source's time coordinate as an
    inferable frequencies (see :py:func:`~xarray.infer_freq`) and is only appropriate
    if the target coordinate, generated from this frequency, has dates equivalent to the
    source. It is usually **not** appropriate to use this mode with:

    - Period-end frequencies: 'A', 'Y', 'Q' or 'M', in opposition to 'AS' 'YS', 'QS' and 'MS'
    - Sub-monthly frequencies that do not divide a day evenly: 'W', 'nD' where `n != 1`
      or 'mH' where 24 % m != 0).

    If one of the source or target calendars is `"360_day"`, `align_on` must
    be specified and two options are offered.

    "year"
      The dates are translated according to their relative position in the year,
      ignoring their original month and day information, meaning that the
      missing/surplus days are added/removed at regular intervals.

      From a `360_day` to a standard calendar, the output will be missing the
      following dates (day of year in parentheses):
        To a leap year:
          January 31st (31), March 31st (91), June 1st (153), July 31st (213),
          September 31st (275) and November 30th (335).
        To a non-leap year:
          February 6th (36), April 19th (109), July 2nd (183),
          September 12th (255), November 25th (329).

      From a standard calendar to a `"360_day"`, the following dates in the
      source array will be dropped:
        From a leap year:
          January 31st (31), April 1st (92), June 1st (153), August 1st (214),
          September 31st (275), December 1st (336)
        From a non-leap year:
          February 6th (37), April 20th (110), July 2nd (183),
          September 13th (256), November 25th (329)

      This option is best used on daily and subdaily data.

    "date"
      The month/day information is conserved and invalid dates are dropped
      from the output. This means that when converting from a `"360_day"` to a
      standard calendar, all 31sts (Jan, March, May, July, August, October and
      December) will be missing as there is no equivalent dates in the
      `"360_day"` calendar and the 29th (on non-leap years) and 30th of February
      will be dropped as there are no equivalent dates in a standard calendar.

      This option is best used with data on a frequency coarser than daily.
    r   	DataArrayzCoordinate z must contain datetime objects.]Source time coordinate contains dates with year 0, which is not supported by target calendar .Z360_dayNzsArgument `align_on` must be specified with either 'date' or 'year' when converting to or from a '360_day' calendar.dater   .year)target_calendarr   c                   s   g | ]\}}t || qS r   )-_convert_to_new_calendar_with_new_day_of_year).0r   Znewdoyr   r   r   r   
<listcomp>   s      z$convert_calendar.<locals>.<listcomp>dimsnameT)Zreturn_indexr   r   F)Zraise_on_invalid)Zdropr"   )Z
fill_valuer   )!xarray.core.dataarrayr   r   
ValueErrorr   dtr   r	   dtyper   anyr   copygroupbymap_interpolate_day_of_yearzipvariable_dataarrayZiselnpuniquer   datar   whereZnotnullr   Zreindexattrsupdatepop)objr   dimZalign_onmissingr   r   r   source_calendaroutZnew_doyZ	new_timesZtime_targetr   r"   r   convert_calendar#   s^    p

  
"


r@   c                 C  sD   t | jjd }| jj}tt|||| jj t||| t S )zsReturns the nearest day in the target calendar of the corresponding
    "decimal year" in the source calendar.
    r   )	intr)   r   r   r4   roundr   Z	dayofyearZastype)r   r   r   r   r>   r   r   r   r/      s    

r/   c                 C  sp   t j|d d| j d|r|ndd}z*t||| j|j|j| j| j| j| j	W S  t
k
rj   tj Y S X dS )a  Convert a datetime object to another calendar with a new day of year.

    Redefines the day of year (and thus ignores the month and day information
    from the source datetime).
    Nanosecond information is lost as cftime.datetime doesn't support it.
    r   days since -01-01r
   r   N)cftimeZnum2dater   r   monthdayhourminutesecondmicrosecondr(   r4   nan)r   Zday_of_yearr   r   Znew_dater   r   r   r       s"    	
	r    c                   s`   ddl m  p| jjt| jr:| jt| jt	dd}  fdd}| 
 d|S )aS  Convert a datetime DataArray to decimal years according to its calendar or the given one.

    The decimal year of a timestamp is its year plus its sub-year component
    converted to the fraction of its year.
    Ex: '2000-03-01 12:00' is 2000.1653 in a standard calendar,
      2000.16301 in a "noleap" or 2000.16806 in a "360_day".
    r   r   r
   )r6   c                   sL   t | jjd }tj| d|ddd} ||t|  f| jdS )Nr   rC   Z04drD   rE   )r%   coordsr&   )rA   r)   r   rF   Zdate2numr   rN   )r   r   Zdoysr   r   r<   r   r   _make_index  s    z._datetime_to_decimal_year.<locals>._make_indexr   )r'   r   r)   r   r	   r*   r,   r   valuesr   r-   r.   )timesr<   r   rP   r   rO   r   _datetime_to_decimal_year  s    

rS   c                 C  s   ddl m} t|tjtfr,|||f|d}t| | r@t|sPtd| d| | jj	}|jj	}| | j
jjdk r|tkrtd| d|  }t| | ||d||< t|||d}|jf ||i}|||< |S )	aw  Interpolates a DataArray or Dataset indexed by a time coordinate to
    another calendar based on decimal year measure.

    Each timestamp in `source` and `target` are first converted to their decimal
    year equivalent then `source` is interpolated on the target coordinate.
    The decimal year of a timestamp is its year plus its sub-year component
    converted to the fraction of its year. For example "2000-03-01 12:00" is
    2000.1653 in a standard calendar or 2000.16301 in a `"noleap"` calendar.

    This method should only be used when the time (HH:MM:SS) information of
    time coordinate is not important.

    Parameters
    ----------
    source: DataArray or Dataset
      The source data to interpolate; must have a time coordinate of a valid
      dtype (:py:class:`numpy.datetime64` or :py:class:`cftime.datetime` objects)
    target: DataArray, DatetimeIndex, or CFTimeIndex
      The target time coordinate of a valid dtype (np.datetime64 or cftime objects)
    dim : str
      The time coordinate name.

    Return
    ------
    DataArray or Dataset
      The source interpolated on the decimal years of target,
    r   r   r$   zBoth 'source.z-' and 'target' must contain datetime objects.r   r   )r<   r   )r'   r   
isinstancepdZDatetimeIndexr   r   r(   r)   r   r   r   r+   r   r,   rS   Zinterp)sourcetargetr<   r   r>   r   r?   Z
target_idxr   r   r   interp_calendar   s4    

rX   )T)r   NNN)r   N)r   )
__future__r   Znumpyr4   ZpandasrU   Zxarray.coding.cftime_offsetsr   r   Zxarray.coding.cftimeindexr   Zxarray.coding.timesr   r   Zxarray.core.commonr   r	   rF   ImportErrorr   r   r@   r/   r    rS   rX   r   r   r   r   <module>   s4   

    
 9
