|
|
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Brandon Nielsen # All rights reserved. # # This software may be modified and distributed under the terms # of the BSD license. See the LICENSE file for details.
from aniso8601.builders import TupleBuilder from aniso8601.builders.python import PythonTimeBuilder from aniso8601.compat import is_string from aniso8601.date import parse_date from aniso8601.decimalfraction import normalize from aniso8601.exceptions import ISOFormatError from aniso8601.resolution import TimeResolution from aniso8601.timezone import parse_timezone
TIMEZONE_DELIMITERS = ["Z", "+", "-"]
def get_time_resolution(isotimestr): # Valid time formats are: # # hh:mm:ss # hhmmss # hh:mm # hhmm # hh # hh:mm:ssZ # hhmmssZ # hh:mmZ # hhmmZ # hhZ # hh:mm:ss±hh:mm # hhmmss±hh:mm # hh:mm±hh:mm # hhmm±hh:mm # hh±hh:mm # hh:mm:ss±hhmm # hhmmss±hhmm # hh:mm±hhmm # hhmm±hhmm # hh±hhmm # hh:mm:ss±hh # hhmmss±hh # hh:mm±hh # hhmm±hh # hh±hh isotimetuple = parse_time(isotimestr, builder=TupleBuilder)
return _get_time_resolution(isotimetuple)
def get_datetime_resolution(isodatetimestr, delimiter="T"): # <date>T<time> # # Time part cannot be omittted so return time resolution isotimetuple = parse_datetime( isodatetimestr, delimiter=delimiter, builder=TupleBuilder ).time
return _get_time_resolution(isotimetuple)
def _get_time_resolution(isotimetuple): if isotimetuple.ss is not None: return TimeResolution.Seconds
if isotimetuple.mm is not None: return TimeResolution.Minutes
return TimeResolution.Hours
def parse_time(isotimestr, builder=PythonTimeBuilder): # Given a string in any ISO 8601 time format, return a datetime.time object # that corresponds to the given time. Fixed offset tzdata will be included # if UTC offset is given in the input string. Valid time formats are: # # hh:mm:ss # hhmmss # hh:mm # hhmm # hh # hh:mm:ssZ # hhmmssZ # hh:mmZ # hhmmZ # hhZ # hh:mm:ss±hh:mm # hhmmss±hh:mm # hh:mm±hh:mm # hhmm±hh:mm # hh±hh:mm # hh:mm:ss±hhmm # hhmmss±hhmm # hh:mm±hhmm # hhmm±hhmm # hh±hhmm # hh:mm:ss±hh # hhmmss±hh # hh:mm±hh # hhmm±hh # hh±hh if is_string(isotimestr) is False: raise ValueError("Time must be string.")
if len(isotimestr) == 0: raise ISOFormatError('"{0}" is not a valid ISO 8601 time.'.format(isotimestr))
timestr = normalize(isotimestr)
hourstr = None minutestr = None secondstr = None tzstr = None
fractionalstr = None
# Split out the timezone for delimiter in TIMEZONE_DELIMITERS: delimiteridx = timestr.find(delimiter)
if delimiteridx != -1: tzstr = timestr[delimiteridx:] timestr = timestr[0:delimiteridx]
# Split out the fractional component if timestr.find(".") != -1: timestr, fractionalstr = timestr.split(".", 1)
if fractionalstr.isdigit() is False: raise ISOFormatError( '"{0}" is not a valid ISO 8601 time.'.format(isotimestr) )
if len(timestr) == 2: # hh hourstr = timestr elif len(timestr) == 4 or len(timestr) == 5: # hh:mm # hhmm if timestr.count(":") == 1: hourstr, minutestr = timestr.split(":") else: hourstr = timestr[0:2] minutestr = timestr[2:] elif len(timestr) == 6 or len(timestr) == 8: # hh:mm:ss # hhmmss if timestr.count(":") == 2: hourstr, minutestr, secondstr = timestr.split(":") else: hourstr = timestr[0:2] minutestr = timestr[2:4] secondstr = timestr[4:] else: raise ISOFormatError('"{0}" is not a valid ISO 8601 time.'.format(isotimestr))
for componentstr in [hourstr, minutestr, secondstr]: if componentstr is not None and componentstr.isdigit() is False: raise ISOFormatError( '"{0}" is not a valid ISO 8601 time.'.format(isotimestr) )
if fractionalstr is not None: if secondstr is not None: secondstr = secondstr + "." + fractionalstr elif minutestr is not None: minutestr = minutestr + "." + fractionalstr else: hourstr = hourstr + "." + fractionalstr
if tzstr is None: tz = None else: tz = parse_timezone(tzstr, builder=TupleBuilder)
return builder.build_time(hh=hourstr, mm=minutestr, ss=secondstr, tz=tz)
def parse_datetime(isodatetimestr, delimiter="T", builder=PythonTimeBuilder): # Given a string in ISO 8601 date time format, return a datetime.datetime # object that corresponds to the given date time. # By default, the ISO 8601 specified T delimiter is used to split the # date and time (<date>T<time>). Fixed offset tzdata will be included # if UTC offset is given in the input string. if is_string(isodatetimestr) is False: raise ValueError("Date time must be string.")
if delimiter not in isodatetimestr: raise ISOFormatError( 'Delimiter "{0}" is not in combined date time ' 'string "{1}".'.format(delimiter, isodatetimestr) )
isodatestr, isotimestr = isodatetimestr.split(delimiter, 1)
datepart = parse_date(isodatestr, builder=TupleBuilder)
timepart = parse_time(isotimestr, builder=TupleBuilder)
return builder.build_datetime(datepart, timepart)
|