
    g~L                     4   d Z ddlZddlZddlZddlZddlZddlZddlmZ ddlm	Z	m
Z
mZ ddlmZ ddlZddlZdZ	 dZdZd	Zd
Zd
ZdZeez  Zeez  Zeez  Z ej4                  d      Z ej4                  edz        Zedz  Zeez  dz  Z ej>                         de fd       Z!e	ej                  ejD                  ej                  f   Z#e	e
e$df   ejJ                  f   Z&de#dej                  fdZ'de	e#e&f   dej                  fdZ(de)de	e#e*ejJ                  f   de)fdZ+djdZ,djdZ-d Z.d Z/d Z0d Z1d Z2d e)dee
e)ef      fd!Z3d" Z4d# Z5d$ Z6i d%d&d'd&d(d&d)d&d*d&d+d,d-d,d.d,d/d0d1d0d2d3d4d3d5d6d7d6d8d9d:d9d;d<d=d=d=d>Z7d? Z8d@ Z9dA Z: G dB dC      Z;dkdDZ< e=dli dEej|                  j                  dF      dGej|                  j                  dF      dHej|                  j                  dI      dJej|                  j                  dK      dLej|                  j                  dM      dNej|                  j                  dO      dPej|                  j                  dO      dQej|                  j                  dR      dSej|                  j                  dR      dTej|                  j                  dU      dVej|                  j                  dU      dWej|                  j                  dX      dYej|                  j                  dX      dZej|                  j                  d[      d\ej|                  j                  d\      d]ej|                  j                  d^      d_ej|                  j                  d^      d`ej|                  j                  da      dbej|                  j                  dc      ddej|                  j                  de      dfej|                  j                  dg      dhej|                  j                  dg      Z@di ZAy)mz:Objects and routines pertaining to date and time (tempora)    N)Number)UnionTupleIterable)castl   _Kr4 l   mG    iL<         days      returnc                  f    t        t        j                  ddd      j                  d            dk7  S )zp
    Some versions of Python render %Y with only three characters :(
    https://bugs.python.org/issue39103
    i  r   %Y   )lendatetimedatestrftime     %/opt/Tautulli/lib/tempora/__init__.py_needs_year_helpr   '   s,     x}}S!Q'00671<<r   .obc                    t        | t        j                        r| S t        t        j                  |       }t        t        j                  |       }t        | t        j                        rt        j                         }t        | t        j                        rt        j                  ddd      }t        j                  j                  ||      S )z
    Given a datetime or date or time object from the ``datetime``
    module, always return a datetime using default values.
    il  r   )
isinstancer   r   r   timecombine)r   r   r   s      r   ensure_datetimer!   4   s    
 "h''(	r"Dr"D"hmm$}}"hmm$}}T1a($$T400r   c                     t        | t        j                  t        f      rt	        j                  | d d  } t        |       S )N   )r   r   struct_timetupler   r!   )r   s    r   infer_datetimer&   D   s8    "t''/02A'2r   fmttc                 0   t        |      }dd|j                  dz  z  fdd|j                  dz  z  ffdd|j                  z  fft               z  z   d fd}d	j	                  t        || j                  d	                  } |j                  |       S )
u  
    Portable strftime.

    In the stdlib, strftime has `known portability problems
    <https://bugs.python.org/issue13305>`_. This function
    aims to smooth over those issues and provide a
    consistent experience across the major platforms.

    >>> strftime('%Y', datetime.datetime(1890, 1, 1))
    '1890'
    >>> strftime('%Y', datetime.datetime(900, 1, 1))
    '0900'

    Supports time.struct_time, tuples, and datetime.datetime objects.

    >>> strftime('%Y-%m-%d', (1976, 5, 7))
    '1976-05-07'

    Also supports date objects

    >>> strftime('%Y', datetime.date(1976, 5, 7))
    '1976'

    Also supports milliseconds using %s.

    >>> strftime('%s', datetime.time(microsecond=20000))
    '020'

    Also supports microseconds (3 digits) using %µ

    >>> strftime('%µ', datetime.time(microsecond=123456))
    '456'

    Historically, %u was used for microseconds, but now
    it honors the value rendered by stdlib.

    >>> strftime('%u', datetime.date(1976, 5, 7))
    '5'

    Also supports microseconds (6 digits) using %f

    >>> strftime('%f', datetime.time(microsecond=23456))
    '023456'

    Even supports time values on date objects (discouraged):

    >>> strftime('%f', datetime.date(1976, 1, 1))
    '000000'
    >>> strftime('%µ', datetime.date(1976, 1, 1))
    '000'
    >>> strftime('%s', datetime.date(1976, 1, 1))
    '000'

    And vice-versa:

    >>> strftime('%Y', datetime.time())
    '1900'
    z%sz%03d  u   %µr   z%04dc                       | j                   | S N)replace)ssubs     r   doSubzstrftime.<locals>.doSub   s    qyy#r   c                 2    t        j                  |       S r,   )	functoolsreduce)r.   r0   subss    r   doSubszstrftime.<locals>.doSubs   s    tQ//r   z%%)r&   microsecondyearr   joinmapsplitr   )r'   r(   r5   r0   r4   s      @@r   r   r   J   s    v 	qA	v$./0	!--$./0 	 "%5%778D
0 ))C		$0
1C::c?r   c                     |@t         j                   j                  | j                         t        j                               }| |z
  }d }t	        |||f      \  }}t        j
                  ||z        }| |z
  }|S )a  
    Find the time which is the specified date/time truncated to the time delta
    relative to the start date/time.
    By default, the start time is midnight of the same day as the specified
    date/time.

    >>> datetime_mod(datetime.datetime(2004, 1, 2, 3),
    ...     datetime.timedelta(days = 1.5),
    ...     start = datetime.datetime(2004, 1, 1))
    datetime.datetime(2004, 1, 1, 0, 0)
    >>> datetime_mod(datetime.datetime(2004, 1, 2, 13),
    ...     datetime.timedelta(days = 1.5),
    ...     start = datetime.datetime(2004, 1, 1))
    datetime.datetime(2004, 1, 2, 12, 0)
    >>> datetime_mod(datetime.datetime(2004, 1, 2, 13),
    ...     datetime.timedelta(days = 7),
    ...     start = datetime.datetime(2004, 1, 1))
    datetime.datetime(2004, 1, 1, 0, 0)
    >>> datetime_mod(datetime.datetime(2004, 1, 10, 13),
    ...     datetime.timedelta(days = 7),
    ...     start = datetime.datetime(2004, 1, 1))
    datetime.datetime(2004, 1, 8, 0, 0)
    c                 b    | j                   t        z  | j                  z   dz  | j                  z   S )N@B )r   seconds_per_daysecondsmicrosecondstds    r   get_time_delta_microsecondsz1datetime_mod.<locals>.get_time_delta_microseconds   s(    /)BJJ6'ABOOSSr   r@   )r   r    r   r   r9   	timedelta)dtperiodstartdeltarC   offsetresults          r   datetime_modrL      su    0 }!!))"'')X]]_EJET 3eV_EME6UV^<F&[FMr   c                 R    t        | ||      }t        | |z
        |dz  k\  r||z  }|S )a  
    Find the nearest even period for the specified date/time.

    >>> datetime_round(datetime.datetime(2004, 11, 13, 8, 11, 13),
    ...     datetime.timedelta(hours = 1))
    datetime.datetime(2004, 11, 13, 8, 0)
    >>> datetime_round(datetime.datetime(2004, 11, 13, 8, 31, 13),
    ...     datetime.timedelta(hours = 1))
    datetime.datetime(2004, 11, 13, 9, 0)
    >>> datetime_round(datetime.datetime(2004, 11, 13, 8, 30),
    ...     datetime.timedelta(hours = 1))
    datetime.datetime(2004, 11, 13, 9, 0)
    r   )rL   abs)rF   rG   rH   rK   s       r   datetime_roundrO      s6     "fe,F
2;6Q;&&Mr   c                     t        j                         }|j                  }| |j                  z
  dkD  r|dz  }|j                  | z
  dkD  r|dz  }|S )aF  
    Returns the nearest year to now inferred from a Julian date.

    >>> freezer = getfixture('freezer')
    >>> freezer.move_to('2019-05-20')
    >>> get_nearest_year_for_day(20)
    2019
    >>> get_nearest_year_for_day(340)
    2018
    >>> freezer.move_to('2019-12-15')
    >>> get_nearest_year_for_day(20)
    2020
       r   )r   gmtimetm_yeartm_yday)daynowrK   s      r   get_nearest_year_for_dayrW      sQ     ++-C[[F
S[[8#!
{{S8#!Mr   c                 l    t        j                  | dd      }|t        j                  |dz
        z  }|S )z
    Gregorian Date is defined as a year and a julian day (1-based
    index into the days of the year).

    >>> gregorian_date(2007, 15)
    datetime.date(2007, 1, 15)
    r   r   )r   r   rE   )r7   
julian_dayrK   s      r   gregorian_daterZ      s4     ]]4A&F
h  j1n55FMr   c                 p   t        | t              r#	 d| j                         z   }t               |   }|S t        | t        j                        r| }|S t        | t        j                        r'| j                  t        d      z  | j                  z   }|S t        d      # t        $ r d}t        |      w xY w)a  
    return the number of seconds in the specified period

    >>> get_period_seconds('day')
    86400
    >>> get_period_seconds(86400)
    86400
    >>> get_period_seconds(datetime.timedelta(hours=24))
    86400
    >>> get_period_seconds('day + os.system("rm -Rf *")')
    Traceback (most recent call last):
    ...
    ValueError: period not in (second, minute, hour, day, month, year)
    seconds_per_z6period not in (second, minute, hour, day, month, year)rU   z"period must be a string or integer)r   strlowerglobalsKeyError
ValueErrornumbersr   r   rE   r   get_period_secondsr?   	TypeError)rG   namerK   msgs       r   rc   rc      s     &#	"!FLLN2DYt_F M 
FGNN	+
 M	 
FH..	/1%886>>I M <==  	"JCS/!	"s    B B5c                    t        | t              r| j                         dk(  ryt        |       d}d}t        t
        t        t        |f}t        t        fd|            }|d|j                  d      dz    }dj                  |      S )	a  
    For a given period (e.g. 'month', 'day', or some numeric interval
    such as 3600 (in secs)), return the format string that can be
    used with strftime to format that time to specify the times
    across that interval, but no more detailed.
    For example,

    >>> get_date_format_string('month')
    '%Y-%m'
    >>> get_date_format_string(3600)
    '%Y-%m-%d %H'
    >>> get_date_format_string('hour')
    '%Y-%m-%d %H'
    >>> get_date_format_string(None)
    Traceback (most recent call last):
        ...
    TypeError: period must be a string or integer
    >>> get_date_format_string('garbage')
    Traceback (most recent call last):
        ...
    ValueError: period not in (second, minute, hour, day, month, year)
    monthz%Y-%m)r   z-%m-%dz %Hz-%Mz-%Sr   c                     | z  S r,   r   )intervalfile_period_secss    r   <lambda>z(get_date_format_string.<locals>.<lambda>?  s    %5%@ r   Nr    )r   r]   r^   rc   seconds_per_yearr>   seconds_per_hourseconds_per_minutelistr9   indexr8   )rG   format_piecesseconds_per_second	intervalsmodsrk   s        @r   get_date_format_stringrw     s    2 &#6<<>W#<)&19MI @)LMD!"5DJJqMA$56M77=!!r   c                  b    t        d      } t        |       D ]  \  }}t        d| d|         y)a*  
    >>> monkeypatch = getfixture('monkeypatch')
    >>> import builtins
    >>> monkeypatch.setattr(builtins, 'input', lambda prompt: '3/hour')
    >>> calculate_prorated_values()
    per minute: 0.05
    per hour: 3.0
    per day: 72.0
    per month: 2191.454166666667
    per year: 26297.45
    z#Enter the rate (3/hour, 50/month)> zper z: N)input_prorated_valuesprint)raterG   values      r   calculate_prorated_valuesr~   D  s<     67D)$/ (VHBug&'(r   r|   c              #      K   t        j                  d|       }t        t         j                  |      j	                         }t        |d         }|t        |d         z  }dD ]  }|t        |      z  }||f  yw)aJ  
    Given a rate (a string in units per unit time), and return that same
    rate for various time periods.

    >>> for period, value in _prorated_values('20/hour'):
    ...     print('{period}: {value:0.3f}'.format(**locals()))
    minute: 0.333
    hour: 20.000
    day: 480.000
    month: 14609.694
    year: 175316.333

    z"(?P<value>[\d.]+)/(?P<period>\w+)$r}   rG   )minutehourrU   rh   r7   N)rematchr   Match	groupdictfloatrc   )r|   r   resr}   value_per_secondrG   period_values          r   rz   rz   U  s      HH:DAE
rxx

)
)
+C#g,E1#h-@@< #'*<V*DDl""#s   A:A<c                 4    t        |       j                         S )um	  
    Take a string representing a span of time and parse it to a time delta.
    Accepts any string of comma-separated numbers each with a unit indicator.

    >>> parse_timedelta('1 day')
    datetime.timedelta(days=1)

    >>> parse_timedelta('1 day, 30 seconds')
    datetime.timedelta(days=1, seconds=30)

    >>> parse_timedelta('47.32 days, 20 minutes, 15.4 milliseconds')
    datetime.timedelta(days=47, seconds=28848, microseconds=15400)

    Supports weeks, months, years

    >>> parse_timedelta('1 week')
    datetime.timedelta(days=7)

    >>> parse_timedelta('1 year, 1 month')
    datetime.timedelta(days=395, seconds=58685)

    Note that months and years strict intervals, not aligned
    to a calendar:

    >>> date = datetime.datetime.fromisoformat('2000-01-01')
    >>> later = date + parse_timedelta('1 year')
    >>> diff = later.replace(year=date.year) - date
    >>> diff.seconds
    20940

    >>> parse_timedelta('foo')
    Traceback (most recent call last):
    ...
    ValueError: Unexpected 'foo'

    >>> parse_timedelta('14 seconds foo')
    Traceback (most recent call last):
    ...
    ValueError: Unexpected 'foo'

    Supports abbreviations:

    >>> parse_timedelta('1s')
    datetime.timedelta(seconds=1)

    >>> parse_timedelta('1sec')
    datetime.timedelta(seconds=1)

    >>> parse_timedelta('5min1sec')
    datetime.timedelta(seconds=301)

    >>> parse_timedelta('1 ms')
    datetime.timedelta(microseconds=1000)

    >>> parse_timedelta('1 µs')
    datetime.timedelta(microseconds=1)

    >>> parse_timedelta('1 us')
    datetime.timedelta(microseconds=1)

    And supports the common colon-separated duration:

    >>> parse_timedelta('14:00:35.362')
    datetime.timedelta(seconds=50435, microseconds=362000)

    TODO: Should this be 14 hours or 14 minutes?

    >>> parse_timedelta('14:00')
    datetime.timedelta(seconds=50400)

    >>> parse_timedelta('14:00 minutes')
    Traceback (most recent call last):
    ...
    ValueError: Cannot specify units with composite delta

    Nanoseconds get rounded to the nearest microsecond:

    >>> parse_timedelta('600 ns')
    datetime.timedelta(microseconds=1)

    >>> parse_timedelta('.002 µs, 499 ns')
    datetime.timedelta(microseconds=1)

    Expect ValueError for other invalid inputs.

    >>> parse_timedelta('13 feet')
    Traceback (most recent call last):
    ...
    ValueError: Invalid unit feets
    )_parse_timedelta_nanosresolve)r]   s    r   parse_timedeltar   l  s    v "#&..00r   c                     t        j                  d|       }t        ||       }t        t        |      }t        |t                     S )Nz)(?P<value>[\d.:]+)\s?(?P<unit>[^\W\d_]+)?)r   finditer_check_unmatchedr9   _parse_timedelta_partsum	_Saved_NS)r]   parts	chk_partsdeltass       r   r   r     s;    KKDcJE ,I&	2Fvy{##r   c              #      K   d }d}| D ]/  } ||||j                                 | |j                         }1  |||d        yw)z3
    Ensure no words appear in unmatched text.
    c                 n    t        j                  d|       }|rt        d|j                  d            y )Nz\w+zUnexpected r   )r   searchra   group)	unmatchedfounds     r   check_unmatchedz)_check_unmatched.<locals>.check_unmatched  s5    		&),{5;;q>*<=>> r   r   N)rH   end)matchestextr   posr   s        r   r   r     sV     
?
 C S5;;=12iik DJs   AAu   µsr6   u   µsecususecmicrosmsmillisecondmsecmillisr.   secondsechr   hrmr   minwweekwkdrU   
nanosecond)nsnsecnanosc                 T    | y| j                         }t        j                  ||      S )Nr   )r^   _unit_lookupget)	raw_matchr   s     r   _resolve_unitr     s*    ??DD$''r   c                     |dk7  rt        d      | j                  d      }d}dj                  d t        ||      D              }t	        |      S )Nr?   z)Cannot specify units with composite delta:)hoursminutesr?    c              3   0   K   | ]  \  }}| d |   yw)r   Nr   ).0r}   units      r   	<genexpr>z-_parse_timedelta_composite.<locals>.<genexpr>  s     PkeT5'4&)Ps   )ra   r:   r8   zipr   )	raw_valuer   valuesunitscomposeds        r   _parse_timedelta_compositer     sO    yDEE__S!F)ExxPS=OPPH!(++r   c                    t        | j                  d            }|j                  d      s|dz  }| j                  d      }d|v rt        ||      S t	        |      }|dk(  rd}|dz  }|dk(  rd}|t
        z  }t        j                  ||      S )	Nr   r.   r}   r   monthsyearsr   r   )r   r   endswithr   r   days_per_yearr   derive)r   r   r   r}   s       r   r   r     s    V,-D==G$I
i))T::)Ex
w%D%((r   c                   t    e Zd ZdZ ej
                         ZdZ eddd      Z	d Z
ed        Zd	 Zd
 Zd Zy)r   z
    Bundle a timedelta with nanoseconds.

    >>> _Saved_NS.derive('microseconds', .001)
    _Saved_NS(td=datetime.timedelta(0), nanoseconds=1)
    r   i ʚ;r=   r*   )r?   millisecondsr@   c                 8    t        |       j                  |       y r,   )varsupdate)selfkwargss     r   __init__z_Saved_NS.__init__,  s    T
&!r   c                 J   |dk(  rt        |      S 	 t        j                  di ||i}t        |      }t        j                  t              5  t        || j                  |   z        dz  |_
        d d d        |S # t        $ r t	        d|       w xY w# 1 sw Y   |S xY w)Nnanoseconds)r   zInvalid unit rA   r*   r   )r   r   rE   rd   ra   
contextlibsuppressr`   int
multiplierr   )clsr   r}   raw_tdr   s        r   r   z_Saved_NS.derive/  s    = //	5''84-8F 6"  * 	G!%#..*>">?$FCO	G
  	5}TF344	5	G
s   A= $B=BB"c                 x    t        | j                  |j                  z   | j                  |j                  z         S )NrB   r   )r   rB   r   )r   others     r   __add__z_Saved_NS.__add__=  s2    ww!t/?/?%BSBS/S
 	
r   c                 x    t        | j                  dz        }| j                  t        j                  |      z   S )z
        Resolve any nanoseconds into the microseconds field,
        discarding any nanosecond resolution (but honoring partial
        microseconds).
        r*   rD   )roundr   rB   r   rE   )r   addl_micross     r   r   z_Saved_NS.resolveB  s3     D,,t34ww++EEEr   c                 <    d| j                   d| j                  dS )Nz_Saved_NS(td=z, nanoseconds=)r   )r   s    r   __repr__z_Saved_NS.__repr__K  s"    twwk8H8H7K1MMr   N)__name__
__module____qualname____doc__r   rE   rB   r   dictr   r   classmethodr   r   r   r   r   r   r   r   r     s[     
			BKJ"  

FNr   r   c              #      K   |t        j                  d      }| t         j                   j                         } | |k  r|  | |z  } | |k  ryyw)a  
    Much like the built-in function range, but works with dates

    >>> range_items = date_range(
    ...     datetime.datetime(2005,12,21),
    ...     datetime.datetime(2005,12,25),
    ... )
    >>> my_range = tuple(range_items)
    >>> datetime.datetime(2005,12,21) in my_range
    True
    >>> datetime.datetime(2005,12,22) in my_range
    True
    >>> datetime.datetime(2005,12,25) in my_range
    False
    >>> from_now = date_range(stop=datetime.datetime(2099, 12, 31))
    >>> next(from_now)
    datetime.datetime(...)
    Nr   r   )r   rE   rV   )rH   stopsteps      r   
date_ranger   O  sT     & |!!q)}!!%%'
$, $,s   AAAAESTzAustralia/SydneyAEDTACSTzAustralia/DarwinACDTzAustralia/AdelaideAWSTzAustralia/PerthESTzAmerica/New_YorkEDTCSTzAmerica/ChicagoCDTMSTzAmerica/DenverMDTPSTzAmerica/Los_AngelesPDTGMTzEtc/GMTUTCCETzEurope/BerlinCESTISTzAsia/KolkataBSTzEurope/LondonMSKzEurope/MoscowEETzEurope/HelsinkiEESTc                  L    t        j                  j                  | dt        i|S )z
    Parse the input using dateutil.parser.parse with friendly tz support.

    >>> parse('2024-07-26 12:59:00 EDT')
    datetime.datetime(...America/New_York...)
    tzinfos)dateutilparserparser  )argsr   s     r   r  r    s#     ??  $BB6BBr   r,   )NNNr   )Br   r   r   r   rb   r2   r   r   typingr   r   r   r   dateutil.parserr  dateutil.tzosc_per_yearosc_per_secondrt   rn   rp   minutes_per_hourhours_per_dayro   r>   r   rE   thirty_days
six_monthsseconds_per_monthhours_per_month	lru_cacheboolr   r   AnyDatetimer   r$   StructDatetimer!   r&   r]   r%   r   rL   rO   rW   rZ   rc   rw   r~   rz   r   r   r   r   r   r   r   r   r   r   tzgettzr  r  r   r   r   <module>r"     s*   <   	     ) )    '     %(88 "]2 ?2 h  b)X]Q%67
$r) -/"4 =$ = = H%%x}}hmmCDuS#X(8(8891 1(9(9 1 u[.89 h>O>O H# H%UD4D4D DE H# HV)X(2
>'"T("#3 #8E#v+,>#? #.[1|$ $	=] 	- M	
 m 	- M m  
8  	&  
8   	&!" #$ )0(,)"0N 0Nf8  			-	.			-	. 
		-	. 
		/	0	
 
		,	- 	,- 	,- 	+, 	+, 	*+ 	*+ 	/0 	/0 	)$ 	%   	/*!" 
		?	+#$ 	.)%& 	/*'( 	/*)* 	+,+, 
		,	--6Cr   