# Date.py Copyright (c) Kari Laitinen # http://www.naturalprogramming.com # 2006-05-01 File created. # 2022-12-18 Converted to Python 3. # Two classes are defined in this file (module). The Date class # represents a date. The DateDistance class is a helper class # that represents a chronological distance between two Date # instance objects. Date method get_distance_to() returns a # DateDistance object and initializes the three data fields, # years, months and days, inside the object. class DateDistance : pass # The single constructor allows Date objects to be constructed # in several ways. class Date : def __init__( self, given_day = 1, given_month = 1, given_year = 1970, given_print_format = 'E' ) : if isinstance( given_day, int ) : # The date was given as numbers in several formal parameters self.this_day = given_day self.this_month = given_month self.this_year = given_year self.date_print_format = given_print_format elif isinstance( given_day, str ) : # The date was given as a string. We'll suppose that the # first parameter contains the given date. date_as_string = given_day # This constructor accepts date strings in two formats: # MM/DD/YYYY is the American format. # DD.MM.YYYY is the format used in Europe. # Member variable self.date_print_format will be set either # to value 'A' or 'E'. This value will be used to select # correct print format when self date object is converted # to a string with the toString() method. if date_as_string[ 2 ] == '/' : self.date_print_format = 'A' # American format self.this_day = int( date_as_string[ 3 : 5 ] ) self.this_month = int( date_as_string[ 0 : 2 ] ) else : self.date_print_format = 'E' # European format self.this_day = int( date_as_string[ 0 : 2 ] ) self.this_month = int( date_as_string[ 3 : 5 ] ) self.this_year = int( date_as_string[ 6 : 10 ] ) else : print( "\n Wrong type of argument to Date constructor." ) def day( self ) : return self.this_day def month( self ) : return self.this_month def year( self ) : return self.this_year def get_date_print_format( self) : return self.date_print_format def is_last_day_of_month( self ) : it_is_last_day_of_month = False if self.this_day > 27 : if self.this_day == 31 : it_is_last_day_of_month = True elif self.this_day == 30 and \ ( self.this_month == 2 or self.this_month == 4 or \ self.this_month == 6 or self.this_month == 9 or \ self.this_month == 11 ) : it_is_last_day_of_month = True elif self.this_day == 29 and self.this_month == 2 : it_is_last_day_of_month = True elif self.this_day == 28 and \ self.this_month == 2 and \ not self.this_is_a_leap_year() : it_is_last_day_of_month = True return it_is_last_day_of_month def this_is_a_leap_year( self ) : return_code = False if self.this_year % 4 == 0 : # Years which are equally divisible by 4 and which # are not full centuries are leap years. Centuries # equally divisible by 4 are, however, leap years. if self.this_year % 100 == 0 : century = self.this_year / 100 if century % 4 == 0 : return_code = True else : return_code = True return return_code def is_within_dates( self, earlier_date, later_date ) : return (( self.is_equal_to( earlier_date ) ) or \ ( self.is_equal_to( later_date ) ) or \ ( self.is_later_than( earlier_date ) and \ self.is_earlier_than( later_date ) ) ) def index_for_day_of_week( self ) : # day_index will get a value in the range from 0 to 6, # 0 meaning Monday and 6 meaning Sunday. day_index = 0 known_date = Date( "06.10.1997" ) # Oct. 6, 1997 is Monday. if known_date.is_later_than( self ) : while known_date.is_not_equal_to( self ) : if day_index > 0 : day_index -= 1 else : day_index = 6 known_date.decrement() else : while known_date.is_not_equal_to( self ) : if day_index < 6 : day_index += 1 else : day_index = 0 known_date.increment() return day_index def get_day_of_week( self ) : days_of_week = ( "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" ) return days_of_week[ self.index_for_day_of_week() ] def increment( self ) : if self.is_last_day_of_month() : self.this_day = 1 if self.this_month < 12 : self.this_month += 1 else : self.this_month = 1 self.this_year += 1 else : self.this_day += 1 def decrement( self ) : if self.this_day > 1 : self.this_day -= 1 else : if self.this_month == 5 or self.this_month == 7 or \ self.this_month == 10 or self.this_month == 12 : self.this_day = 30 self.this_month -= 1 elif self.this_month == 2 or self.this_month == 4 or \ self.this_month == 6 or self.this_month == 8 or \ self.this_month == 9 or self.this_month == 11 : self.this_day = 31 self.this_month -= 1 elif self.this_month == 1 : self.this_day = 31 self.this_month = 12 self.this_year -= 1 elif self.this_month == 3 : self.this_month = 2 if self.this_is_a_leap_year() : self.this_day = 29 else : self.this_day = 28 def get_distance_to( self, another_date ) : distance_to_return = DateDistance() if self.is_earlier_than( another_date ) : start_date = self end_date = another_date else : start_date = another_date end_date = self # We will suppose that day 30 is the last day of every # month. This way we minimize calculation errors. if start_date.is_last_day_of_month() or \ ( start_date.day() == 28 and \ start_date.month() == 2 ) : start_day = 30 else : start_day = start_date.day() if end_date.is_last_day_of_month() or \ ( end_date.day() == 28 and \ end_date.month() == 2 ) : end_day = 30 else : end_day = end_date.day() distance_to_return.years = end_date.year() - start_date.year() distance_to_return.months = end_date.month() - start_date.month() distance_to_return.days = end_day - start_day if distance_to_return.days < 0 : distance_to_return.months -= 1 distance_to_return.days = distance_to_return.days + 30 if distance_to_return.months < 0 : distance_to_return.years -= 1 distance_to_return.months = distance_to_return.months + 12 return distance_to_return def get_week_number( self ) : # January 1, 1883 was a Monday with week number 1. # January 1, 1990 was a Monday with week number 1. date_to_increment = Date( "01.01.1883" ) week_number = 1 local_index_for_day_of_week = 0 # 0 means Monday while date_to_increment.is_earlier_than( self ) : date_to_increment.increment() if local_index_for_day_of_week == 6 : # 6 means Sunday local_index_for_day_of_week = 0 # back to Monday if week_number < 52 : week_number += 1 elif week_number == 52 : if date_to_increment.day() <= 28 and \ date_to_increment.month() == 12 : week_number = 53 else : week_number = 1 else : # must be week_number 53 week_number = 1 else : local_index_for_day_of_week += 1 return week_number def is_equal_to( self, another_date ) : return ( self.this_day == another_date.day() and \ self.this_month == another_date.month() and \ self.this_year == another_date.year() ) def is_not_equal_to( self, another_date ) : return ( self.this_day != another_date.day() or \ self.this_month != another_date.month() or \ self.this_year != another_date.year() ) def is_earlier_than( self, another_date ) : return ( ( self.this_year < another_date.year() ) or \ ( ( self.this_year == another_date.year() ) and \ ( self.this_month < another_date.month() ) ) or \ ( ( self.this_year == another_date.year() ) and \ ( self.this_month == another_date.month() ) and \ ( self.this_day < another_date.day() ) ) ) def is_later_than( self, another_date ) : return ( ( self.this_year > another_date.year() ) or \ ( ( self.this_year == another_date.year() ) and \ ( self.this_month > another_date.month() ) ) or \ ( ( self.this_year == another_date.year() ) and \ ( self.this_month == another_date.month() ) and \ ( self.this_day > another_date.day() ) ) ) def __str__( self ) : day_as_string = "%02d" % self.this_day month_as_string = "%02d" % self.this_month year_as_string = "%d" % self.this_year string_to_caller = day_as_string + "." + month_as_string \ + "." + year_as_string if self.date_print_format == 'A' : string_to_caller = month_as_string + "/" + day_as_string \ + "/" + year_as_string return string_to_caller