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

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) 2025, Brandon Nielsen 

4# All rights reserved. 

5# 

6# This software may be modified and distributed under the terms 

7# of the BSD license. See the LICENSE file for details. 

8 

9from aniso8601.builders import TupleBuilder 

10from aniso8601.builders.python import PythonTimeBuilder 

11from aniso8601.compat import is_string 

12from aniso8601.date import parse_date 

13from aniso8601.decimalfraction import normalize 

14from aniso8601.exceptions import ISOFormatError 

15from aniso8601.resolution import TimeResolution 

16from aniso8601.timezone import parse_timezone 

17 

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

19 

20 

21def get_time_resolution(isotimestr): 

22 # Valid time formats are: 

23 # 

24 # hh:mm:ss 

25 # hhmmss 

26 # hh:mm 

27 # hhmm 

28 # hh 

29 # hh:mm:ssZ 

30 # hhmmssZ 

31 # hh:mmZ 

32 # hhmmZ 

33 # hhZ 

34 # hh:mm:ss±hh:mm 

35 # hhmmss±hh:mm 

36 # hh:mm±hh:mm 

37 # hhmm±hh:mm 

38 # hh±hh:mm 

39 # hh:mm:ss±hhmm 

40 # hhmmss±hhmm 

41 # hh:mm±hhmm 

42 # hhmm±hhmm 

43 # hh±hhmm 

44 # hh:mm:ss±hh 

45 # hhmmss±hh 

46 # hh:mm±hh 

47 # hhmm±hh 

48 # hh±hh 

49 isotimetuple = parse_time(isotimestr, builder=TupleBuilder) 

50 

51 return _get_time_resolution(isotimetuple) 

52 

53 

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

55 # <date>T<time> 

56 # 

57 # Time part cannot be omitted so return time resolution 

58 isotimetuple = parse_datetime( 

59 isodatetimestr, delimiter=delimiter, builder=TupleBuilder 

60 ).time 

61 

62 return _get_time_resolution(isotimetuple) 

63 

64 

65def _get_time_resolution(isotimetuple): 

66 if isotimetuple.ss is not None: 

67 return TimeResolution.Seconds 

68 

69 if isotimetuple.mm is not None: 

70 return TimeResolution.Minutes 

71 

72 return TimeResolution.Hours 

73 

74 

75def parse_time(isotimestr, builder=PythonTimeBuilder): 

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

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

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

79 # 

80 # hh:mm:ss 

81 # hhmmss 

82 # hh:mm 

83 # hhmm 

84 # hh 

85 # hh:mm:ssZ 

86 # hhmmssZ 

87 # hh:mmZ 

88 # hhmmZ 

89 # hhZ 

90 # hh:mm:ss±hh:mm 

91 # hhmmss±hh:mm 

92 # hh:mm±hh:mm 

93 # hhmm±hh:mm 

94 # hh±hh:mm 

95 # hh:mm:ss±hhmm 

96 # hhmmss±hhmm 

97 # hh:mm±hhmm 

98 # hhmm±hhmm 

99 # hh±hhmm 

100 # hh:mm:ss±hh 

101 # hhmmss±hh 

102 # hh:mm±hh 

103 # hhmm±hh 

104 # hh±hh 

105 if is_string(isotimestr) is False: 

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

107 

108 if len(isotimestr) == 0: 

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

110 

111 timestr = normalize(isotimestr) 

112 

113 hourstr = None 

114 minutestr = None 

115 secondstr = None 

116 tzstr = None 

117 

118 fractionalstr = None 

119 

120 # Split out the timezone 

121 for delimiter in TIMEZONE_DELIMITERS: 

122 delimiteridx = timestr.find(delimiter) 

123 

124 if delimiteridx != -1: 

125 tzstr = timestr[delimiteridx:] 

126 timestr = timestr[0:delimiteridx] 

127 

128 # Split out the fractional component 

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

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

131 

132 if fractionalstr.isdigit() is False: 

133 raise ISOFormatError( 

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

135 ) 

136 

137 if len(timestr) == 2: 

138 # hh 

139 hourstr = timestr 

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

141 # hh:mm 

142 # hhmm 

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

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

145 else: 

146 hourstr = timestr[0:2] 

147 minutestr = timestr[2:] 

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

149 # hh:mm:ss 

150 # hhmmss 

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

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

153 else: 

154 hourstr = timestr[0:2] 

155 minutestr = timestr[2:4] 

156 secondstr = timestr[4:] 

157 else: 

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

159 

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

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

162 raise ISOFormatError( 

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

164 ) 

165 

166 if fractionalstr is not None: 

167 if secondstr is not None: 

168 secondstr = secondstr + "." + fractionalstr 

169 elif minutestr is not None: 

170 minutestr = minutestr + "." + fractionalstr 

171 else: 

172 hourstr = hourstr + "." + fractionalstr 

173 

174 if tzstr is None: 

175 tz = None 

176 else: 

177 tz = parse_timezone(tzstr, builder=TupleBuilder) 

178 

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

180 

181 

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

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

184 # object that corresponds to the given date time. 

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

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

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

188 if is_string(isodatetimestr) is False: 

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

190 

191 if delimiter not in isodatetimestr: 

192 raise ISOFormatError( 

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

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

195 ) 

196 

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

198 

199 datepart = parse_date(isodatestr, builder=TupleBuilder) 

200 

201 timepart = parse_time(isotimestr, builder=TupleBuilder) 

202 

203 return builder.build_datetime(datepart, timepart)