2
\$\begingroup\$

Create a Time class.

Time is on my side

Specification

  • Basic attributes: hour, minute, second and day (default is 0), + getters, + setters (using a decorator)

  • If you try to initialize an incorrect value of seconds or minutes, i.e. greater than or equal to 60, raise an exception from your own WrongSeconds or WrongMinutes class

Add the following operators:

  • +: works as follows - adds hours to hours, minutes to minutes, and so on, e.g. 2 h 30 m 4 s + 6 h 35 m 50 s = 9 h 5 m 54 s (we also return an instance of the Time class), if we exceed 24 h, the day should be 1

  • <: Compare first day, hours, then minutes, seconds. Return True / False, e.g. 2 h 40 m 30 s d 0 < 1 h 59 m 30 s d 0 → False, 2 h 40 m 30 s d -1 < 1 h 59 m 30 s d 0 → True

  • __str__: Time should be displayed as: "20:05:25, day without any changes" (when day = 1, -1 : next / previous day)

Add the function zone, which for a given instance of the Time class returns the appropriate time in another time zone. The argument of the function is the city for which we find the time: Warsaw, London, New York, Tokyo, Syndey, Moscow, Los Angeles. We assume that the default time is GMT.

  • PEP8
  • DOC
  • tests

And here is the code!

class WrongSeconds(Exception):
    def __str__(self):
        return "Wrong amount of seconds passed (greater or equal 60)"
    
class WrongMinutes(Exception):
    def __str__(self):
        return "Wrong amount of minutes passed (greater or equal 60)"

    
    
class Time:
    TIME_ZONES = { "Warsaw": 2,
                   "London": 1,
                   "Tokio": 9,
                   "Sydney": 10,
                   "Moscow": 3,
                   "Los angeles": -7 }
    
    def __init__(self, d = 0, h = 0, m = 0, s = 0):
        self.days = d
        self.hours = h
        self.minutes = m
        self.seconds = s
        
    @property    
    def days(self):
        return self._d
    
    @days.setter
    def days(self, val):
        self._d = val
        
    @property    
    def hours(self):
        return self._h
    
    @hours.setter
    def hours(self, val):
        if val < 0:
            self.days += val//60
            val = 24 - abs(val)%24
        elif val >= 24:
            self.days += val//24
            val %= 24
        self._h = val
        
    @property    
    def minutes(self):
        return self._m
    
    @minutes.setter
    def minutes(self, val):
        if "_m" not in self.__dict__ and  val >= 60:
            raise WrongMinutes
        if val < 0:
            self.hours += val//60
            val = 60 - abs(val)%60
        elif val >= 60:
            self.hours += val//60
            val %= 60
        self._m = val
        
    @property    
    def seconds(self):
        return self._s
    
    @seconds.setter
    def seconds(self, val):
        if "_s" not in self.__dict__ and  val >= 60:
            raise WrongSeconds
        if val < 0:
            self.minutes += val//60
            val = 60 - abs(val)%60
        elif val >= 60:
            self.minutes += val//60
            val %= 60
        self._s = val
        
    def strefa(self, city):
        if city.capitalize() not in self.TIME_ZONES: raise ValueError(f"City {city} not in time zone database")
        
        result_time = self.__class__( self.days, self.hours, self.minutes, self.seconds)
        result_time.hours += self.TIME_ZONES[city.capitalize()]
        
        return str(result_time)
        
        
    def __add__(self, other):
        assert isinstance(other, self.__class__), f"Cannot add {type(other)} to a time object"
        result = self.__class__()
        
        result.seconds += self.seconds + other.seconds
        result.minutes += self.minutes + other.minutes
        result.hours   += self.hours   + other.hours
        result.days    += self.days    + other.days
        
        return result 
    
    def __lt__(self, other):
        assert isinstance(other, self.__class__), f"Cannot compare {type(other)} with a time object"
        
        if self.days < other.days: return True
        elif self.hours < other.hours: return True
        elif self.minutes < other.minutes: return True
        elif self.seconds < other.seconds: return True
        
        return False
        
    def __str__(self):
        s = f"{self.hours}:{self.minutes:02}:{self.seconds:02}"
                
        if self.days == 0 :
            return s + ", day without any changes"
        
        elif self.days == 1 :
            return s + ", the next day"
        
        elif self.days == -1 :
            return s + ", previous day"
        
        return s + f", day: {self.days}"
        
        
        
        
#  --- TEST CASES ---
try:
    time = Time(m = 60)
except Exception as e:
    print(e.__class__, e, "\n")
    
time = Time(d = 0, h = 23, m = 59, s = 59)
print(time)

time.seconds += 1
print(time, "\n")

time1 = Time(h = 3, m = 2, s = 1)
time2 = Time(h = 6, m = 5, s = 4)
print(time1 + time2, "\n")

print(time1 < time2 )
print(time2 < time1, "\n")

time_gmt = Time(h = 2, m = 0, s = 0)
print(time_gmt.strefa("London")) 
print(time_gmt.strefa("Sydney")) 
print(time_gmt.strefa("Los Angeles")) 
print("------------------------------------")
print("Comparisons")

t1 = Time(h = 14, m = 4, s = 35)
t2 =  Time(h = 8, m = 50, s = 25)

print(t1 + t2)
print(t1)
print(t2)
print(t1 < t2)

Output:

<class '__main__.WrongMinutes'> Wrong amount of minutes passed (greater or equal 60) 

23:59:59, day without any changes
0:00:00, the next day 

9:07:05, day without any changes 

True
False 

3:00:00, day without any changes
12:00:00, day without any changes
19:00:00, previous day
------------------------------------
Comparisons
22:55:00, day without any changes
14:04:35, day without any changes
8:50:25, day without any changes
True

And here is my question, how to get a better solution? Maybe shorter? Maybe get rid of some operations, terms?

\$\endgroup\$
6
  • \$\begingroup\$ If you can't represent the 61st second of a minute, what's supposed to happen when there's a leap second?? \$\endgroup\$ Jun 22, 2021 at 16:19
  • \$\begingroup\$ um raise a wrongseconds/wrongminutes error i guess \$\endgroup\$ Jun 22, 2021 at 16:21
  • \$\begingroup\$ That's fine - the code just pretends leap seconds don't exist. I'm poking fun a little, but also pointing out that real date-time code has a lot of awkward real-world stuff to deal with, so better using the libraries than writing your own for actual use. \$\endgroup\$ Jun 22, 2021 at 16:23
  • 1
    \$\begingroup\$ Well you know, sometimes (because its a task for school) I also don't see any sense in creating such complicated and thus simple things, but yeah, here we are! :D \$\endgroup\$ Jun 22, 2021 at 16:25
  • \$\begingroup\$ One comment is, whenever you are implementing either of the comparison operators, you need to make sure you always implement them all. This would be, <, <=, >, >=, == and !=. Otherwise, the whoever used your Time class might run into trouble during comparisons without noticing. \$\endgroup\$ Jun 22, 2021 at 19:35

1 Answer 1

1
\$\begingroup\$

One small point:

When we inherit from Exception, we get its (actually, BaseException's) str() implementation, so we don't need to provide __str__() if we initialise the base class args with the required string:

class WrongSeconds(Exception):
    def __init__(self):
        self.args = [ "Wrong amount of seconds passed (greater or equal 60)" ]

In the addition, we have these new values:

    result.seconds += self.seconds + other.seconds
    result.minutes += self.minutes + other.minutes
    result.hours   += self.hours   + other.hours
    result.days    += self.days    + other.days

I don't see anything that handles overflow of any of these values (e.g. if hours > 24, we need to add a day and subtract 24 of the hours to get the values back into their allowed ranges).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.