22
33from datetime import timedelta
44
5- from .translator import Translator
5+ from .mixins . interval import WordableIntervalMixin , AbsoluteIntervalMixin
66
77
8- class Interval (timedelta ):
8+ class BaseInterval (timedelta ):
9+ """
10+ Base class for all inherited interval classes.
11+ """
912
1013 _y = None
1114 _m = None
@@ -16,8 +19,6 @@ class Interval(timedelta):
1619 _s = None
1720 _invert = None
1821
19- _translator = None
20-
2122 def __init__ (self , days = 0 , seconds = 0 , microseconds = 0 ,
2223 milliseconds = 0 , minutes = 0 , hours = 0 , weeks = 0 ):
2324 total = self .total_seconds ()
@@ -29,68 +30,6 @@ def __init__(self, days=0, seconds=0, microseconds=0,
2930 self ._seconds = abs (int (total )) % 86400 * m
3031 self ._days = abs (int (total )) // 86400 * m
3132
32- @classmethod
33- def instance (cls , delta ):
34- """
35- Creates a Interval from a timedelta
36-
37- :type delta: timedelta
38-
39- :rtype: Interval
40- """
41- return cls (days = delta .days , seconds = delta .seconds , microseconds = delta .microseconds )
42-
43- # Localization
44-
45- @classmethod
46- def translator (cls ):
47- """
48- Initialize the translator instance if necessary.
49-
50- :rtype: Translator
51- """
52- if cls ._translator is None :
53- cls ._translator = Translator ('en' )
54- cls .set_locale ('en' )
55-
56- return cls ._translator
57-
58- @classmethod
59- def set_translator (cls , translator ):
60- """
61- Set the translator instance to use.
62-
63- :param translator: The translator
64- :type translator: Translator
65- """
66- cls ._translator = translator
67-
68- @classmethod
69- def get_locale (cls ):
70- """
71- Get the current translator locale.
72-
73- :rtype: str
74- """
75- return cls .translator ().locale
76-
77- @classmethod
78- def set_locale (cls , locale ):
79- """
80- Set the current translator locale and
81- indicate if the source locale file exists.
82-
83- :type locale: str
84-
85- :rtype: bool
86- """
87- if not cls .translator ().register_resource (locale ):
88- return False
89-
90- cls .translator ().locale = locale
91-
92- return True
93-
9433 def total_minutes (self ):
9534 return self .total_seconds () / 60
9635
@@ -199,90 +138,102 @@ def _sign(self, value):
199138
200139 return 1
201140
202- def in_words (self , locale = None ):
203- """
204- Get the current interval in words in the current locale.
205-
206- Ex: 6 jours 23 heures 58 minutes
207-
208- :rtype: str
209- """
210- periods = [
211- ('week' , self .weeks ),
212- ('day' , self .days_exclude_weeks ),
213- ('hour' , self .hours ),
214- ('minute' , self .minutes ),
215- ('second' , self .seconds )
216- ]
217-
218- parts = []
219- for period in periods :
220- unit , count = period
221- if abs (count ) > 0 :
222- parts .append (
223- self .translator ().transchoice (unit , count , {'count' : count }, locale = locale )
224- )
225-
226- return ' ' .join (parts )
227-
228- def __str__ (self ):
229- return self .in_words ()
230-
231- def __repr__ (self ):
232- return '<{0} [{1}]>' .format (self .__class__ .__name__ , str (self ))
233-
234141 def __add__ (self , other ):
235142 if isinstance (other , timedelta ):
236- return Interval (seconds = self .total_seconds () + other .total_seconds ())
143+ return self . __class__ (seconds = self .total_seconds () + other .total_seconds ())
237144
238145 return NotImplemented
239146
240147 def __sub__ (self , other ):
241148 if isinstance (other , timedelta ):
242- # for CPython compatibility, we cannot use
243- # our __class__ here, but need a real timedelta
244- return Interval (seconds = self .total_seconds () - other .total_seconds ())
149+ return self .__class__ (seconds = self .total_seconds () - other .total_seconds ())
245150
246151 return NotImplemented
247152
248153 def __neg__ (self ):
249- # for CPython compatibility, we cannot use
250- # our __class__ here, but need a real timedelta
251- return Interval (seconds = - self .total_seconds ())
154+ return self .__class__ (seconds = - self .total_seconds ())
252155
253156
254- class AbsoluteInterval (Interval ):
157+ class Interval (WordableIntervalMixin , BaseInterval ):
158+ """
159+ Replacement for the standard timedelta class.
255160
256- def total_seconds ( self ):
257- return abs ( super ( AbsoluteInterval , self ). total_seconds ())
161+ Provides several improvements over the base class.
162+ """
258163
259- @property
260- def weeks (self ):
261- return abs (super (AbsoluteInterval , self ).weeks )
164+ @classmethod
165+ def instance (cls , delta ):
166+ """
167+ Creates a Interval from a timedelta
262168
263- @property
264- def days (self ):
265- return abs (super (AbsoluteInterval , self ).days )
169+ :type delta: timedelta
266170
267- @ property
268- def hours ( self ):
269- return abs ( super ( AbsoluteInterval , self ). hours )
171+ :rtype: Interval
172+ """
173+ return cls ( days = delta . days , seconds = delta . seconds , microseconds = delta . microseconds )
270174
271- @property
272- def minutes (self ):
273- return abs (super (AbsoluteInterval , self ).minutes )
274175
275- @property
276- def seconds (self ):
277- return abs (super (AbsoluteInterval , self ).seconds )
176+ class AbsoluteInterval (AbsoluteIntervalMixin , Interval ):
177+ """
178+ Interval that expresses a time difference in absolute values.
179+ """
278180
279- @property
280- def microseconds (self ):
281- return abs (super (AbsoluteInterval , self ).microseconds )
282181
283- @property
284- def invert (self ):
285- return super (AbsoluteInterval , self ).total_seconds () < 0
182+ class DatetimeAwareInterval (WordableIntervalMixin , BaseInterval ):
183+ """
184+ Interval class that is aware of the datetimes that generated the
185+ time difference.
186+ """
286187
287- def _sign (self , value ):
288- return 1
188+ def __new__ (cls , dt1 , dt2 , days = 0 , seconds = 0 , microseconds = 0 ,
189+ milliseconds = 0 , minutes = 0 , hours = 0 , weeks = 0 ):
190+ return super (DatetimeAwareInterval , cls ).__new__ (
191+ cls , days , seconds , microseconds , milliseconds , minutes , hours , weeks
192+ )
193+
194+ def __init__ (self , dt1 , dt2 , days = 0 , seconds = 0 , microseconds = 0 ,
195+ milliseconds = 0 , minutes = 0 , hours = 0 , weeks = 0 ):
196+ super (DatetimeAwareInterval , self ).__init__ (
197+ days , seconds , microseconds , milliseconds , minutes , hours , weeks
198+ )
199+
200+ self ._dt1 = dt1
201+ self ._dt2 = dt2
202+
203+ @classmethod
204+ def instance (cls , delta , dt1 , dt2 ):
205+ return cls (dt1 , dt2 , days = delta .days , seconds = delta .seconds , microseconds = delta .microseconds )
206+
207+
208+ class DatetimeAwareAbsoluteInterval (AbsoluteIntervalMixin , DatetimeAwareInterval ):
209+ """
210+ Interval class that is aware of the datetimes that generated the
211+ time difference and expresses it in absolute values.
212+ """
213+
214+
215+ class IntervalFactory (object ):
216+ """
217+ Factory to get the poper interval class.
218+ """
219+
220+ @classmethod
221+ def get (cls , dt1 , dt2 , absolute = False ):
222+ """
223+ Gets the proper interval class for two given datetimes.
224+
225+ :param dt1: The first datetime
226+ :type dt1: Pendulum or datetime
227+
228+ :param dt2: The second datetime
229+ :type dt2: Pendulum or datetime
230+
231+ :rtype: Interval
232+ """
233+ delta = dt1 - dt2
234+
235+ klass = DatetimeAwareInterval
236+ if absolute :
237+ klass = DatetimeAwareAbsoluteInterval
238+
239+ return klass .instance (delta , dt1 , dt2 )
0 commit comments