Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/isodate/isostrf.py: 51%
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"""This module provides an alternative strftime method.
3The strftime method in this module allows only a subset of Python's strftime
4format codes, plus a few additional. It supports the full range of date values
5possible with standard Python date/time objects. Furthermore there are several
6pr-defined format strings in this module to make ease producing of ISO 8601
7conforming strings.
8"""
10import re
11from datetime import date, time, timedelta
12from typing import Callable, Union
14from isodate.duration import Duration
15from isodate.isotzinfo import tz_isoformat
17# Date specific format strings
18DATE_BAS_COMPLETE = "%Y%m%d"
19DATE_EXT_COMPLETE = "%Y-%m-%d"
20DATE_BAS_WEEK_COMPLETE = "%YW%W%w"
21DATE_EXT_WEEK_COMPLETE = "%Y-W%W-%w"
22DATE_BAS_ORD_COMPLETE = "%Y%j"
23DATE_EXT_ORD_COMPLETE = "%Y-%j"
24DATE_BAS_WEEK = "%YW%W"
25DATE_EXT_WEEK = "%Y-W%W"
26DATE_BAS_MONTH = "%Y%m"
27DATE_EXT_MONTH = "%Y-%m"
28DATE_YEAR = "%Y"
29DATE_CENTURY = "%C"
31# Time specific format strings
32TIME_BAS_COMPLETE = "%H%M%S"
33TIME_EXT_COMPLETE = "%H:%M:%S"
34TIME_BAS_MINUTE = "%H%M"
35TIME_EXT_MINUTE = "%H:%M"
36TIME_HOUR = "%H"
38# Time zone formats
39TZ_BAS = "%z"
40TZ_EXT = "%Z"
41TZ_HOUR = "%h"
43# DateTime formats
44DT_EXT_COMPLETE = DATE_EXT_COMPLETE + "T" + TIME_EXT_COMPLETE + TZ_EXT
45DT_BAS_COMPLETE = DATE_BAS_COMPLETE + "T" + TIME_BAS_COMPLETE + TZ_BAS
46DT_EXT_ORD_COMPLETE = DATE_EXT_ORD_COMPLETE + "T" + TIME_EXT_COMPLETE + TZ_EXT
47DT_BAS_ORD_COMPLETE = DATE_BAS_ORD_COMPLETE + "T" + TIME_BAS_COMPLETE + TZ_BAS
48DT_EXT_WEEK_COMPLETE = DATE_EXT_WEEK_COMPLETE + "T" + TIME_EXT_COMPLETE + TZ_EXT
49DT_BAS_WEEK_COMPLETE = DATE_BAS_WEEK_COMPLETE + "T" + TIME_BAS_COMPLETE + TZ_BAS
51# Duration formts
52D_DEFAULT = "P%P"
53D_WEEK = "P%p"
54D_ALT_EXT = "P" + DATE_EXT_COMPLETE + "T" + TIME_EXT_COMPLETE
55D_ALT_BAS = "P" + DATE_BAS_COMPLETE + "T" + TIME_BAS_COMPLETE
56D_ALT_EXT_ORD = "P" + DATE_EXT_ORD_COMPLETE + "T" + TIME_EXT_COMPLETE
57D_ALT_BAS_ORD = "P" + DATE_BAS_ORD_COMPLETE + "T" + TIME_BAS_COMPLETE
59STRF_DT_MAP: dict[str, Callable[[Union[time, date], int], str]] = {
60 "%d": lambda tdt, yds: "%02d" % tdt.day, # type: ignore [union-attr]
61 "%f": lambda tdt, yds: "%06d" % tdt.microsecond, # type: ignore [union-attr]
62 "%H": lambda tdt, yds: "%02d" % tdt.hour, # type: ignore [union-attr]
63 "%j": lambda tdt, yds: "%03d" % (tdt.toordinal() - date(tdt.year, 1, 1).toordinal() + 1), # type: ignore [union-attr, operator] # noqa: E501
64 "%m": lambda tdt, yds: "%02d" % tdt.month, # type: ignore [union-attr]
65 "%M": lambda tdt, yds: "%02d" % tdt.minute, # type: ignore [union-attr]
66 "%S": lambda tdt, yds: "%02d" % tdt.second, # type: ignore [union-attr]
67 "%w": lambda tdt, yds: "%1d" % tdt.isoweekday(), # type: ignore [union-attr]
68 "%W": lambda tdt, yds: "%02d" % tdt.isocalendar()[1], # type: ignore [union-attr]
69 "%Y": lambda tdt, yds: (((yds != 4) and "+") or "") + (("%%0%dd" % yds) % tdt.year), # type: ignore [union-attr] # noqa: E501
70 "%C": lambda tdt, yds: (((yds != 4) and "+") or "") # type: ignore [union-attr]
71 + (("%%0%dd" % (yds - 2)) % (tdt.year / 100)), # type: ignore [union-attr]
72 "%h": lambda tdt, yds: tz_isoformat(tdt, "%h"), # type: ignore [arg-type]
73 "%Z": lambda tdt, yds: tz_isoformat(tdt, "%Z"), # type: ignore [arg-type]
74 "%z": lambda tdt, yds: tz_isoformat(tdt, "%z"), # type: ignore [arg-type]
75 "%%": lambda tdt, yds: "%",
76}
78STRF_D_MAP: dict[str, Callable[[Union[timedelta, Duration], int], str]] = {
79 "%d": lambda tdt, yds: "%02d" % tdt.days,
80 "%f": lambda tdt, yds: "%06d" % tdt.microseconds,
81 "%H": lambda tdt, yds: "%02d" % (tdt.seconds / 60 / 60),
82 "%m": lambda tdt, yds: "%02d" % tdt.months, # type: ignore [union-attr]
83 "%M": lambda tdt, yds: "%02d" % ((tdt.seconds / 60) % 60),
84 "%S": lambda tdt, yds: "%02d" % (tdt.seconds % 60),
85 "%W": lambda tdt, yds: "%02d" % (abs(tdt.days / 7)),
86 "%Y": lambda tdt, yds: (((yds != 4) and "+") or "") + (("%%0%dd" % yds) % tdt.years), # type: ignore [union-attr] # noqa: E501
87 "%C": lambda tdt, yds: (((yds != 4) and "+") or "")
88 + (("%%0%dd" % (yds - 2)) % (tdt.years / 100)), # type: ignore [union-attr]
89 "%%": lambda tdt, yds: "%",
90}
93def _strfduration(tdt: Union[timedelta, Duration], format: str, yeardigits: int = 4) -> str:
94 """This is the work method for timedelta and Duration instances.
96 See strftime for more details.
97 """
99 def repl(match: re.Match[str]) -> str:
100 """Lookup format command and return corresponding replacement."""
101 if match.group(0) in STRF_D_MAP:
102 return STRF_D_MAP[match.group(0)](tdt, yeardigits)
103 elif match.group(0) == "%P":
104 ret: list[str] = []
105 if isinstance(tdt, Duration):
106 if tdt.years:
107 ret.append("%sY" % abs(tdt.years))
108 if tdt.months:
109 ret.append("%sM" % abs(tdt.months))
110 usecs = abs((tdt.days * 24 * 60 * 60 + tdt.seconds) * 1000000 + tdt.microseconds)
111 seconds, usecs = divmod(usecs, 1000000)
112 minutes, seconds = divmod(seconds, 60)
113 hours, minutes = divmod(minutes, 60)
114 days, hours = divmod(hours, 24)
115 if days:
116 ret.append("%sD" % days)
117 if hours or minutes or seconds or usecs:
118 ret.append("T")
119 if hours:
120 ret.append("%sH" % hours)
121 if minutes:
122 ret.append("%sM" % minutes)
123 if seconds or usecs:
124 if usecs:
125 ret.append(("%d.%06d" % (seconds, usecs)).rstrip("0"))
126 else:
127 ret.append("%d" % seconds)
128 ret.append("S")
129 # at least one component has to be there.
130 return "".join(ret) if ret else "0D"
131 elif match.group(0) == "%p":
132 return str(abs(tdt.days // 7)) + "W"
133 return match.group(0)
135 return re.sub("%d|%f|%H|%m|%M|%S|%W|%Y|%C|%%|%P|%p", repl, format)
138def _strfdt(tdt: Union[time, date], format: str, yeardigits: int = 4) -> str:
139 """This is the work method for time and date instances.
141 See strftime for more details.
142 """
144 def repl(match: re.Match[str]) -> str:
145 """Lookup format command and return corresponding replacement."""
146 if match.group(0) in STRF_DT_MAP:
147 return STRF_DT_MAP[match.group(0)](tdt, yeardigits)
148 return match.group(0)
150 return re.sub("%d|%f|%H|%j|%m|%M|%S|%w|%W|%Y|%C|%z|%Z|%h|%%", repl, format)
153def strftime(tdt: Union[timedelta, Duration, time, date], format: str, yeardigits: int = 4) -> str:
154 """Directive Meaning Notes.
156 %d Day of the month as a decimal number [01,31].
157 %f Microsecond as a decimal number [0,999999], zero-padded
158 on the left (1)
159 %H Hour (24-hour clock) as a decimal number [00,23].
160 %j Day of the year as a decimal number [001,366].
161 %m Month as a decimal number [01,12].
162 %M Minute as a decimal number [00,59].
163 %S Second as a decimal number [00,61]. (3)
164 %w Weekday as a decimal number [0(Monday),6].
165 %W Week number of the year (Monday as the first day of the week)
166 as a decimal number [00,53]. All days in a new year preceding the
167 first Monday are considered to be in week 0. (4)
168 %Y Year with century as a decimal number. [0000,9999]
169 %C Century as a decimal number. [00,99]
170 %z UTC offset in the form +HHMM or -HHMM (empty string if the
171 object is naive). (5)
172 %Z Time zone name (empty string if the object is naive).
173 %P ISO8601 duration format.
174 %p ISO8601 duration format in weeks.
175 %% A literal '%' character.
177 """
178 if isinstance(tdt, (timedelta, Duration)):
179 return _strfduration(tdt, format, yeardigits)
180 return _strfdt(tdt, format, yeardigits)