Source code for ladybug.dt

# coding=utf-8
"""Ladybug datetime."""
from __future__ import division

from datetime import datetime, date, time

MONTHNAMES = ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
              'Oct', 'Nov', 'Dec')


[docs]class DateTime(datetime): """Create Ladybug Date time. Args: month: A value for month between 1-12 (Default: 1). day: A value for day between 1-31 (Default: 1). hour: A value for hour between 0-23 (Default: 0). minute: A value for month between 0-59 (Default: 0). leap_year: A boolean to indicate if datetime is for a leap year (Default: False). Properties: * month * day * hour * leap_year * doy * hoy * int_hoy * minute * moy * float_hour * tzinfo * year """ __slots__ = () def __new__(cls, month=1, day=1, hour=0, minute=0, leap_year=False): """Create Ladybug datetime. """ year = 2016 if leap_year else 2017 hour, minute = Time._calculate_hour_and_minute(hour + minute / 60.0) try: return datetime.__new__(cls, year, month, day, hour, minute) except ValueError as e: raise ValueError("{}:\n\t({}/{}@{}:{})(m/d@h:m)".format( e, month, day, hour, minute )) def __reduce_ex__(self, protocol): """Call the __new__() constructor when the class instance is unpickled. This method is necessary for the pickle.loads() call to work. """ return (type(self), (self.month, self.day, self.hour, self.minute))
[docs] @classmethod def from_dict(cls, data): """Create datetime from a dictionary. Args: data: A python dictionary in the following format .. code-block:: python { 'month': 1 #A value for month between 1-12. (Default: 1) 'day': 1 # A value for day between 1-31. (Default: 1) 'hour': 0 # A value for hour between 0-23. (Default: 0) 'minute': 0 # A value for month between 0-59. (Default: 0) } """ month = data['month'] if 'month' in data else 1 day = data['day'] if 'day' in data else 1 hour = data['hour'] if 'hour' in data else 0 minute = data['minute'] if 'minute' in data else 0 leap_year = data['leap_year'] if 'leap_year' in data else False return cls(month, day, hour, minute, leap_year)
[docs] @classmethod def from_hoy(cls, hoy, leap_year=False): """Create Ladybug Datetime from an hour of the year. Args: hoy: A float value 0 <= and < 8760 leap_year: Boolean to note whether the Date Time is a part of a leap year. Default: False. """ return cls.from_moy(round(hoy * 60), leap_year)
[docs] @classmethod def from_moy(cls, moy, leap_year=False): """Create Ladybug Datetime from a minute of the year. Args: moy: An integer value 0 <= and < 525600 leap_year: Boolean to note whether the Date Time is a part of a leap year. Default: False. """ if not leap_year: num_of_minutes_until_month = (0, 44640, 84960, 129600, 172800, 217440, 260640, 305280, 349920, 393120, 437760, 480960, 525600) else: num_of_minutes_until_month = (0, 44640, 84960 + 1440, 129600 + 1440, 172800 + 1440, 217440 + 1440, 260640 + 1440, 305280 + 1440, 349920 + 1440, 393120 + 1440, 437760 + 1440, 480960 + 1440, 525600 + 1440) # find month moy = int(moy) for month_count in range(12): if moy < num_of_minutes_until_month[month_count + 1]: month = month_count + 1 break try: day = int((moy - num_of_minutes_until_month[month - 1]) / (60 * 24)) + 1 except UnboundLocalError: raise ValueError( "moy must be positive and smaller than 525600. Invalid input %d" % (moy) ) else: hour = int((moy / 60) % 24) minute = int(moy % 60) return cls(month, day, hour, minute, leap_year)
[docs] @classmethod def from_date_time_string(cls, datetime_string, leap_year=False): """Create Ladybug DateTime from a DateTime string. Args: datetime_string: A text string representing a DateTime (ie. "21 Jun 12:00") leap_year: Boolean to note whether the Date Time is a part of a leap year. Default: False. Usage: .. code-block:: python dt = DateTime.from_date_time_string("31 Dec 12:00") """ try: dt = datetime.strptime(datetime_string, '%d %b %H:%M') except AttributeError: # older Python version before strptime vals = datetime_string.split(' ') tim = vals[-1].split(':') dt = datetime(2016, MONTHNAMES.index(vals[1]) + 1, int(vals[0]), int(tim[0]), int(tim[1])) return cls(dt.month, dt.day, dt.hour, dt.minute, leap_year)
[docs] @classmethod def from_array(cls, datetime_array): """Create Ladybug DateTime from am array of integers. Args: datetime_array: An array of 4 integers (and optional leap_year boolean) ordered as follows: (month, day, hour, minute, leap_year) """ return cls(*datetime_array)
[docs] @classmethod def from_date_and_time(cls, date, time): """Create Ladybug DateTime from a Date and a Time object. Args: date: A ladybug Date object. time: A ladybug Time object. """ leap_year = True if date.year % 4 == 0 else False return cls(date.month, date.day, time.hour, time.minute, leap_year)
[docs] @classmethod def from_first_hour(cls, leap_year=False): """Create Ladybug DateTime for the first hour of the year. Args: leap_year: Boolean to note whether the Date Time is a part of a leap year. Default: False. """ return cls(1, 1, 0, leap_year=leap_year)
[docs] @classmethod def from_last_hour(cls, leap_year=False): """Create Ladybug DateTime for the last hour of the year. Args: leap_year: Boolean to note whether the Date Time is a part of a leap year. Default: False. """ return cls(12, 31, 23, leap_year=leap_year)
@property def leap_year(self): """Boolean to note whether DateTime belongs to a leap year or not.""" return self.year == 2016 @property def doy(self): """Calculate day of the year for this date time.""" return self.timetuple().tm_yday @property def hoy(self): """Calculate hour of the year for this date time.""" return (self.doy - 1) * 24 + self.float_hour @property def moy(self): """Calculate minute of the year for this date time.""" return self.int_hoy * 60 + self.minute # minute of the year @property def float_hour(self): """Get hour and minute as a float value, e.g. 6.25 for 6:15.""" return self.hour + self.minute / 60.0 @property def int_hoy(self): """Calculate hour of the year for this date time as an integer. This output assumes the minute is 0. """ return (self.doy - 1) * 24 + self.hour @property def date(self): """Get a Date object associated with this DateTime.""" return Date(self.month, self.day, self.leap_year) @property def time(self): """Get a Time object associated with this DateTime.""" return Time(self.hour, self.minute)
[docs] def add_minute(self, minute): """Create a new DateTime after the minutes are added. Args: minute: An integer value for minutes. """ _moy = self.moy + int(minute) return self.__class__.from_moy(_moy, self.leap_year)
[docs] def sub_minute(self, minute): """Create a new DateTime after the minutes are subtracted. Args: minute: An integer value for the number of minutes. """ return self.add_minute(-minute)
[docs] def add_hour(self, hour): """Create a new DateTime from this time + timedelta. Args: hour: A float value in hours (e.g. .5 = half an hour) """ return self.add_minute(hour * 60)
[docs] def sub_hour(self, hour): """Create a new DateTime from this time - timedelta. Args: hour: A float value in hours (e.g. .5 is half an hour and 2 is two hours). """ return self.add_hour(-hour)
[docs] def to_simple_string(self, separator="_"): """Return a simplified string.""" return self.strftime('%d_%b_%H_%M').replace("_", separator)
[docs] def to_array(self): """Return datetime as an array of values.""" if not self.leap_year: return (self.month, self.day, self.hour, self.minute) return (self.month, self.day, self.hour, self.minute, 1)
[docs] def to_dict(self): """Get datetime as a dictionary.""" base = { 'month': self.month, 'day': self.day, 'hour': self.hour, 'minute': self.minute, 'type': 'DateTime' } if self.leap_year: base['leap_year'] = True return base
def __str__(self): """Return date time as a string.""" return self.strftime('%d %b %H:%M')
[docs] def ToString(self): """Overwrite .NET ToString.""" return self.__str__()
def __repr__(self): """Return date time as a string.""" return self.__str__()
[docs]class Date(date): """Ladybug Date. Args: month: A value for month between 1-12. Default: 1. day: A value for day between 1-31. Default: 1. leap_year: A boolean to indicate if date is for a leap year. Default: False. Properties: * day * doy * leap_year * month * year """ __slots__ = () def __new__(cls, month=1, day=1, leap_year=False): """Create Ladybug Date. """ year = 2016 if leap_year else 2017 try: return date.__new__(cls, year, month, day) except ValueError as e: raise ValueError("{}:\n\t({}/{})(m/d)".format(e, month, day)) def __reduce_ex__(self, protocol): """Call the __new__() constructor when the class instance is unpickled. This method is necessary for the pickle.loads() call to work. """ return (type(self), (self.month, self.day, self.leap_year))
[docs] @classmethod def from_dict(cls, data): """Create date from a dictionary. Args: data: A python dictionary in the following format .. code-block:: python { 'month': 1 # A value for month between 1-12. (Default: 1) 'day': 1 # A value for day between 1-31. (Default: 1) } """ month = data['month'] if 'month' in data else 1 day = data['day'] if 'day' in data else 1 leap_year = data['leap_year'] if 'leap_year' in data else False return cls(month, day, leap_year)
[docs] @classmethod def from_doy(cls, doy, leap_year=False): """Create Ladybug Date from an day of the year. Args: doy: An int value 0 <= and < 366 """ if not leap_year: days_until_month = (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 366) else: days_until_month = (0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 367) doy = int(doy) for month_count in range(12): # find month if doy < days_until_month[month_count + 1]: month = month_count + 1 break try: day = int(doy - days_until_month[month - 1]) if day == 0: month -= 1 day = int(doy - days_until_month[month - 1]) except UnboundLocalError: raise ValueError( "doy must be positive and smaller than 366. Invalid input %d" % (doy) ) return cls(month, day, leap_year)
[docs] @classmethod def from_date_string(cls, date_string, leap_year=False): """Create Ladybug Date from a Date string. Usage: .. code-block:: python dt = Date.from_date_string("31 Dec") """ try: dt = datetime.strptime(date_string, '%d %b') except AttributeError: # older Python version before strptime vals = date_string.split(' ') dt = datetime(2016, MONTHNAMES.index(vals[1]) + 1, int(vals[0])) return cls(dt.month, dt.day, leap_year)
[docs] @classmethod def from_array(cls, date_array): """Create Ladybug Date from am array of integers. Args: datetime_array: An array of 2 integers (and optional leap_year boolean) ordered as follows: (month, day, leap_year) """ return cls(*date_array)
@property def leap_year(self): """Boolean to note whether Date belongs to a leap year or not.""" return self.year == 2016 @property def doy(self): """Calculate day of the year for this date.""" return self.timetuple().tm_yday
[docs] def to_array(self): """Return date as an array of values.""" if not self.leap_year: return (self.month, self.day) return (self.month, self.day, 1)
[docs] def to_dict(self): """Get date as a dictionary.""" base = {'month': self.month, 'day': self.day, 'type': 'Date'} if self.leap_year: base['leap_year'] = True return base
def __str__(self): """Return date as a string.""" return self.strftime('%d %b')
[docs] def ToString(self): """Overwrite .NET ToString.""" return self.__str__()
def __repr__(self): """Return date as a string.""" return self.__str__()
[docs]class Time(time): """Create Ladybug Time. Args: hour: A value for hour between 0-23 (Default: 0). minute: A value for month between 0-59 (Default: 0). Properties: * hour * minute * mod * second * tzinfo """ __slots__ = () def __new__(cls, hour=0, minute=0): """Create Ladybug Time. """ hour, minute = cls._calculate_hour_and_minute(hour + minute / 60.0) try: return time.__new__(cls, hour, minute) except ValueError as e: raise ValueError("{}:\n\t({}:{})(h:m)".format(e, hour, minute)) def __reduce_ex__(self, protocol): """Call the __new__() constructor when the class instance is unpickled. This method is necessary for the pickle.loads() call to work. """ return (type(self), (self.hour, self.minute))
[docs] @classmethod def from_dict(cls, data): """Create time from a dictionary. Args: data: A python dictionary in the following format .. code-block:: python { 'hour': 0 # A value for hour between 0-23. (Default: 0) 'minute': 0 # A value for month between 0-59. (Default: 0) } """ hour = data['hour'] if 'hour' in data else 0 minute = data['minute'] if 'minute' in data else 0 return cls(hour, minute)
[docs] @classmethod def from_mod(cls, mod): """Create Ladybug Time from a minute of the day. Args: mod: An int value 0 <= and < 1440 """ hour, minute = cls._calculate_hour_and_minute(mod / 60.0) return cls(hour, minute)
[docs] @classmethod def from_time_string(cls, time_string, leap_year=False): """Create Ladybug Time from a Time string. Usage: .. code-block:: python dt = Time.from_time_string("12:00") """ try: dt = datetime.strptime(time_string, '%H:%M') except AttributeError: # older Python version before strptime vals = time_string.split(':') dt = datetime(int(vals[0]), int(vals[1])) return cls(dt.hour, dt.minute)
[docs] @classmethod def from_array(cls, time_array): """Create Ladybug Time from am array of integers. Args: datetime_array: An array of 2 integers ordered as follows: (hour, minute) """ return cls(*time_array)
@property def mod(self): """Calculate minute of the day for this time.""" return self.hour * 60 + self.minute @property def float_hour(self): """Get hour and minute as a float value, e.g. 6.25 for 6:15.""" return self.hour + self.minute / 60.0
[docs] def to_array(self): """Return time as an array of values.""" return (self.hour, self.minute)
[docs] def to_dict(self): """Get time as a dictionary.""" return {'hour': self.hour, 'minute': self.minute, 'type': 'Time'}
@staticmethod def _calculate_hour_and_minute(float_hour): """Calculate hour and minutes as integers from a float hour.""" hour = int(float_hour) minute = int(round((float_hour - hour) * 60)) if minute == 60: return hour + 1, 0 else: return hour, minute def __str__(self): """Return time as a string.""" return self.strftime('%H:%M')
[docs] def ToString(self): """Overwrite .NET ToString.""" return self.__str__()
def __repr__(self): """Return time as a string.""" return self.__str__()