
    sojW                        d dl Z d dlZd dlmZ d dlZd dlZd dlmZ d dlmZ d dl	m
Z
 d dlmZ d dlmZ d dlmZ d dlZd dlmZ d d	lmZ d d
lmZ d dlmZ d dlmZ d dlmZ d dl m!Z!m"Z" d dl#m$Z$ 	 	 dZde%de j         de j         de j         fdZ&d[de%de de fdZ'de%de(fdZ)d\de*e+z  de(de%fdZ,d\de*de(de%fdZ-d\de*de(de%fdZ.d\de*de(de%fd Z/d[de%de de%fd!Z0	 	 	 d]d"e j         de j         de(d#e(de%f
d$Z1de de*fd%Z2de de+fd&Z3d\de de(de%fd'Z4	 d[de%de de%fd(Z5d[d"e de de*fd)Z6d[d"e de de*fd*Z7d[d"e de de*fd+Z8d[de*d,e j         de fd-Z9d[de*d,e j         de fd.Z:d[de+d,e j         de fd/Z;d[de*d,e j         de fd0Z<de d"e de fd1Z=de%de fd2Z>de*de fd3Z?de*de fd4Z@de*de fd5ZAdddejB        fd6e d7ed8e(d9e(d:e%de%fd;ZC	 	 	 d]d<e d7ed8e(d9e(de%f
d=ZDd6e d7ede fd>ZEd?e%de%fd@ZFdA ZGdBe%de*fdCZH	 	 	 	 d^dEedFejI        dBe%dGe(dHedIeJe         dJeKde(fdKZL	 dZdLe dMe dNejM        de(fdOZN	 dZdLe dMe dNejM        de(fdPZO ejP        dQR          ZQdSe%d7ede j         fdTZRd7ede%e%ffdUZSd_de*de j         fdVZTd[dWe j         dXe j         de(fdYZUdS )`    N)ZoneInfo)CronTrigger)croniter)	Nominatim)Update)ContextTypes)TimezoneFinder)phrases)User)Screen)DateValidationException)Keyboard)full_message_sendescape_valid_markdown_chars)get_unit_value_from_stringcron_expressionstart_datetimeprevious_fire_timereturnc                     |.t           j                             t           j        j                  }t	          j        |           }|                    ||          S )z
    Get the next run time
    :param cron_expression: The cron expression
    :param start_datetime: The start datetime. If None, the current datetime is used
    :param previous_fire_time: The previous fire time
    :return: The next run time
    )datetimenowtimezoneutcr   from_crontabget_next_fire_time)r   r   r   cron_triggers        /app/src/service/date_service.pyget_next_runr      sM     !*..x/@/DEE+O<<L**+=~NNN    c                     |.t           j                             t           j        j                  }t	          | |          }|                    t           j                   S )z
    Get the previous run time
    :param cron_expression: The cron expression
    :param start_datetime: The start datetime. If None, the current datetime is used
    :return: The previous run time
    )r   r   r   r   r   get_prev)r   r   itrs      r   get_previous_runr$   -   sJ     !*..x/@/DEE
?N
3
3C<<)***r    c                     t          |           }|                                t          j                                                                        k    S )z
    Check if the next run is today
    :param cron_expression: The cron expression
    :return: Whether the next run is today
    )r   dater   r   )r   next_runs     r   next_run_is_todayr(   <   s?     O,,H==??h/3355::<<<<r    Fseconds	show_fullc                 H   t          | dz            }| dz  }t          |dz            }| dz  }t          |dz            }| dz  }t          |dz            }|dz  }| dk    rdS d}|dk    r|dk    r	|| d	z  }n|| d
z  }|dk    r|dk    s|r|dk    r	|| dz  }n|| dz  }|dk    r=|dk    r|dk    s|r/t          |          dk    r|dz  }|dk    r	|| dz  }n|| dz  }|dk    rI|dk     r|dk    r|dk    s|r5t          |          dk    r|dz  }|dk    r|dk    r	|| dz  }n|| dz  }|dk    rO|dk    r|dk    r|dk    r|dk    s|r5t          |          dk    r|dz  }|dk    r|dk    r	|| dz  }n|| dz  }|                                S )z
    Converts seconds to days, hours, minutes, seconds
    :param seconds: Seconds to convert
    :param show_full: Whether to show all units when available
    :return: Days, hours e.g. 1 day 2 hours 5 minutes
    i:	 Q   <   r   z	0 seconds    z weekz weeksz dayz days z hourz hours   z minutez minutesz secondz seconds)intlenstrip)r)   r*   weeksremaining_secondsdayshoursminutesresults           r   convert_seconds_to_durationr<   G   s{    6!""E&( E)**D%!T)**E$#r)**G!||{FqyyA::ooo%FF&&&&F axxUaZZ9Z199mmm#FFnnn$F qyytqyyUaZZIZv;;??cMFA::ooo%FF&&&&F {{		eqjjYjv;;??cMFQ;;!||W----W.... A	A%1**uzzizv;;??cMFq   A%%.7777.8888<<>>r    r:   c                 *    t          | dz  |          S )z
    Converts minutes to days, hours, minutes, seconds
    :param minutes: Minutes to convert
    :param show_full: Whether to show all units when available
    :return: Days, hours e.g. 1 day 2 hours 5 minutes
    r.   r*   r<   )r:   r*   s     r   convert_minutes_to_durationr@      s     'w|yIIIIr    r9   c                 *    t          | dz  |          S )z
    Converts hours to days, hours, minutes, seconds
    :param hours: Hours to convert
    :param show_full: Whether to show all units when available
    :return: Days, hours e.g. 1 day 2 hours 5 minutes
    r-   r>   r?   )r9   r*   s     r   convert_hours_to_durationrB      s     'ut|yIIIIr    r8   c                 *    t          | dz  |          S )z
    Converts days to days, hours, minutes, seconds
    :param days: Days to convert
    :param show_full: Whether to show all units when available
    :return: Days, hours e.g. 1 day 2 hours 5 minutes
    r,   r>   r?   )r8   r*   s     r   convert_days_to_durationrD      s     'te|yIIIIr    c                     |.t           j                             t           j        j                  }t	          | |          }t          ||z
                                            S )a  
    Get the difference between the next run time and the current datetime
    :param cron_expression: The cron expression
    :param start_datetime: The start datetime. If None, the current datetime is used
    :return: The difference in days and hours e.g. 1 day 2h hours
    )r   r   r   r   r   r<   total_secondsr   r   r'   s      r   cron_datetime_differencerH      sT     !*..x/@/DEEO^<<H&>(A'P'P'R'RSSSr    end_datetimestart_is_midnightc                 ,   | t          d          S |9t          j                                        }|r|                    dddd          }|                     d          } t          t	          | |z
                                            |          S )a  
    Get the remaining time until the end_datetime
    :param end_datetime: The end datetime
    :param start_datetime: The start datetime. If None, the current datetime is used
    :param show_full: Whether to show all units when available
    :param start_is_midnight: Whether to use midnight as the start time
    :return: The remaining time in days and hours e.g. 1 day 2h hours
    Nr   hourminutesecondmicrosecondtzinfor>   )r<   r   r   replacer3   rF   )rI   r   r*   rJ   s       r   get_remaining_durationrT      s     *1---!*..00 	_+331Q\]3^^N  ''t'44L&\N*99;;<<	   r    c                     | dS |                      d          } t          t          j                                        | z
                                            S )z
    Get the elapsed time since the start_datetime
    :param start_datetime: The start datetime
    :return: The elapsed time in seconds
    Nr   rQ   )rS   r3   r   r   rF   r   s    r   get_elapsed_secondsrW      sU     q $++4+88N!%%''.8GGIIJJJr    c                 &    t          |           dz  S )z
    Get the elapsed time since the start_datetime
    :param start_datetime: The start datetime
    :return: The elapsed time in hours
    r-   )rW   rV   s    r   get_elapsed_hoursrY      s     ~..$66r    c                 >    t          t          |           |          S )z
    Get the elapsed time since the start_datetime
    :param start_datetime: The start datetime
    :param show_full: Whether to show all units when available
    :return: The elapsed time in days and hours e.g. 1 day 2h hours
    r>   )r<   rW   )r   r*   s     r   get_elapsed_durationr[      s!     '':>'J'JV_````r    c                     |.t           j                             t           j        j                  }t	          | |          }t          |          S )a  
    Get the remaining time until the next cron run
    :param cron_expression: The cron expression
    :param start_datetime: The start datetime. If None, the current datetime is used
    :return: The remaining time in days and hours e.g. 1 day 2h hours
    )r   r   r   r   r   rT   rG   s      r   !get_remaining_time_from_next_cronr]      sC     !*..x/@/DEEO^<<H!(+++r    c                    |                      d          } |.t          j                            t          j        j                  }|                     d          }| |k     rdS t          | |z
                                            S )z
    Get the remaining time in seconds until the end_datetime
    :param end_datetime: The end datetime
    :param start_datetime: The start datetime. If None, the current datetime is used
    :return: The remaining time in seconds
    NrQ   r   )rS   r   r   r   r   r3   rF   rI   r   s     r   get_remaining_time_in_secondsr`     s      ''t'44L!*..x/@/DEE#++4+88N n$$q~-<<>>???r    c                 (    t          | |          dz  S )z
    Get the remaining time in minutes until the end_datetime
    :param end_datetime: The end datetime
    :param start_datetime: The start datetime. If None, the current datetime is used
    :return: The remaining time in minutes
    r.   r`   r_   s     r   get_remaining_time_in_minutesrc     s     )~FF"LLr    c                 (    t          | |          dz  S )z
    Get the remaining time in days until the end_datetime
    :param end_datetime: The end datetime
    :param start_datetime: The start datetime. If None, the current datetime is used
    :return: The remaining time in days
    r,   rb   r_   s     r   get_remaining_time_in_daysre   %  s     )~FF%OOr    
start_timec                     |t           j                                         }|t          j        t          |                     z   S )z
    Get the datetime in the future
    :param seconds: The number of seconds in the future
    :param start_time: The start time. If None, the current datetime is used
    :return: The datetime in the future
    Nr)   )r   r   	timedeltar3   )r)   rf   s     r   get_datetime_in_future_secondsrj   /  s=     &**,,
*3w<<@@@@@r    c                 (    t          | dz  |          S )z
    Get the datetime in the future
    :param minutes: The number of minutes in the future
    :param start_time: The start time. If None, the current datetime is used
    :return: The datetime in the future
    r.   )rj   )r:   rf   s     r   get_datetime_in_future_minutesrl   =  s     *'B,
CCCr    c                 B    t          t          | dz            |          S )z
    Get the datetime in the future
    :param hours: The number of hours in the future
    :param start_time: The start time. If None, the current datetime is used
    :return: The datetime in the future
    r-   )rj   r3   )r9   rf   s     r   get_datetime_in_future_hoursrn   H  s      *#edl*;*;ZHHHr    c                 (    t          | dz  |          S )z
    Get the datetime in the future
    :param days: The number of days in the future
    :param start_time: The start time. If None, the current datetime is used
    :return: The datetime in the future
    r2   )rn   )r8   rf   s     r   get_datetime_in_future_daysrp   S  s     (r	:>>>r    c           
          | t          j        t          j        dt	          || z
                                                                z   S )z
    Get a random time between the start and end datetime
    :param start_datetime: The start datetime
    :param end_datetime: The end datetime
    :return: The random datetime
    r   rh   )r   ri   randomrandintr3   rF   )r   rI   s     r   get_random_time_betweenrt   ^  sM     H.q#|n'D&S&S&U&U"V"VWW    r    c                     t          t          j                            t          j        j                  t          |                     S )z
    Get a random time between now and the next cron run
    :param cron_expression: The cron expression
    :return: The random datetime
    )rt   r   r   r   r   r   )r   s    r   get_random_time_between_by_cronrv   k  s:     #h/344l?6S6S  r    c                 t    t          t          j                                        t          |                     S )z|
    Get a random time between now and next n days
    :param days: The number of days
    :return: The random datetime
    )rt   r   r   rp   r8   s    r   get_random_time_between_by_daysry   w  s-     #8#4#8#8#:#:<WX\<]<]^^^r    c                 t    t          t          j                                        t          |                     S )z
    Get a random time between now and next n hours
    :param hours: The number of hours
    :return: The random datetime
    )rt   r   r   rn   )r9   s    r    get_random_time_between_by_hoursr{     s-     #8#4#8#8#:#:<XY^<_<_```r    c                 t    t          t          j                                        t          |                     S )z
    Get a random time between now and next n seconds
    :param seconds: The number of seconds
    :return: The random datetime
    )rt   r   r   rj   rh   s    r   "get_random_time_between_by_secondsr}     s3     #!?!H!H  r    	date_timeuseradd_remaining_timeadd_elapsed_timetime_formatc                 X   t          | |                              |          }|s|s|S |r@t          |           r|S |t          j                            t          |                     z   S t          |           r|S |t          j                            t          |                     z   S )aY  
    Default datetime format
    :param date_time: The datetime
    :param user: The user. If given, the timezone of the user will be used
    :param add_remaining_time: Whether to add the remaining time
    :param add_elapsed_time: Whether to add the elapsed time
    :param time_format: The time format
    :return: The formatted datetime
    )
get_time_with_timezonestrftimedatetime_is_beforer
   DATETIME_REMAINING_PARENTHESISformatrT   datetime_is_afterDATETIME_ELAPSED_PARENTHESISr[   )r~   r   r   r   r   texts         r   default_datetime_formatr     s    " ")T22;;KHHD &6  
i(( 	Kg<CC"9--
 
 
 	
 ## '6==>RS\>]>]^^^^r    r&   c                 >    t          | |||t          j                  S )a   
    Default date format
    :param date: The date
    :param user: The user. If given, the timezone of the user will be used
    :param add_remaining_time: Whether to add the remaining time
    :param add_elapsed_time: Whether to add the elapsed time
    :return: The formatted date
    )r   r   r   )r   cSTANDARD_DATE_FORMAT)r&   r   r   r   s       r   default_date_formatr     s-     #-)*   r    c                     || S | j         ?|                     t          t          j                                                            } |                     |                                          S )z
    Get the time with timezone
    :param date_time: The datetime
    :param user: The user. If given, the timezone of the user will be used
    :return: The time with timezone
    NrQ   )rR   rS   r   EnvTZget
astimezoneget_timezone)r~   r   s     r   r   r     sb     | %%Xcfjjll-C-C%DD	 1 1 3 3444r    timezone_namec                    | t           j                                        } t          |           }t          j                            |                                          }t          |                                d          \  }}|dk     rd}nd}| t          t          |                    ddt          t          |                    d}|S )zF
    Get the UTC offset for a timezone
    return: The UTC offset
    Nr-   r   -+02:)r   r   r   r   r   r   	utcoffsetdivmodrF   absr3   )r   r   current_offsetr9   r:   sign
offset_strs          r   get_utc_offsetr     s    
 

 &&H&**844>>@@NN88::DAANE7qyy E#c%jj//EEEs3w<</@/@EEEJr    c                     t          d          }|                    |           }|r1t                      }|                    |j        |j                  }|S d S )Ntimezone_app)
user_agent)lnglat)r   geocoder	   timezone_at	longitudelatitude)location_name
geolocatorlocationtftimezone_strs        r   get_timezone_from_locationr     s^    n555J!!-00H ~~(*<(BS~TTtr    durationc                 |    	 t          |           dz  S # t          $ r t          | t          j                  cY S w xY w)z
    Get the duration from a string (e.g. 1h, 2min, 3sec)
    :param duration: The duration
    :return: The duration in seconds
    r.   )r3   
ValueErrorr   r   DURATION_UNIT_TO_SECONDS)r   s    r   get_duration_from_stringr     sQ    P8}}r!! P P P)(A4NOOOOOPs    $;;Tupdatecontextadd_delete_buttoninbound_keyboardprevious_screens"previous_screen_list_keyboard_infoc           
         K   	 t          |           dS # t          $ r* t          |t          j        | ||||           d{V  Y dS w xY w)a  
    Validates the duration
    :param update: Telegram update
    :param context: Telegram context
    :param duration: The duration
    :param add_delete_button: Whether to add a delete button to the keyboard
    :param inbound_keyboard: The inbound keyboard
    :param previous_screens: The previous screens, for the back button if in private chat
    :param previous_screen_list_keyboard_info: The previous screen list keyboard info, for the back
     button if in private
    :return: Whether the duration is valid
    T)r   r   r   r   r   NF)r   r   r   r
   ACTION_INVALID_DURATION)r   r   r   r   r   r   r   s          r   validate_durationr     s      , ***t 
 
 
+/--/Q
 
 
 	
 	
 	
 	
 	
 	
 	
 uu
s    0A	A	dtstarting_datetzc                 T   | dS |+t          t          j                                                  }| j        |                     |          } |4|j        |                    |          }n5|                    |          }nt          j                            |          }| |k     S )z
    Check if the datetime is before the current datetime
    :param dt: The datetime
    :param starting_date: The starting date
    :param tz: The timezone
    :return: Whether the datetime is after the current datetime
    NTrQ   )	r   r   r   r   rR   rS   r   r   r   r   r   r   s      r   r   r   B  s     
zt	zcfjjll## 
yZZrZ""  ')111<<MM)44R88MM )--b11r    c                 &    t          | ||           S )z
    Check if the datetime is after the current datetime
    :param dt: The datetime
    :param starting_date: The starting date
    :param tz: The timezone
    :return: Whether the datetime is after the current datetime
    )r   r   s      r   r   r   c  s     ""mR8888r    en_AU)localeIDr   c                    t          j        t                    }|                                }t          j                            |                              d          }|                    | ||          d                             d          }||k    rt                      |	                    t          t          j                                                            S )z
    Get the datetime from a natural language string
    :param text: The text
    :param user: The user
    :return: The datetime
    r   )rP   rQ   )parsedatetimeCalendarPDT_CONSTANTSr   r   r   rS   parseDTr   r   r   r   r   r   )r   r   caluser_tzr   parsed_datetimes         r   "get_datetime_from_natural_languager   t  s     
 
/
/C!!G &.227;;CCPQCRRCkk$Gk<<Q?GGTUGVVO #%''' %%hsvzz||&<&<===r    c                     | j         t          | j                   nt          j        }t	          | j                   }||fS )zx
    Get the user timezone and offset text
    :param user: The user
    :return: The user timezone and offset text
    )r   r   r
   !PVT_TXT_SETTINGS_TIMEZONE_UNKNOWNr   )r   timezone_textoffset_texts      r   !get_user_timezone_and_offset_textr     sE     =$ 	$DM2226 
 !//K+%%r    c                     t           j                                                             dddd          t          j        |           z
  S )zk
    Get the day in the past
    :param days: The number of days in the past
    :return: The datetime
    r   rL   rx   )r   r   rS   ri   rx   s    r   get_day_in_pastr     sO       ""**q +  %%%& &r    date_1date_2c                     |t           j                                         }|                                 |                                k    S )z
    Check if two dates are the same day
    :param date_1: The first date
    :param date_2: The second date
    :return: Whether the dates are the same day
    )r   r   r&   )r   r   s     r   is_same_dayr     s8     ~"&&((;;==FKKMM))r    )NN)N)F)NFF)TNNN)r   )Vr   rr   zoneinfor   r   pytzapscheduler.triggers.cronr   r   geopy.geocodersr   src.tg_compatr   src.tg_compat.extr   timezonefinderr	   	constantsr   resources.EnvironmentEnvironmentr   	resourcesr
   src.model.Userr   src.model.enums.Screenr   src.model.error.CustomExceptionr   src.model.pojo.Keyboardr   src.service.message_servicer   r   src.utils.string_utilsr   strr   r$   boolr(   r3   floatr<   r@   rB   rD   rH   rT   rW   rY   r[   r]   r`   rc   re   rj   rl   rn   rp   rt   rv   ry   r{   r}   STANDARD_DATE_TIME_FORMATr   r   r   r   r   r   DEFAULT_TYPElistdictr   rR   r   r   	Constantsr   r   r   r   r    r    r   <module>r      sm	                1 1 1 1 1 1       % % % % % %             * * * * * * ) ) ) ) ) )     # # # # # #             ) ) ) ) ) ) C C C C C C , , , , , , V V V V V V V V = = = = = =
 )-,0O OO%O !)O 	O O O O(+ +c +8 +x + + + +=s =t = = = =@ @u @ @RU @ @ @ @FJ J J J# J J J JJ JS JT Jc J J J JJ J3 J4 JC J J J JT Tc T8 TWZ T T T T" )-#	 #%  	
 	   <K KS K K K K7h 75 7 7 7 7a a ad as a a a a 6:, ,,*2,, , , , @ @ @( @^a @ @ @ @(M M M( M^a M M M MP PX Px P[^ P P P PA AC AX=N AZb A A A AD DC DX=N DZb D D D DI I I8;L IX` I I I I? ?c ?x7H ?T\ ? ? ? ?
H 
H 
QY 
 
 
 
	S 	X 	 	 	 	_# _( _ _ _ _aC aH a a a a	 	 	 	 	 	 $"2!_ !_!_
!_ !_ 	!_
 !_ 	!_ !_ !_ !_L $"	 

  	
 	   05h 5d 5x 5 5 5 5$# #    4  Ps Ps P P P P$ #!%%)/3# ##&# # 	#
 # 6l# )-# 
# # # #N EI !)6:k	   D EI9 99!)96:k9	9 9 9 9 ('999>S > >AR > > > >.&D &c3Z & & & &"	& 	&# 	&h&7 	& 	& 	& 	&* *) *83D *PT * * * * * *r    