Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/isodate/isotime.py: 24%

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

46 statements  

1""" 

2This modules provides a method to parse an ISO 8601:2004 time string to a 

3Python datetime.time instance. 

4 

5It supports all basic and extended formats including time zone specifications 

6as described in the ISO standard. 

7""" 

8 

9import re 

10from datetime import time 

11from decimal import ROUND_FLOOR, Decimal 

12 

13from isodate.isoerror import ISO8601Error 

14from isodate.isostrf import TIME_EXT_COMPLETE, TZ_EXT, strftime 

15from isodate.isotzinfo import TZ_REGEX, build_tzinfo 

16 

17TIME_REGEX_CACHE = [] 

18# used to cache regular expressions to parse ISO time strings. 

19 

20 

21def build_time_regexps(): 

22 """ 

23 Build regular expressions to parse ISO time string. 

24 

25 The regular expressions are compiled and stored in TIME_REGEX_CACHE 

26 for later reuse. 

27 """ 

28 if not TIME_REGEX_CACHE: 

29 # ISO 8601 time representations allow decimal fractions on least 

30 # significant time component. Command and Full Stop are both valid 

31 # fraction separators. 

32 # The letter 'T' is allowed as time designator in front of a time 

33 # expression. 

34 # Immediately after a time expression, a time zone definition is 

35 # allowed. 

36 # a TZ may be missing (local time), be a 'Z' for UTC or a string of 

37 # +-hh:mm where the ':mm' part can be skipped. 

38 # TZ information patterns: 

39 # '' 

40 # Z 

41 # +-hh:mm 

42 # +-hhmm 

43 # +-hh => 

44 # isotzinfo.TZ_REGEX 

45 def add_re(regex_text): 

46 TIME_REGEX_CACHE.append(re.compile(r"\A" + regex_text + TZ_REGEX + r"\Z")) 

47 

48 # 1. complete time: 

49 # hh:mm:ss.ss ... extended format 

50 add_re( 

51 r"T?(?P<hour>[0-9]{2}):" 

52 r"(?P<minute>[0-9]{2}):" 

53 r"(?P<second>[0-9]{2}" 

54 r"([,.][0-9]+)?)" 

55 ) 

56 # hhmmss.ss ... basic format 

57 add_re( 

58 r"T?(?P<hour>[0-9]{2})" 

59 r"(?P<minute>[0-9]{2})" 

60 r"(?P<second>[0-9]{2}" 

61 r"([,.][0-9]+)?)" 

62 ) 

63 # 2. reduced accuracy: 

64 # hh:mm.mm ... extended format 

65 add_re(r"T?(?P<hour>[0-9]{2}):" r"(?P<minute>[0-9]{2}" r"([,.][0-9]+)?)") 

66 # hhmm.mm ... basic format 

67 add_re(r"T?(?P<hour>[0-9]{2})" r"(?P<minute>[0-9]{2}" r"([,.][0-9]+)?)") 

68 # hh.hh ... basic format 

69 add_re(r"T?(?P<hour>[0-9]{2}" r"([,.][0-9]+)?)") 

70 return TIME_REGEX_CACHE 

71 

72 

73def parse_time(timestring): 

74 """ 

75 Parses ISO 8601 times into datetime.time objects. 

76 

77 Following ISO 8601 formats are supported: 

78 (as decimal separator a ',' or a '.' is allowed) 

79 hhmmss.ssTZD basic complete time 

80 hh:mm:ss.ssTZD extended complete time 

81 hhmm.mmTZD basic reduced accuracy time 

82 hh:mm.mmTZD extended reduced accuracy time 

83 hh.hhTZD basic reduced accuracy time 

84 TZD is the time zone designator which can be in the following format: 

85 no designator indicates local time zone 

86 Z UTC 

87 +-hhmm basic hours and minutes 

88 +-hh:mm extended hours and minutes 

89 +-hh hours 

90 """ 

91 isotimes = build_time_regexps() 

92 for pattern in isotimes: 

93 match = pattern.match(timestring) 

94 if match: 

95 groups = match.groupdict() 

96 for key, value in groups.items(): 

97 if value is not None: 

98 groups[key] = value.replace(",", ".") 

99 tzinfo = build_tzinfo( 

100 groups["tzname"], 

101 groups["tzsign"], 

102 int(groups["tzhour"] or 0), 

103 int(groups["tzmin"] or 0), 

104 ) 

105 if "second" in groups: 

106 second = Decimal(groups["second"]).quantize( 

107 Decimal(".000001"), rounding=ROUND_FLOOR 

108 ) 

109 microsecond = (second - int(second)) * int(1e6) 

110 # int(...) ... no rounding 

111 # to_integral() ... rounding 

112 return time( 

113 int(groups["hour"]), 

114 int(groups["minute"]), 

115 int(second), 

116 int(microsecond.to_integral()), 

117 tzinfo, 

118 ) 

119 if "minute" in groups: 

120 minute = Decimal(groups["minute"]) 

121 second = Decimal((minute - int(minute)) * 60).quantize( 

122 Decimal(".000001"), rounding=ROUND_FLOOR 

123 ) 

124 microsecond = (second - int(second)) * int(1e6) 

125 return time( 

126 int(groups["hour"]), 

127 int(minute), 

128 int(second), 

129 int(microsecond.to_integral()), 

130 tzinfo, 

131 ) 

132 else: 

133 microsecond, second, minute = 0, 0, 0 

134 hour = Decimal(groups["hour"]) 

135 minute = (hour - int(hour)) * 60 

136 second = (minute - int(minute)) * 60 

137 microsecond = (second - int(second)) * int(1e6) 

138 return time( 

139 int(hour), 

140 int(minute), 

141 int(second), 

142 int(microsecond.to_integral()), 

143 tzinfo, 

144 ) 

145 raise ISO8601Error("Unrecognised ISO 8601 time format: %r" % timestring) 

146 

147 

148def time_isoformat(ttime, format=TIME_EXT_COMPLETE + TZ_EXT): 

149 """ 

150 Format time strings. 

151 

152 This method is just a wrapper around isodate.isostrf.strftime and uses 

153 Time-Extended-Complete with extended time zone as default format. 

154 """ 

155 return strftime(ttime, format)