U
    CvfCk                     @  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
mZmZ d dlZd dlZd dlmZmZ d dlmZmZmZmZmZmZmZ d dlmZ d d	lmZm Z  d d
l!m"Z"m#Z#m$Z$ d dl%m&Z& d dl'm(Z( zd dl)Z)W n e*k
r   dZ)Y nX e	r"d dl+m,Z, eedf Z-dddhZ.de/de/de/de/dd e/dd d e/dd d d dZ0dddddddZ1ddd d!d"d#gZ2e2d$g Z3e4ddd d!d"d#d$gZ5d%d&d'd(d)Z6d*d+ Z7d%d%d,d-d.Z8d%d%d/d0d1Z9d%d2d,d3d4Z:d%d%d5d6d7d8d9Z;d:d%d%d:d;d<d=Z<d:d%d%d:d>d?d@Z=dxd%dAd5d:d7dBdCZ>dDdE Z?dFdG Z@d%d:d,dHdIZAd%dJd,dKdLZBd%dMd,dNdOZCd%dPdQdRZDdSdPdTdUZEd%dPdVdWZFd%dPdXdYZGd%dPdZd[ZHdyd&d:d]d^d_ZIdzd&d:d]d`daZJdbdc ZKd%d5d&dddedfZLd%d%d,dgdhZMd%d%d:didjdkZNd:dPdldmZOd{dAdAdndidodpZPd|dAdqd,drdsZQG dtdu dueZRG dvdw dweZSdS )}    )annotationsN)datetime	timedelta)partial)TYPE_CHECKINGCallableHashableUnion)OutOfBoundsDatetimeOutOfBoundsTimedelta)SerializationWarningVariableCoderlazy_elemwise_funcpop_tosafe_setitemunpack_for_decodingunpack_for_encoding)indexing)contains_cftime_datetimesis_np_datetime_like)first_n_itemsformat_timestamp	last_item)is_duck_dask_array)Variable)
CFCalendarstandardZ	gregorianproleptic_gregorian   g     @@g    .Ag    eA<      )nsusmssmhDi  i@B i l    $'- l    `;P )microsecondsmillisecondssecondsminuteshoursdaysr-   r,   r+   r*   r)   r(   nanosecondsstrbool)calendarreturnc                 C  s   |   tkS N)lower_STANDARD_CALENDARS)r1    r6   7/tmp/pip-unpacked-wheel-h316xyqg/xarray/coding/times.py_is_standard_calendarT   s    r8   c              
   C  s   t | jrdS t| } |  }|  }zt|tj t|tj W nT tj	j
k
r^   Y dS  tk
r } z|jd dkrW Y 
dS  W 5 d }~X Y nX dS d S )NTFr   zyear 0 is out of range)r   dtypenpasarrayminmaxconvert_time_or_go_backpd	Timestamperrorsr
   
ValueErrorargs)timesZtminZtmaxerrr6   r6   r7   _is_numpy_compatible_time_rangeX   s    


rF   )unitsr2   c                 C  s4   |   } | ds|  d} dddddddd|  S )	Nr$   r!   r"   r#   r%   r&   r'   )r.   r(   r)   r*   r+   r,   r-   )r4   endswithrG   r6   r6   r7   _netcdf_to_numpy_timeunitl   s    

rJ   )ref_dater2   c                 C  s|   t d| }|r| S t d| }|s2td|  dd | D \}}t|d| }d|  d| d	}t|t |S )
Nz	.*\d{4}.*z	(\d+)(.*)z'invalid reference date for time units: c                 s  s   | ]
}|V  qd S r3   r6   .0r$   r6   r6   r7   	<genexpr>   s     z&_ensure_padded_year.<locals>.<genexpr>Z04dz!Ambiguous reference date string: z. The first value is assumed to be the year hence will be padded with zeros to remove the ambiguity (the padded reference date string is: zb). To remove this message, remove the ambiguity by padding your reference date strings with zeros.)rematchrB   groupsintwarningswarnr   )rK   Zmatches_yearZmatches_start_digitsZref_yearZeverything_elseZref_date_paddedZwarning_msgr6   r6   r7   _ensure_padded_year{   s    	rU   ztuple[str, str]c                 C  sD   t d| }|std|  dd | D \}}t|}||fS )Nz(.+) since (.+)zinvalid time units: c                 s  s   | ]}|  V  qd S r3   )striprL   r6   r6   r7   rN      s     z,_unpack_netcdf_time_units.<locals>.<genexpr>)rO   rP   rB   rQ   rU   )rG   matchesdelta_unitsrK   r6   r6   r7   _unpack_netcdf_time_units   s    rY   bool | Noneznp.dtype)rG   r1   
use_cftimer2   c           
      C  s   t t | }tt|dp"dgt|p.dgg}zt||||}W nD tk
r   |d krbdnd|}d|d|d}t	|Y nX t
|dtd	}	|	S )
Nr   r   zthe default calendarz	calendar zunable to decode time units z with z_. Try opening your dataset with decode_times=False or installing cftime if it is not installed.r9   object)r   Z!ImplicitToExplicitIndexingAdapterZas_indexabler:   Zconcatenater   r   decode_cf_datetime	ExceptionrB   getattrr9   )
datarG   r1   r[   valuesZexample_valueresultZcalendar_msgmsgr9   r6   r6   r7   _decode_cf_datetime_dtype   s    rd   z
np.ndarray)	num_datesrG   r1   r2   c                 C  sD   t d krtd| jdkr2tt j| ||ddS tjg tdS d S )NNo module named 'cftime'r   T)Zonly_use_cftime_datetimesr9   )cftimeModuleNotFoundErrorsizer:   r;   Znum2datearrayr\   )re   rG   r1   r6   r6   r7   _decode_datetime_with_cftime   s    
rl   )flat_num_datesrG   r1   r2   c              	   C  s   t |std|t|\}}t|}zt|}W n tk
rP   tY nX t	 F t
ddt | jdkrt|  ||  t|  ||  W 5 Q R X | jjdkr| tj} | t|  tj}t|d| jS )NzECannot decode times from a non-standard calendar, {!r}, using pandas.ignorezinvalid value encounteredr   iur!   )r8   r
   formatrY   rJ   r?   r@   rB   rS   catch_warningsfilterwarningsRuntimeWarningrj   to_timedeltar<   r=   r9   kindastyper:   int64_NS_PER_TIME_DELTAra   )rm   rG   r1   deltarK   Zflat_num_dates_ns_intr6   r6   r7   _decode_datetime_with_pandas   s.    


rz   z
str | Nonec                 C  s   t | } |  }|dkrd}|dkrzt|||}W q ttttfk
r   t|	t
||}|t |  jdk s|t |  jdkrt|rtjdtdd nt|rt|}Y qX n|rt|||}nt|||}|| jS )a  Given an array of numeric dates in netCDF format, convert it into a
    numpy array of date time objects.

    For standard (Gregorian) calendars, this function uses vectorized
    operations, which makes it much faster than cftime.num2date. In such a
    case, the returned array will be of type np.datetime64.

    Note that time unit in `units` must not be smaller than microseconds and
    not larger than days.

    See Also
    --------
    cftime.num2date
    Nr   i  i  zUnable to decode time axis into full numpy.datetime64 objects, continuing using cftime.datetime objects instead, reason: dates out of range   )
stacklevel)r:   r;   ravelrz   KeyErrorr
   r   OverflowErrorrl   rv   floatZ	nanargminyearZ	nanargmaxr8   rS   rT   r   cftime_to_nptimereshapeshape)re   rG   r1   r[   rm   datesr6   r6   r7   r]     s:    
  	r]   c                 K  s$   t j| f| }|jdks t|S )Ntimedelta64[ns])r?   rt   to_numpyr9   AssertionErrorvaluekwargsrb   r6   r6   r7   to_timedelta_unboxed;  s    r   c                 K  s$   t j| f| }|jdks t|S )Ndatetime64[ns])r?   Zto_datetimer   r9   r   r   r6   r6   r7   to_datetime_unboxedA  s    r   c                 C  s.   t | } t|}t|  |d}|| jS )znGiven an array of numeric timedeltas in netCDF format, convert it into a
    numpy timedelta64[ns] array.
    )unit)r:   r;   rJ   r   r}   r   r   )Znum_timedeltasrG   rb   r6   r6   r7   decode_cf_timedeltaG  s    
r   r   c                 C  s   t t|  dS )Nr(   )r   _US_PER_TIME_DELTArI   r6   r6   r7   _unit_timedelta_cftimeQ  s    r   znp.timedelta64c                 C  s   t | }tt| dS )Nr!   )rJ   r:   timedelta64rx   )rG   Znumpy_unitsr6   r6   r7   _unit_timedelta_numpyU  s    r   )r2   c                 C  sr   | j t dkr(t}t}tdd}| }nt}t}tdd}t	| }|D ]"}t
||| |krJ|  S qJdS )NOr   r   r!   r*   )r9   r:   _NETCDF_TIME_UNITS_CFTIMEr   r   _NETCDF_TIME_UNITS_NUMPYr   r   r?   ZTimedeltaIndexall)unique_timedeltasZ
time_unitsZunit_timedeltaZzero_timedelta
timedeltasZ	time_unitr6   r6   r7   _infer_time_units_from_diffZ  s    


r   r   c                 C  s   t | jrdS | jtdkrv| jdkrvtdk	rvt| jd }t|rd| }t	|tj
rd| }t	|tjrv|jS tddS )z7Given an array of datetimes, infer the CF calendar namer   r   r   Nz(Array does not contain datetime objects.)r   r9   r:   rj   rh   r;   Zflatr   Zcompute
isinstanceZndarrayitemr   r1   rB   )r   sampler6   r6   r7   infer_calendar_namer  s    
r   c                 C  s   t |  } t | jdkrXt| } | t|  } t| dkrH| d nd}t|}n t| dkrl| d nd}t	|}t 
t | }t|}| d| S )a  Given an array of datetimes, returns a CF compatible time-unit string of
    the form "{time_unit} since {date[0]}", where `time_unit` is 'days',
    'hours', 'minutes' or 'seconds' (the first one that can evenly divide all
    unique time deltas in `dates`)
    r   r   z
1970-01-01 since )r:   r;   r}   r9   r   r?   notnulllenr@   format_cftime_datetimeuniqueZdiffr   )r   Zreference_dater   rG   r6   r6   r7   infer_datetime_units  s    r   c              	   C  s$   d | j| j| j| j| j| j| jS )zbConverts a cftime.datetime object to a string with the format:
    YYYY-MM-DD HH:MM:SS.UUUUUU
    z0{:04d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}.{:06d})rp   r   monthdayhourminutesecondmicrosecond)dater6   r6   r7   r     s    r   c                 C  s.   t t|  } t| t|  }t|S )zGiven an array of timedeltas, returns a CF compatible time-unit from
    {'days', 'hours', 'minutes' 'seconds'} (the first one that can evenly
    divide all unique time deltas in `deltas`)
    )r   r:   r;   r}   r   r?   r   r   )Zdeltasr   r6   r6   r7   infer_timedelta_units  s    r   T)raise_on_invalidr2   c                 C  s   t | } t j| jdd}t | D ]|\}}z(t|j|j|j	|j
|j|j|j}W n< tk
r } z|r|td||nd}W 5 d}~X Y nX t |||< q$|S )zGiven an array of cftime.datetime objects, return an array of
    numpy.datetime64 objects of the same size

    If raise_on_invalid is True (default), invalid dates trigger a ValueError.
    Otherwise, the invalid element is replaced by np.NaT.zM8[ns]rg   zGCannot convert date {} to a date in the standard calendar.  Reason: {}.ZNaTN)r:   r;   emptyr   Zndenumerater?   r@   r   r   r   r   r   r   r   rB   rp   
datetime64)rD   r   newitdter6   r6   r7   r     s0    
       r   c                 C  s   |t jtjfkr&t| js&t| |dS t| jr:t | } tj| j	dd}t
| D ]\}}z&||j|j|j|j|j|j|j}W nJ tk
r } z,|rtd||dddj|ntj}W 5 d}~X Y nX |||< qR|S )am  Given an array of datetimes, return the same dates in another cftime or numpy date type.

    Useful to convert between calendars in numpy and cftime or between cftime calendars.

    If raise_on_valid is True (default), invalid dates trigger a ValueError.
    Otherwise, the invalid element is replaced by np.NaN for cftime types and np.NaT for np.datetime64.
    )r   r   rg   zACannot convert date {} to a date in the {} calendar.  Reason: {}.i  r   N)r?   r@   r:   r   r   r9   r   DatetimeIndexr   r   	enumerater   r   r   r   r   r   r   rB   rp   r1   NaN)rD   	date_typer   r   r   r   r   r   r6   r6   r7   convert_times  s<    

        
r   c                 C  s   z$|| j | j| j| j| j| j| jW S  tk
r:    Y n tk
r   z,|| j | j| jd | j| j| j| jW  Y S  tk
r   || j | j| jd | j| j| j| j Y  Y S X Y nX dS )a  Convert a single date to a new date_type (cftime.datetime or pd.Timestamp).

    If the new date is invalid, it goes back a day and tries again. If it is still
    invalid, goes back a second day.

    This is meant to convert end-of-month dates into a new calendar.
    r      N)	r   r   r   r   r   r   r   r
   rB   )r   r   r6   r6   r7   r>     sB    	
	r>   )target_calendarr[   r2   c                 C  sJ   |dk	rFt |r.t| rdS |dkrFtdn|dkrFtd| ddS )aa  Return whether conversion of the source to the target calendar should
    result in a cftime-backed array.

    Source is a 1D datetime array, target_cal a string (calendar name) and
    use_cftime is a boolean or None. If use_cftime is None, this returns True
    if the source's range and target calendar are convertible to np.datetime64 objects.
    TFzPSource time range is not valid for numpy datetimes. Try using `use_cftime=True`.z
Calendar 'z9' is only valid with cftime. Try using `use_cftime=True`.)r8   rF   rB   )sourcer   r[   r6   r6   r7   _should_cftime_be_used   s    
r   c              	   C  s@   t | \}}z| dt| } W n ttfk
r:   Y nX | S )Nr   )rY   r   r
   rB   )rG   ry   rK   r6   r6   r7   _cleanup_netcdf_time_units;  s    r   )rG   r1   r2   c                   sb   t dkrtdt| jtjr0| dt}  fddtfdd| 	 D 
| jS )zFallback method for encoding dates using cftime.

    This method is more flexible than xarray's parsing using datetime64[ns]
    arrays but also slower because it loops over each element.
    Nrf   zM8[us]c                   sX   z"| d krt jntj|  ddW S  tk
rR   | d kr@t jnt|   Y S X d S )NF)Z
longdouble)r:   nanrh   Zdate2num	TypeError)d)r1   rG   r6   r7   encode_datetimeS  s    z5_encode_datetime_with_cftime.<locals>.encode_datetimec                   s   g | ]} |qS r6   r6   )rM   r   )r   r6   r7   
<listcomp>a  s     z0_encode_datetime_with_cftime.<locals>.<listcomp>)rh   ri   r:   
issubdtyper9   r   rv   r   rk   r}   r   r   )r   rG   r1   r6   )r1   r   rG   r7   _encode_datetime_with_cftimeF  s    r   c                 C  s$   t j| t jd}| |k r |} | S )Nrg   )r:   r;   rw   r   )numZint_numr6   r6   r7   cast_to_int_if_safed  s    r   ztuple[np.ndarray, str, str]c              
   C  s.  t | } |dkrt| }nt|}|dkr4t| }t|\}}zt|rV| jjdkrZt	| jdksht
t|}t d|d}t|}|jdk	r|d}t|  }|| }	t |	| t ddkr|	| }
n|	| }
|
j| j}
W n( t	ttfk
r   t| ||}
Y nX t|
}
|
||fS )zGiven an array of datetime objects, returns the tuple `(num, units,
    calendar)` suitable for a CF compliant time variable.

    Unlike `date2num`, this function can handle datetime64 arrays.

    See Also
    --------
    cftime.date2num
    Nr   r   r   r   r   r!   )r:   r;   r   r   r   rY   r8   r9   ru   r
   r   rJ   r   rv   r?   r@   tzZ
tz_convertr   r}   r   ra   r   r   r   rB   r   r   )r   rG   r1   ry   Z	_ref_daterX   Z
time_deltarK   Zdates_as_indexZtime_deltasr   r6   r6   r7   encode_cf_datetimek  s4    





r   ztuple[np.ndarray, str]c                 C  sR   |d krt | }t|}d|  td| }tt| tj|}t|}||fS )Ng      ?r   )	r   rJ   r:   r   wherer?   Zisnullr   r   )r   rG   Znp_unitr   r6   r6   r7   encode_cf_timedelta  s    r   c                   @  sF   e Zd ZddddddZddddd	d
dZddddd	ddZdS )CFDatetimeCoderNrZ   None)r[   r2   c                 C  s
   || _ d S r3   )r[   )selfr[   r6   r6   r7   __init__  s    zCFDatetimeCoder.__init__r   T_Namevariablenamer2   c           	      C  s   t |jjt jst|r~t|\}}}}t||dd |dd \}}}t	|d||d t	|d||d t
||||ddS |S d S )NrG   r1   r   TZfastpath)r:   r   r`   r9   r   r   r   r   popr   r   )	r   r   r   dimsr`   attrsencodingrG   r1   r6   r6   r7   encode  s       
 

zCFDatetimeCoder.encodec                 C  s   |j dd }t|trd|krt|\}}}}t||d}t||d}t|||| j}	tt	||| jd}
t
||
|	}t||||ddS |S d S )NrG   Zsincer1   )rG   r1   r[   Tr   )r   getr   r/   r   r   rd   r[   r   r]   r   r   )r   r   r   rG   r   r`   r   r   r1   r9   	transformr6   r6   r7   decode  s    zCFDatetimeCoder.decode)N)N)N)__name__
__module____qualname__r   r   r   r6   r6   r6   r7   r     s   r   c                   @  s4   e Zd Zd	ddddddZd
ddddddZdS )CFTimedeltaCoderNr   r   r   c                 C  sb   t |jjt jrZt|\}}}}t||dd \}}t|d||d t	||||ddS |S d S )NrG   r   Tr   )
r:   r   r`   r9   r   r   r   r   r   r   )r   r   r   r   r`   r   r   rG   r6   r6   r7   r     s    zCFTimedeltaCoder.encodec           
      C  sz   |j dd }t|trr|tkrrt|\}}}}t||d}tt|d}t	
d}	t|||	d}t||||ddS |S d S )NrG   rI   r   rg   Tr   )r   r   r   r/   
TIME_UNITSr   r   r   r   r:   r9   r   r   )
r   r   r   rG   r   r`   r   r   r   r9   r6   r6   r7   r     s    
zCFTimedeltaCoder.decode)N)N)r   r   r   r   r   r6   r6   r6   r7   r     s   r   )NN)T)T)NN)N)T
__future__r   rO   rS   r   r   	functoolsr   typingr   r   r   r	   Znumpyr:   Zpandasr?   Zpandas.errorsr
   r   Zxarray.coding.variablesr   r   r   r   r   r   r   Zxarray.corer   Zxarray.core.commonr   r   Zxarray.core.formattingr   r   r   Zxarray.core.pycompatr   Zxarray.core.variabler   rh   ImportErrorZxarray.core.typesr   r   r5   rR   rx   r   r   r   	frozensetr   r8   rF   rJ   rU   rY   rd   rl   rz   r]   r   r   r   r   r   r   r   r   r   r   r   r   r>   r   r   r   r   r   r   r   r   r6   r6   r6   r7   <module>   s   $	



	
"0   6

$-   ;)