Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/aniso8601/time.py: 83%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

78 statements  

1# -*- coding: utf-8 -*- 

2 

3# Copyright (c) 2026, Brandon Nielsen 

4# SPDX-License-Identifier: BSD-3-Clause 

5 

6from aniso8601.builders import TupleBuilder 

7from aniso8601.builders.python import PythonTimeBuilder 

8from aniso8601.compat import is_string 

9from aniso8601.date import parse_date 

10from aniso8601.decimalfraction import normalize 

11from aniso8601.exceptions import ISOFormatError 

12from aniso8601.resolution import TimeResolution 

13from aniso8601.timezone import parse_timezone 

14 

15TIMEZONE_DELIMITERS = ["Z", "+", "-"] 

16 

17 

18def get_time_resolution(isotimestr): 

19 # Valid time formats are: 

20 # 

21 # hh:mm:ss 

22 # hhmmss 

23 # hh:mm 

24 # hhmm 

25 # hh 

26 # hh:mm:ssZ 

27 # hhmmssZ 

28 # hh:mmZ 

29 # hhmmZ 

30 # hhZ 

31 # hh:mm:ss±hh:mm 

32 # hhmmss±hh:mm 

33 # hh:mm±hh:mm 

34 # hhmm±hh:mm 

35 # hh±hh:mm 

36 # hh:mm:ss±hhmm 

37 # hhmmss±hhmm 

38 # hh:mm±hhmm 

39 # hhmm±hhmm 

40 # hh±hhmm 

41 # hh:mm:ss±hh 

42 # hhmmss±hh 

43 # hh:mm±hh 

44 # hhmm±hh 

45 # hh±hh 

46 isotimetuple = parse_time(isotimestr, builder=TupleBuilder) 

47 

48 return _get_time_resolution(isotimetuple) 

49 

50 

51def get_datetime_resolution(isodatetimestr, delimiter="T"): 

52 # <date>T<time> 

53 # 

54 # Time part cannot be omitted so return time resolution 

55 isotimetuple = parse_datetime( 

56 isodatetimestr, delimiter=delimiter, builder=TupleBuilder 

57 ).time 

58 

59 return _get_time_resolution(isotimetuple) 

60 

61 

62def _get_time_resolution(isotimetuple): 

63 if isotimetuple.ss is not None: 

64 return TimeResolution.Seconds 

65 

66 if isotimetuple.mm is not None: 

67 return TimeResolution.Minutes 

68 

69 return TimeResolution.Hours 

70 

71 

72def parse_time(isotimestr, builder=PythonTimeBuilder): 

73 # Given a string in any ISO 8601 time format, return a datetime.time object 

74 # that corresponds to the given time. Fixed offset tzdata will be included 

75 # if UTC offset is given in the input string. Valid time formats are: 

76 # 

77 # hh:mm:ss 

78 # hhmmss 

79 # hh:mm 

80 # hhmm 

81 # hh 

82 # hh:mm:ssZ 

83 # hhmmssZ 

84 # hh:mmZ 

85 # hhmmZ 

86 # hhZ 

87 # hh:mm:ss±hh:mm 

88 # hhmmss±hh:mm 

89 # hh:mm±hh:mm 

90 # hhmm±hh:mm 

91 # hh±hh:mm 

92 # hh:mm:ss±hhmm 

93 # hhmmss±hhmm 

94 # hh:mm±hhmm 

95 # hhmm±hhmm 

96 # hh±hhmm 

97 # hh:mm:ss±hh 

98 # hhmmss±hh 

99 # hh:mm±hh 

100 # hhmm±hh 

101 # hh±hh 

102 if is_string(isotimestr) is False: 

103 raise ValueError("Time must be string.") 

104 

105 if len(isotimestr) == 0: 

106 raise ISOFormatError('"{0}" is not a valid ISO 8601 time.'.format(isotimestr)) 

107 

108 timestr = normalize(isotimestr) 

109 

110 hourstr = None 

111 minutestr = None 

112 secondstr = None 

113 tzstr = None 

114 

115 fractionalstr = None 

116 

117 # Split out the timezone 

118 for delimiter in TIMEZONE_DELIMITERS: 

119 delimiteridx = timestr.find(delimiter) 

120 

121 if delimiteridx != -1: 

122 tzstr = timestr[delimiteridx:] 

123 timestr = timestr[0:delimiteridx] 

124 

125 # Split out the fractional component 

126 if timestr.find(".") != -1: 

127 timestr, fractionalstr = timestr.split(".", 1) 

128 

129 if fractionalstr.isdigit() is False: 

130 raise ISOFormatError( 

131 '"{0}" is not a valid ISO 8601 time.'.format(isotimestr) 

132 ) 

133 

134 if len(timestr) == 2: 

135 # hh 

136 hourstr = timestr 

137 elif len(timestr) == 4 or len(timestr) == 5: 

138 # hh:mm 

139 # hhmm 

140 if timestr.count(":") == 1: 

141 hourstr, minutestr = timestr.split(":") 

142 else: 

143 hourstr = timestr[0:2] 

144 minutestr = timestr[2:] 

145 elif len(timestr) == 6 or len(timestr) == 8: 

146 # hh:mm:ss 

147 # hhmmss 

148 if timestr.count(":") == 2: 

149 hourstr, minutestr, secondstr = timestr.split(":") 

150 else: 

151 hourstr = timestr[0:2] 

152 minutestr = timestr[2:4] 

153 secondstr = timestr[4:] 

154 else: 

155 raise ISOFormatError('"{0}" is not a valid ISO 8601 time.'.format(isotimestr)) 

156 

157 for componentstr in [hourstr, minutestr, secondstr]: 

158 if componentstr is not None and componentstr.isdigit() is False: 

159 raise ISOFormatError( 

160 '"{0}" is not a valid ISO 8601 time.'.format(isotimestr) 

161 ) 

162 

163 if fractionalstr is not None: 

164 if secondstr is not None: 

165 secondstr = secondstr + "." + fractionalstr 

166 elif minutestr is not None: 

167 minutestr = minutestr + "." + fractionalstr 

168 else: 

169 hourstr = hourstr + "." + fractionalstr 

170 

171 if tzstr is None: 

172 tz = None 

173 else: 

174 tz = parse_timezone(tzstr, builder=TupleBuilder) 

175 

176 return builder.build_time(hh=hourstr, mm=minutestr, ss=secondstr, tz=tz) 

177 

178 

179def parse_datetime(isodatetimestr, delimiter="T", builder=PythonTimeBuilder): 

180 # Given a string in ISO 8601 date time format, return a datetime.datetime 

181 # object that corresponds to the given date time. 

182 # By default, the ISO 8601 specified T delimiter is used to split the 

183 # date and time (<date>T<time>). Fixed offset tzdata will be included 

184 # if UTC offset is given in the input string. 

185 if is_string(isodatetimestr) is False: 

186 raise ValueError("Date time must be string.") 

187 

188 if delimiter not in isodatetimestr: 

189 raise ISOFormatError( 

190 'Delimiter "{0}" is not in combined date time ' 

191 'string "{1}".'.format(delimiter, isodatetimestr) 

192 ) 

193 

194 isodatestr, isotimestr = isodatetimestr.split(delimiter, 1) 

195 

196 datepart = parse_date(isodatestr, builder=TupleBuilder) 

197 

198 timepart = parse_time(isotimestr, builder=TupleBuilder) 

199 

200 return builder.build_datetime(datepart, timepart)