def _parse_week_day(datestr): #datestr is of the format YYYY-Www-D, YYYYWwwD # #W is the week number prefix, ww is the week number, between 1 and 53 #0 is not a valid week number, which differs from the Python implementation # #D is the weekday number, between 1 and 7, which differs from the Python #implementation which is between 0 and 6 isoyear = int(datestr[0:4]) gregorianyearstart = _iso_year_start(isoyear) #Week number will be the two characters after the W windex = datestr.find('W') isoweeknumber = int(datestr[windex + 1:windex + 3]) if isoweeknumber == 0 or isoweeknumber > 53: raise WeekOutOfBoundsError('Week number must be between 1..53.') if datestr.find('-') != -1 and len(datestr) == 10: #YYYY-Www-D isoday = int(datestr[9:10]) elif len(datestr) == 8: #YYYYWwwD isoday = int(datestr[7:8]) else: raise ISOFormatError( '"{0}" is not a valid ISO 8601 week date.'.format(datestr)) if isoday == 0 or isoday > 7: raise DayOutOfBoundsError('Weekday number must be between 1..7.') return gregorianyearstart + datetime.timedelta(weeks=isoweeknumber - 1, days=isoday - 1)
def build_date(cls, YYYY=None, MM=None, DD=None, Www=None, D=None, DDD=None): if YYYY is not None: # Truncated dates, like '19', refer to 1900-1999 inclusive, # we simply parse to 1900 if len(YYYY) < 4: # Shift 0s in from the left to form complete year YYYY = YYYY.ljust(4, "0") year = cls.cast(YYYY, int, thrownmessage="Invalid year string.") if MM is not None: month = cls.cast(MM, int, thrownmessage="Invalid month string.") else: month = 1 if DD is not None: day = cls.cast(DD, int, thrownmessage="Invalid day string.") else: day = 1 if Www is not None: weeknumber = cls.cast(Www, int, thrownmessage="Invalid week string.") if weeknumber == 0 or weeknumber > 53: raise WeekOutOfBoundsError("Week number must be between " "1..53.") else: weeknumber = None if DDD is not None: dayofyear = cls.cast(DDD, int, thrownmessage="Invalid day string.") else: dayofyear = None if D is not None: dayofweek = cls.cast(D, int, thrownmessage="Invalid day string.") if dayofweek == 0 or dayofweek > 7: raise DayOutOfBoundsError("Weekday number must be between " "1..7.") else: dayofweek = None # 0000 (1 BC) is not representable as a Python date so a ValueError is # raised if year == 0: raise YearOutOfBoundsError("Year must be between 1..9999.") if dayofyear is not None: return PythonTimeBuilder._build_ordinal_date(year, dayofyear) if weeknumber is not None: return PythonTimeBuilder._build_week_date( year, weeknumber, isoday=dayofweek ) return datetime.date(year, month, day)
def _parse_week(datestr): #datestr is of the format YYYY-Www, YYYYWww # #W is the week number prefix, ww is the week number, between 1 and 53 #0 is not a valid week number, which differs from the Python implementation isoyear = int(datestr[0:4]) gregorianyearstart = _iso_year_start(isoyear) #Week number will be the two characters after the W windex = datestr.find('W') isoweeknumber = int(datestr[windex + 1:windex + 3]) if isoweeknumber == 0 or isoweeknumber > 53: raise WeekOutOfBoundsError('Week number must be between 1..53.') return gregorianyearstart + datetime.timedelta(weeks=isoweeknumber - 1, days=0)
def range_check_duration(cls, PnY=None, PnM=None, PnW=None, PnD=None, TnH=None, TnM=None, TnS=None, rangedict=None): years = 0 months = 0 days = 0 weeks = 0 hours = 0 minutes = 0 seconds = 0 microseconds = 0 PnY, PnM, PnW, PnD, TnH, TnM, TnS = BaseTimeBuilder.range_check_duration( PnY, PnM, PnW, PnD, TnH, TnM, TnS, rangedict=cls.DURATION_RANGE_DICT) if PnY is not None: if type(PnY) is FractionalComponent: years = PnY.principal microseconds = PnY.microsecondremainder else: years = PnY if years * DAYS_PER_YEAR > TIMEDELTA_MAX_DAYS: raise YearOutOfBoundsError( 'Duration exceeds maximum timedelta size.') if PnM is not None: if type(PnM) is FractionalComponent: months = PnM.principal microseconds = PnM.microsecondremainder else: months = PnM if months * DAYS_PER_MONTH > TIMEDELTA_MAX_DAYS: raise MonthOutOfBoundsError( 'Duration exceeds maximum timedelta size.') if PnW is not None: if type(PnW) is FractionalComponent: weeks = PnW.principal microseconds = PnW.microsecondremainder else: weeks = PnW if weeks * DAYS_PER_WEEK > TIMEDELTA_MAX_DAYS: raise WeekOutOfBoundsError( 'Duration exceeds maximum timedelta size.') if PnD is not None: if type(PnD) is FractionalComponent: days = PnD.principal microseconds = PnD.microsecondremainder else: days = PnD if days > TIMEDELTA_MAX_DAYS: raise DayOutOfBoundsError( 'Duration exceeds maximum timedelta size.') if TnH is not None: if type(TnH) is FractionalComponent: hours = TnH.principal microseconds = TnH.microsecondremainder else: hours = TnH if hours // HOURS_PER_DAY > TIMEDELTA_MAX_DAYS: raise HoursOutOfBoundsError( 'Duration exceeds maximum timedelta size.') if TnM is not None: if type(TnM) is FractionalComponent: minutes = TnM.principal microseconds = TnM.microsecondremainder else: minutes = TnM if minutes // MINUTES_PER_DAY > TIMEDELTA_MAX_DAYS: raise MinutesOutOfBoundsError( 'Duration exceeds maximum timedelta size.') if TnS is not None: if type(TnS) is FractionalComponent: seconds = TnS.principal microseconds = TnS.microsecondremainder else: seconds = TnS if seconds // SECONDS_PER_DAY > TIMEDELTA_MAX_DAYS: raise SecondsOutOfBoundsError( 'Duration exceeds maximum timedelta size.') years, months, weeks, days, hours, minutes, seconds, microseconds = PythonTimeBuilder._distribute_microseconds( microseconds, (years, months, weeks, days, hours, minutes, seconds), (MICROSECONDS_PER_YEAR, MICROSECONDS_PER_MONTH, MICROSECONDS_PER_WEEK, MICROSECONDS_PER_DAY, MICROSECONDS_PER_HOUR, MICROSECONDS_PER_MINUTE, MICROSECONDS_PER_SECOND)) #Note that weeks can be handled without conversion to days totaldays = years * DAYS_PER_YEAR + months * DAYS_PER_MONTH + days #Check against timedelta limits if totaldays + weeks * DAYS_PER_WEEK + hours // HOURS_PER_DAY + minutes // MINUTES_PER_DAY + seconds // SECONDS_PER_DAY > TIMEDELTA_MAX_DAYS: raise DayOutOfBoundsError( 'Duration exceeds maximum timedelta size.') return (None, None, weeks, totaldays, hours, minutes, FractionalComponent(seconds, microseconds))