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
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
1# -*- coding: utf-8 -*-
3# Copyright (c) 2026, Brandon Nielsen
4# SPDX-License-Identifier: BSD-3-Clause
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
15TIMEZONE_DELIMITERS = ["Z", "+", "-"]
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)
48 return _get_time_resolution(isotimetuple)
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
59 return _get_time_resolution(isotimetuple)
62def _get_time_resolution(isotimetuple):
63 if isotimetuple.ss is not None:
64 return TimeResolution.Seconds
66 if isotimetuple.mm is not None:
67 return TimeResolution.Minutes
69 return TimeResolution.Hours
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.")
105 if len(isotimestr) == 0:
106 raise ISOFormatError('"{0}" is not a valid ISO 8601 time.'.format(isotimestr))
108 timestr = normalize(isotimestr)
110 hourstr = None
111 minutestr = None
112 secondstr = None
113 tzstr = None
115 fractionalstr = None
117 # Split out the timezone
118 for delimiter in TIMEZONE_DELIMITERS:
119 delimiteridx = timestr.find(delimiter)
121 if delimiteridx != -1:
122 tzstr = timestr[delimiteridx:]
123 timestr = timestr[0:delimiteridx]
125 # Split out the fractional component
126 if timestr.find(".") != -1:
127 timestr, fractionalstr = timestr.split(".", 1)
129 if fractionalstr.isdigit() is False:
130 raise ISOFormatError(
131 '"{0}" is not a valid ISO 8601 time.'.format(isotimestr)
132 )
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))
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 )
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
171 if tzstr is None:
172 tz = None
173 else:
174 tz = parse_timezone(tzstr, builder=TupleBuilder)
176 return builder.build_time(hh=hourstr, mm=minutestr, ss=secondstr, tz=tz)
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.")
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 )
194 isodatestr, isotimestr = isodatetimestr.split(delimiter, 1)
196 datepart = parse_date(isodatestr, builder=TupleBuilder)
198 timepart = parse_time(isotimestr, builder=TupleBuilder)
200 return builder.build_datetime(datepart, timepart)