salmankhanpm commited on
Commit
98f38e2
·
verified ·
1 Parent(s): dc9bb20

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. dateutil/parser/__init__.py +61 -0
  2. dateutil/parser/_parser.py +1613 -0
  3. dateutil/parser/isoparser.py +416 -0
  4. dateutil/tz/__init__.py +12 -0
  5. dateutil/tz/_common.py +419 -0
  6. dateutil/tz/_factories.py +80 -0
  7. dateutil/tz/tz.py +1849 -0
  8. dateutil/tz/win.py +370 -0
  9. dateutil/zoneinfo/__init__.py +167 -0
  10. dateutil/zoneinfo/rebuild.py +75 -0
  11. pandas/_config/__init__.py +45 -0
  12. pandas/_config/config.py +954 -0
  13. pandas/_config/dates.py +26 -0
  14. pandas/_config/display.py +62 -0
  15. pandas/_config/localization.py +176 -0
  16. pandas/_libs/__init__.py +27 -0
  17. pandas/_libs/algos.pyi +443 -0
  18. pandas/_libs/arrays.pyi +40 -0
  19. pandas/_libs/byteswap.cpython-312-x86_64-linux-gnu.so +0 -0
  20. pandas/_libs/byteswap.pyi +5 -0
  21. pandas/_libs/groupby.pyi +234 -0
  22. pandas/_libs/hashing.cpython-312-x86_64-linux-gnu.so +0 -0
  23. pandas/_libs/hashing.pyi +9 -0
  24. pandas/_libs/hashtable.pyi +274 -0
  25. pandas/_libs/index.pyi +107 -0
  26. pandas/_libs/indexing.cpython-312-x86_64-linux-gnu.so +0 -0
  27. pandas/_libs/indexing.pyi +17 -0
  28. pandas/_libs/internals.pyi +96 -0
  29. pandas/_libs/interval.pyi +174 -0
  30. pandas/_libs/join.pyi +79 -0
  31. pandas/_libs/json.cpython-312-x86_64-linux-gnu.so +0 -0
  32. pandas/_libs/json.pyi +23 -0
  33. pandas/_libs/lib.pyi +238 -0
  34. pandas/_libs/missing.pyi +17 -0
  35. pandas/_libs/ops.pyi +53 -0
  36. pandas/_libs/ops_dispatch.cpython-312-x86_64-linux-gnu.so +0 -0
  37. pandas/_libs/ops_dispatch.pyi +5 -0
  38. pandas/_libs/pandas_datetime.cpython-312-x86_64-linux-gnu.so +0 -0
  39. pandas/_libs/pandas_parser.cpython-312-x86_64-linux-gnu.so +0 -0
  40. pandas/_libs/parsers.pyi +77 -0
  41. pandas/_libs/properties.cpython-312-x86_64-linux-gnu.so +0 -0
  42. pandas/_libs/properties.pyi +27 -0
  43. pandas/_libs/reshape.pyi +16 -0
  44. pandas/_libs/sas.pyi +7 -0
  45. pandas/_libs/sparse.pyi +51 -0
  46. pandas/_libs/testing.pyi +14 -0
  47. pandas/_libs/tslib.pyi +33 -0
  48. pandas/_libs/tslibs/__init__.py +89 -0
  49. pandas/_libs/tslibs/base.cpython-312-x86_64-linux-gnu.so +0 -0
  50. pandas/_libs/tslibs/ccalendar.cpython-312-x86_64-linux-gnu.so +0 -0
dateutil/parser/__init__.py ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ from ._parser import parse, parser, parserinfo, ParserError
3
+ from ._parser import DEFAULTPARSER, DEFAULTTZPARSER
4
+ from ._parser import UnknownTimezoneWarning
5
+
6
+ from ._parser import __doc__
7
+
8
+ from .isoparser import isoparser, isoparse
9
+
10
+ __all__ = ['parse', 'parser', 'parserinfo',
11
+ 'isoparse', 'isoparser',
12
+ 'ParserError',
13
+ 'UnknownTimezoneWarning']
14
+
15
+
16
+ ###
17
+ # Deprecate portions of the private interface so that downstream code that
18
+ # is improperly relying on it is given *some* notice.
19
+
20
+
21
+ def __deprecated_private_func(f):
22
+ from functools import wraps
23
+ import warnings
24
+
25
+ msg = ('{name} is a private function and may break without warning, '
26
+ 'it will be moved and or renamed in future versions.')
27
+ msg = msg.format(name=f.__name__)
28
+
29
+ @wraps(f)
30
+ def deprecated_func(*args, **kwargs):
31
+ warnings.warn(msg, DeprecationWarning)
32
+ return f(*args, **kwargs)
33
+
34
+ return deprecated_func
35
+
36
+ def __deprecate_private_class(c):
37
+ import warnings
38
+
39
+ msg = ('{name} is a private class and may break without warning, '
40
+ 'it will be moved and or renamed in future versions.')
41
+ msg = msg.format(name=c.__name__)
42
+
43
+ class private_class(c):
44
+ __doc__ = c.__doc__
45
+
46
+ def __init__(self, *args, **kwargs):
47
+ warnings.warn(msg, DeprecationWarning)
48
+ super(private_class, self).__init__(*args, **kwargs)
49
+
50
+ private_class.__name__ = c.__name__
51
+
52
+ return private_class
53
+
54
+
55
+ from ._parser import _timelex, _resultbase
56
+ from ._parser import _tzparser, _parsetz
57
+
58
+ _timelex = __deprecate_private_class(_timelex)
59
+ _tzparser = __deprecate_private_class(_tzparser)
60
+ _resultbase = __deprecate_private_class(_resultbase)
61
+ _parsetz = __deprecated_private_func(_parsetz)
dateutil/parser/_parser.py ADDED
@@ -0,0 +1,1613 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ This module offers a generic date/time string parser which is able to parse
4
+ most known formats to represent a date and/or time.
5
+
6
+ This module attempts to be forgiving with regards to unlikely input formats,
7
+ returning a datetime object even for dates which are ambiguous. If an element
8
+ of a date/time stamp is omitted, the following rules are applied:
9
+
10
+ - If AM or PM is left unspecified, a 24-hour clock is assumed, however, an hour
11
+ on a 12-hour clock (``0 <= hour <= 12``) *must* be specified if AM or PM is
12
+ specified.
13
+ - If a time zone is omitted, a timezone-naive datetime is returned.
14
+
15
+ If any other elements are missing, they are taken from the
16
+ :class:`datetime.datetime` object passed to the parameter ``default``. If this
17
+ results in a day number exceeding the valid number of days per month, the
18
+ value falls back to the end of the month.
19
+
20
+ Additional resources about date/time string formats can be found below:
21
+
22
+ - `A summary of the international standard date and time notation
23
+ <https://www.cl.cam.ac.uk/~mgk25/iso-time.html>`_
24
+ - `W3C Date and Time Formats <https://www.w3.org/TR/NOTE-datetime>`_
25
+ - `Time Formats (Planetary Rings Node) <https://pds-rings.seti.org:443/tools/time_formats.html>`_
26
+ - `CPAN ParseDate module
27
+ <https://metacpan.org/pod/release/MUIR/Time-modules-2013.0912/lib/Time/ParseDate.pm>`_
28
+ - `Java SimpleDateFormat Class
29
+ <https://docs.oracle.com/javase/6/docs/api/java/text/SimpleDateFormat.html>`_
30
+ """
31
+ from __future__ import unicode_literals
32
+
33
+ import datetime
34
+ import re
35
+ import string
36
+ import time
37
+ import warnings
38
+
39
+ from calendar import monthrange
40
+ from io import StringIO
41
+
42
+ import six
43
+ from six import integer_types, text_type
44
+
45
+ from decimal import Decimal
46
+
47
+ from warnings import warn
48
+
49
+ from .. import relativedelta
50
+ from .. import tz
51
+
52
+ __all__ = ["parse", "parserinfo", "ParserError"]
53
+
54
+
55
+ # TODO: pandas.core.tools.datetimes imports this explicitly. Might be worth
56
+ # making public and/or figuring out if there is something we can
57
+ # take off their plate.
58
+ class _timelex(object):
59
+ # Fractional seconds are sometimes split by a comma
60
+ _split_decimal = re.compile("([.,])")
61
+
62
+ def __init__(self, instream):
63
+ if isinstance(instream, (bytes, bytearray)):
64
+ instream = instream.decode()
65
+
66
+ if isinstance(instream, text_type):
67
+ instream = StringIO(instream)
68
+ elif getattr(instream, 'read', None) is None:
69
+ raise TypeError('Parser must be a string or character stream, not '
70
+ '{itype}'.format(itype=instream.__class__.__name__))
71
+
72
+ self.instream = instream
73
+ self.charstack = []
74
+ self.tokenstack = []
75
+ self.eof = False
76
+
77
+ def get_token(self):
78
+ """
79
+ This function breaks the time string into lexical units (tokens), which
80
+ can be parsed by the parser. Lexical units are demarcated by changes in
81
+ the character set, so any continuous string of letters is considered
82
+ one unit, any continuous string of numbers is considered one unit.
83
+
84
+ The main complication arises from the fact that dots ('.') can be used
85
+ both as separators (e.g. "Sep.20.2009") or decimal points (e.g.
86
+ "4:30:21.447"). As such, it is necessary to read the full context of
87
+ any dot-separated strings before breaking it into tokens; as such, this
88
+ function maintains a "token stack", for when the ambiguous context
89
+ demands that multiple tokens be parsed at once.
90
+ """
91
+ if self.tokenstack:
92
+ return self.tokenstack.pop(0)
93
+
94
+ seenletters = False
95
+ token = None
96
+ state = None
97
+
98
+ while not self.eof:
99
+ # We only realize that we've reached the end of a token when we
100
+ # find a character that's not part of the current token - since
101
+ # that character may be part of the next token, it's stored in the
102
+ # charstack.
103
+ if self.charstack:
104
+ nextchar = self.charstack.pop(0)
105
+ else:
106
+ nextchar = self.instream.read(1)
107
+ while nextchar == '\x00':
108
+ nextchar = self.instream.read(1)
109
+
110
+ if not nextchar:
111
+ self.eof = True
112
+ break
113
+ elif not state:
114
+ # First character of the token - determines if we're starting
115
+ # to parse a word, a number or something else.
116
+ token = nextchar
117
+ if self.isword(nextchar):
118
+ state = 'a'
119
+ elif self.isnum(nextchar):
120
+ state = '0'
121
+ elif self.isspace(nextchar):
122
+ token = ' '
123
+ break # emit token
124
+ else:
125
+ break # emit token
126
+ elif state == 'a':
127
+ # If we've already started reading a word, we keep reading
128
+ # letters until we find something that's not part of a word.
129
+ seenletters = True
130
+ if self.isword(nextchar):
131
+ token += nextchar
132
+ elif nextchar == '.':
133
+ token += nextchar
134
+ state = 'a.'
135
+ else:
136
+ self.charstack.append(nextchar)
137
+ break # emit token
138
+ elif state == '0':
139
+ # If we've already started reading a number, we keep reading
140
+ # numbers until we find something that doesn't fit.
141
+ if self.isnum(nextchar):
142
+ token += nextchar
143
+ elif nextchar == '.' or (nextchar == ',' and len(token) >= 2):
144
+ token += nextchar
145
+ state = '0.'
146
+ else:
147
+ self.charstack.append(nextchar)
148
+ break # emit token
149
+ elif state == 'a.':
150
+ # If we've seen some letters and a dot separator, continue
151
+ # parsing, and the tokens will be broken up later.
152
+ seenletters = True
153
+ if nextchar == '.' or self.isword(nextchar):
154
+ token += nextchar
155
+ elif self.isnum(nextchar) and token[-1] == '.':
156
+ token += nextchar
157
+ state = '0.'
158
+ else:
159
+ self.charstack.append(nextchar)
160
+ break # emit token
161
+ elif state == '0.':
162
+ # If we've seen at least one dot separator, keep going, we'll
163
+ # break up the tokens later.
164
+ if nextchar == '.' or self.isnum(nextchar):
165
+ token += nextchar
166
+ elif self.isword(nextchar) and token[-1] == '.':
167
+ token += nextchar
168
+ state = 'a.'
169
+ else:
170
+ self.charstack.append(nextchar)
171
+ break # emit token
172
+
173
+ if (state in ('a.', '0.') and (seenletters or token.count('.') > 1 or
174
+ token[-1] in '.,')):
175
+ l = self._split_decimal.split(token)
176
+ token = l[0]
177
+ for tok in l[1:]:
178
+ if tok:
179
+ self.tokenstack.append(tok)
180
+
181
+ if state == '0.' and token.count('.') == 0:
182
+ token = token.replace(',', '.')
183
+
184
+ return token
185
+
186
+ def __iter__(self):
187
+ return self
188
+
189
+ def __next__(self):
190
+ token = self.get_token()
191
+ if token is None:
192
+ raise StopIteration
193
+
194
+ return token
195
+
196
+ def next(self):
197
+ return self.__next__() # Python 2.x support
198
+
199
+ @classmethod
200
+ def split(cls, s):
201
+ return list(cls(s))
202
+
203
+ @classmethod
204
+ def isword(cls, nextchar):
205
+ """ Whether or not the next character is part of a word """
206
+ return nextchar.isalpha()
207
+
208
+ @classmethod
209
+ def isnum(cls, nextchar):
210
+ """ Whether the next character is part of a number """
211
+ return nextchar.isdigit()
212
+
213
+ @classmethod
214
+ def isspace(cls, nextchar):
215
+ """ Whether the next character is whitespace """
216
+ return nextchar.isspace()
217
+
218
+
219
+ class _resultbase(object):
220
+
221
+ def __init__(self):
222
+ for attr in self.__slots__:
223
+ setattr(self, attr, None)
224
+
225
+ def _repr(self, classname):
226
+ l = []
227
+ for attr in self.__slots__:
228
+ value = getattr(self, attr)
229
+ if value is not None:
230
+ l.append("%s=%s" % (attr, repr(value)))
231
+ return "%s(%s)" % (classname, ", ".join(l))
232
+
233
+ def __len__(self):
234
+ return (sum(getattr(self, attr) is not None
235
+ for attr in self.__slots__))
236
+
237
+ def __repr__(self):
238
+ return self._repr(self.__class__.__name__)
239
+
240
+
241
+ class parserinfo(object):
242
+ """
243
+ Class which handles what inputs are accepted. Subclass this to customize
244
+ the language and acceptable values for each parameter.
245
+
246
+ :param dayfirst:
247
+ Whether to interpret the first value in an ambiguous 3-integer date
248
+ (e.g. 01/05/09) as the day (``True``) or month (``False``). If
249
+ ``yearfirst`` is set to ``True``, this distinguishes between YDM
250
+ and YMD. Default is ``False``.
251
+
252
+ :param yearfirst:
253
+ Whether to interpret the first value in an ambiguous 3-integer date
254
+ (e.g. 01/05/09) as the year. If ``True``, the first number is taken
255
+ to be the year, otherwise the last number is taken to be the year.
256
+ Default is ``False``.
257
+ """
258
+
259
+ # m from a.m/p.m, t from ISO T separator
260
+ JUMP = [" ", ".", ",", ";", "-", "/", "'",
261
+ "at", "on", "and", "ad", "m", "t", "of",
262
+ "st", "nd", "rd", "th"]
263
+
264
+ WEEKDAYS = [("Mon", "Monday"),
265
+ ("Tue", "Tuesday"), # TODO: "Tues"
266
+ ("Wed", "Wednesday"),
267
+ ("Thu", "Thursday"), # TODO: "Thurs"
268
+ ("Fri", "Friday"),
269
+ ("Sat", "Saturday"),
270
+ ("Sun", "Sunday")]
271
+ MONTHS = [("Jan", "January"),
272
+ ("Feb", "February"), # TODO: "Febr"
273
+ ("Mar", "March"),
274
+ ("Apr", "April"),
275
+ ("May", "May"),
276
+ ("Jun", "June"),
277
+ ("Jul", "July"),
278
+ ("Aug", "August"),
279
+ ("Sep", "Sept", "September"),
280
+ ("Oct", "October"),
281
+ ("Nov", "November"),
282
+ ("Dec", "December")]
283
+ HMS = [("h", "hour", "hours"),
284
+ ("m", "minute", "minutes"),
285
+ ("s", "second", "seconds")]
286
+ AMPM = [("am", "a"),
287
+ ("pm", "p")]
288
+ UTCZONE = ["UTC", "GMT", "Z", "z"]
289
+ PERTAIN = ["of"]
290
+ TZOFFSET = {}
291
+ # TODO: ERA = ["AD", "BC", "CE", "BCE", "Stardate",
292
+ # "Anno Domini", "Year of Our Lord"]
293
+
294
+ def __init__(self, dayfirst=False, yearfirst=False):
295
+ self._jump = self._convert(self.JUMP)
296
+ self._weekdays = self._convert(self.WEEKDAYS)
297
+ self._months = self._convert(self.MONTHS)
298
+ self._hms = self._convert(self.HMS)
299
+ self._ampm = self._convert(self.AMPM)
300
+ self._utczone = self._convert(self.UTCZONE)
301
+ self._pertain = self._convert(self.PERTAIN)
302
+
303
+ self.dayfirst = dayfirst
304
+ self.yearfirst = yearfirst
305
+
306
+ self._year = time.localtime().tm_year
307
+ self._century = self._year // 100 * 100
308
+
309
+ def _convert(self, lst):
310
+ dct = {}
311
+ for i, v in enumerate(lst):
312
+ if isinstance(v, tuple):
313
+ for v in v:
314
+ dct[v.lower()] = i
315
+ else:
316
+ dct[v.lower()] = i
317
+ return dct
318
+
319
+ def jump(self, name):
320
+ return name.lower() in self._jump
321
+
322
+ def weekday(self, name):
323
+ try:
324
+ return self._weekdays[name.lower()]
325
+ except KeyError:
326
+ pass
327
+ return None
328
+
329
+ def month(self, name):
330
+ try:
331
+ return self._months[name.lower()] + 1
332
+ except KeyError:
333
+ pass
334
+ return None
335
+
336
+ def hms(self, name):
337
+ try:
338
+ return self._hms[name.lower()]
339
+ except KeyError:
340
+ return None
341
+
342
+ def ampm(self, name):
343
+ try:
344
+ return self._ampm[name.lower()]
345
+ except KeyError:
346
+ return None
347
+
348
+ def pertain(self, name):
349
+ return name.lower() in self._pertain
350
+
351
+ def utczone(self, name):
352
+ return name.lower() in self._utczone
353
+
354
+ def tzoffset(self, name):
355
+ if name in self._utczone:
356
+ return 0
357
+
358
+ return self.TZOFFSET.get(name)
359
+
360
+ def convertyear(self, year, century_specified=False):
361
+ """
362
+ Converts two-digit years to year within [-50, 49]
363
+ range of self._year (current local time)
364
+ """
365
+
366
+ # Function contract is that the year is always positive
367
+ assert year >= 0
368
+
369
+ if year < 100 and not century_specified:
370
+ # assume current century to start
371
+ year += self._century
372
+
373
+ if year >= self._year + 50: # if too far in future
374
+ year -= 100
375
+ elif year < self._year - 50: # if too far in past
376
+ year += 100
377
+
378
+ return year
379
+
380
+ def validate(self, res):
381
+ # move to info
382
+ if res.year is not None:
383
+ res.year = self.convertyear(res.year, res.century_specified)
384
+
385
+ if ((res.tzoffset == 0 and not res.tzname) or
386
+ (res.tzname == 'Z' or res.tzname == 'z')):
387
+ res.tzname = "UTC"
388
+ res.tzoffset = 0
389
+ elif res.tzoffset != 0 and res.tzname and self.utczone(res.tzname):
390
+ res.tzoffset = 0
391
+ return True
392
+
393
+
394
+ class _ymd(list):
395
+ def __init__(self, *args, **kwargs):
396
+ super(self.__class__, self).__init__(*args, **kwargs)
397
+ self.century_specified = False
398
+ self.dstridx = None
399
+ self.mstridx = None
400
+ self.ystridx = None
401
+
402
+ @property
403
+ def has_year(self):
404
+ return self.ystridx is not None
405
+
406
+ @property
407
+ def has_month(self):
408
+ return self.mstridx is not None
409
+
410
+ @property
411
+ def has_day(self):
412
+ return self.dstridx is not None
413
+
414
+ def could_be_day(self, value):
415
+ if self.has_day:
416
+ return False
417
+ elif not self.has_month:
418
+ return 1 <= value <= 31
419
+ elif not self.has_year:
420
+ # Be permissive, assume leap year
421
+ month = self[self.mstridx]
422
+ return 1 <= value <= monthrange(2000, month)[1]
423
+ else:
424
+ month = self[self.mstridx]
425
+ year = self[self.ystridx]
426
+ return 1 <= value <= monthrange(year, month)[1]
427
+
428
+ def append(self, val, label=None):
429
+ if hasattr(val, '__len__'):
430
+ if val.isdigit() and len(val) > 2:
431
+ self.century_specified = True
432
+ if label not in [None, 'Y']: # pragma: no cover
433
+ raise ValueError(label)
434
+ label = 'Y'
435
+ elif val > 100:
436
+ self.century_specified = True
437
+ if label not in [None, 'Y']: # pragma: no cover
438
+ raise ValueError(label)
439
+ label = 'Y'
440
+
441
+ super(self.__class__, self).append(int(val))
442
+
443
+ if label == 'M':
444
+ if self.has_month:
445
+ raise ValueError('Month is already set')
446
+ self.mstridx = len(self) - 1
447
+ elif label == 'D':
448
+ if self.has_day:
449
+ raise ValueError('Day is already set')
450
+ self.dstridx = len(self) - 1
451
+ elif label == 'Y':
452
+ if self.has_year:
453
+ raise ValueError('Year is already set')
454
+ self.ystridx = len(self) - 1
455
+
456
+ def _resolve_from_stridxs(self, strids):
457
+ """
458
+ Try to resolve the identities of year/month/day elements using
459
+ ystridx, mstridx, and dstridx, if enough of these are specified.
460
+ """
461
+ if len(self) == 3 and len(strids) == 2:
462
+ # we can back out the remaining stridx value
463
+ missing = [x for x in range(3) if x not in strids.values()]
464
+ key = [x for x in ['y', 'm', 'd'] if x not in strids]
465
+ assert len(missing) == len(key) == 1
466
+ key = key[0]
467
+ val = missing[0]
468
+ strids[key] = val
469
+
470
+ assert len(self) == len(strids) # otherwise this should not be called
471
+ out = {key: self[strids[key]] for key in strids}
472
+ return (out.get('y'), out.get('m'), out.get('d'))
473
+
474
+ def resolve_ymd(self, yearfirst, dayfirst):
475
+ len_ymd = len(self)
476
+ year, month, day = (None, None, None)
477
+
478
+ strids = (('y', self.ystridx),
479
+ ('m', self.mstridx),
480
+ ('d', self.dstridx))
481
+
482
+ strids = {key: val for key, val in strids if val is not None}
483
+ if (len(self) == len(strids) > 0 or
484
+ (len(self) == 3 and len(strids) == 2)):
485
+ return self._resolve_from_stridxs(strids)
486
+
487
+ mstridx = self.mstridx
488
+
489
+ if len_ymd > 3:
490
+ raise ValueError("More than three YMD values")
491
+ elif len_ymd == 1 or (mstridx is not None and len_ymd == 2):
492
+ # One member, or two members with a month string
493
+ if mstridx is not None:
494
+ month = self[mstridx]
495
+ # since mstridx is 0 or 1, self[mstridx-1] always
496
+ # looks up the other element
497
+ other = self[mstridx - 1]
498
+ else:
499
+ other = self[0]
500
+
501
+ if len_ymd > 1 or mstridx is None:
502
+ if other > 31:
503
+ year = other
504
+ else:
505
+ day = other
506
+
507
+ elif len_ymd == 2:
508
+ # Two members with numbers
509
+ if self[0] > 31:
510
+ # 99-01
511
+ year, month = self
512
+ elif self[1] > 31:
513
+ # 01-99
514
+ month, year = self
515
+ elif dayfirst and self[1] <= 12:
516
+ # 13-01
517
+ day, month = self
518
+ else:
519
+ # 01-13
520
+ month, day = self
521
+
522
+ elif len_ymd == 3:
523
+ # Three members
524
+ if mstridx == 0:
525
+ if self[1] > 31:
526
+ # Apr-2003-25
527
+ month, year, day = self
528
+ else:
529
+ month, day, year = self
530
+ elif mstridx == 1:
531
+ if self[0] > 31 or (yearfirst and self[2] <= 31):
532
+ # 99-Jan-01
533
+ year, month, day = self
534
+ else:
535
+ # 01-Jan-01
536
+ # Give precedence to day-first, since
537
+ # two-digit years is usually hand-written.
538
+ day, month, year = self
539
+
540
+ elif mstridx == 2:
541
+ # WTF!?
542
+ if self[1] > 31:
543
+ # 01-99-Jan
544
+ day, year, month = self
545
+ else:
546
+ # 99-01-Jan
547
+ year, day, month = self
548
+
549
+ else:
550
+ if (self[0] > 31 or
551
+ self.ystridx == 0 or
552
+ (yearfirst and self[1] <= 12 and self[2] <= 31)):
553
+ # 99-01-01
554
+ if dayfirst and self[2] <= 12:
555
+ year, day, month = self
556
+ else:
557
+ year, month, day = self
558
+ elif self[0] > 12 or (dayfirst and self[1] <= 12):
559
+ # 13-01-01
560
+ day, month, year = self
561
+ else:
562
+ # 01-13-01
563
+ month, day, year = self
564
+
565
+ return year, month, day
566
+
567
+
568
+ class parser(object):
569
+ def __init__(self, info=None):
570
+ self.info = info or parserinfo()
571
+
572
+ def parse(self, timestr, default=None,
573
+ ignoretz=False, tzinfos=None, **kwargs):
574
+ """
575
+ Parse the date/time string into a :class:`datetime.datetime` object.
576
+
577
+ :param timestr:
578
+ Any date/time string using the supported formats.
579
+
580
+ :param default:
581
+ The default datetime object, if this is a datetime object and not
582
+ ``None``, elements specified in ``timestr`` replace elements in the
583
+ default object.
584
+
585
+ :param ignoretz:
586
+ If set ``True``, time zones in parsed strings are ignored and a
587
+ naive :class:`datetime.datetime` object is returned.
588
+
589
+ :param tzinfos:
590
+ Additional time zone names / aliases which may be present in the
591
+ string. This argument maps time zone names (and optionally offsets
592
+ from those time zones) to time zones. This parameter can be a
593
+ dictionary with timezone aliases mapping time zone names to time
594
+ zones or a function taking two parameters (``tzname`` and
595
+ ``tzoffset``) and returning a time zone.
596
+
597
+ The timezones to which the names are mapped can be an integer
598
+ offset from UTC in seconds or a :class:`tzinfo` object.
599
+
600
+ .. doctest::
601
+ :options: +NORMALIZE_WHITESPACE
602
+
603
+ >>> from dateutil.parser import parse
604
+ >>> from dateutil.tz import gettz
605
+ >>> tzinfos = {"BRST": -7200, "CST": gettz("America/Chicago")}
606
+ >>> parse("2012-01-19 17:21:00 BRST", tzinfos=tzinfos)
607
+ datetime.datetime(2012, 1, 19, 17, 21, tzinfo=tzoffset(u'BRST', -7200))
608
+ >>> parse("2012-01-19 17:21:00 CST", tzinfos=tzinfos)
609
+ datetime.datetime(2012, 1, 19, 17, 21,
610
+ tzinfo=tzfile('/usr/share/zoneinfo/America/Chicago'))
611
+
612
+ This parameter is ignored if ``ignoretz`` is set.
613
+
614
+ :param \\*\\*kwargs:
615
+ Keyword arguments as passed to ``_parse()``.
616
+
617
+ :return:
618
+ Returns a :class:`datetime.datetime` object or, if the
619
+ ``fuzzy_with_tokens`` option is ``True``, returns a tuple, the
620
+ first element being a :class:`datetime.datetime` object, the second
621
+ a tuple containing the fuzzy tokens.
622
+
623
+ :raises ParserError:
624
+ Raised for invalid or unknown string format, if the provided
625
+ :class:`tzinfo` is not in a valid format, or if an invalid date
626
+ would be created.
627
+
628
+ :raises TypeError:
629
+ Raised for non-string or character stream input.
630
+
631
+ :raises OverflowError:
632
+ Raised if the parsed date exceeds the largest valid C integer on
633
+ your system.
634
+ """
635
+
636
+ if default is None:
637
+ default = datetime.datetime.now().replace(hour=0, minute=0,
638
+ second=0, microsecond=0)
639
+
640
+ res, skipped_tokens = self._parse(timestr, **kwargs)
641
+
642
+ if res is None:
643
+ raise ParserError("Unknown string format: %s", timestr)
644
+
645
+ if len(res) == 0:
646
+ raise ParserError("String does not contain a date: %s", timestr)
647
+
648
+ try:
649
+ ret = self._build_naive(res, default)
650
+ except ValueError as e:
651
+ six.raise_from(ParserError(str(e) + ": %s", timestr), e)
652
+
653
+ if not ignoretz:
654
+ ret = self._build_tzaware(ret, res, tzinfos)
655
+
656
+ if kwargs.get('fuzzy_with_tokens', False):
657
+ return ret, skipped_tokens
658
+ else:
659
+ return ret
660
+
661
+ class _result(_resultbase):
662
+ __slots__ = ["year", "month", "day", "weekday",
663
+ "hour", "minute", "second", "microsecond",
664
+ "tzname", "tzoffset", "ampm","any_unused_tokens"]
665
+
666
+ def _parse(self, timestr, dayfirst=None, yearfirst=None, fuzzy=False,
667
+ fuzzy_with_tokens=False):
668
+ """
669
+ Private method which performs the heavy lifting of parsing, called from
670
+ ``parse()``, which passes on its ``kwargs`` to this function.
671
+
672
+ :param timestr:
673
+ The string to parse.
674
+
675
+ :param dayfirst:
676
+ Whether to interpret the first value in an ambiguous 3-integer date
677
+ (e.g. 01/05/09) as the day (``True``) or month (``False``). If
678
+ ``yearfirst`` is set to ``True``, this distinguishes between YDM
679
+ and YMD. If set to ``None``, this value is retrieved from the
680
+ current :class:`parserinfo` object (which itself defaults to
681
+ ``False``).
682
+
683
+ :param yearfirst:
684
+ Whether to interpret the first value in an ambiguous 3-integer date
685
+ (e.g. 01/05/09) as the year. If ``True``, the first number is taken
686
+ to be the year, otherwise the last number is taken to be the year.
687
+ If this is set to ``None``, the value is retrieved from the current
688
+ :class:`parserinfo` object (which itself defaults to ``False``).
689
+
690
+ :param fuzzy:
691
+ Whether to allow fuzzy parsing, allowing for string like "Today is
692
+ January 1, 2047 at 8:21:00AM".
693
+
694
+ :param fuzzy_with_tokens:
695
+ If ``True``, ``fuzzy`` is automatically set to True, and the parser
696
+ will return a tuple where the first element is the parsed
697
+ :class:`datetime.datetime` datetimestamp and the second element is
698
+ a tuple containing the portions of the string which were ignored:
699
+
700
+ .. doctest::
701
+
702
+ >>> from dateutil.parser import parse
703
+ >>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True)
704
+ (datetime.datetime(2047, 1, 1, 8, 21), (u'Today is ', u' ', u'at '))
705
+
706
+ """
707
+ if fuzzy_with_tokens:
708
+ fuzzy = True
709
+
710
+ info = self.info
711
+
712
+ if dayfirst is None:
713
+ dayfirst = info.dayfirst
714
+
715
+ if yearfirst is None:
716
+ yearfirst = info.yearfirst
717
+
718
+ res = self._result()
719
+ l = _timelex.split(timestr) # Splits the timestr into tokens
720
+
721
+ skipped_idxs = []
722
+
723
+ # year/month/day list
724
+ ymd = _ymd()
725
+
726
+ len_l = len(l)
727
+ i = 0
728
+ try:
729
+ while i < len_l:
730
+
731
+ # Check if it's a number
732
+ value_repr = l[i]
733
+ try:
734
+ value = float(value_repr)
735
+ except ValueError:
736
+ value = None
737
+
738
+ if value is not None:
739
+ # Numeric token
740
+ i = self._parse_numeric_token(l, i, info, ymd, res, fuzzy)
741
+
742
+ # Check weekday
743
+ elif info.weekday(l[i]) is not None:
744
+ value = info.weekday(l[i])
745
+ res.weekday = value
746
+
747
+ # Check month name
748
+ elif info.month(l[i]) is not None:
749
+ value = info.month(l[i])
750
+ ymd.append(value, 'M')
751
+
752
+ if i + 1 < len_l:
753
+ if l[i + 1] in ('-', '/'):
754
+ # Jan-01[-99]
755
+ sep = l[i + 1]
756
+ ymd.append(l[i + 2])
757
+
758
+ if i + 3 < len_l and l[i + 3] == sep:
759
+ # Jan-01-99
760
+ ymd.append(l[i + 4])
761
+ i += 2
762
+
763
+ i += 2
764
+
765
+ elif (i + 4 < len_l and l[i + 1] == l[i + 3] == ' ' and
766
+ info.pertain(l[i + 2])):
767
+ # Jan of 01
768
+ # In this case, 01 is clearly year
769
+ if l[i + 4].isdigit():
770
+ # Convert it here to become unambiguous
771
+ value = int(l[i + 4])
772
+ year = str(info.convertyear(value))
773
+ ymd.append(year, 'Y')
774
+ else:
775
+ # Wrong guess
776
+ pass
777
+ # TODO: not hit in tests
778
+ i += 4
779
+
780
+ # Check am/pm
781
+ elif info.ampm(l[i]) is not None:
782
+ value = info.ampm(l[i])
783
+ val_is_ampm = self._ampm_valid(res.hour, res.ampm, fuzzy)
784
+
785
+ if val_is_ampm:
786
+ res.hour = self._adjust_ampm(res.hour, value)
787
+ res.ampm = value
788
+
789
+ elif fuzzy:
790
+ skipped_idxs.append(i)
791
+
792
+ # Check for a timezone name
793
+ elif self._could_be_tzname(res.hour, res.tzname, res.tzoffset, l[i]):
794
+ res.tzname = l[i]
795
+ res.tzoffset = info.tzoffset(res.tzname)
796
+
797
+ # Check for something like GMT+3, or BRST+3. Notice
798
+ # that it doesn't mean "I am 3 hours after GMT", but
799
+ # "my time +3 is GMT". If found, we reverse the
800
+ # logic so that timezone parsing code will get it
801
+ # right.
802
+ if i + 1 < len_l and l[i + 1] in ('+', '-'):
803
+ l[i + 1] = ('+', '-')[l[i + 1] == '+']
804
+ res.tzoffset = None
805
+ if info.utczone(res.tzname):
806
+ # With something like GMT+3, the timezone
807
+ # is *not* GMT.
808
+ res.tzname = None
809
+
810
+ # Check for a numbered timezone
811
+ elif res.hour is not None and l[i] in ('+', '-'):
812
+ signal = (-1, 1)[l[i] == '+']
813
+ len_li = len(l[i + 1])
814
+
815
+ # TODO: check that l[i + 1] is integer?
816
+ if len_li == 4:
817
+ # -0300
818
+ hour_offset = int(l[i + 1][:2])
819
+ min_offset = int(l[i + 1][2:])
820
+ elif i + 2 < len_l and l[i + 2] == ':':
821
+ # -03:00
822
+ hour_offset = int(l[i + 1])
823
+ min_offset = int(l[i + 3]) # TODO: Check that l[i+3] is minute-like?
824
+ i += 2
825
+ elif len_li <= 2:
826
+ # -[0]3
827
+ hour_offset = int(l[i + 1][:2])
828
+ min_offset = 0
829
+ else:
830
+ raise ValueError(timestr)
831
+
832
+ res.tzoffset = signal * (hour_offset * 3600 + min_offset * 60)
833
+
834
+ # Look for a timezone name between parenthesis
835
+ if (i + 5 < len_l and
836
+ info.jump(l[i + 2]) and l[i + 3] == '(' and
837
+ l[i + 5] == ')' and
838
+ 3 <= len(l[i + 4]) and
839
+ self._could_be_tzname(res.hour, res.tzname,
840
+ None, l[i + 4])):
841
+ # -0300 (BRST)
842
+ res.tzname = l[i + 4]
843
+ i += 4
844
+
845
+ i += 1
846
+
847
+ # Check jumps
848
+ elif not (info.jump(l[i]) or fuzzy):
849
+ raise ValueError(timestr)
850
+
851
+ else:
852
+ skipped_idxs.append(i)
853
+ i += 1
854
+
855
+ # Process year/month/day
856
+ year, month, day = ymd.resolve_ymd(yearfirst, dayfirst)
857
+
858
+ res.century_specified = ymd.century_specified
859
+ res.year = year
860
+ res.month = month
861
+ res.day = day
862
+
863
+ except (IndexError, ValueError):
864
+ return None, None
865
+
866
+ if not info.validate(res):
867
+ return None, None
868
+
869
+ if fuzzy_with_tokens:
870
+ skipped_tokens = self._recombine_skipped(l, skipped_idxs)
871
+ return res, tuple(skipped_tokens)
872
+ else:
873
+ return res, None
874
+
875
+ def _parse_numeric_token(self, tokens, idx, info, ymd, res, fuzzy):
876
+ # Token is a number
877
+ value_repr = tokens[idx]
878
+ try:
879
+ value = self._to_decimal(value_repr)
880
+ except Exception as e:
881
+ six.raise_from(ValueError('Unknown numeric token'), e)
882
+
883
+ len_li = len(value_repr)
884
+
885
+ len_l = len(tokens)
886
+
887
+ if (len(ymd) == 3 and len_li in (2, 4) and
888
+ res.hour is None and
889
+ (idx + 1 >= len_l or
890
+ (tokens[idx + 1] != ':' and
891
+ info.hms(tokens[idx + 1]) is None))):
892
+ # 19990101T23[59]
893
+ s = tokens[idx]
894
+ res.hour = int(s[:2])
895
+
896
+ if len_li == 4:
897
+ res.minute = int(s[2:])
898
+
899
+ elif len_li == 6 or (len_li > 6 and tokens[idx].find('.') == 6):
900
+ # YYMMDD or HHMMSS[.ss]
901
+ s = tokens[idx]
902
+
903
+ if not ymd and '.' not in tokens[idx]:
904
+ ymd.append(s[:2])
905
+ ymd.append(s[2:4])
906
+ ymd.append(s[4:])
907
+ else:
908
+ # 19990101T235959[.59]
909
+
910
+ # TODO: Check if res attributes already set.
911
+ res.hour = int(s[:2])
912
+ res.minute = int(s[2:4])
913
+ res.second, res.microsecond = self._parsems(s[4:])
914
+
915
+ elif len_li in (8, 12, 14):
916
+ # YYYYMMDD
917
+ s = tokens[idx]
918
+ ymd.append(s[:4], 'Y')
919
+ ymd.append(s[4:6])
920
+ ymd.append(s[6:8])
921
+
922
+ if len_li > 8:
923
+ res.hour = int(s[8:10])
924
+ res.minute = int(s[10:12])
925
+
926
+ if len_li > 12:
927
+ res.second = int(s[12:])
928
+
929
+ elif self._find_hms_idx(idx, tokens, info, allow_jump=True) is not None:
930
+ # HH[ ]h or MM[ ]m or SS[.ss][ ]s
931
+ hms_idx = self._find_hms_idx(idx, tokens, info, allow_jump=True)
932
+ (idx, hms) = self._parse_hms(idx, tokens, info, hms_idx)
933
+ if hms is not None:
934
+ # TODO: checking that hour/minute/second are not
935
+ # already set?
936
+ self._assign_hms(res, value_repr, hms)
937
+
938
+ elif idx + 2 < len_l and tokens[idx + 1] == ':':
939
+ # HH:MM[:SS[.ss]]
940
+ res.hour = int(value)
941
+ value = self._to_decimal(tokens[idx + 2]) # TODO: try/except for this?
942
+ (res.minute, res.second) = self._parse_min_sec(value)
943
+
944
+ if idx + 4 < len_l and tokens[idx + 3] == ':':
945
+ res.second, res.microsecond = self._parsems(tokens[idx + 4])
946
+
947
+ idx += 2
948
+
949
+ idx += 2
950
+
951
+ elif idx + 1 < len_l and tokens[idx + 1] in ('-', '/', '.'):
952
+ sep = tokens[idx + 1]
953
+ ymd.append(value_repr)
954
+
955
+ if idx + 2 < len_l and not info.jump(tokens[idx + 2]):
956
+ if tokens[idx + 2].isdigit():
957
+ # 01-01[-01]
958
+ ymd.append(tokens[idx + 2])
959
+ else:
960
+ # 01-Jan[-01]
961
+ value = info.month(tokens[idx + 2])
962
+
963
+ if value is not None:
964
+ ymd.append(value, 'M')
965
+ else:
966
+ raise ValueError()
967
+
968
+ if idx + 3 < len_l and tokens[idx + 3] == sep:
969
+ # We have three members
970
+ value = info.month(tokens[idx + 4])
971
+
972
+ if value is not None:
973
+ ymd.append(value, 'M')
974
+ else:
975
+ ymd.append(tokens[idx + 4])
976
+ idx += 2
977
+
978
+ idx += 1
979
+ idx += 1
980
+
981
+ elif idx + 1 >= len_l or info.jump(tokens[idx + 1]):
982
+ if idx + 2 < len_l and info.ampm(tokens[idx + 2]) is not None:
983
+ # 12 am
984
+ hour = int(value)
985
+ res.hour = self._adjust_ampm(hour, info.ampm(tokens[idx + 2]))
986
+ idx += 1
987
+ else:
988
+ # Year, month or day
989
+ ymd.append(value)
990
+ idx += 1
991
+
992
+ elif info.ampm(tokens[idx + 1]) is not None and (0 <= value < 24):
993
+ # 12am
994
+ hour = int(value)
995
+ res.hour = self._adjust_ampm(hour, info.ampm(tokens[idx + 1]))
996
+ idx += 1
997
+
998
+ elif ymd.could_be_day(value):
999
+ ymd.append(value)
1000
+
1001
+ elif not fuzzy:
1002
+ raise ValueError()
1003
+
1004
+ return idx
1005
+
1006
+ def _find_hms_idx(self, idx, tokens, info, allow_jump):
1007
+ len_l = len(tokens)
1008
+
1009
+ if idx+1 < len_l and info.hms(tokens[idx+1]) is not None:
1010
+ # There is an "h", "m", or "s" label following this token. We take
1011
+ # assign the upcoming label to the current token.
1012
+ # e.g. the "12" in 12h"
1013
+ hms_idx = idx + 1
1014
+
1015
+ elif (allow_jump and idx+2 < len_l and tokens[idx+1] == ' ' and
1016
+ info.hms(tokens[idx+2]) is not None):
1017
+ # There is a space and then an "h", "m", or "s" label.
1018
+ # e.g. the "12" in "12 h"
1019
+ hms_idx = idx + 2
1020
+
1021
+ elif idx > 0 and info.hms(tokens[idx-1]) is not None:
1022
+ # There is a "h", "m", or "s" preceding this token. Since neither
1023
+ # of the previous cases was hit, there is no label following this
1024
+ # token, so we use the previous label.
1025
+ # e.g. the "04" in "12h04"
1026
+ hms_idx = idx-1
1027
+
1028
+ elif (1 < idx == len_l-1 and tokens[idx-1] == ' ' and
1029
+ info.hms(tokens[idx-2]) is not None):
1030
+ # If we are looking at the final token, we allow for a
1031
+ # backward-looking check to skip over a space.
1032
+ # TODO: Are we sure this is the right condition here?
1033
+ hms_idx = idx - 2
1034
+
1035
+ else:
1036
+ hms_idx = None
1037
+
1038
+ return hms_idx
1039
+
1040
+ def _assign_hms(self, res, value_repr, hms):
1041
+ # See GH issue #427, fixing float rounding
1042
+ value = self._to_decimal(value_repr)
1043
+
1044
+ if hms == 0:
1045
+ # Hour
1046
+ res.hour = int(value)
1047
+ if value % 1:
1048
+ res.minute = int(60*(value % 1))
1049
+
1050
+ elif hms == 1:
1051
+ (res.minute, res.second) = self._parse_min_sec(value)
1052
+
1053
+ elif hms == 2:
1054
+ (res.second, res.microsecond) = self._parsems(value_repr)
1055
+
1056
+ def _could_be_tzname(self, hour, tzname, tzoffset, token):
1057
+ return (hour is not None and
1058
+ tzname is None and
1059
+ tzoffset is None and
1060
+ len(token) <= 5 and
1061
+ (all(x in string.ascii_uppercase for x in token)
1062
+ or token in self.info.UTCZONE))
1063
+
1064
+ def _ampm_valid(self, hour, ampm, fuzzy):
1065
+ """
1066
+ For fuzzy parsing, 'a' or 'am' (both valid English words)
1067
+ may erroneously trigger the AM/PM flag. Deal with that
1068
+ here.
1069
+ """
1070
+ val_is_ampm = True
1071
+
1072
+ # If there's already an AM/PM flag, this one isn't one.
1073
+ if fuzzy and ampm is not None:
1074
+ val_is_ampm = False
1075
+
1076
+ # If AM/PM is found and hour is not, raise a ValueError
1077
+ if hour is None:
1078
+ if fuzzy:
1079
+ val_is_ampm = False
1080
+ else:
1081
+ raise ValueError('No hour specified with AM or PM flag.')
1082
+ elif not 0 <= hour <= 12:
1083
+ # If AM/PM is found, it's a 12 hour clock, so raise
1084
+ # an error for invalid range
1085
+ if fuzzy:
1086
+ val_is_ampm = False
1087
+ else:
1088
+ raise ValueError('Invalid hour specified for 12-hour clock.')
1089
+
1090
+ return val_is_ampm
1091
+
1092
+ def _adjust_ampm(self, hour, ampm):
1093
+ if hour < 12 and ampm == 1:
1094
+ hour += 12
1095
+ elif hour == 12 and ampm == 0:
1096
+ hour = 0
1097
+ return hour
1098
+
1099
+ def _parse_min_sec(self, value):
1100
+ # TODO: Every usage of this function sets res.second to the return
1101
+ # value. Are there any cases where second will be returned as None and
1102
+ # we *don't* want to set res.second = None?
1103
+ minute = int(value)
1104
+ second = None
1105
+
1106
+ sec_remainder = value % 1
1107
+ if sec_remainder:
1108
+ second = int(60 * sec_remainder)
1109
+ return (minute, second)
1110
+
1111
+ def _parse_hms(self, idx, tokens, info, hms_idx):
1112
+ # TODO: Is this going to admit a lot of false-positives for when we
1113
+ # just happen to have digits and "h", "m" or "s" characters in non-date
1114
+ # text? I guess hex hashes won't have that problem, but there's plenty
1115
+ # of random junk out there.
1116
+ if hms_idx is None:
1117
+ hms = None
1118
+ new_idx = idx
1119
+ elif hms_idx > idx:
1120
+ hms = info.hms(tokens[hms_idx])
1121
+ new_idx = hms_idx
1122
+ else:
1123
+ # Looking backwards, increment one.
1124
+ hms = info.hms(tokens[hms_idx]) + 1
1125
+ new_idx = idx
1126
+
1127
+ return (new_idx, hms)
1128
+
1129
+ # ------------------------------------------------------------------
1130
+ # Handling for individual tokens. These are kept as methods instead
1131
+ # of functions for the sake of customizability via subclassing.
1132
+
1133
+ def _parsems(self, value):
1134
+ """Parse a I[.F] seconds value into (seconds, microseconds)."""
1135
+ if "." not in value:
1136
+ return int(value), 0
1137
+ else:
1138
+ i, f = value.split(".")
1139
+ return int(i), int(f.ljust(6, "0")[:6])
1140
+
1141
+ def _to_decimal(self, val):
1142
+ try:
1143
+ decimal_value = Decimal(val)
1144
+ # See GH 662, edge case, infinite value should not be converted
1145
+ # via `_to_decimal`
1146
+ if not decimal_value.is_finite():
1147
+ raise ValueError("Converted decimal value is infinite or NaN")
1148
+ except Exception as e:
1149
+ msg = "Could not convert %s to decimal" % val
1150
+ six.raise_from(ValueError(msg), e)
1151
+ else:
1152
+ return decimal_value
1153
+
1154
+ # ------------------------------------------------------------------
1155
+ # Post-Parsing construction of datetime output. These are kept as
1156
+ # methods instead of functions for the sake of customizability via
1157
+ # subclassing.
1158
+
1159
+ def _build_tzinfo(self, tzinfos, tzname, tzoffset):
1160
+ if callable(tzinfos):
1161
+ tzdata = tzinfos(tzname, tzoffset)
1162
+ else:
1163
+ tzdata = tzinfos.get(tzname)
1164
+ # handle case where tzinfo is paased an options that returns None
1165
+ # eg tzinfos = {'BRST' : None}
1166
+ if isinstance(tzdata, datetime.tzinfo) or tzdata is None:
1167
+ tzinfo = tzdata
1168
+ elif isinstance(tzdata, text_type):
1169
+ tzinfo = tz.tzstr(tzdata)
1170
+ elif isinstance(tzdata, integer_types):
1171
+ tzinfo = tz.tzoffset(tzname, tzdata)
1172
+ else:
1173
+ raise TypeError("Offset must be tzinfo subclass, tz string, "
1174
+ "or int offset.")
1175
+ return tzinfo
1176
+
1177
+ def _build_tzaware(self, naive, res, tzinfos):
1178
+ if (callable(tzinfos) or (tzinfos and res.tzname in tzinfos)):
1179
+ tzinfo = self._build_tzinfo(tzinfos, res.tzname, res.tzoffset)
1180
+ aware = naive.replace(tzinfo=tzinfo)
1181
+ aware = self._assign_tzname(aware, res.tzname)
1182
+
1183
+ elif res.tzname and res.tzname in time.tzname:
1184
+ aware = naive.replace(tzinfo=tz.tzlocal())
1185
+
1186
+ # Handle ambiguous local datetime
1187
+ aware = self._assign_tzname(aware, res.tzname)
1188
+
1189
+ # This is mostly relevant for winter GMT zones parsed in the UK
1190
+ if (aware.tzname() != res.tzname and
1191
+ res.tzname in self.info.UTCZONE):
1192
+ aware = aware.replace(tzinfo=tz.UTC)
1193
+
1194
+ elif res.tzoffset == 0:
1195
+ aware = naive.replace(tzinfo=tz.UTC)
1196
+
1197
+ elif res.tzoffset:
1198
+ aware = naive.replace(tzinfo=tz.tzoffset(res.tzname, res.tzoffset))
1199
+
1200
+ elif not res.tzname and not res.tzoffset:
1201
+ # i.e. no timezone information was found.
1202
+ aware = naive
1203
+
1204
+ elif res.tzname:
1205
+ # tz-like string was parsed but we don't know what to do
1206
+ # with it
1207
+ warnings.warn("tzname {tzname} identified but not understood. "
1208
+ "Pass `tzinfos` argument in order to correctly "
1209
+ "return a timezone-aware datetime. In a future "
1210
+ "version, this will raise an "
1211
+ "exception.".format(tzname=res.tzname),
1212
+ category=UnknownTimezoneWarning)
1213
+ aware = naive
1214
+
1215
+ return aware
1216
+
1217
+ def _build_naive(self, res, default):
1218
+ repl = {}
1219
+ for attr in ("year", "month", "day", "hour",
1220
+ "minute", "second", "microsecond"):
1221
+ value = getattr(res, attr)
1222
+ if value is not None:
1223
+ repl[attr] = value
1224
+
1225
+ if 'day' not in repl:
1226
+ # If the default day exceeds the last day of the month, fall back
1227
+ # to the end of the month.
1228
+ cyear = default.year if res.year is None else res.year
1229
+ cmonth = default.month if res.month is None else res.month
1230
+ cday = default.day if res.day is None else res.day
1231
+
1232
+ if cday > monthrange(cyear, cmonth)[1]:
1233
+ repl['day'] = monthrange(cyear, cmonth)[1]
1234
+
1235
+ naive = default.replace(**repl)
1236
+
1237
+ if res.weekday is not None and not res.day:
1238
+ naive = naive + relativedelta.relativedelta(weekday=res.weekday)
1239
+
1240
+ return naive
1241
+
1242
+ def _assign_tzname(self, dt, tzname):
1243
+ if dt.tzname() != tzname:
1244
+ new_dt = tz.enfold(dt, fold=1)
1245
+ if new_dt.tzname() == tzname:
1246
+ return new_dt
1247
+
1248
+ return dt
1249
+
1250
+ def _recombine_skipped(self, tokens, skipped_idxs):
1251
+ """
1252
+ >>> tokens = ["foo", " ", "bar", " ", "19June2000", "baz"]
1253
+ >>> skipped_idxs = [0, 1, 2, 5]
1254
+ >>> _recombine_skipped(tokens, skipped_idxs)
1255
+ ["foo bar", "baz"]
1256
+ """
1257
+ skipped_tokens = []
1258
+ for i, idx in enumerate(sorted(skipped_idxs)):
1259
+ if i > 0 and idx - 1 == skipped_idxs[i - 1]:
1260
+ skipped_tokens[-1] = skipped_tokens[-1] + tokens[idx]
1261
+ else:
1262
+ skipped_tokens.append(tokens[idx])
1263
+
1264
+ return skipped_tokens
1265
+
1266
+
1267
+ DEFAULTPARSER = parser()
1268
+
1269
+
1270
+ def parse(timestr, parserinfo=None, **kwargs):
1271
+ """
1272
+
1273
+ Parse a string in one of the supported formats, using the
1274
+ ``parserinfo`` parameters.
1275
+
1276
+ :param timestr:
1277
+ A string containing a date/time stamp.
1278
+
1279
+ :param parserinfo:
1280
+ A :class:`parserinfo` object containing parameters for the parser.
1281
+ If ``None``, the default arguments to the :class:`parserinfo`
1282
+ constructor are used.
1283
+
1284
+ The ``**kwargs`` parameter takes the following keyword arguments:
1285
+
1286
+ :param default:
1287
+ The default datetime object, if this is a datetime object and not
1288
+ ``None``, elements specified in ``timestr`` replace elements in the
1289
+ default object.
1290
+
1291
+ :param ignoretz:
1292
+ If set ``True``, time zones in parsed strings are ignored and a naive
1293
+ :class:`datetime` object is returned.
1294
+
1295
+ :param tzinfos:
1296
+ Additional time zone names / aliases which may be present in the
1297
+ string. This argument maps time zone names (and optionally offsets
1298
+ from those time zones) to time zones. This parameter can be a
1299
+ dictionary with timezone aliases mapping time zone names to time
1300
+ zones or a function taking two parameters (``tzname`` and
1301
+ ``tzoffset``) and returning a time zone.
1302
+
1303
+ The timezones to which the names are mapped can be an integer
1304
+ offset from UTC in seconds or a :class:`tzinfo` object.
1305
+
1306
+ .. doctest::
1307
+ :options: +NORMALIZE_WHITESPACE
1308
+
1309
+ >>> from dateutil.parser import parse
1310
+ >>> from dateutil.tz import gettz
1311
+ >>> tzinfos = {"BRST": -7200, "CST": gettz("America/Chicago")}
1312
+ >>> parse("2012-01-19 17:21:00 BRST", tzinfos=tzinfos)
1313
+ datetime.datetime(2012, 1, 19, 17, 21, tzinfo=tzoffset(u'BRST', -7200))
1314
+ >>> parse("2012-01-19 17:21:00 CST", tzinfos=tzinfos)
1315
+ datetime.datetime(2012, 1, 19, 17, 21,
1316
+ tzinfo=tzfile('/usr/share/zoneinfo/America/Chicago'))
1317
+
1318
+ This parameter is ignored if ``ignoretz`` is set.
1319
+
1320
+ :param dayfirst:
1321
+ Whether to interpret the first value in an ambiguous 3-integer date
1322
+ (e.g. 01/05/09) as the day (``True``) or month (``False``). If
1323
+ ``yearfirst`` is set to ``True``, this distinguishes between YDM and
1324
+ YMD. If set to ``None``, this value is retrieved from the current
1325
+ :class:`parserinfo` object (which itself defaults to ``False``).
1326
+
1327
+ :param yearfirst:
1328
+ Whether to interpret the first value in an ambiguous 3-integer date
1329
+ (e.g. 01/05/09) as the year. If ``True``, the first number is taken to
1330
+ be the year, otherwise the last number is taken to be the year. If
1331
+ this is set to ``None``, the value is retrieved from the current
1332
+ :class:`parserinfo` object (which itself defaults to ``False``).
1333
+
1334
+ :param fuzzy:
1335
+ Whether to allow fuzzy parsing, allowing for string like "Today is
1336
+ January 1, 2047 at 8:21:00AM".
1337
+
1338
+ :param fuzzy_with_tokens:
1339
+ If ``True``, ``fuzzy`` is automatically set to True, and the parser
1340
+ will return a tuple where the first element is the parsed
1341
+ :class:`datetime.datetime` datetimestamp and the second element is
1342
+ a tuple containing the portions of the string which were ignored:
1343
+
1344
+ .. doctest::
1345
+
1346
+ >>> from dateutil.parser import parse
1347
+ >>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True)
1348
+ (datetime.datetime(2047, 1, 1, 8, 21), (u'Today is ', u' ', u'at '))
1349
+
1350
+ :return:
1351
+ Returns a :class:`datetime.datetime` object or, if the
1352
+ ``fuzzy_with_tokens`` option is ``True``, returns a tuple, the
1353
+ first element being a :class:`datetime.datetime` object, the second
1354
+ a tuple containing the fuzzy tokens.
1355
+
1356
+ :raises ParserError:
1357
+ Raised for invalid or unknown string formats, if the provided
1358
+ :class:`tzinfo` is not in a valid format, or if an invalid date would
1359
+ be created.
1360
+
1361
+ :raises OverflowError:
1362
+ Raised if the parsed date exceeds the largest valid C integer on
1363
+ your system.
1364
+ """
1365
+ if parserinfo:
1366
+ return parser(parserinfo).parse(timestr, **kwargs)
1367
+ else:
1368
+ return DEFAULTPARSER.parse(timestr, **kwargs)
1369
+
1370
+
1371
+ class _tzparser(object):
1372
+
1373
+ class _result(_resultbase):
1374
+
1375
+ __slots__ = ["stdabbr", "stdoffset", "dstabbr", "dstoffset",
1376
+ "start", "end"]
1377
+
1378
+ class _attr(_resultbase):
1379
+ __slots__ = ["month", "week", "weekday",
1380
+ "yday", "jyday", "day", "time"]
1381
+
1382
+ def __repr__(self):
1383
+ return self._repr("")
1384
+
1385
+ def __init__(self):
1386
+ _resultbase.__init__(self)
1387
+ self.start = self._attr()
1388
+ self.end = self._attr()
1389
+
1390
+ def parse(self, tzstr):
1391
+ res = self._result()
1392
+ l = [x for x in re.split(r'([,:.]|[a-zA-Z]+|[0-9]+)',tzstr) if x]
1393
+ used_idxs = list()
1394
+ try:
1395
+
1396
+ len_l = len(l)
1397
+
1398
+ i = 0
1399
+ while i < len_l:
1400
+ # BRST+3[BRDT[+2]]
1401
+ j = i
1402
+ while j < len_l and not [x for x in l[j]
1403
+ if x in "0123456789:,-+"]:
1404
+ j += 1
1405
+ if j != i:
1406
+ if not res.stdabbr:
1407
+ offattr = "stdoffset"
1408
+ res.stdabbr = "".join(l[i:j])
1409
+ else:
1410
+ offattr = "dstoffset"
1411
+ res.dstabbr = "".join(l[i:j])
1412
+
1413
+ for ii in range(j):
1414
+ used_idxs.append(ii)
1415
+ i = j
1416
+ if (i < len_l and (l[i] in ('+', '-') or l[i][0] in
1417
+ "0123456789")):
1418
+ if l[i] in ('+', '-'):
1419
+ # Yes, that's right. See the TZ variable
1420
+ # documentation.
1421
+ signal = (1, -1)[l[i] == '+']
1422
+ used_idxs.append(i)
1423
+ i += 1
1424
+ else:
1425
+ signal = -1
1426
+ len_li = len(l[i])
1427
+ if len_li == 4:
1428
+ # -0300
1429
+ setattr(res, offattr, (int(l[i][:2]) * 3600 +
1430
+ int(l[i][2:]) * 60) * signal)
1431
+ elif i + 1 < len_l and l[i + 1] == ':':
1432
+ # -03:00
1433
+ setattr(res, offattr,
1434
+ (int(l[i]) * 3600 +
1435
+ int(l[i + 2]) * 60) * signal)
1436
+ used_idxs.append(i)
1437
+ i += 2
1438
+ elif len_li <= 2:
1439
+ # -[0]3
1440
+ setattr(res, offattr,
1441
+ int(l[i][:2]) * 3600 * signal)
1442
+ else:
1443
+ return None
1444
+ used_idxs.append(i)
1445
+ i += 1
1446
+ if res.dstabbr:
1447
+ break
1448
+ else:
1449
+ break
1450
+
1451
+
1452
+ if i < len_l:
1453
+ for j in range(i, len_l):
1454
+ if l[j] == ';':
1455
+ l[j] = ','
1456
+
1457
+ assert l[i] == ','
1458
+
1459
+ i += 1
1460
+
1461
+ if i >= len_l:
1462
+ pass
1463
+ elif (8 <= l.count(',') <= 9 and
1464
+ not [y for x in l[i:] if x != ','
1465
+ for y in x if y not in "0123456789+-"]):
1466
+ # GMT0BST,3,0,30,3600,10,0,26,7200[,3600]
1467
+ for x in (res.start, res.end):
1468
+ x.month = int(l[i])
1469
+ used_idxs.append(i)
1470
+ i += 2
1471
+ if l[i] == '-':
1472
+ value = int(l[i + 1]) * -1
1473
+ used_idxs.append(i)
1474
+ i += 1
1475
+ else:
1476
+ value = int(l[i])
1477
+ used_idxs.append(i)
1478
+ i += 2
1479
+ if value:
1480
+ x.week = value
1481
+ x.weekday = (int(l[i]) - 1) % 7
1482
+ else:
1483
+ x.day = int(l[i])
1484
+ used_idxs.append(i)
1485
+ i += 2
1486
+ x.time = int(l[i])
1487
+ used_idxs.append(i)
1488
+ i += 2
1489
+ if i < len_l:
1490
+ if l[i] in ('-', '+'):
1491
+ signal = (-1, 1)[l[i] == "+"]
1492
+ used_idxs.append(i)
1493
+ i += 1
1494
+ else:
1495
+ signal = 1
1496
+ used_idxs.append(i)
1497
+ res.dstoffset = (res.stdoffset + int(l[i]) * signal)
1498
+
1499
+ # This was a made-up format that is not in normal use
1500
+ warn(('Parsed time zone "%s"' % tzstr) +
1501
+ 'is in a non-standard dateutil-specific format, which ' +
1502
+ 'is now deprecated; support for parsing this format ' +
1503
+ 'will be removed in future versions. It is recommended ' +
1504
+ 'that you switch to a standard format like the GNU ' +
1505
+ 'TZ variable format.', tz.DeprecatedTzFormatWarning)
1506
+ elif (l.count(',') == 2 and l[i:].count('/') <= 2 and
1507
+ not [y for x in l[i:] if x not in (',', '/', 'J', 'M',
1508
+ '.', '-', ':')
1509
+ for y in x if y not in "0123456789"]):
1510
+ for x in (res.start, res.end):
1511
+ if l[i] == 'J':
1512
+ # non-leap year day (1 based)
1513
+ used_idxs.append(i)
1514
+ i += 1
1515
+ x.jyday = int(l[i])
1516
+ elif l[i] == 'M':
1517
+ # month[-.]week[-.]weekday
1518
+ used_idxs.append(i)
1519
+ i += 1
1520
+ x.month = int(l[i])
1521
+ used_idxs.append(i)
1522
+ i += 1
1523
+ assert l[i] in ('-', '.')
1524
+ used_idxs.append(i)
1525
+ i += 1
1526
+ x.week = int(l[i])
1527
+ if x.week == 5:
1528
+ x.week = -1
1529
+ used_idxs.append(i)
1530
+ i += 1
1531
+ assert l[i] in ('-', '.')
1532
+ used_idxs.append(i)
1533
+ i += 1
1534
+ x.weekday = (int(l[i]) - 1) % 7
1535
+ else:
1536
+ # year day (zero based)
1537
+ x.yday = int(l[i]) + 1
1538
+
1539
+ used_idxs.append(i)
1540
+ i += 1
1541
+
1542
+ if i < len_l and l[i] == '/':
1543
+ used_idxs.append(i)
1544
+ i += 1
1545
+ # start time
1546
+ len_li = len(l[i])
1547
+ if len_li == 4:
1548
+ # -0300
1549
+ x.time = (int(l[i][:2]) * 3600 +
1550
+ int(l[i][2:]) * 60)
1551
+ elif i + 1 < len_l and l[i + 1] == ':':
1552
+ # -03:00
1553
+ x.time = int(l[i]) * 3600 + int(l[i + 2]) * 60
1554
+ used_idxs.append(i)
1555
+ i += 2
1556
+ if i + 1 < len_l and l[i + 1] == ':':
1557
+ used_idxs.append(i)
1558
+ i += 2
1559
+ x.time += int(l[i])
1560
+ elif len_li <= 2:
1561
+ # -[0]3
1562
+ x.time = (int(l[i][:2]) * 3600)
1563
+ else:
1564
+ return None
1565
+ used_idxs.append(i)
1566
+ i += 1
1567
+
1568
+ assert i == len_l or l[i] == ','
1569
+
1570
+ i += 1
1571
+
1572
+ assert i >= len_l
1573
+
1574
+ except (IndexError, ValueError, AssertionError):
1575
+ return None
1576
+
1577
+ unused_idxs = set(range(len_l)).difference(used_idxs)
1578
+ res.any_unused_tokens = not {l[n] for n in unused_idxs}.issubset({",",":"})
1579
+ return res
1580
+
1581
+
1582
+ DEFAULTTZPARSER = _tzparser()
1583
+
1584
+
1585
+ def _parsetz(tzstr):
1586
+ return DEFAULTTZPARSER.parse(tzstr)
1587
+
1588
+
1589
+ class ParserError(ValueError):
1590
+ """Exception subclass used for any failure to parse a datetime string.
1591
+
1592
+ This is a subclass of :py:exc:`ValueError`, and should be raised any time
1593
+ earlier versions of ``dateutil`` would have raised ``ValueError``.
1594
+
1595
+ .. versionadded:: 2.8.1
1596
+ """
1597
+ def __str__(self):
1598
+ try:
1599
+ return self.args[0] % self.args[1:]
1600
+ except (TypeError, IndexError):
1601
+ return super(ParserError, self).__str__()
1602
+
1603
+ def __repr__(self):
1604
+ args = ", ".join("'%s'" % arg for arg in self.args)
1605
+ return "%s(%s)" % (self.__class__.__name__, args)
1606
+
1607
+
1608
+ class UnknownTimezoneWarning(RuntimeWarning):
1609
+ """Raised when the parser finds a timezone it cannot parse into a tzinfo.
1610
+
1611
+ .. versionadded:: 2.7.0
1612
+ """
1613
+ # vim:ts=4:sw=4:et
dateutil/parser/isoparser.py ADDED
@@ -0,0 +1,416 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ This module offers a parser for ISO-8601 strings
4
+
5
+ It is intended to support all valid date, time and datetime formats per the
6
+ ISO-8601 specification.
7
+
8
+ ..versionadded:: 2.7.0
9
+ """
10
+ from datetime import datetime, timedelta, time, date
11
+ import calendar
12
+ from dateutil import tz
13
+
14
+ from functools import wraps
15
+
16
+ import re
17
+ import six
18
+
19
+ __all__ = ["isoparse", "isoparser"]
20
+
21
+
22
+ def _takes_ascii(f):
23
+ @wraps(f)
24
+ def func(self, str_in, *args, **kwargs):
25
+ # If it's a stream, read the whole thing
26
+ str_in = getattr(str_in, 'read', lambda: str_in)()
27
+
28
+ # If it's unicode, turn it into bytes, since ISO-8601 only covers ASCII
29
+ if isinstance(str_in, six.text_type):
30
+ # ASCII is the same in UTF-8
31
+ try:
32
+ str_in = str_in.encode('ascii')
33
+ except UnicodeEncodeError as e:
34
+ msg = 'ISO-8601 strings should contain only ASCII characters'
35
+ six.raise_from(ValueError(msg), e)
36
+
37
+ return f(self, str_in, *args, **kwargs)
38
+
39
+ return func
40
+
41
+
42
+ class isoparser(object):
43
+ def __init__(self, sep=None):
44
+ """
45
+ :param sep:
46
+ A single character that separates date and time portions. If
47
+ ``None``, the parser will accept any single character.
48
+ For strict ISO-8601 adherence, pass ``'T'``.
49
+ """
50
+ if sep is not None:
51
+ if (len(sep) != 1 or ord(sep) >= 128 or sep in '0123456789'):
52
+ raise ValueError('Separator must be a single, non-numeric ' +
53
+ 'ASCII character')
54
+
55
+ sep = sep.encode('ascii')
56
+
57
+ self._sep = sep
58
+
59
+ @_takes_ascii
60
+ def isoparse(self, dt_str):
61
+ """
62
+ Parse an ISO-8601 datetime string into a :class:`datetime.datetime`.
63
+
64
+ An ISO-8601 datetime string consists of a date portion, followed
65
+ optionally by a time portion - the date and time portions are separated
66
+ by a single character separator, which is ``T`` in the official
67
+ standard. Incomplete date formats (such as ``YYYY-MM``) may *not* be
68
+ combined with a time portion.
69
+
70
+ Supported date formats are:
71
+
72
+ Common:
73
+
74
+ - ``YYYY``
75
+ - ``YYYY-MM``
76
+ - ``YYYY-MM-DD`` or ``YYYYMMDD``
77
+
78
+ Uncommon:
79
+
80
+ - ``YYYY-Www`` or ``YYYYWww`` - ISO week (day defaults to 0)
81
+ - ``YYYY-Www-D`` or ``YYYYWwwD`` - ISO week and day
82
+
83
+ The ISO week and day numbering follows the same logic as
84
+ :func:`datetime.date.isocalendar`.
85
+
86
+ Supported time formats are:
87
+
88
+ - ``hh``
89
+ - ``hh:mm`` or ``hhmm``
90
+ - ``hh:mm:ss`` or ``hhmmss``
91
+ - ``hh:mm:ss.ssssss`` (Up to 6 sub-second digits)
92
+
93
+ Midnight is a special case for `hh`, as the standard supports both
94
+ 00:00 and 24:00 as a representation. The decimal separator can be
95
+ either a dot or a comma.
96
+
97
+
98
+ .. caution::
99
+
100
+ Support for fractional components other than seconds is part of the
101
+ ISO-8601 standard, but is not currently implemented in this parser.
102
+
103
+ Supported time zone offset formats are:
104
+
105
+ - `Z` (UTC)
106
+ - `±HH:MM`
107
+ - `±HHMM`
108
+ - `±HH`
109
+
110
+ Offsets will be represented as :class:`dateutil.tz.tzoffset` objects,
111
+ with the exception of UTC, which will be represented as
112
+ :class:`dateutil.tz.tzutc`. Time zone offsets equivalent to UTC (such
113
+ as `+00:00`) will also be represented as :class:`dateutil.tz.tzutc`.
114
+
115
+ :param dt_str:
116
+ A string or stream containing only an ISO-8601 datetime string
117
+
118
+ :return:
119
+ Returns a :class:`datetime.datetime` representing the string.
120
+ Unspecified components default to their lowest value.
121
+
122
+ .. warning::
123
+
124
+ As of version 2.7.0, the strictness of the parser should not be
125
+ considered a stable part of the contract. Any valid ISO-8601 string
126
+ that parses correctly with the default settings will continue to
127
+ parse correctly in future versions, but invalid strings that
128
+ currently fail (e.g. ``2017-01-01T00:00+00:00:00``) are not
129
+ guaranteed to continue failing in future versions if they encode
130
+ a valid date.
131
+
132
+ .. versionadded:: 2.7.0
133
+ """
134
+ components, pos = self._parse_isodate(dt_str)
135
+
136
+ if len(dt_str) > pos:
137
+ if self._sep is None or dt_str[pos:pos + 1] == self._sep:
138
+ components += self._parse_isotime(dt_str[pos + 1:])
139
+ else:
140
+ raise ValueError('String contains unknown ISO components')
141
+
142
+ if len(components) > 3 and components[3] == 24:
143
+ components[3] = 0
144
+ return datetime(*components) + timedelta(days=1)
145
+
146
+ return datetime(*components)
147
+
148
+ @_takes_ascii
149
+ def parse_isodate(self, datestr):
150
+ """
151
+ Parse the date portion of an ISO string.
152
+
153
+ :param datestr:
154
+ The string portion of an ISO string, without a separator
155
+
156
+ :return:
157
+ Returns a :class:`datetime.date` object
158
+ """
159
+ components, pos = self._parse_isodate(datestr)
160
+ if pos < len(datestr):
161
+ raise ValueError('String contains unknown ISO ' +
162
+ 'components: {!r}'.format(datestr.decode('ascii')))
163
+ return date(*components)
164
+
165
+ @_takes_ascii
166
+ def parse_isotime(self, timestr):
167
+ """
168
+ Parse the time portion of an ISO string.
169
+
170
+ :param timestr:
171
+ The time portion of an ISO string, without a separator
172
+
173
+ :return:
174
+ Returns a :class:`datetime.time` object
175
+ """
176
+ components = self._parse_isotime(timestr)
177
+ if components[0] == 24:
178
+ components[0] = 0
179
+ return time(*components)
180
+
181
+ @_takes_ascii
182
+ def parse_tzstr(self, tzstr, zero_as_utc=True):
183
+ """
184
+ Parse a valid ISO time zone string.
185
+
186
+ See :func:`isoparser.isoparse` for details on supported formats.
187
+
188
+ :param tzstr:
189
+ A string representing an ISO time zone offset
190
+
191
+ :param zero_as_utc:
192
+ Whether to return :class:`dateutil.tz.tzutc` for zero-offset zones
193
+
194
+ :return:
195
+ Returns :class:`dateutil.tz.tzoffset` for offsets and
196
+ :class:`dateutil.tz.tzutc` for ``Z`` and (if ``zero_as_utc`` is
197
+ specified) offsets equivalent to UTC.
198
+ """
199
+ return self._parse_tzstr(tzstr, zero_as_utc=zero_as_utc)
200
+
201
+ # Constants
202
+ _DATE_SEP = b'-'
203
+ _TIME_SEP = b':'
204
+ _FRACTION_REGEX = re.compile(b'[\\.,]([0-9]+)')
205
+
206
+ def _parse_isodate(self, dt_str):
207
+ try:
208
+ return self._parse_isodate_common(dt_str)
209
+ except ValueError:
210
+ return self._parse_isodate_uncommon(dt_str)
211
+
212
+ def _parse_isodate_common(self, dt_str):
213
+ len_str = len(dt_str)
214
+ components = [1, 1, 1]
215
+
216
+ if len_str < 4:
217
+ raise ValueError('ISO string too short')
218
+
219
+ # Year
220
+ components[0] = int(dt_str[0:4])
221
+ pos = 4
222
+ if pos >= len_str:
223
+ return components, pos
224
+
225
+ has_sep = dt_str[pos:pos + 1] == self._DATE_SEP
226
+ if has_sep:
227
+ pos += 1
228
+
229
+ # Month
230
+ if len_str - pos < 2:
231
+ raise ValueError('Invalid common month')
232
+
233
+ components[1] = int(dt_str[pos:pos + 2])
234
+ pos += 2
235
+
236
+ if pos >= len_str:
237
+ if has_sep:
238
+ return components, pos
239
+ else:
240
+ raise ValueError('Invalid ISO format')
241
+
242
+ if has_sep:
243
+ if dt_str[pos:pos + 1] != self._DATE_SEP:
244
+ raise ValueError('Invalid separator in ISO string')
245
+ pos += 1
246
+
247
+ # Day
248
+ if len_str - pos < 2:
249
+ raise ValueError('Invalid common day')
250
+ components[2] = int(dt_str[pos:pos + 2])
251
+ return components, pos + 2
252
+
253
+ def _parse_isodate_uncommon(self, dt_str):
254
+ if len(dt_str) < 4:
255
+ raise ValueError('ISO string too short')
256
+
257
+ # All ISO formats start with the year
258
+ year = int(dt_str[0:4])
259
+
260
+ has_sep = dt_str[4:5] == self._DATE_SEP
261
+
262
+ pos = 4 + has_sep # Skip '-' if it's there
263
+ if dt_str[pos:pos + 1] == b'W':
264
+ # YYYY-?Www-?D?
265
+ pos += 1
266
+ weekno = int(dt_str[pos:pos + 2])
267
+ pos += 2
268
+
269
+ dayno = 1
270
+ if len(dt_str) > pos:
271
+ if (dt_str[pos:pos + 1] == self._DATE_SEP) != has_sep:
272
+ raise ValueError('Inconsistent use of dash separator')
273
+
274
+ pos += has_sep
275
+
276
+ dayno = int(dt_str[pos:pos + 1])
277
+ pos += 1
278
+
279
+ base_date = self._calculate_weekdate(year, weekno, dayno)
280
+ else:
281
+ # YYYYDDD or YYYY-DDD
282
+ if len(dt_str) - pos < 3:
283
+ raise ValueError('Invalid ordinal day')
284
+
285
+ ordinal_day = int(dt_str[pos:pos + 3])
286
+ pos += 3
287
+
288
+ if ordinal_day < 1 or ordinal_day > (365 + calendar.isleap(year)):
289
+ raise ValueError('Invalid ordinal day' +
290
+ ' {} for year {}'.format(ordinal_day, year))
291
+
292
+ base_date = date(year, 1, 1) + timedelta(days=ordinal_day - 1)
293
+
294
+ components = [base_date.year, base_date.month, base_date.day]
295
+ return components, pos
296
+
297
+ def _calculate_weekdate(self, year, week, day):
298
+ """
299
+ Calculate the day of corresponding to the ISO year-week-day calendar.
300
+
301
+ This function is effectively the inverse of
302
+ :func:`datetime.date.isocalendar`.
303
+
304
+ :param year:
305
+ The year in the ISO calendar
306
+
307
+ :param week:
308
+ The week in the ISO calendar - range is [1, 53]
309
+
310
+ :param day:
311
+ The day in the ISO calendar - range is [1 (MON), 7 (SUN)]
312
+
313
+ :return:
314
+ Returns a :class:`datetime.date`
315
+ """
316
+ if not 0 < week < 54:
317
+ raise ValueError('Invalid week: {}'.format(week))
318
+
319
+ if not 0 < day < 8: # Range is 1-7
320
+ raise ValueError('Invalid weekday: {}'.format(day))
321
+
322
+ # Get week 1 for the specific year:
323
+ jan_4 = date(year, 1, 4) # Week 1 always has January 4th in it
324
+ week_1 = jan_4 - timedelta(days=jan_4.isocalendar()[2] - 1)
325
+
326
+ # Now add the specific number of weeks and days to get what we want
327
+ week_offset = (week - 1) * 7 + (day - 1)
328
+ return week_1 + timedelta(days=week_offset)
329
+
330
+ def _parse_isotime(self, timestr):
331
+ len_str = len(timestr)
332
+ components = [0, 0, 0, 0, None]
333
+ pos = 0
334
+ comp = -1
335
+
336
+ if len_str < 2:
337
+ raise ValueError('ISO time too short')
338
+
339
+ has_sep = False
340
+
341
+ while pos < len_str and comp < 5:
342
+ comp += 1
343
+
344
+ if timestr[pos:pos + 1] in b'-+Zz':
345
+ # Detect time zone boundary
346
+ components[-1] = self._parse_tzstr(timestr[pos:])
347
+ pos = len_str
348
+ break
349
+
350
+ if comp == 1 and timestr[pos:pos+1] == self._TIME_SEP:
351
+ has_sep = True
352
+ pos += 1
353
+ elif comp == 2 and has_sep:
354
+ if timestr[pos:pos+1] != self._TIME_SEP:
355
+ raise ValueError('Inconsistent use of colon separator')
356
+ pos += 1
357
+
358
+ if comp < 3:
359
+ # Hour, minute, second
360
+ components[comp] = int(timestr[pos:pos + 2])
361
+ pos += 2
362
+
363
+ if comp == 3:
364
+ # Fraction of a second
365
+ frac = self._FRACTION_REGEX.match(timestr[pos:])
366
+ if not frac:
367
+ continue
368
+
369
+ us_str = frac.group(1)[:6] # Truncate to microseconds
370
+ components[comp] = int(us_str) * 10**(6 - len(us_str))
371
+ pos += len(frac.group())
372
+
373
+ if pos < len_str:
374
+ raise ValueError('Unused components in ISO string')
375
+
376
+ if components[0] == 24:
377
+ # Standard supports 00:00 and 24:00 as representations of midnight
378
+ if any(component != 0 for component in components[1:4]):
379
+ raise ValueError('Hour may only be 24 at 24:00:00.000')
380
+
381
+ return components
382
+
383
+ def _parse_tzstr(self, tzstr, zero_as_utc=True):
384
+ if tzstr == b'Z' or tzstr == b'z':
385
+ return tz.UTC
386
+
387
+ if len(tzstr) not in {3, 5, 6}:
388
+ raise ValueError('Time zone offset must be 1, 3, 5 or 6 characters')
389
+
390
+ if tzstr[0:1] == b'-':
391
+ mult = -1
392
+ elif tzstr[0:1] == b'+':
393
+ mult = 1
394
+ else:
395
+ raise ValueError('Time zone offset requires sign')
396
+
397
+ hours = int(tzstr[1:3])
398
+ if len(tzstr) == 3:
399
+ minutes = 0
400
+ else:
401
+ minutes = int(tzstr[(4 if tzstr[3:4] == self._TIME_SEP else 3):])
402
+
403
+ if zero_as_utc and hours == 0 and minutes == 0:
404
+ return tz.UTC
405
+ else:
406
+ if minutes > 59:
407
+ raise ValueError('Invalid minutes in time zone offset')
408
+
409
+ if hours > 23:
410
+ raise ValueError('Invalid hours in time zone offset')
411
+
412
+ return tz.tzoffset(None, mult * (hours * 60 + minutes) * 60)
413
+
414
+
415
+ DEFAULT_ISOPARSER = isoparser()
416
+ isoparse = DEFAULT_ISOPARSER.isoparse
dateutil/tz/__init__.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ from .tz import *
3
+ from .tz import __doc__
4
+
5
+ __all__ = ["tzutc", "tzoffset", "tzlocal", "tzfile", "tzrange",
6
+ "tzstr", "tzical", "tzwin", "tzwinlocal", "gettz",
7
+ "enfold", "datetime_ambiguous", "datetime_exists",
8
+ "resolve_imaginary", "UTC", "DeprecatedTzFormatWarning"]
9
+
10
+
11
+ class DeprecatedTzFormatWarning(Warning):
12
+ """Warning raised when time zones are parsed from deprecated formats."""
dateutil/tz/_common.py ADDED
@@ -0,0 +1,419 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from six import PY2
2
+
3
+ from functools import wraps
4
+
5
+ from datetime import datetime, timedelta, tzinfo
6
+
7
+
8
+ ZERO = timedelta(0)
9
+
10
+ __all__ = ['tzname_in_python2', 'enfold']
11
+
12
+
13
+ def tzname_in_python2(namefunc):
14
+ """Change unicode output into bytestrings in Python 2
15
+
16
+ tzname() API changed in Python 3. It used to return bytes, but was changed
17
+ to unicode strings
18
+ """
19
+ if PY2:
20
+ @wraps(namefunc)
21
+ def adjust_encoding(*args, **kwargs):
22
+ name = namefunc(*args, **kwargs)
23
+ if name is not None:
24
+ name = name.encode()
25
+
26
+ return name
27
+
28
+ return adjust_encoding
29
+ else:
30
+ return namefunc
31
+
32
+
33
+ # The following is adapted from Alexander Belopolsky's tz library
34
+ # https://github.com/abalkin/tz
35
+ if hasattr(datetime, 'fold'):
36
+ # This is the pre-python 3.6 fold situation
37
+ def enfold(dt, fold=1):
38
+ """
39
+ Provides a unified interface for assigning the ``fold`` attribute to
40
+ datetimes both before and after the implementation of PEP-495.
41
+
42
+ :param fold:
43
+ The value for the ``fold`` attribute in the returned datetime. This
44
+ should be either 0 or 1.
45
+
46
+ :return:
47
+ Returns an object for which ``getattr(dt, 'fold', 0)`` returns
48
+ ``fold`` for all versions of Python. In versions prior to
49
+ Python 3.6, this is a ``_DatetimeWithFold`` object, which is a
50
+ subclass of :py:class:`datetime.datetime` with the ``fold``
51
+ attribute added, if ``fold`` is 1.
52
+
53
+ .. versionadded:: 2.6.0
54
+ """
55
+ return dt.replace(fold=fold)
56
+
57
+ else:
58
+ class _DatetimeWithFold(datetime):
59
+ """
60
+ This is a class designed to provide a PEP 495-compliant interface for
61
+ Python versions before 3.6. It is used only for dates in a fold, so
62
+ the ``fold`` attribute is fixed at ``1``.
63
+
64
+ .. versionadded:: 2.6.0
65
+ """
66
+ __slots__ = ()
67
+
68
+ def replace(self, *args, **kwargs):
69
+ """
70
+ Return a datetime with the same attributes, except for those
71
+ attributes given new values by whichever keyword arguments are
72
+ specified. Note that tzinfo=None can be specified to create a naive
73
+ datetime from an aware datetime with no conversion of date and time
74
+ data.
75
+
76
+ This is reimplemented in ``_DatetimeWithFold`` because pypy3 will
77
+ return a ``datetime.datetime`` even if ``fold`` is unchanged.
78
+ """
79
+ argnames = (
80
+ 'year', 'month', 'day', 'hour', 'minute', 'second',
81
+ 'microsecond', 'tzinfo'
82
+ )
83
+
84
+ for arg, argname in zip(args, argnames):
85
+ if argname in kwargs:
86
+ raise TypeError('Duplicate argument: {}'.format(argname))
87
+
88
+ kwargs[argname] = arg
89
+
90
+ for argname in argnames:
91
+ if argname not in kwargs:
92
+ kwargs[argname] = getattr(self, argname)
93
+
94
+ dt_class = self.__class__ if kwargs.get('fold', 1) else datetime
95
+
96
+ return dt_class(**kwargs)
97
+
98
+ @property
99
+ def fold(self):
100
+ return 1
101
+
102
+ def enfold(dt, fold=1):
103
+ """
104
+ Provides a unified interface for assigning the ``fold`` attribute to
105
+ datetimes both before and after the implementation of PEP-495.
106
+
107
+ :param fold:
108
+ The value for the ``fold`` attribute in the returned datetime. This
109
+ should be either 0 or 1.
110
+
111
+ :return:
112
+ Returns an object for which ``getattr(dt, 'fold', 0)`` returns
113
+ ``fold`` for all versions of Python. In versions prior to
114
+ Python 3.6, this is a ``_DatetimeWithFold`` object, which is a
115
+ subclass of :py:class:`datetime.datetime` with the ``fold``
116
+ attribute added, if ``fold`` is 1.
117
+
118
+ .. versionadded:: 2.6.0
119
+ """
120
+ if getattr(dt, 'fold', 0) == fold:
121
+ return dt
122
+
123
+ args = dt.timetuple()[:6]
124
+ args += (dt.microsecond, dt.tzinfo)
125
+
126
+ if fold:
127
+ return _DatetimeWithFold(*args)
128
+ else:
129
+ return datetime(*args)
130
+
131
+
132
+ def _validate_fromutc_inputs(f):
133
+ """
134
+ The CPython version of ``fromutc`` checks that the input is a ``datetime``
135
+ object and that ``self`` is attached as its ``tzinfo``.
136
+ """
137
+ @wraps(f)
138
+ def fromutc(self, dt):
139
+ if not isinstance(dt, datetime):
140
+ raise TypeError("fromutc() requires a datetime argument")
141
+ if dt.tzinfo is not self:
142
+ raise ValueError("dt.tzinfo is not self")
143
+
144
+ return f(self, dt)
145
+
146
+ return fromutc
147
+
148
+
149
+ class _tzinfo(tzinfo):
150
+ """
151
+ Base class for all ``dateutil`` ``tzinfo`` objects.
152
+ """
153
+
154
+ def is_ambiguous(self, dt):
155
+ """
156
+ Whether or not the "wall time" of a given datetime is ambiguous in this
157
+ zone.
158
+
159
+ :param dt:
160
+ A :py:class:`datetime.datetime`, naive or time zone aware.
161
+
162
+
163
+ :return:
164
+ Returns ``True`` if ambiguous, ``False`` otherwise.
165
+
166
+ .. versionadded:: 2.6.0
167
+ """
168
+
169
+ dt = dt.replace(tzinfo=self)
170
+
171
+ wall_0 = enfold(dt, fold=0)
172
+ wall_1 = enfold(dt, fold=1)
173
+
174
+ same_offset = wall_0.utcoffset() == wall_1.utcoffset()
175
+ same_dt = wall_0.replace(tzinfo=None) == wall_1.replace(tzinfo=None)
176
+
177
+ return same_dt and not same_offset
178
+
179
+ def _fold_status(self, dt_utc, dt_wall):
180
+ """
181
+ Determine the fold status of a "wall" datetime, given a representation
182
+ of the same datetime as a (naive) UTC datetime. This is calculated based
183
+ on the assumption that ``dt.utcoffset() - dt.dst()`` is constant for all
184
+ datetimes, and that this offset is the actual number of hours separating
185
+ ``dt_utc`` and ``dt_wall``.
186
+
187
+ :param dt_utc:
188
+ Representation of the datetime as UTC
189
+
190
+ :param dt_wall:
191
+ Representation of the datetime as "wall time". This parameter must
192
+ either have a `fold` attribute or have a fold-naive
193
+ :class:`datetime.tzinfo` attached, otherwise the calculation may
194
+ fail.
195
+ """
196
+ if self.is_ambiguous(dt_wall):
197
+ delta_wall = dt_wall - dt_utc
198
+ _fold = int(delta_wall == (dt_utc.utcoffset() - dt_utc.dst()))
199
+ else:
200
+ _fold = 0
201
+
202
+ return _fold
203
+
204
+ def _fold(self, dt):
205
+ return getattr(dt, 'fold', 0)
206
+
207
+ def _fromutc(self, dt):
208
+ """
209
+ Given a timezone-aware datetime in a given timezone, calculates a
210
+ timezone-aware datetime in a new timezone.
211
+
212
+ Since this is the one time that we *know* we have an unambiguous
213
+ datetime object, we take this opportunity to determine whether the
214
+ datetime is ambiguous and in a "fold" state (e.g. if it's the first
215
+ occurrence, chronologically, of the ambiguous datetime).
216
+
217
+ :param dt:
218
+ A timezone-aware :class:`datetime.datetime` object.
219
+ """
220
+
221
+ # Re-implement the algorithm from Python's datetime.py
222
+ dtoff = dt.utcoffset()
223
+ if dtoff is None:
224
+ raise ValueError("fromutc() requires a non-None utcoffset() "
225
+ "result")
226
+
227
+ # The original datetime.py code assumes that `dst()` defaults to
228
+ # zero during ambiguous times. PEP 495 inverts this presumption, so
229
+ # for pre-PEP 495 versions of python, we need to tweak the algorithm.
230
+ dtdst = dt.dst()
231
+ if dtdst is None:
232
+ raise ValueError("fromutc() requires a non-None dst() result")
233
+ delta = dtoff - dtdst
234
+
235
+ dt += delta
236
+ # Set fold=1 so we can default to being in the fold for
237
+ # ambiguous dates.
238
+ dtdst = enfold(dt, fold=1).dst()
239
+ if dtdst is None:
240
+ raise ValueError("fromutc(): dt.dst gave inconsistent "
241
+ "results; cannot convert")
242
+ return dt + dtdst
243
+
244
+ @_validate_fromutc_inputs
245
+ def fromutc(self, dt):
246
+ """
247
+ Given a timezone-aware datetime in a given timezone, calculates a
248
+ timezone-aware datetime in a new timezone.
249
+
250
+ Since this is the one time that we *know* we have an unambiguous
251
+ datetime object, we take this opportunity to determine whether the
252
+ datetime is ambiguous and in a "fold" state (e.g. if it's the first
253
+ occurrence, chronologically, of the ambiguous datetime).
254
+
255
+ :param dt:
256
+ A timezone-aware :class:`datetime.datetime` object.
257
+ """
258
+ dt_wall = self._fromutc(dt)
259
+
260
+ # Calculate the fold status given the two datetimes.
261
+ _fold = self._fold_status(dt, dt_wall)
262
+
263
+ # Set the default fold value for ambiguous dates
264
+ return enfold(dt_wall, fold=_fold)
265
+
266
+
267
+ class tzrangebase(_tzinfo):
268
+ """
269
+ This is an abstract base class for time zones represented by an annual
270
+ transition into and out of DST. Child classes should implement the following
271
+ methods:
272
+
273
+ * ``__init__(self, *args, **kwargs)``
274
+ * ``transitions(self, year)`` - this is expected to return a tuple of
275
+ datetimes representing the DST on and off transitions in standard
276
+ time.
277
+
278
+ A fully initialized ``tzrangebase`` subclass should also provide the
279
+ following attributes:
280
+ * ``hasdst``: Boolean whether or not the zone uses DST.
281
+ * ``_dst_offset`` / ``_std_offset``: :class:`datetime.timedelta` objects
282
+ representing the respective UTC offsets.
283
+ * ``_dst_abbr`` / ``_std_abbr``: Strings representing the timezone short
284
+ abbreviations in DST and STD, respectively.
285
+ * ``_hasdst``: Whether or not the zone has DST.
286
+
287
+ .. versionadded:: 2.6.0
288
+ """
289
+ def __init__(self):
290
+ raise NotImplementedError('tzrangebase is an abstract base class')
291
+
292
+ def utcoffset(self, dt):
293
+ isdst = self._isdst(dt)
294
+
295
+ if isdst is None:
296
+ return None
297
+ elif isdst:
298
+ return self._dst_offset
299
+ else:
300
+ return self._std_offset
301
+
302
+ def dst(self, dt):
303
+ isdst = self._isdst(dt)
304
+
305
+ if isdst is None:
306
+ return None
307
+ elif isdst:
308
+ return self._dst_base_offset
309
+ else:
310
+ return ZERO
311
+
312
+ @tzname_in_python2
313
+ def tzname(self, dt):
314
+ if self._isdst(dt):
315
+ return self._dst_abbr
316
+ else:
317
+ return self._std_abbr
318
+
319
+ def fromutc(self, dt):
320
+ """ Given a datetime in UTC, return local time """
321
+ if not isinstance(dt, datetime):
322
+ raise TypeError("fromutc() requires a datetime argument")
323
+
324
+ if dt.tzinfo is not self:
325
+ raise ValueError("dt.tzinfo is not self")
326
+
327
+ # Get transitions - if there are none, fixed offset
328
+ transitions = self.transitions(dt.year)
329
+ if transitions is None:
330
+ return dt + self.utcoffset(dt)
331
+
332
+ # Get the transition times in UTC
333
+ dston, dstoff = transitions
334
+
335
+ dston -= self._std_offset
336
+ dstoff -= self._std_offset
337
+
338
+ utc_transitions = (dston, dstoff)
339
+ dt_utc = dt.replace(tzinfo=None)
340
+
341
+ isdst = self._naive_isdst(dt_utc, utc_transitions)
342
+
343
+ if isdst:
344
+ dt_wall = dt + self._dst_offset
345
+ else:
346
+ dt_wall = dt + self._std_offset
347
+
348
+ _fold = int(not isdst and self.is_ambiguous(dt_wall))
349
+
350
+ return enfold(dt_wall, fold=_fold)
351
+
352
+ def is_ambiguous(self, dt):
353
+ """
354
+ Whether or not the "wall time" of a given datetime is ambiguous in this
355
+ zone.
356
+
357
+ :param dt:
358
+ A :py:class:`datetime.datetime`, naive or time zone aware.
359
+
360
+
361
+ :return:
362
+ Returns ``True`` if ambiguous, ``False`` otherwise.
363
+
364
+ .. versionadded:: 2.6.0
365
+ """
366
+ if not self.hasdst:
367
+ return False
368
+
369
+ start, end = self.transitions(dt.year)
370
+
371
+ dt = dt.replace(tzinfo=None)
372
+ return (end <= dt < end + self._dst_base_offset)
373
+
374
+ def _isdst(self, dt):
375
+ if not self.hasdst:
376
+ return False
377
+ elif dt is None:
378
+ return None
379
+
380
+ transitions = self.transitions(dt.year)
381
+
382
+ if transitions is None:
383
+ return False
384
+
385
+ dt = dt.replace(tzinfo=None)
386
+
387
+ isdst = self._naive_isdst(dt, transitions)
388
+
389
+ # Handle ambiguous dates
390
+ if not isdst and self.is_ambiguous(dt):
391
+ return not self._fold(dt)
392
+ else:
393
+ return isdst
394
+
395
+ def _naive_isdst(self, dt, transitions):
396
+ dston, dstoff = transitions
397
+
398
+ dt = dt.replace(tzinfo=None)
399
+
400
+ if dston < dstoff:
401
+ isdst = dston <= dt < dstoff
402
+ else:
403
+ isdst = not dstoff <= dt < dston
404
+
405
+ return isdst
406
+
407
+ @property
408
+ def _dst_base_offset(self):
409
+ return self._dst_offset - self._std_offset
410
+
411
+ __hash__ = None
412
+
413
+ def __ne__(self, other):
414
+ return not (self == other)
415
+
416
+ def __repr__(self):
417
+ return "%s(...)" % self.__class__.__name__
418
+
419
+ __reduce__ = object.__reduce__
dateutil/tz/_factories.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import timedelta
2
+ import weakref
3
+ from collections import OrderedDict
4
+
5
+ from six.moves import _thread
6
+
7
+
8
+ class _TzSingleton(type):
9
+ def __init__(cls, *args, **kwargs):
10
+ cls.__instance = None
11
+ super(_TzSingleton, cls).__init__(*args, **kwargs)
12
+
13
+ def __call__(cls):
14
+ if cls.__instance is None:
15
+ cls.__instance = super(_TzSingleton, cls).__call__()
16
+ return cls.__instance
17
+
18
+
19
+ class _TzFactory(type):
20
+ def instance(cls, *args, **kwargs):
21
+ """Alternate constructor that returns a fresh instance"""
22
+ return type.__call__(cls, *args, **kwargs)
23
+
24
+
25
+ class _TzOffsetFactory(_TzFactory):
26
+ def __init__(cls, *args, **kwargs):
27
+ cls.__instances = weakref.WeakValueDictionary()
28
+ cls.__strong_cache = OrderedDict()
29
+ cls.__strong_cache_size = 8
30
+
31
+ cls._cache_lock = _thread.allocate_lock()
32
+
33
+ def __call__(cls, name, offset):
34
+ if isinstance(offset, timedelta):
35
+ key = (name, offset.total_seconds())
36
+ else:
37
+ key = (name, offset)
38
+
39
+ instance = cls.__instances.get(key, None)
40
+ if instance is None:
41
+ instance = cls.__instances.setdefault(key,
42
+ cls.instance(name, offset))
43
+
44
+ # This lock may not be necessary in Python 3. See GH issue #901
45
+ with cls._cache_lock:
46
+ cls.__strong_cache[key] = cls.__strong_cache.pop(key, instance)
47
+
48
+ # Remove an item if the strong cache is overpopulated
49
+ if len(cls.__strong_cache) > cls.__strong_cache_size:
50
+ cls.__strong_cache.popitem(last=False)
51
+
52
+ return instance
53
+
54
+
55
+ class _TzStrFactory(_TzFactory):
56
+ def __init__(cls, *args, **kwargs):
57
+ cls.__instances = weakref.WeakValueDictionary()
58
+ cls.__strong_cache = OrderedDict()
59
+ cls.__strong_cache_size = 8
60
+
61
+ cls.__cache_lock = _thread.allocate_lock()
62
+
63
+ def __call__(cls, s, posix_offset=False):
64
+ key = (s, posix_offset)
65
+ instance = cls.__instances.get(key, None)
66
+
67
+ if instance is None:
68
+ instance = cls.__instances.setdefault(key,
69
+ cls.instance(s, posix_offset))
70
+
71
+ # This lock may not be necessary in Python 3. See GH issue #901
72
+ with cls.__cache_lock:
73
+ cls.__strong_cache[key] = cls.__strong_cache.pop(key, instance)
74
+
75
+ # Remove an item if the strong cache is overpopulated
76
+ if len(cls.__strong_cache) > cls.__strong_cache_size:
77
+ cls.__strong_cache.popitem(last=False)
78
+
79
+ return instance
80
+
dateutil/tz/tz.py ADDED
@@ -0,0 +1,1849 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ This module offers timezone implementations subclassing the abstract
4
+ :py:class:`datetime.tzinfo` type. There are classes to handle tzfile format
5
+ files (usually are in :file:`/etc/localtime`, :file:`/usr/share/zoneinfo`,
6
+ etc), TZ environment string (in all known formats), given ranges (with help
7
+ from relative deltas), local machine timezone, fixed offset timezone, and UTC
8
+ timezone.
9
+ """
10
+ import datetime
11
+ import struct
12
+ import time
13
+ import sys
14
+ import os
15
+ import bisect
16
+ import weakref
17
+ from collections import OrderedDict
18
+
19
+ import six
20
+ from six import string_types
21
+ from six.moves import _thread
22
+ from ._common import tzname_in_python2, _tzinfo
23
+ from ._common import tzrangebase, enfold
24
+ from ._common import _validate_fromutc_inputs
25
+
26
+ from ._factories import _TzSingleton, _TzOffsetFactory
27
+ from ._factories import _TzStrFactory
28
+ try:
29
+ from .win import tzwin, tzwinlocal
30
+ except ImportError:
31
+ tzwin = tzwinlocal = None
32
+
33
+ # For warning about rounding tzinfo
34
+ from warnings import warn
35
+
36
+ ZERO = datetime.timedelta(0)
37
+ EPOCH = datetime.datetime(1970, 1, 1, 0, 0)
38
+ EPOCHORDINAL = EPOCH.toordinal()
39
+
40
+
41
+ @six.add_metaclass(_TzSingleton)
42
+ class tzutc(datetime.tzinfo):
43
+ """
44
+ This is a tzinfo object that represents the UTC time zone.
45
+
46
+ **Examples:**
47
+
48
+ .. doctest::
49
+
50
+ >>> from datetime import *
51
+ >>> from dateutil.tz import *
52
+
53
+ >>> datetime.now()
54
+ datetime.datetime(2003, 9, 27, 9, 40, 1, 521290)
55
+
56
+ >>> datetime.now(tzutc())
57
+ datetime.datetime(2003, 9, 27, 12, 40, 12, 156379, tzinfo=tzutc())
58
+
59
+ >>> datetime.now(tzutc()).tzname()
60
+ 'UTC'
61
+
62
+ .. versionchanged:: 2.7.0
63
+ ``tzutc()`` is now a singleton, so the result of ``tzutc()`` will
64
+ always return the same object.
65
+
66
+ .. doctest::
67
+
68
+ >>> from dateutil.tz import tzutc, UTC
69
+ >>> tzutc() is tzutc()
70
+ True
71
+ >>> tzutc() is UTC
72
+ True
73
+ """
74
+ def utcoffset(self, dt):
75
+ return ZERO
76
+
77
+ def dst(self, dt):
78
+ return ZERO
79
+
80
+ @tzname_in_python2
81
+ def tzname(self, dt):
82
+ return "UTC"
83
+
84
+ def is_ambiguous(self, dt):
85
+ """
86
+ Whether or not the "wall time" of a given datetime is ambiguous in this
87
+ zone.
88
+
89
+ :param dt:
90
+ A :py:class:`datetime.datetime`, naive or time zone aware.
91
+
92
+
93
+ :return:
94
+ Returns ``True`` if ambiguous, ``False`` otherwise.
95
+
96
+ .. versionadded:: 2.6.0
97
+ """
98
+ return False
99
+
100
+ @_validate_fromutc_inputs
101
+ def fromutc(self, dt):
102
+ """
103
+ Fast track version of fromutc() returns the original ``dt`` object for
104
+ any valid :py:class:`datetime.datetime` object.
105
+ """
106
+ return dt
107
+
108
+ def __eq__(self, other):
109
+ if not isinstance(other, (tzutc, tzoffset)):
110
+ return NotImplemented
111
+
112
+ return (isinstance(other, tzutc) or
113
+ (isinstance(other, tzoffset) and other._offset == ZERO))
114
+
115
+ __hash__ = None
116
+
117
+ def __ne__(self, other):
118
+ return not (self == other)
119
+
120
+ def __repr__(self):
121
+ return "%s()" % self.__class__.__name__
122
+
123
+ __reduce__ = object.__reduce__
124
+
125
+
126
+ #: Convenience constant providing a :class:`tzutc()` instance
127
+ #:
128
+ #: .. versionadded:: 2.7.0
129
+ UTC = tzutc()
130
+
131
+
132
+ @six.add_metaclass(_TzOffsetFactory)
133
+ class tzoffset(datetime.tzinfo):
134
+ """
135
+ A simple class for representing a fixed offset from UTC.
136
+
137
+ :param name:
138
+ The timezone name, to be returned when ``tzname()`` is called.
139
+ :param offset:
140
+ The time zone offset in seconds, or (since version 2.6.0, represented
141
+ as a :py:class:`datetime.timedelta` object).
142
+ """
143
+ def __init__(self, name, offset):
144
+ self._name = name
145
+
146
+ try:
147
+ # Allow a timedelta
148
+ offset = offset.total_seconds()
149
+ except (TypeError, AttributeError):
150
+ pass
151
+
152
+ self._offset = datetime.timedelta(seconds=_get_supported_offset(offset))
153
+
154
+ def utcoffset(self, dt):
155
+ return self._offset
156
+
157
+ def dst(self, dt):
158
+ return ZERO
159
+
160
+ @tzname_in_python2
161
+ def tzname(self, dt):
162
+ return self._name
163
+
164
+ @_validate_fromutc_inputs
165
+ def fromutc(self, dt):
166
+ return dt + self._offset
167
+
168
+ def is_ambiguous(self, dt):
169
+ """
170
+ Whether or not the "wall time" of a given datetime is ambiguous in this
171
+ zone.
172
+
173
+ :param dt:
174
+ A :py:class:`datetime.datetime`, naive or time zone aware.
175
+ :return:
176
+ Returns ``True`` if ambiguous, ``False`` otherwise.
177
+
178
+ .. versionadded:: 2.6.0
179
+ """
180
+ return False
181
+
182
+ def __eq__(self, other):
183
+ if not isinstance(other, tzoffset):
184
+ return NotImplemented
185
+
186
+ return self._offset == other._offset
187
+
188
+ __hash__ = None
189
+
190
+ def __ne__(self, other):
191
+ return not (self == other)
192
+
193
+ def __repr__(self):
194
+ return "%s(%s, %s)" % (self.__class__.__name__,
195
+ repr(self._name),
196
+ int(self._offset.total_seconds()))
197
+
198
+ __reduce__ = object.__reduce__
199
+
200
+
201
+ class tzlocal(_tzinfo):
202
+ """
203
+ A :class:`tzinfo` subclass built around the ``time`` timezone functions.
204
+ """
205
+ def __init__(self):
206
+ super(tzlocal, self).__init__()
207
+
208
+ self._std_offset = datetime.timedelta(seconds=-time.timezone)
209
+ if time.daylight:
210
+ self._dst_offset = datetime.timedelta(seconds=-time.altzone)
211
+ else:
212
+ self._dst_offset = self._std_offset
213
+
214
+ self._dst_saved = self._dst_offset - self._std_offset
215
+ self._hasdst = bool(self._dst_saved)
216
+ self._tznames = tuple(time.tzname)
217
+
218
+ def utcoffset(self, dt):
219
+ if dt is None and self._hasdst:
220
+ return None
221
+
222
+ if self._isdst(dt):
223
+ return self._dst_offset
224
+ else:
225
+ return self._std_offset
226
+
227
+ def dst(self, dt):
228
+ if dt is None and self._hasdst:
229
+ return None
230
+
231
+ if self._isdst(dt):
232
+ return self._dst_offset - self._std_offset
233
+ else:
234
+ return ZERO
235
+
236
+ @tzname_in_python2
237
+ def tzname(self, dt):
238
+ return self._tznames[self._isdst(dt)]
239
+
240
+ def is_ambiguous(self, dt):
241
+ """
242
+ Whether or not the "wall time" of a given datetime is ambiguous in this
243
+ zone.
244
+
245
+ :param dt:
246
+ A :py:class:`datetime.datetime`, naive or time zone aware.
247
+
248
+
249
+ :return:
250
+ Returns ``True`` if ambiguous, ``False`` otherwise.
251
+
252
+ .. versionadded:: 2.6.0
253
+ """
254
+ naive_dst = self._naive_is_dst(dt)
255
+ return (not naive_dst and
256
+ (naive_dst != self._naive_is_dst(dt - self._dst_saved)))
257
+
258
+ def _naive_is_dst(self, dt):
259
+ timestamp = _datetime_to_timestamp(dt)
260
+ return time.localtime(timestamp + time.timezone).tm_isdst
261
+
262
+ def _isdst(self, dt, fold_naive=True):
263
+ # We can't use mktime here. It is unstable when deciding if
264
+ # the hour near to a change is DST or not.
265
+ #
266
+ # timestamp = time.mktime((dt.year, dt.month, dt.day, dt.hour,
267
+ # dt.minute, dt.second, dt.weekday(), 0, -1))
268
+ # return time.localtime(timestamp).tm_isdst
269
+ #
270
+ # The code above yields the following result:
271
+ #
272
+ # >>> import tz, datetime
273
+ # >>> t = tz.tzlocal()
274
+ # >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname()
275
+ # 'BRDT'
276
+ # >>> datetime.datetime(2003,2,16,0,tzinfo=t).tzname()
277
+ # 'BRST'
278
+ # >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname()
279
+ # 'BRST'
280
+ # >>> datetime.datetime(2003,2,15,22,tzinfo=t).tzname()
281
+ # 'BRDT'
282
+ # >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname()
283
+ # 'BRDT'
284
+ #
285
+ # Here is a more stable implementation:
286
+ #
287
+ if not self._hasdst:
288
+ return False
289
+
290
+ # Check for ambiguous times:
291
+ dstval = self._naive_is_dst(dt)
292
+ fold = getattr(dt, 'fold', None)
293
+
294
+ if self.is_ambiguous(dt):
295
+ if fold is not None:
296
+ return not self._fold(dt)
297
+ else:
298
+ return True
299
+
300
+ return dstval
301
+
302
+ def __eq__(self, other):
303
+ if isinstance(other, tzlocal):
304
+ return (self._std_offset == other._std_offset and
305
+ self._dst_offset == other._dst_offset)
306
+ elif isinstance(other, tzutc):
307
+ return (not self._hasdst and
308
+ self._tznames[0] in {'UTC', 'GMT'} and
309
+ self._std_offset == ZERO)
310
+ elif isinstance(other, tzoffset):
311
+ return (not self._hasdst and
312
+ self._tznames[0] == other._name and
313
+ self._std_offset == other._offset)
314
+ else:
315
+ return NotImplemented
316
+
317
+ __hash__ = None
318
+
319
+ def __ne__(self, other):
320
+ return not (self == other)
321
+
322
+ def __repr__(self):
323
+ return "%s()" % self.__class__.__name__
324
+
325
+ __reduce__ = object.__reduce__
326
+
327
+
328
+ class _ttinfo(object):
329
+ __slots__ = ["offset", "delta", "isdst", "abbr",
330
+ "isstd", "isgmt", "dstoffset"]
331
+
332
+ def __init__(self):
333
+ for attr in self.__slots__:
334
+ setattr(self, attr, None)
335
+
336
+ def __repr__(self):
337
+ l = []
338
+ for attr in self.__slots__:
339
+ value = getattr(self, attr)
340
+ if value is not None:
341
+ l.append("%s=%s" % (attr, repr(value)))
342
+ return "%s(%s)" % (self.__class__.__name__, ", ".join(l))
343
+
344
+ def __eq__(self, other):
345
+ if not isinstance(other, _ttinfo):
346
+ return NotImplemented
347
+
348
+ return (self.offset == other.offset and
349
+ self.delta == other.delta and
350
+ self.isdst == other.isdst and
351
+ self.abbr == other.abbr and
352
+ self.isstd == other.isstd and
353
+ self.isgmt == other.isgmt and
354
+ self.dstoffset == other.dstoffset)
355
+
356
+ __hash__ = None
357
+
358
+ def __ne__(self, other):
359
+ return not (self == other)
360
+
361
+ def __getstate__(self):
362
+ state = {}
363
+ for name in self.__slots__:
364
+ state[name] = getattr(self, name, None)
365
+ return state
366
+
367
+ def __setstate__(self, state):
368
+ for name in self.__slots__:
369
+ if name in state:
370
+ setattr(self, name, state[name])
371
+
372
+
373
+ class _tzfile(object):
374
+ """
375
+ Lightweight class for holding the relevant transition and time zone
376
+ information read from binary tzfiles.
377
+ """
378
+ attrs = ['trans_list', 'trans_list_utc', 'trans_idx', 'ttinfo_list',
379
+ 'ttinfo_std', 'ttinfo_dst', 'ttinfo_before', 'ttinfo_first']
380
+
381
+ def __init__(self, **kwargs):
382
+ for attr in self.attrs:
383
+ setattr(self, attr, kwargs.get(attr, None))
384
+
385
+
386
+ class tzfile(_tzinfo):
387
+ """
388
+ This is a ``tzinfo`` subclass that allows one to use the ``tzfile(5)``
389
+ format timezone files to extract current and historical zone information.
390
+
391
+ :param fileobj:
392
+ This can be an opened file stream or a file name that the time zone
393
+ information can be read from.
394
+
395
+ :param filename:
396
+ This is an optional parameter specifying the source of the time zone
397
+ information in the event that ``fileobj`` is a file object. If omitted
398
+ and ``fileobj`` is a file stream, this parameter will be set either to
399
+ ``fileobj``'s ``name`` attribute or to ``repr(fileobj)``.
400
+
401
+ See `Sources for Time Zone and Daylight Saving Time Data
402
+ <https://data.iana.org/time-zones/tz-link.html>`_ for more information.
403
+ Time zone files can be compiled from the `IANA Time Zone database files
404
+ <https://www.iana.org/time-zones>`_ with the `zic time zone compiler
405
+ <https://www.freebsd.org/cgi/man.cgi?query=zic&sektion=8>`_
406
+
407
+ .. note::
408
+
409
+ Only construct a ``tzfile`` directly if you have a specific timezone
410
+ file on disk that you want to read into a Python ``tzinfo`` object.
411
+ If you want to get a ``tzfile`` representing a specific IANA zone,
412
+ (e.g. ``'America/New_York'``), you should call
413
+ :func:`dateutil.tz.gettz` with the zone identifier.
414
+
415
+
416
+ **Examples:**
417
+
418
+ Using the US Eastern time zone as an example, we can see that a ``tzfile``
419
+ provides time zone information for the standard Daylight Saving offsets:
420
+
421
+ .. testsetup:: tzfile
422
+
423
+ from dateutil.tz import gettz
424
+ from datetime import datetime
425
+
426
+ .. doctest:: tzfile
427
+
428
+ >>> NYC = gettz('America/New_York')
429
+ >>> NYC
430
+ tzfile('/usr/share/zoneinfo/America/New_York')
431
+
432
+ >>> print(datetime(2016, 1, 3, tzinfo=NYC)) # EST
433
+ 2016-01-03 00:00:00-05:00
434
+
435
+ >>> print(datetime(2016, 7, 7, tzinfo=NYC)) # EDT
436
+ 2016-07-07 00:00:00-04:00
437
+
438
+
439
+ The ``tzfile`` structure contains a fully history of the time zone,
440
+ so historical dates will also have the right offsets. For example, before
441
+ the adoption of the UTC standards, New York used local solar mean time:
442
+
443
+ .. doctest:: tzfile
444
+
445
+ >>> print(datetime(1901, 4, 12, tzinfo=NYC)) # LMT
446
+ 1901-04-12 00:00:00-04:56
447
+
448
+ And during World War II, New York was on "Eastern War Time", which was a
449
+ state of permanent daylight saving time:
450
+
451
+ .. doctest:: tzfile
452
+
453
+ >>> print(datetime(1944, 2, 7, tzinfo=NYC)) # EWT
454
+ 1944-02-07 00:00:00-04:00
455
+
456
+ """
457
+
458
+ def __init__(self, fileobj, filename=None):
459
+ super(tzfile, self).__init__()
460
+
461
+ file_opened_here = False
462
+ if isinstance(fileobj, string_types):
463
+ self._filename = fileobj
464
+ fileobj = open(fileobj, 'rb')
465
+ file_opened_here = True
466
+ elif filename is not None:
467
+ self._filename = filename
468
+ elif hasattr(fileobj, "name"):
469
+ self._filename = fileobj.name
470
+ else:
471
+ self._filename = repr(fileobj)
472
+
473
+ if fileobj is not None:
474
+ if not file_opened_here:
475
+ fileobj = _nullcontext(fileobj)
476
+
477
+ with fileobj as file_stream:
478
+ tzobj = self._read_tzfile(file_stream)
479
+
480
+ self._set_tzdata(tzobj)
481
+
482
+ def _set_tzdata(self, tzobj):
483
+ """ Set the time zone data of this object from a _tzfile object """
484
+ # Copy the relevant attributes over as private attributes
485
+ for attr in _tzfile.attrs:
486
+ setattr(self, '_' + attr, getattr(tzobj, attr))
487
+
488
+ def _read_tzfile(self, fileobj):
489
+ out = _tzfile()
490
+
491
+ # From tzfile(5):
492
+ #
493
+ # The time zone information files used by tzset(3)
494
+ # begin with the magic characters "TZif" to identify
495
+ # them as time zone information files, followed by
496
+ # sixteen bytes reserved for future use, followed by
497
+ # six four-byte values of type long, written in a
498
+ # ``standard'' byte order (the high-order byte
499
+ # of the value is written first).
500
+ if fileobj.read(4).decode() != "TZif":
501
+ raise ValueError("magic not found")
502
+
503
+ fileobj.read(16)
504
+
505
+ (
506
+ # The number of UTC/local indicators stored in the file.
507
+ ttisgmtcnt,
508
+
509
+ # The number of standard/wall indicators stored in the file.
510
+ ttisstdcnt,
511
+
512
+ # The number of leap seconds for which data is
513
+ # stored in the file.
514
+ leapcnt,
515
+
516
+ # The number of "transition times" for which data
517
+ # is stored in the file.
518
+ timecnt,
519
+
520
+ # The number of "local time types" for which data
521
+ # is stored in the file (must not be zero).
522
+ typecnt,
523
+
524
+ # The number of characters of "time zone
525
+ # abbreviation strings" stored in the file.
526
+ charcnt,
527
+
528
+ ) = struct.unpack(">6l", fileobj.read(24))
529
+
530
+ # The above header is followed by tzh_timecnt four-byte
531
+ # values of type long, sorted in ascending order.
532
+ # These values are written in ``standard'' byte order.
533
+ # Each is used as a transition time (as returned by
534
+ # time(2)) at which the rules for computing local time
535
+ # change.
536
+
537
+ if timecnt:
538
+ out.trans_list_utc = list(struct.unpack(">%dl" % timecnt,
539
+ fileobj.read(timecnt*4)))
540
+ else:
541
+ out.trans_list_utc = []
542
+
543
+ # Next come tzh_timecnt one-byte values of type unsigned
544
+ # char; each one tells which of the different types of
545
+ # ``local time'' types described in the file is associated
546
+ # with the same-indexed transition time. These values
547
+ # serve as indices into an array of ttinfo structures that
548
+ # appears next in the file.
549
+
550
+ if timecnt:
551
+ out.trans_idx = struct.unpack(">%dB" % timecnt,
552
+ fileobj.read(timecnt))
553
+ else:
554
+ out.trans_idx = []
555
+
556
+ # Each ttinfo structure is written as a four-byte value
557
+ # for tt_gmtoff of type long, in a standard byte
558
+ # order, followed by a one-byte value for tt_isdst
559
+ # and a one-byte value for tt_abbrind. In each
560
+ # structure, tt_gmtoff gives the number of
561
+ # seconds to be added to UTC, tt_isdst tells whether
562
+ # tm_isdst should be set by localtime(3), and
563
+ # tt_abbrind serves as an index into the array of
564
+ # time zone abbreviation characters that follow the
565
+ # ttinfo structure(s) in the file.
566
+
567
+ ttinfo = []
568
+
569
+ for i in range(typecnt):
570
+ ttinfo.append(struct.unpack(">lbb", fileobj.read(6)))
571
+
572
+ abbr = fileobj.read(charcnt).decode()
573
+
574
+ # Then there are tzh_leapcnt pairs of four-byte
575
+ # values, written in standard byte order; the
576
+ # first value of each pair gives the time (as
577
+ # returned by time(2)) at which a leap second
578
+ # occurs; the second gives the total number of
579
+ # leap seconds to be applied after the given time.
580
+ # The pairs of values are sorted in ascending order
581
+ # by time.
582
+
583
+ # Not used, for now (but seek for correct file position)
584
+ if leapcnt:
585
+ fileobj.seek(leapcnt * 8, os.SEEK_CUR)
586
+
587
+ # Then there are tzh_ttisstdcnt standard/wall
588
+ # indicators, each stored as a one-byte value;
589
+ # they tell whether the transition times associated
590
+ # with local time types were specified as standard
591
+ # time or wall clock time, and are used when
592
+ # a time zone file is used in handling POSIX-style
593
+ # time zone environment variables.
594
+
595
+ if ttisstdcnt:
596
+ isstd = struct.unpack(">%db" % ttisstdcnt,
597
+ fileobj.read(ttisstdcnt))
598
+
599
+ # Finally, there are tzh_ttisgmtcnt UTC/local
600
+ # indicators, each stored as a one-byte value;
601
+ # they tell whether the transition times associated
602
+ # with local time types were specified as UTC or
603
+ # local time, and are used when a time zone file
604
+ # is used in handling POSIX-style time zone envi-
605
+ # ronment variables.
606
+
607
+ if ttisgmtcnt:
608
+ isgmt = struct.unpack(">%db" % ttisgmtcnt,
609
+ fileobj.read(ttisgmtcnt))
610
+
611
+ # Build ttinfo list
612
+ out.ttinfo_list = []
613
+ for i in range(typecnt):
614
+ gmtoff, isdst, abbrind = ttinfo[i]
615
+ gmtoff = _get_supported_offset(gmtoff)
616
+ tti = _ttinfo()
617
+ tti.offset = gmtoff
618
+ tti.dstoffset = datetime.timedelta(0)
619
+ tti.delta = datetime.timedelta(seconds=gmtoff)
620
+ tti.isdst = isdst
621
+ tti.abbr = abbr[abbrind:abbr.find('\x00', abbrind)]
622
+ tti.isstd = (ttisstdcnt > i and isstd[i] != 0)
623
+ tti.isgmt = (ttisgmtcnt > i and isgmt[i] != 0)
624
+ out.ttinfo_list.append(tti)
625
+
626
+ # Replace ttinfo indexes for ttinfo objects.
627
+ out.trans_idx = [out.ttinfo_list[idx] for idx in out.trans_idx]
628
+
629
+ # Set standard, dst, and before ttinfos. before will be
630
+ # used when a given time is before any transitions,
631
+ # and will be set to the first non-dst ttinfo, or to
632
+ # the first dst, if all of them are dst.
633
+ out.ttinfo_std = None
634
+ out.ttinfo_dst = None
635
+ out.ttinfo_before = None
636
+ if out.ttinfo_list:
637
+ if not out.trans_list_utc:
638
+ out.ttinfo_std = out.ttinfo_first = out.ttinfo_list[0]
639
+ else:
640
+ for i in range(timecnt-1, -1, -1):
641
+ tti = out.trans_idx[i]
642
+ if not out.ttinfo_std and not tti.isdst:
643
+ out.ttinfo_std = tti
644
+ elif not out.ttinfo_dst and tti.isdst:
645
+ out.ttinfo_dst = tti
646
+
647
+ if out.ttinfo_std and out.ttinfo_dst:
648
+ break
649
+ else:
650
+ if out.ttinfo_dst and not out.ttinfo_std:
651
+ out.ttinfo_std = out.ttinfo_dst
652
+
653
+ for tti in out.ttinfo_list:
654
+ if not tti.isdst:
655
+ out.ttinfo_before = tti
656
+ break
657
+ else:
658
+ out.ttinfo_before = out.ttinfo_list[0]
659
+
660
+ # Now fix transition times to become relative to wall time.
661
+ #
662
+ # I'm not sure about this. In my tests, the tz source file
663
+ # is setup to wall time, and in the binary file isstd and
664
+ # isgmt are off, so it should be in wall time. OTOH, it's
665
+ # always in gmt time. Let me know if you have comments
666
+ # about this.
667
+ lastdst = None
668
+ lastoffset = None
669
+ lastdstoffset = None
670
+ lastbaseoffset = None
671
+ out.trans_list = []
672
+
673
+ for i, tti in enumerate(out.trans_idx):
674
+ offset = tti.offset
675
+ dstoffset = 0
676
+
677
+ if lastdst is not None:
678
+ if tti.isdst:
679
+ if not lastdst:
680
+ dstoffset = offset - lastoffset
681
+
682
+ if not dstoffset and lastdstoffset:
683
+ dstoffset = lastdstoffset
684
+
685
+ tti.dstoffset = datetime.timedelta(seconds=dstoffset)
686
+ lastdstoffset = dstoffset
687
+
688
+ # If a time zone changes its base offset during a DST transition,
689
+ # then you need to adjust by the previous base offset to get the
690
+ # transition time in local time. Otherwise you use the current
691
+ # base offset. Ideally, I would have some mathematical proof of
692
+ # why this is true, but I haven't really thought about it enough.
693
+ baseoffset = offset - dstoffset
694
+ adjustment = baseoffset
695
+ if (lastbaseoffset is not None and baseoffset != lastbaseoffset
696
+ and tti.isdst != lastdst):
697
+ # The base DST has changed
698
+ adjustment = lastbaseoffset
699
+
700
+ lastdst = tti.isdst
701
+ lastoffset = offset
702
+ lastbaseoffset = baseoffset
703
+
704
+ out.trans_list.append(out.trans_list_utc[i] + adjustment)
705
+
706
+ out.trans_idx = tuple(out.trans_idx)
707
+ out.trans_list = tuple(out.trans_list)
708
+ out.trans_list_utc = tuple(out.trans_list_utc)
709
+
710
+ return out
711
+
712
+ def _find_last_transition(self, dt, in_utc=False):
713
+ # If there's no list, there are no transitions to find
714
+ if not self._trans_list:
715
+ return None
716
+
717
+ timestamp = _datetime_to_timestamp(dt)
718
+
719
+ # Find where the timestamp fits in the transition list - if the
720
+ # timestamp is a transition time, it's part of the "after" period.
721
+ trans_list = self._trans_list_utc if in_utc else self._trans_list
722
+ idx = bisect.bisect_right(trans_list, timestamp)
723
+
724
+ # We want to know when the previous transition was, so subtract off 1
725
+ return idx - 1
726
+
727
+ def _get_ttinfo(self, idx):
728
+ # For no list or after the last transition, default to _ttinfo_std
729
+ if idx is None or (idx + 1) >= len(self._trans_list):
730
+ return self._ttinfo_std
731
+
732
+ # If there is a list and the time is before it, return _ttinfo_before
733
+ if idx < 0:
734
+ return self._ttinfo_before
735
+
736
+ return self._trans_idx[idx]
737
+
738
+ def _find_ttinfo(self, dt):
739
+ idx = self._resolve_ambiguous_time(dt)
740
+
741
+ return self._get_ttinfo(idx)
742
+
743
+ def fromutc(self, dt):
744
+ """
745
+ The ``tzfile`` implementation of :py:func:`datetime.tzinfo.fromutc`.
746
+
747
+ :param dt:
748
+ A :py:class:`datetime.datetime` object.
749
+
750
+ :raises TypeError:
751
+ Raised if ``dt`` is not a :py:class:`datetime.datetime` object.
752
+
753
+ :raises ValueError:
754
+ Raised if this is called with a ``dt`` which does not have this
755
+ ``tzinfo`` attached.
756
+
757
+ :return:
758
+ Returns a :py:class:`datetime.datetime` object representing the
759
+ wall time in ``self``'s time zone.
760
+ """
761
+ # These isinstance checks are in datetime.tzinfo, so we'll preserve
762
+ # them, even if we don't care about duck typing.
763
+ if not isinstance(dt, datetime.datetime):
764
+ raise TypeError("fromutc() requires a datetime argument")
765
+
766
+ if dt.tzinfo is not self:
767
+ raise ValueError("dt.tzinfo is not self")
768
+
769
+ # First treat UTC as wall time and get the transition we're in.
770
+ idx = self._find_last_transition(dt, in_utc=True)
771
+ tti = self._get_ttinfo(idx)
772
+
773
+ dt_out = dt + datetime.timedelta(seconds=tti.offset)
774
+
775
+ fold = self.is_ambiguous(dt_out, idx=idx)
776
+
777
+ return enfold(dt_out, fold=int(fold))
778
+
779
+ def is_ambiguous(self, dt, idx=None):
780
+ """
781
+ Whether or not the "wall time" of a given datetime is ambiguous in this
782
+ zone.
783
+
784
+ :param dt:
785
+ A :py:class:`datetime.datetime`, naive or time zone aware.
786
+
787
+
788
+ :return:
789
+ Returns ``True`` if ambiguous, ``False`` otherwise.
790
+
791
+ .. versionadded:: 2.6.0
792
+ """
793
+ if idx is None:
794
+ idx = self._find_last_transition(dt)
795
+
796
+ # Calculate the difference in offsets from current to previous
797
+ timestamp = _datetime_to_timestamp(dt)
798
+ tti = self._get_ttinfo(idx)
799
+
800
+ if idx is None or idx <= 0:
801
+ return False
802
+
803
+ od = self._get_ttinfo(idx - 1).offset - tti.offset
804
+ tt = self._trans_list[idx] # Transition time
805
+
806
+ return timestamp < tt + od
807
+
808
+ def _resolve_ambiguous_time(self, dt):
809
+ idx = self._find_last_transition(dt)
810
+
811
+ # If we have no transitions, return the index
812
+ _fold = self._fold(dt)
813
+ if idx is None or idx == 0:
814
+ return idx
815
+
816
+ # If it's ambiguous and we're in a fold, shift to a different index.
817
+ idx_offset = int(not _fold and self.is_ambiguous(dt, idx))
818
+
819
+ return idx - idx_offset
820
+
821
+ def utcoffset(self, dt):
822
+ if dt is None:
823
+ return None
824
+
825
+ if not self._ttinfo_std:
826
+ return ZERO
827
+
828
+ return self._find_ttinfo(dt).delta
829
+
830
+ def dst(self, dt):
831
+ if dt is None:
832
+ return None
833
+
834
+ if not self._ttinfo_dst:
835
+ return ZERO
836
+
837
+ tti = self._find_ttinfo(dt)
838
+
839
+ if not tti.isdst:
840
+ return ZERO
841
+
842
+ # The documentation says that utcoffset()-dst() must
843
+ # be constant for every dt.
844
+ return tti.dstoffset
845
+
846
+ @tzname_in_python2
847
+ def tzname(self, dt):
848
+ if not self._ttinfo_std or dt is None:
849
+ return None
850
+ return self._find_ttinfo(dt).abbr
851
+
852
+ def __eq__(self, other):
853
+ if not isinstance(other, tzfile):
854
+ return NotImplemented
855
+ return (self._trans_list == other._trans_list and
856
+ self._trans_idx == other._trans_idx and
857
+ self._ttinfo_list == other._ttinfo_list)
858
+
859
+ __hash__ = None
860
+
861
+ def __ne__(self, other):
862
+ return not (self == other)
863
+
864
+ def __repr__(self):
865
+ return "%s(%s)" % (self.__class__.__name__, repr(self._filename))
866
+
867
+ def __reduce__(self):
868
+ return self.__reduce_ex__(None)
869
+
870
+ def __reduce_ex__(self, protocol):
871
+ return (self.__class__, (None, self._filename), self.__dict__)
872
+
873
+
874
+ class tzrange(tzrangebase):
875
+ """
876
+ The ``tzrange`` object is a time zone specified by a set of offsets and
877
+ abbreviations, equivalent to the way the ``TZ`` variable can be specified
878
+ in POSIX-like systems, but using Python delta objects to specify DST
879
+ start, end and offsets.
880
+
881
+ :param stdabbr:
882
+ The abbreviation for standard time (e.g. ``'EST'``).
883
+
884
+ :param stdoffset:
885
+ An integer or :class:`datetime.timedelta` object or equivalent
886
+ specifying the base offset from UTC.
887
+
888
+ If unspecified, +00:00 is used.
889
+
890
+ :param dstabbr:
891
+ The abbreviation for DST / "Summer" time (e.g. ``'EDT'``).
892
+
893
+ If specified, with no other DST information, DST is assumed to occur
894
+ and the default behavior or ``dstoffset``, ``start`` and ``end`` is
895
+ used. If unspecified and no other DST information is specified, it
896
+ is assumed that this zone has no DST.
897
+
898
+ If this is unspecified and other DST information is *is* specified,
899
+ DST occurs in the zone but the time zone abbreviation is left
900
+ unchanged.
901
+
902
+ :param dstoffset:
903
+ A an integer or :class:`datetime.timedelta` object or equivalent
904
+ specifying the UTC offset during DST. If unspecified and any other DST
905
+ information is specified, it is assumed to be the STD offset +1 hour.
906
+
907
+ :param start:
908
+ A :class:`relativedelta.relativedelta` object or equivalent specifying
909
+ the time and time of year that daylight savings time starts. To
910
+ specify, for example, that DST starts at 2AM on the 2nd Sunday in
911
+ March, pass:
912
+
913
+ ``relativedelta(hours=2, month=3, day=1, weekday=SU(+2))``
914
+
915
+ If unspecified and any other DST information is specified, the default
916
+ value is 2 AM on the first Sunday in April.
917
+
918
+ :param end:
919
+ A :class:`relativedelta.relativedelta` object or equivalent
920
+ representing the time and time of year that daylight savings time
921
+ ends, with the same specification method as in ``start``. One note is
922
+ that this should point to the first time in the *standard* zone, so if
923
+ a transition occurs at 2AM in the DST zone and the clocks are set back
924
+ 1 hour to 1AM, set the ``hours`` parameter to +1.
925
+
926
+
927
+ **Examples:**
928
+
929
+ .. testsetup:: tzrange
930
+
931
+ from dateutil.tz import tzrange, tzstr
932
+
933
+ .. doctest:: tzrange
934
+
935
+ >>> tzstr('EST5EDT') == tzrange("EST", -18000, "EDT")
936
+ True
937
+
938
+ >>> from dateutil.relativedelta import *
939
+ >>> range1 = tzrange("EST", -18000, "EDT")
940
+ >>> range2 = tzrange("EST", -18000, "EDT", -14400,
941
+ ... relativedelta(hours=+2, month=4, day=1,
942
+ ... weekday=SU(+1)),
943
+ ... relativedelta(hours=+1, month=10, day=31,
944
+ ... weekday=SU(-1)))
945
+ >>> tzstr('EST5EDT') == range1 == range2
946
+ True
947
+
948
+ """
949
+ def __init__(self, stdabbr, stdoffset=None,
950
+ dstabbr=None, dstoffset=None,
951
+ start=None, end=None):
952
+
953
+ global relativedelta
954
+ from dateutil import relativedelta
955
+
956
+ self._std_abbr = stdabbr
957
+ self._dst_abbr = dstabbr
958
+
959
+ try:
960
+ stdoffset = stdoffset.total_seconds()
961
+ except (TypeError, AttributeError):
962
+ pass
963
+
964
+ try:
965
+ dstoffset = dstoffset.total_seconds()
966
+ except (TypeError, AttributeError):
967
+ pass
968
+
969
+ if stdoffset is not None:
970
+ self._std_offset = datetime.timedelta(seconds=stdoffset)
971
+ else:
972
+ self._std_offset = ZERO
973
+
974
+ if dstoffset is not None:
975
+ self._dst_offset = datetime.timedelta(seconds=dstoffset)
976
+ elif dstabbr and stdoffset is not None:
977
+ self._dst_offset = self._std_offset + datetime.timedelta(hours=+1)
978
+ else:
979
+ self._dst_offset = ZERO
980
+
981
+ if dstabbr and start is None:
982
+ self._start_delta = relativedelta.relativedelta(
983
+ hours=+2, month=4, day=1, weekday=relativedelta.SU(+1))
984
+ else:
985
+ self._start_delta = start
986
+
987
+ if dstabbr and end is None:
988
+ self._end_delta = relativedelta.relativedelta(
989
+ hours=+1, month=10, day=31, weekday=relativedelta.SU(-1))
990
+ else:
991
+ self._end_delta = end
992
+
993
+ self._dst_base_offset_ = self._dst_offset - self._std_offset
994
+ self.hasdst = bool(self._start_delta)
995
+
996
+ def transitions(self, year):
997
+ """
998
+ For a given year, get the DST on and off transition times, expressed
999
+ always on the standard time side. For zones with no transitions, this
1000
+ function returns ``None``.
1001
+
1002
+ :param year:
1003
+ The year whose transitions you would like to query.
1004
+
1005
+ :return:
1006
+ Returns a :class:`tuple` of :class:`datetime.datetime` objects,
1007
+ ``(dston, dstoff)`` for zones with an annual DST transition, or
1008
+ ``None`` for fixed offset zones.
1009
+ """
1010
+ if not self.hasdst:
1011
+ return None
1012
+
1013
+ base_year = datetime.datetime(year, 1, 1)
1014
+
1015
+ start = base_year + self._start_delta
1016
+ end = base_year + self._end_delta
1017
+
1018
+ return (start, end)
1019
+
1020
+ def __eq__(self, other):
1021
+ if not isinstance(other, tzrange):
1022
+ return NotImplemented
1023
+
1024
+ return (self._std_abbr == other._std_abbr and
1025
+ self._dst_abbr == other._dst_abbr and
1026
+ self._std_offset == other._std_offset and
1027
+ self._dst_offset == other._dst_offset and
1028
+ self._start_delta == other._start_delta and
1029
+ self._end_delta == other._end_delta)
1030
+
1031
+ @property
1032
+ def _dst_base_offset(self):
1033
+ return self._dst_base_offset_
1034
+
1035
+
1036
+ @six.add_metaclass(_TzStrFactory)
1037
+ class tzstr(tzrange):
1038
+ """
1039
+ ``tzstr`` objects are time zone objects specified by a time-zone string as
1040
+ it would be passed to a ``TZ`` variable on POSIX-style systems (see
1041
+ the `GNU C Library: TZ Variable`_ for more details).
1042
+
1043
+ There is one notable exception, which is that POSIX-style time zones use an
1044
+ inverted offset format, so normally ``GMT+3`` would be parsed as an offset
1045
+ 3 hours *behind* GMT. The ``tzstr`` time zone object will parse this as an
1046
+ offset 3 hours *ahead* of GMT. If you would like to maintain the POSIX
1047
+ behavior, pass a ``True`` value to ``posix_offset``.
1048
+
1049
+ The :class:`tzrange` object provides the same functionality, but is
1050
+ specified using :class:`relativedelta.relativedelta` objects. rather than
1051
+ strings.
1052
+
1053
+ :param s:
1054
+ A time zone string in ``TZ`` variable format. This can be a
1055
+ :class:`bytes` (2.x: :class:`str`), :class:`str` (2.x:
1056
+ :class:`unicode`) or a stream emitting unicode characters
1057
+ (e.g. :class:`StringIO`).
1058
+
1059
+ :param posix_offset:
1060
+ Optional. If set to ``True``, interpret strings such as ``GMT+3`` or
1061
+ ``UTC+3`` as being 3 hours *behind* UTC rather than ahead, per the
1062
+ POSIX standard.
1063
+
1064
+ .. caution::
1065
+
1066
+ Prior to version 2.7.0, this function also supported time zones
1067
+ in the format:
1068
+
1069
+ * ``EST5EDT,4,0,6,7200,10,0,26,7200,3600``
1070
+ * ``EST5EDT,4,1,0,7200,10,-1,0,7200,3600``
1071
+
1072
+ This format is non-standard and has been deprecated; this function
1073
+ will raise a :class:`DeprecatedTZFormatWarning` until
1074
+ support is removed in a future version.
1075
+
1076
+ .. _`GNU C Library: TZ Variable`:
1077
+ https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html
1078
+ """
1079
+ def __init__(self, s, posix_offset=False):
1080
+ global parser
1081
+ from dateutil.parser import _parser as parser
1082
+
1083
+ self._s = s
1084
+
1085
+ res = parser._parsetz(s)
1086
+ if res is None or res.any_unused_tokens:
1087
+ raise ValueError("unknown string format")
1088
+
1089
+ # Here we break the compatibility with the TZ variable handling.
1090
+ # GMT-3 actually *means* the timezone -3.
1091
+ if res.stdabbr in ("GMT", "UTC") and not posix_offset:
1092
+ res.stdoffset *= -1
1093
+
1094
+ # We must initialize it first, since _delta() needs
1095
+ # _std_offset and _dst_offset set. Use False in start/end
1096
+ # to avoid building it two times.
1097
+ tzrange.__init__(self, res.stdabbr, res.stdoffset,
1098
+ res.dstabbr, res.dstoffset,
1099
+ start=False, end=False)
1100
+
1101
+ if not res.dstabbr:
1102
+ self._start_delta = None
1103
+ self._end_delta = None
1104
+ else:
1105
+ self._start_delta = self._delta(res.start)
1106
+ if self._start_delta:
1107
+ self._end_delta = self._delta(res.end, isend=1)
1108
+
1109
+ self.hasdst = bool(self._start_delta)
1110
+
1111
+ def _delta(self, x, isend=0):
1112
+ from dateutil import relativedelta
1113
+ kwargs = {}
1114
+ if x.month is not None:
1115
+ kwargs["month"] = x.month
1116
+ if x.weekday is not None:
1117
+ kwargs["weekday"] = relativedelta.weekday(x.weekday, x.week)
1118
+ if x.week > 0:
1119
+ kwargs["day"] = 1
1120
+ else:
1121
+ kwargs["day"] = 31
1122
+ elif x.day:
1123
+ kwargs["day"] = x.day
1124
+ elif x.yday is not None:
1125
+ kwargs["yearday"] = x.yday
1126
+ elif x.jyday is not None:
1127
+ kwargs["nlyearday"] = x.jyday
1128
+ if not kwargs:
1129
+ # Default is to start on first sunday of april, and end
1130
+ # on last sunday of october.
1131
+ if not isend:
1132
+ kwargs["month"] = 4
1133
+ kwargs["day"] = 1
1134
+ kwargs["weekday"] = relativedelta.SU(+1)
1135
+ else:
1136
+ kwargs["month"] = 10
1137
+ kwargs["day"] = 31
1138
+ kwargs["weekday"] = relativedelta.SU(-1)
1139
+ if x.time is not None:
1140
+ kwargs["seconds"] = x.time
1141
+ else:
1142
+ # Default is 2AM.
1143
+ kwargs["seconds"] = 7200
1144
+ if isend:
1145
+ # Convert to standard time, to follow the documented way
1146
+ # of working with the extra hour. See the documentation
1147
+ # of the tzinfo class.
1148
+ delta = self._dst_offset - self._std_offset
1149
+ kwargs["seconds"] -= delta.seconds + delta.days * 86400
1150
+ return relativedelta.relativedelta(**kwargs)
1151
+
1152
+ def __repr__(self):
1153
+ return "%s(%s)" % (self.__class__.__name__, repr(self._s))
1154
+
1155
+
1156
+ class _tzicalvtzcomp(object):
1157
+ def __init__(self, tzoffsetfrom, tzoffsetto, isdst,
1158
+ tzname=None, rrule=None):
1159
+ self.tzoffsetfrom = datetime.timedelta(seconds=tzoffsetfrom)
1160
+ self.tzoffsetto = datetime.timedelta(seconds=tzoffsetto)
1161
+ self.tzoffsetdiff = self.tzoffsetto - self.tzoffsetfrom
1162
+ self.isdst = isdst
1163
+ self.tzname = tzname
1164
+ self.rrule = rrule
1165
+
1166
+
1167
+ class _tzicalvtz(_tzinfo):
1168
+ def __init__(self, tzid, comps=[]):
1169
+ super(_tzicalvtz, self).__init__()
1170
+
1171
+ self._tzid = tzid
1172
+ self._comps = comps
1173
+ self._cachedate = []
1174
+ self._cachecomp = []
1175
+ self._cache_lock = _thread.allocate_lock()
1176
+
1177
+ def _find_comp(self, dt):
1178
+ if len(self._comps) == 1:
1179
+ return self._comps[0]
1180
+
1181
+ dt = dt.replace(tzinfo=None)
1182
+
1183
+ try:
1184
+ with self._cache_lock:
1185
+ return self._cachecomp[self._cachedate.index(
1186
+ (dt, self._fold(dt)))]
1187
+ except ValueError:
1188
+ pass
1189
+
1190
+ lastcompdt = None
1191
+ lastcomp = None
1192
+
1193
+ for comp in self._comps:
1194
+ compdt = self._find_compdt(comp, dt)
1195
+
1196
+ if compdt and (not lastcompdt or lastcompdt < compdt):
1197
+ lastcompdt = compdt
1198
+ lastcomp = comp
1199
+
1200
+ if not lastcomp:
1201
+ # RFC says nothing about what to do when a given
1202
+ # time is before the first onset date. We'll look for the
1203
+ # first standard component, or the first component, if
1204
+ # none is found.
1205
+ for comp in self._comps:
1206
+ if not comp.isdst:
1207
+ lastcomp = comp
1208
+ break
1209
+ else:
1210
+ lastcomp = comp[0]
1211
+
1212
+ with self._cache_lock:
1213
+ self._cachedate.insert(0, (dt, self._fold(dt)))
1214
+ self._cachecomp.insert(0, lastcomp)
1215
+
1216
+ if len(self._cachedate) > 10:
1217
+ self._cachedate.pop()
1218
+ self._cachecomp.pop()
1219
+
1220
+ return lastcomp
1221
+
1222
+ def _find_compdt(self, comp, dt):
1223
+ if comp.tzoffsetdiff < ZERO and self._fold(dt):
1224
+ dt -= comp.tzoffsetdiff
1225
+
1226
+ compdt = comp.rrule.before(dt, inc=True)
1227
+
1228
+ return compdt
1229
+
1230
+ def utcoffset(self, dt):
1231
+ if dt is None:
1232
+ return None
1233
+
1234
+ return self._find_comp(dt).tzoffsetto
1235
+
1236
+ def dst(self, dt):
1237
+ comp = self._find_comp(dt)
1238
+ if comp.isdst:
1239
+ return comp.tzoffsetdiff
1240
+ else:
1241
+ return ZERO
1242
+
1243
+ @tzname_in_python2
1244
+ def tzname(self, dt):
1245
+ return self._find_comp(dt).tzname
1246
+
1247
+ def __repr__(self):
1248
+ return "<tzicalvtz %s>" % repr(self._tzid)
1249
+
1250
+ __reduce__ = object.__reduce__
1251
+
1252
+
1253
+ class tzical(object):
1254
+ """
1255
+ This object is designed to parse an iCalendar-style ``VTIMEZONE`` structure
1256
+ as set out in `RFC 5545`_ Section 4.6.5 into one or more `tzinfo` objects.
1257
+
1258
+ :param `fileobj`:
1259
+ A file or stream in iCalendar format, which should be UTF-8 encoded
1260
+ with CRLF endings.
1261
+
1262
+ .. _`RFC 5545`: https://tools.ietf.org/html/rfc5545
1263
+ """
1264
+ def __init__(self, fileobj):
1265
+ global rrule
1266
+ from dateutil import rrule
1267
+
1268
+ if isinstance(fileobj, string_types):
1269
+ self._s = fileobj
1270
+ # ical should be encoded in UTF-8 with CRLF
1271
+ fileobj = open(fileobj, 'r')
1272
+ else:
1273
+ self._s = getattr(fileobj, 'name', repr(fileobj))
1274
+ fileobj = _nullcontext(fileobj)
1275
+
1276
+ self._vtz = {}
1277
+
1278
+ with fileobj as fobj:
1279
+ self._parse_rfc(fobj.read())
1280
+
1281
+ def keys(self):
1282
+ """
1283
+ Retrieves the available time zones as a list.
1284
+ """
1285
+ return list(self._vtz.keys())
1286
+
1287
+ def get(self, tzid=None):
1288
+ """
1289
+ Retrieve a :py:class:`datetime.tzinfo` object by its ``tzid``.
1290
+
1291
+ :param tzid:
1292
+ If there is exactly one time zone available, omitting ``tzid``
1293
+ or passing :py:const:`None` value returns it. Otherwise a valid
1294
+ key (which can be retrieved from :func:`keys`) is required.
1295
+
1296
+ :raises ValueError:
1297
+ Raised if ``tzid`` is not specified but there are either more
1298
+ or fewer than 1 zone defined.
1299
+
1300
+ :returns:
1301
+ Returns either a :py:class:`datetime.tzinfo` object representing
1302
+ the relevant time zone or :py:const:`None` if the ``tzid`` was
1303
+ not found.
1304
+ """
1305
+ if tzid is None:
1306
+ if len(self._vtz) == 0:
1307
+ raise ValueError("no timezones defined")
1308
+ elif len(self._vtz) > 1:
1309
+ raise ValueError("more than one timezone available")
1310
+ tzid = next(iter(self._vtz))
1311
+
1312
+ return self._vtz.get(tzid)
1313
+
1314
+ def _parse_offset(self, s):
1315
+ s = s.strip()
1316
+ if not s:
1317
+ raise ValueError("empty offset")
1318
+ if s[0] in ('+', '-'):
1319
+ signal = (-1, +1)[s[0] == '+']
1320
+ s = s[1:]
1321
+ else:
1322
+ signal = +1
1323
+ if len(s) == 4:
1324
+ return (int(s[:2]) * 3600 + int(s[2:]) * 60) * signal
1325
+ elif len(s) == 6:
1326
+ return (int(s[:2]) * 3600 + int(s[2:4]) * 60 + int(s[4:])) * signal
1327
+ else:
1328
+ raise ValueError("invalid offset: " + s)
1329
+
1330
+ def _parse_rfc(self, s):
1331
+ lines = s.splitlines()
1332
+ if not lines:
1333
+ raise ValueError("empty string")
1334
+
1335
+ # Unfold
1336
+ i = 0
1337
+ while i < len(lines):
1338
+ line = lines[i].rstrip()
1339
+ if not line:
1340
+ del lines[i]
1341
+ elif i > 0 and line[0] == " ":
1342
+ lines[i-1] += line[1:]
1343
+ del lines[i]
1344
+ else:
1345
+ i += 1
1346
+
1347
+ tzid = None
1348
+ comps = []
1349
+ invtz = False
1350
+ comptype = None
1351
+ for line in lines:
1352
+ if not line:
1353
+ continue
1354
+ name, value = line.split(':', 1)
1355
+ parms = name.split(';')
1356
+ if not parms:
1357
+ raise ValueError("empty property name")
1358
+ name = parms[0].upper()
1359
+ parms = parms[1:]
1360
+ if invtz:
1361
+ if name == "BEGIN":
1362
+ if value in ("STANDARD", "DAYLIGHT"):
1363
+ # Process component
1364
+ pass
1365
+ else:
1366
+ raise ValueError("unknown component: "+value)
1367
+ comptype = value
1368
+ founddtstart = False
1369
+ tzoffsetfrom = None
1370
+ tzoffsetto = None
1371
+ rrulelines = []
1372
+ tzname = None
1373
+ elif name == "END":
1374
+ if value == "VTIMEZONE":
1375
+ if comptype:
1376
+ raise ValueError("component not closed: "+comptype)
1377
+ if not tzid:
1378
+ raise ValueError("mandatory TZID not found")
1379
+ if not comps:
1380
+ raise ValueError(
1381
+ "at least one component is needed")
1382
+ # Process vtimezone
1383
+ self._vtz[tzid] = _tzicalvtz(tzid, comps)
1384
+ invtz = False
1385
+ elif value == comptype:
1386
+ if not founddtstart:
1387
+ raise ValueError("mandatory DTSTART not found")
1388
+ if tzoffsetfrom is None:
1389
+ raise ValueError(
1390
+ "mandatory TZOFFSETFROM not found")
1391
+ if tzoffsetto is None:
1392
+ raise ValueError(
1393
+ "mandatory TZOFFSETFROM not found")
1394
+ # Process component
1395
+ rr = None
1396
+ if rrulelines:
1397
+ rr = rrule.rrulestr("\n".join(rrulelines),
1398
+ compatible=True,
1399
+ ignoretz=True,
1400
+ cache=True)
1401
+ comp = _tzicalvtzcomp(tzoffsetfrom, tzoffsetto,
1402
+ (comptype == "DAYLIGHT"),
1403
+ tzname, rr)
1404
+ comps.append(comp)
1405
+ comptype = None
1406
+ else:
1407
+ raise ValueError("invalid component end: "+value)
1408
+ elif comptype:
1409
+ if name == "DTSTART":
1410
+ # DTSTART in VTIMEZONE takes a subset of valid RRULE
1411
+ # values under RFC 5545.
1412
+ for parm in parms:
1413
+ if parm != 'VALUE=DATE-TIME':
1414
+ msg = ('Unsupported DTSTART param in ' +
1415
+ 'VTIMEZONE: ' + parm)
1416
+ raise ValueError(msg)
1417
+ rrulelines.append(line)
1418
+ founddtstart = True
1419
+ elif name in ("RRULE", "RDATE", "EXRULE", "EXDATE"):
1420
+ rrulelines.append(line)
1421
+ elif name == "TZOFFSETFROM":
1422
+ if parms:
1423
+ raise ValueError(
1424
+ "unsupported %s parm: %s " % (name, parms[0]))
1425
+ tzoffsetfrom = self._parse_offset(value)
1426
+ elif name == "TZOFFSETTO":
1427
+ if parms:
1428
+ raise ValueError(
1429
+ "unsupported TZOFFSETTO parm: "+parms[0])
1430
+ tzoffsetto = self._parse_offset(value)
1431
+ elif name == "TZNAME":
1432
+ if parms:
1433
+ raise ValueError(
1434
+ "unsupported TZNAME parm: "+parms[0])
1435
+ tzname = value
1436
+ elif name == "COMMENT":
1437
+ pass
1438
+ else:
1439
+ raise ValueError("unsupported property: "+name)
1440
+ else:
1441
+ if name == "TZID":
1442
+ if parms:
1443
+ raise ValueError(
1444
+ "unsupported TZID parm: "+parms[0])
1445
+ tzid = value
1446
+ elif name in ("TZURL", "LAST-MODIFIED", "COMMENT"):
1447
+ pass
1448
+ else:
1449
+ raise ValueError("unsupported property: "+name)
1450
+ elif name == "BEGIN" and value == "VTIMEZONE":
1451
+ tzid = None
1452
+ comps = []
1453
+ invtz = True
1454
+
1455
+ def __repr__(self):
1456
+ return "%s(%s)" % (self.__class__.__name__, repr(self._s))
1457
+
1458
+
1459
+ if sys.platform != "win32":
1460
+ TZFILES = ["/etc/localtime", "localtime"]
1461
+ TZPATHS = ["/usr/share/zoneinfo",
1462
+ "/usr/lib/zoneinfo",
1463
+ "/usr/share/lib/zoneinfo",
1464
+ "/etc/zoneinfo"]
1465
+ else:
1466
+ TZFILES = []
1467
+ TZPATHS = []
1468
+
1469
+
1470
+ def __get_gettz():
1471
+ tzlocal_classes = (tzlocal,)
1472
+ if tzwinlocal is not None:
1473
+ tzlocal_classes += (tzwinlocal,)
1474
+
1475
+ class GettzFunc(object):
1476
+ """
1477
+ Retrieve a time zone object from a string representation
1478
+
1479
+ This function is intended to retrieve the :py:class:`tzinfo` subclass
1480
+ that best represents the time zone that would be used if a POSIX
1481
+ `TZ variable`_ were set to the same value.
1482
+
1483
+ If no argument or an empty string is passed to ``gettz``, local time
1484
+ is returned:
1485
+
1486
+ .. code-block:: python3
1487
+
1488
+ >>> gettz()
1489
+ tzfile('/etc/localtime')
1490
+
1491
+ This function is also the preferred way to map IANA tz database keys
1492
+ to :class:`tzfile` objects:
1493
+
1494
+ .. code-block:: python3
1495
+
1496
+ >>> gettz('Pacific/Kiritimati')
1497
+ tzfile('/usr/share/zoneinfo/Pacific/Kiritimati')
1498
+
1499
+ On Windows, the standard is extended to include the Windows-specific
1500
+ zone names provided by the operating system:
1501
+
1502
+ .. code-block:: python3
1503
+
1504
+ >>> gettz('Egypt Standard Time')
1505
+ tzwin('Egypt Standard Time')
1506
+
1507
+ Passing a GNU ``TZ`` style string time zone specification returns a
1508
+ :class:`tzstr` object:
1509
+
1510
+ .. code-block:: python3
1511
+
1512
+ >>> gettz('AEST-10AEDT-11,M10.1.0/2,M4.1.0/3')
1513
+ tzstr('AEST-10AEDT-11,M10.1.0/2,M4.1.0/3')
1514
+
1515
+ :param name:
1516
+ A time zone name (IANA, or, on Windows, Windows keys), location of
1517
+ a ``tzfile(5)`` zoneinfo file or ``TZ`` variable style time zone
1518
+ specifier. An empty string, no argument or ``None`` is interpreted
1519
+ as local time.
1520
+
1521
+ :return:
1522
+ Returns an instance of one of ``dateutil``'s :py:class:`tzinfo`
1523
+ subclasses.
1524
+
1525
+ .. versionchanged:: 2.7.0
1526
+
1527
+ After version 2.7.0, any two calls to ``gettz`` using the same
1528
+ input strings will return the same object:
1529
+
1530
+ .. code-block:: python3
1531
+
1532
+ >>> tz.gettz('America/Chicago') is tz.gettz('America/Chicago')
1533
+ True
1534
+
1535
+ In addition to improving performance, this ensures that
1536
+ `"same zone" semantics`_ are used for datetimes in the same zone.
1537
+
1538
+
1539
+ .. _`TZ variable`:
1540
+ https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html
1541
+
1542
+ .. _`"same zone" semantics`:
1543
+ https://blog.ganssle.io/articles/2018/02/aware-datetime-arithmetic.html
1544
+ """
1545
+ def __init__(self):
1546
+
1547
+ self.__instances = weakref.WeakValueDictionary()
1548
+ self.__strong_cache_size = 8
1549
+ self.__strong_cache = OrderedDict()
1550
+ self._cache_lock = _thread.allocate_lock()
1551
+
1552
+ def __call__(self, name=None):
1553
+ with self._cache_lock:
1554
+ rv = self.__instances.get(name, None)
1555
+
1556
+ if rv is None:
1557
+ rv = self.nocache(name=name)
1558
+ if not (name is None
1559
+ or isinstance(rv, tzlocal_classes)
1560
+ or rv is None):
1561
+ # tzlocal is slightly more complicated than the other
1562
+ # time zone providers because it depends on environment
1563
+ # at construction time, so don't cache that.
1564
+ #
1565
+ # We also cannot store weak references to None, so we
1566
+ # will also not store that.
1567
+ self.__instances[name] = rv
1568
+ else:
1569
+ # No need for strong caching, return immediately
1570
+ return rv
1571
+
1572
+ self.__strong_cache[name] = self.__strong_cache.pop(name, rv)
1573
+
1574
+ if len(self.__strong_cache) > self.__strong_cache_size:
1575
+ self.__strong_cache.popitem(last=False)
1576
+
1577
+ return rv
1578
+
1579
+ def set_cache_size(self, size):
1580
+ with self._cache_lock:
1581
+ self.__strong_cache_size = size
1582
+ while len(self.__strong_cache) > size:
1583
+ self.__strong_cache.popitem(last=False)
1584
+
1585
+ def cache_clear(self):
1586
+ with self._cache_lock:
1587
+ self.__instances = weakref.WeakValueDictionary()
1588
+ self.__strong_cache.clear()
1589
+
1590
+ @staticmethod
1591
+ def nocache(name=None):
1592
+ """A non-cached version of gettz"""
1593
+ tz = None
1594
+ if not name:
1595
+ try:
1596
+ name = os.environ["TZ"]
1597
+ except KeyError:
1598
+ pass
1599
+ if name is None or name in ("", ":"):
1600
+ for filepath in TZFILES:
1601
+ if not os.path.isabs(filepath):
1602
+ filename = filepath
1603
+ for path in TZPATHS:
1604
+ filepath = os.path.join(path, filename)
1605
+ if os.path.isfile(filepath):
1606
+ break
1607
+ else:
1608
+ continue
1609
+ if os.path.isfile(filepath):
1610
+ try:
1611
+ tz = tzfile(filepath)
1612
+ break
1613
+ except (IOError, OSError, ValueError):
1614
+ pass
1615
+ else:
1616
+ tz = tzlocal()
1617
+ else:
1618
+ try:
1619
+ if name.startswith(":"):
1620
+ name = name[1:]
1621
+ except TypeError as e:
1622
+ if isinstance(name, bytes):
1623
+ new_msg = "gettz argument should be str, not bytes"
1624
+ six.raise_from(TypeError(new_msg), e)
1625
+ else:
1626
+ raise
1627
+ if os.path.isabs(name):
1628
+ if os.path.isfile(name):
1629
+ tz = tzfile(name)
1630
+ else:
1631
+ tz = None
1632
+ else:
1633
+ for path in TZPATHS:
1634
+ filepath = os.path.join(path, name)
1635
+ if not os.path.isfile(filepath):
1636
+ filepath = filepath.replace(' ', '_')
1637
+ if not os.path.isfile(filepath):
1638
+ continue
1639
+ try:
1640
+ tz = tzfile(filepath)
1641
+ break
1642
+ except (IOError, OSError, ValueError):
1643
+ pass
1644
+ else:
1645
+ tz = None
1646
+ if tzwin is not None:
1647
+ try:
1648
+ tz = tzwin(name)
1649
+ except (WindowsError, UnicodeEncodeError):
1650
+ # UnicodeEncodeError is for Python 2.7 compat
1651
+ tz = None
1652
+
1653
+ if not tz:
1654
+ from dateutil.zoneinfo import get_zonefile_instance
1655
+ tz = get_zonefile_instance().get(name)
1656
+
1657
+ if not tz:
1658
+ for c in name:
1659
+ # name is not a tzstr unless it has at least
1660
+ # one offset. For short values of "name", an
1661
+ # explicit for loop seems to be the fastest way
1662
+ # To determine if a string contains a digit
1663
+ if c in "0123456789":
1664
+ try:
1665
+ tz = tzstr(name)
1666
+ except ValueError:
1667
+ pass
1668
+ break
1669
+ else:
1670
+ if name in ("GMT", "UTC"):
1671
+ tz = UTC
1672
+ elif name in time.tzname:
1673
+ tz = tzlocal()
1674
+ return tz
1675
+
1676
+ return GettzFunc()
1677
+
1678
+
1679
+ gettz = __get_gettz()
1680
+ del __get_gettz
1681
+
1682
+
1683
+ def datetime_exists(dt, tz=None):
1684
+ """
1685
+ Given a datetime and a time zone, determine whether or not a given datetime
1686
+ would fall in a gap.
1687
+
1688
+ :param dt:
1689
+ A :class:`datetime.datetime` (whose time zone will be ignored if ``tz``
1690
+ is provided.)
1691
+
1692
+ :param tz:
1693
+ A :class:`datetime.tzinfo` with support for the ``fold`` attribute. If
1694
+ ``None`` or not provided, the datetime's own time zone will be used.
1695
+
1696
+ :return:
1697
+ Returns a boolean value whether or not the "wall time" exists in
1698
+ ``tz``.
1699
+
1700
+ .. versionadded:: 2.7.0
1701
+ """
1702
+ if tz is None:
1703
+ if dt.tzinfo is None:
1704
+ raise ValueError('Datetime is naive and no time zone provided.')
1705
+ tz = dt.tzinfo
1706
+
1707
+ dt = dt.replace(tzinfo=None)
1708
+
1709
+ # This is essentially a test of whether or not the datetime can survive
1710
+ # a round trip to UTC.
1711
+ dt_rt = dt.replace(tzinfo=tz).astimezone(UTC).astimezone(tz)
1712
+ dt_rt = dt_rt.replace(tzinfo=None)
1713
+
1714
+ return dt == dt_rt
1715
+
1716
+
1717
+ def datetime_ambiguous(dt, tz=None):
1718
+ """
1719
+ Given a datetime and a time zone, determine whether or not a given datetime
1720
+ is ambiguous (i.e if there are two times differentiated only by their DST
1721
+ status).
1722
+
1723
+ :param dt:
1724
+ A :class:`datetime.datetime` (whose time zone will be ignored if ``tz``
1725
+ is provided.)
1726
+
1727
+ :param tz:
1728
+ A :class:`datetime.tzinfo` with support for the ``fold`` attribute. If
1729
+ ``None`` or not provided, the datetime's own time zone will be used.
1730
+
1731
+ :return:
1732
+ Returns a boolean value whether or not the "wall time" is ambiguous in
1733
+ ``tz``.
1734
+
1735
+ .. versionadded:: 2.6.0
1736
+ """
1737
+ if tz is None:
1738
+ if dt.tzinfo is None:
1739
+ raise ValueError('Datetime is naive and no time zone provided.')
1740
+
1741
+ tz = dt.tzinfo
1742
+
1743
+ # If a time zone defines its own "is_ambiguous" function, we'll use that.
1744
+ is_ambiguous_fn = getattr(tz, 'is_ambiguous', None)
1745
+ if is_ambiguous_fn is not None:
1746
+ try:
1747
+ return tz.is_ambiguous(dt)
1748
+ except Exception:
1749
+ pass
1750
+
1751
+ # If it doesn't come out and tell us it's ambiguous, we'll just check if
1752
+ # the fold attribute has any effect on this particular date and time.
1753
+ dt = dt.replace(tzinfo=tz)
1754
+ wall_0 = enfold(dt, fold=0)
1755
+ wall_1 = enfold(dt, fold=1)
1756
+
1757
+ same_offset = wall_0.utcoffset() == wall_1.utcoffset()
1758
+ same_dst = wall_0.dst() == wall_1.dst()
1759
+
1760
+ return not (same_offset and same_dst)
1761
+
1762
+
1763
+ def resolve_imaginary(dt):
1764
+ """
1765
+ Given a datetime that may be imaginary, return an existing datetime.
1766
+
1767
+ This function assumes that an imaginary datetime represents what the
1768
+ wall time would be in a zone had the offset transition not occurred, so
1769
+ it will always fall forward by the transition's change in offset.
1770
+
1771
+ .. doctest::
1772
+
1773
+ >>> from dateutil import tz
1774
+ >>> from datetime import datetime
1775
+ >>> NYC = tz.gettz('America/New_York')
1776
+ >>> print(tz.resolve_imaginary(datetime(2017, 3, 12, 2, 30, tzinfo=NYC)))
1777
+ 2017-03-12 03:30:00-04:00
1778
+
1779
+ >>> KIR = tz.gettz('Pacific/Kiritimati')
1780
+ >>> print(tz.resolve_imaginary(datetime(1995, 1, 1, 12, 30, tzinfo=KIR)))
1781
+ 1995-01-02 12:30:00+14:00
1782
+
1783
+ As a note, :func:`datetime.astimezone` is guaranteed to produce a valid,
1784
+ existing datetime, so a round-trip to and from UTC is sufficient to get
1785
+ an extant datetime, however, this generally "falls back" to an earlier time
1786
+ rather than falling forward to the STD side (though no guarantees are made
1787
+ about this behavior).
1788
+
1789
+ :param dt:
1790
+ A :class:`datetime.datetime` which may or may not exist.
1791
+
1792
+ :return:
1793
+ Returns an existing :class:`datetime.datetime`. If ``dt`` was not
1794
+ imaginary, the datetime returned is guaranteed to be the same object
1795
+ passed to the function.
1796
+
1797
+ .. versionadded:: 2.7.0
1798
+ """
1799
+ if dt.tzinfo is not None and not datetime_exists(dt):
1800
+
1801
+ curr_offset = (dt + datetime.timedelta(hours=24)).utcoffset()
1802
+ old_offset = (dt - datetime.timedelta(hours=24)).utcoffset()
1803
+
1804
+ dt += curr_offset - old_offset
1805
+
1806
+ return dt
1807
+
1808
+
1809
+ def _datetime_to_timestamp(dt):
1810
+ """
1811
+ Convert a :class:`datetime.datetime` object to an epoch timestamp in
1812
+ seconds since January 1, 1970, ignoring the time zone.
1813
+ """
1814
+ return (dt.replace(tzinfo=None) - EPOCH).total_seconds()
1815
+
1816
+
1817
+ if sys.version_info >= (3, 6):
1818
+ def _get_supported_offset(second_offset):
1819
+ return second_offset
1820
+ else:
1821
+ def _get_supported_offset(second_offset):
1822
+ # For python pre-3.6, round to full-minutes if that's not the case.
1823
+ # Python's datetime doesn't accept sub-minute timezones. Check
1824
+ # http://python.org/sf/1447945 or https://bugs.python.org/issue5288
1825
+ # for some information.
1826
+ old_offset = second_offset
1827
+ calculated_offset = 60 * ((second_offset + 30) // 60)
1828
+ return calculated_offset
1829
+
1830
+
1831
+ try:
1832
+ # Python 3.7 feature
1833
+ from contextlib import nullcontext as _nullcontext
1834
+ except ImportError:
1835
+ class _nullcontext(object):
1836
+ """
1837
+ Class for wrapping contexts so that they are passed through in a
1838
+ with statement.
1839
+ """
1840
+ def __init__(self, context):
1841
+ self.context = context
1842
+
1843
+ def __enter__(self):
1844
+ return self.context
1845
+
1846
+ def __exit__(*args, **kwargs):
1847
+ pass
1848
+
1849
+ # vim:ts=4:sw=4:et
dateutil/tz/win.py ADDED
@@ -0,0 +1,370 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ This module provides an interface to the native time zone data on Windows,
4
+ including :py:class:`datetime.tzinfo` implementations.
5
+
6
+ Attempting to import this module on a non-Windows platform will raise an
7
+ :py:obj:`ImportError`.
8
+ """
9
+ # This code was originally contributed by Jeffrey Harris.
10
+ import datetime
11
+ import struct
12
+
13
+ from six.moves import winreg
14
+ from six import text_type
15
+
16
+ try:
17
+ import ctypes
18
+ from ctypes import wintypes
19
+ except ValueError:
20
+ # ValueError is raised on non-Windows systems for some horrible reason.
21
+ raise ImportError("Running tzwin on non-Windows system")
22
+
23
+ from ._common import tzrangebase
24
+
25
+ __all__ = ["tzwin", "tzwinlocal", "tzres"]
26
+
27
+ ONEWEEK = datetime.timedelta(7)
28
+
29
+ TZKEYNAMENT = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones"
30
+ TZKEYNAME9X = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones"
31
+ TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation"
32
+
33
+
34
+ def _settzkeyname():
35
+ handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
36
+ try:
37
+ winreg.OpenKey(handle, TZKEYNAMENT).Close()
38
+ TZKEYNAME = TZKEYNAMENT
39
+ except WindowsError:
40
+ TZKEYNAME = TZKEYNAME9X
41
+ handle.Close()
42
+ return TZKEYNAME
43
+
44
+
45
+ TZKEYNAME = _settzkeyname()
46
+
47
+
48
+ class tzres(object):
49
+ """
50
+ Class for accessing ``tzres.dll``, which contains timezone name related
51
+ resources.
52
+
53
+ .. versionadded:: 2.5.0
54
+ """
55
+ p_wchar = ctypes.POINTER(wintypes.WCHAR) # Pointer to a wide char
56
+
57
+ def __init__(self, tzres_loc='tzres.dll'):
58
+ # Load the user32 DLL so we can load strings from tzres
59
+ user32 = ctypes.WinDLL('user32')
60
+
61
+ # Specify the LoadStringW function
62
+ user32.LoadStringW.argtypes = (wintypes.HINSTANCE,
63
+ wintypes.UINT,
64
+ wintypes.LPWSTR,
65
+ ctypes.c_int)
66
+
67
+ self.LoadStringW = user32.LoadStringW
68
+ self._tzres = ctypes.WinDLL(tzres_loc)
69
+ self.tzres_loc = tzres_loc
70
+
71
+ def load_name(self, offset):
72
+ """
73
+ Load a timezone name from a DLL offset (integer).
74
+
75
+ >>> from dateutil.tzwin import tzres
76
+ >>> tzr = tzres()
77
+ >>> print(tzr.load_name(112))
78
+ 'Eastern Standard Time'
79
+
80
+ :param offset:
81
+ A positive integer value referring to a string from the tzres dll.
82
+
83
+ .. note::
84
+
85
+ Offsets found in the registry are generally of the form
86
+ ``@tzres.dll,-114``. The offset in this case is 114, not -114.
87
+
88
+ """
89
+ resource = self.p_wchar()
90
+ lpBuffer = ctypes.cast(ctypes.byref(resource), wintypes.LPWSTR)
91
+ nchar = self.LoadStringW(self._tzres._handle, offset, lpBuffer, 0)
92
+ return resource[:nchar]
93
+
94
+ def name_from_string(self, tzname_str):
95
+ """
96
+ Parse strings as returned from the Windows registry into the time zone
97
+ name as defined in the registry.
98
+
99
+ >>> from dateutil.tzwin import tzres
100
+ >>> tzr = tzres()
101
+ >>> print(tzr.name_from_string('@tzres.dll,-251'))
102
+ 'Dateline Daylight Time'
103
+ >>> print(tzr.name_from_string('Eastern Standard Time'))
104
+ 'Eastern Standard Time'
105
+
106
+ :param tzname_str:
107
+ A timezone name string as returned from a Windows registry key.
108
+
109
+ :return:
110
+ Returns the localized timezone string from tzres.dll if the string
111
+ is of the form `@tzres.dll,-offset`, else returns the input string.
112
+ """
113
+ if not tzname_str.startswith('@'):
114
+ return tzname_str
115
+
116
+ name_splt = tzname_str.split(',-')
117
+ try:
118
+ offset = int(name_splt[1])
119
+ except:
120
+ raise ValueError("Malformed timezone string.")
121
+
122
+ return self.load_name(offset)
123
+
124
+
125
+ class tzwinbase(tzrangebase):
126
+ """tzinfo class based on win32's timezones available in the registry."""
127
+ def __init__(self):
128
+ raise NotImplementedError('tzwinbase is an abstract base class')
129
+
130
+ def __eq__(self, other):
131
+ # Compare on all relevant dimensions, including name.
132
+ if not isinstance(other, tzwinbase):
133
+ return NotImplemented
134
+
135
+ return (self._std_offset == other._std_offset and
136
+ self._dst_offset == other._dst_offset and
137
+ self._stddayofweek == other._stddayofweek and
138
+ self._dstdayofweek == other._dstdayofweek and
139
+ self._stdweeknumber == other._stdweeknumber and
140
+ self._dstweeknumber == other._dstweeknumber and
141
+ self._stdhour == other._stdhour and
142
+ self._dsthour == other._dsthour and
143
+ self._stdminute == other._stdminute and
144
+ self._dstminute == other._dstminute and
145
+ self._std_abbr == other._std_abbr and
146
+ self._dst_abbr == other._dst_abbr)
147
+
148
+ @staticmethod
149
+ def list():
150
+ """Return a list of all time zones known to the system."""
151
+ with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle:
152
+ with winreg.OpenKey(handle, TZKEYNAME) as tzkey:
153
+ result = [winreg.EnumKey(tzkey, i)
154
+ for i in range(winreg.QueryInfoKey(tzkey)[0])]
155
+ return result
156
+
157
+ def display(self):
158
+ """
159
+ Return the display name of the time zone.
160
+ """
161
+ return self._display
162
+
163
+ def transitions(self, year):
164
+ """
165
+ For a given year, get the DST on and off transition times, expressed
166
+ always on the standard time side. For zones with no transitions, this
167
+ function returns ``None``.
168
+
169
+ :param year:
170
+ The year whose transitions you would like to query.
171
+
172
+ :return:
173
+ Returns a :class:`tuple` of :class:`datetime.datetime` objects,
174
+ ``(dston, dstoff)`` for zones with an annual DST transition, or
175
+ ``None`` for fixed offset zones.
176
+ """
177
+
178
+ if not self.hasdst:
179
+ return None
180
+
181
+ dston = picknthweekday(year, self._dstmonth, self._dstdayofweek,
182
+ self._dsthour, self._dstminute,
183
+ self._dstweeknumber)
184
+
185
+ dstoff = picknthweekday(year, self._stdmonth, self._stddayofweek,
186
+ self._stdhour, self._stdminute,
187
+ self._stdweeknumber)
188
+
189
+ # Ambiguous dates default to the STD side
190
+ dstoff -= self._dst_base_offset
191
+
192
+ return dston, dstoff
193
+
194
+ def _get_hasdst(self):
195
+ return self._dstmonth != 0
196
+
197
+ @property
198
+ def _dst_base_offset(self):
199
+ return self._dst_base_offset_
200
+
201
+
202
+ class tzwin(tzwinbase):
203
+ """
204
+ Time zone object created from the zone info in the Windows registry
205
+
206
+ These are similar to :py:class:`dateutil.tz.tzrange` objects in that
207
+ the time zone data is provided in the format of a single offset rule
208
+ for either 0 or 2 time zone transitions per year.
209
+
210
+ :param: name
211
+ The name of a Windows time zone key, e.g. "Eastern Standard Time".
212
+ The full list of keys can be retrieved with :func:`tzwin.list`.
213
+ """
214
+
215
+ def __init__(self, name):
216
+ self._name = name
217
+
218
+ with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle:
219
+ tzkeyname = text_type("{kn}\\{name}").format(kn=TZKEYNAME, name=name)
220
+ with winreg.OpenKey(handle, tzkeyname) as tzkey:
221
+ keydict = valuestodict(tzkey)
222
+
223
+ self._std_abbr = keydict["Std"]
224
+ self._dst_abbr = keydict["Dlt"]
225
+
226
+ self._display = keydict["Display"]
227
+
228
+ # See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm
229
+ tup = struct.unpack("=3l16h", keydict["TZI"])
230
+ stdoffset = -tup[0]-tup[1] # Bias + StandardBias * -1
231
+ dstoffset = stdoffset-tup[2] # + DaylightBias * -1
232
+ self._std_offset = datetime.timedelta(minutes=stdoffset)
233
+ self._dst_offset = datetime.timedelta(minutes=dstoffset)
234
+
235
+ # for the meaning see the win32 TIME_ZONE_INFORMATION structure docs
236
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/ms725481(v=vs.85).aspx
237
+ (self._stdmonth,
238
+ self._stddayofweek, # Sunday = 0
239
+ self._stdweeknumber, # Last = 5
240
+ self._stdhour,
241
+ self._stdminute) = tup[4:9]
242
+
243
+ (self._dstmonth,
244
+ self._dstdayofweek, # Sunday = 0
245
+ self._dstweeknumber, # Last = 5
246
+ self._dsthour,
247
+ self._dstminute) = tup[12:17]
248
+
249
+ self._dst_base_offset_ = self._dst_offset - self._std_offset
250
+ self.hasdst = self._get_hasdst()
251
+
252
+ def __repr__(self):
253
+ return "tzwin(%s)" % repr(self._name)
254
+
255
+ def __reduce__(self):
256
+ return (self.__class__, (self._name,))
257
+
258
+
259
+ class tzwinlocal(tzwinbase):
260
+ """
261
+ Class representing the local time zone information in the Windows registry
262
+
263
+ While :class:`dateutil.tz.tzlocal` makes system calls (via the :mod:`time`
264
+ module) to retrieve time zone information, ``tzwinlocal`` retrieves the
265
+ rules directly from the Windows registry and creates an object like
266
+ :class:`dateutil.tz.tzwin`.
267
+
268
+ Because Windows does not have an equivalent of :func:`time.tzset`, on
269
+ Windows, :class:`dateutil.tz.tzlocal` instances will always reflect the
270
+ time zone settings *at the time that the process was started*, meaning
271
+ changes to the machine's time zone settings during the run of a program
272
+ on Windows will **not** be reflected by :class:`dateutil.tz.tzlocal`.
273
+ Because ``tzwinlocal`` reads the registry directly, it is unaffected by
274
+ this issue.
275
+ """
276
+ def __init__(self):
277
+ with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle:
278
+ with winreg.OpenKey(handle, TZLOCALKEYNAME) as tzlocalkey:
279
+ keydict = valuestodict(tzlocalkey)
280
+
281
+ self._std_abbr = keydict["StandardName"]
282
+ self._dst_abbr = keydict["DaylightName"]
283
+
284
+ try:
285
+ tzkeyname = text_type('{kn}\\{sn}').format(kn=TZKEYNAME,
286
+ sn=self._std_abbr)
287
+ with winreg.OpenKey(handle, tzkeyname) as tzkey:
288
+ _keydict = valuestodict(tzkey)
289
+ self._display = _keydict["Display"]
290
+ except OSError:
291
+ self._display = None
292
+
293
+ stdoffset = -keydict["Bias"]-keydict["StandardBias"]
294
+ dstoffset = stdoffset-keydict["DaylightBias"]
295
+
296
+ self._std_offset = datetime.timedelta(minutes=stdoffset)
297
+ self._dst_offset = datetime.timedelta(minutes=dstoffset)
298
+
299
+ # For reasons unclear, in this particular key, the day of week has been
300
+ # moved to the END of the SYSTEMTIME structure.
301
+ tup = struct.unpack("=8h", keydict["StandardStart"])
302
+
303
+ (self._stdmonth,
304
+ self._stdweeknumber, # Last = 5
305
+ self._stdhour,
306
+ self._stdminute) = tup[1:5]
307
+
308
+ self._stddayofweek = tup[7]
309
+
310
+ tup = struct.unpack("=8h", keydict["DaylightStart"])
311
+
312
+ (self._dstmonth,
313
+ self._dstweeknumber, # Last = 5
314
+ self._dsthour,
315
+ self._dstminute) = tup[1:5]
316
+
317
+ self._dstdayofweek = tup[7]
318
+
319
+ self._dst_base_offset_ = self._dst_offset - self._std_offset
320
+ self.hasdst = self._get_hasdst()
321
+
322
+ def __repr__(self):
323
+ return "tzwinlocal()"
324
+
325
+ def __str__(self):
326
+ # str will return the standard name, not the daylight name.
327
+ return "tzwinlocal(%s)" % repr(self._std_abbr)
328
+
329
+ def __reduce__(self):
330
+ return (self.__class__, ())
331
+
332
+
333
+ def picknthweekday(year, month, dayofweek, hour, minute, whichweek):
334
+ """ dayofweek == 0 means Sunday, whichweek 5 means last instance """
335
+ first = datetime.datetime(year, month, 1, hour, minute)
336
+
337
+ # This will work if dayofweek is ISO weekday (1-7) or Microsoft-style (0-6),
338
+ # Because 7 % 7 = 0
339
+ weekdayone = first.replace(day=((dayofweek - first.isoweekday()) % 7) + 1)
340
+ wd = weekdayone + ((whichweek - 1) * ONEWEEK)
341
+ if (wd.month != month):
342
+ wd -= ONEWEEK
343
+
344
+ return wd
345
+
346
+
347
+ def valuestodict(key):
348
+ """Convert a registry key's values to a dictionary."""
349
+ dout = {}
350
+ size = winreg.QueryInfoKey(key)[1]
351
+ tz_res = None
352
+
353
+ for i in range(size):
354
+ key_name, value, dtype = winreg.EnumValue(key, i)
355
+ if dtype == winreg.REG_DWORD or dtype == winreg.REG_DWORD_LITTLE_ENDIAN:
356
+ # If it's a DWORD (32-bit integer), it's stored as unsigned - convert
357
+ # that to a proper signed integer
358
+ if value & (1 << 31):
359
+ value = value - (1 << 32)
360
+ elif dtype == winreg.REG_SZ:
361
+ # If it's a reference to the tzres DLL, load the actual string
362
+ if value.startswith('@tzres'):
363
+ tz_res = tz_res or tzres()
364
+ value = tz_res.name_from_string(value)
365
+
366
+ value = value.rstrip('\x00') # Remove trailing nulls
367
+
368
+ dout[key_name] = value
369
+
370
+ return dout
dateutil/zoneinfo/__init__.py ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ import warnings
3
+ import json
4
+
5
+ from tarfile import TarFile
6
+ from pkgutil import get_data
7
+ from io import BytesIO
8
+
9
+ from dateutil.tz import tzfile as _tzfile
10
+
11
+ __all__ = ["get_zonefile_instance", "gettz", "gettz_db_metadata"]
12
+
13
+ ZONEFILENAME = "dateutil-zoneinfo.tar.gz"
14
+ METADATA_FN = 'METADATA'
15
+
16
+
17
+ class tzfile(_tzfile):
18
+ def __reduce__(self):
19
+ return (gettz, (self._filename,))
20
+
21
+
22
+ def getzoneinfofile_stream():
23
+ try:
24
+ return BytesIO(get_data(__name__, ZONEFILENAME))
25
+ except IOError as e: # TODO switch to FileNotFoundError?
26
+ warnings.warn("I/O error({0}): {1}".format(e.errno, e.strerror))
27
+ return None
28
+
29
+
30
+ class ZoneInfoFile(object):
31
+ def __init__(self, zonefile_stream=None):
32
+ if zonefile_stream is not None:
33
+ with TarFile.open(fileobj=zonefile_stream) as tf:
34
+ self.zones = {zf.name: tzfile(tf.extractfile(zf), filename=zf.name)
35
+ for zf in tf.getmembers()
36
+ if zf.isfile() and zf.name != METADATA_FN}
37
+ # deal with links: They'll point to their parent object. Less
38
+ # waste of memory
39
+ links = {zl.name: self.zones[zl.linkname]
40
+ for zl in tf.getmembers() if
41
+ zl.islnk() or zl.issym()}
42
+ self.zones.update(links)
43
+ try:
44
+ metadata_json = tf.extractfile(tf.getmember(METADATA_FN))
45
+ metadata_str = metadata_json.read().decode('UTF-8')
46
+ self.metadata = json.loads(metadata_str)
47
+ except KeyError:
48
+ # no metadata in tar file
49
+ self.metadata = None
50
+ else:
51
+ self.zones = {}
52
+ self.metadata = None
53
+
54
+ def get(self, name, default=None):
55
+ """
56
+ Wrapper for :func:`ZoneInfoFile.zones.get`. This is a convenience method
57
+ for retrieving zones from the zone dictionary.
58
+
59
+ :param name:
60
+ The name of the zone to retrieve. (Generally IANA zone names)
61
+
62
+ :param default:
63
+ The value to return in the event of a missing key.
64
+
65
+ .. versionadded:: 2.6.0
66
+
67
+ """
68
+ return self.zones.get(name, default)
69
+
70
+
71
+ # The current API has gettz as a module function, although in fact it taps into
72
+ # a stateful class. So as a workaround for now, without changing the API, we
73
+ # will create a new "global" class instance the first time a user requests a
74
+ # timezone. Ugly, but adheres to the api.
75
+ #
76
+ # TODO: Remove after deprecation period.
77
+ _CLASS_ZONE_INSTANCE = []
78
+
79
+
80
+ def get_zonefile_instance(new_instance=False):
81
+ """
82
+ This is a convenience function which provides a :class:`ZoneInfoFile`
83
+ instance using the data provided by the ``dateutil`` package. By default, it
84
+ caches a single instance of the ZoneInfoFile object and returns that.
85
+
86
+ :param new_instance:
87
+ If ``True``, a new instance of :class:`ZoneInfoFile` is instantiated and
88
+ used as the cached instance for the next call. Otherwise, new instances
89
+ are created only as necessary.
90
+
91
+ :return:
92
+ Returns a :class:`ZoneInfoFile` object.
93
+
94
+ .. versionadded:: 2.6
95
+ """
96
+ if new_instance:
97
+ zif = None
98
+ else:
99
+ zif = getattr(get_zonefile_instance, '_cached_instance', None)
100
+
101
+ if zif is None:
102
+ zif = ZoneInfoFile(getzoneinfofile_stream())
103
+
104
+ get_zonefile_instance._cached_instance = zif
105
+
106
+ return zif
107
+
108
+
109
+ def gettz(name):
110
+ """
111
+ This retrieves a time zone from the local zoneinfo tarball that is packaged
112
+ with dateutil.
113
+
114
+ :param name:
115
+ An IANA-style time zone name, as found in the zoneinfo file.
116
+
117
+ :return:
118
+ Returns a :class:`dateutil.tz.tzfile` time zone object.
119
+
120
+ .. warning::
121
+ It is generally inadvisable to use this function, and it is only
122
+ provided for API compatibility with earlier versions. This is *not*
123
+ equivalent to ``dateutil.tz.gettz()``, which selects an appropriate
124
+ time zone based on the inputs, favoring system zoneinfo. This is ONLY
125
+ for accessing the dateutil-specific zoneinfo (which may be out of
126
+ date compared to the system zoneinfo).
127
+
128
+ .. deprecated:: 2.6
129
+ If you need to use a specific zoneinfofile over the system zoneinfo,
130
+ instantiate a :class:`dateutil.zoneinfo.ZoneInfoFile` object and call
131
+ :func:`dateutil.zoneinfo.ZoneInfoFile.get(name)` instead.
132
+
133
+ Use :func:`get_zonefile_instance` to retrieve an instance of the
134
+ dateutil-provided zoneinfo.
135
+ """
136
+ warnings.warn("zoneinfo.gettz() will be removed in future versions, "
137
+ "to use the dateutil-provided zoneinfo files, instantiate a "
138
+ "ZoneInfoFile object and use ZoneInfoFile.zones.get() "
139
+ "instead. See the documentation for details.",
140
+ DeprecationWarning)
141
+
142
+ if len(_CLASS_ZONE_INSTANCE) == 0:
143
+ _CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream()))
144
+ return _CLASS_ZONE_INSTANCE[0].zones.get(name)
145
+
146
+
147
+ def gettz_db_metadata():
148
+ """ Get the zonefile metadata
149
+
150
+ See `zonefile_metadata`_
151
+
152
+ :returns:
153
+ A dictionary with the database metadata
154
+
155
+ .. deprecated:: 2.6
156
+ See deprecation warning in :func:`zoneinfo.gettz`. To get metadata,
157
+ query the attribute ``zoneinfo.ZoneInfoFile.metadata``.
158
+ """
159
+ warnings.warn("zoneinfo.gettz_db_metadata() will be removed in future "
160
+ "versions, to use the dateutil-provided zoneinfo files, "
161
+ "ZoneInfoFile object and query the 'metadata' attribute "
162
+ "instead. See the documentation for details.",
163
+ DeprecationWarning)
164
+
165
+ if len(_CLASS_ZONE_INSTANCE) == 0:
166
+ _CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream()))
167
+ return _CLASS_ZONE_INSTANCE[0].metadata
dateutil/zoneinfo/rebuild.py ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ import os
3
+ import tempfile
4
+ import shutil
5
+ import json
6
+ from subprocess import check_call, check_output
7
+ from tarfile import TarFile
8
+
9
+ from dateutil.zoneinfo import METADATA_FN, ZONEFILENAME
10
+
11
+
12
+ def rebuild(filename, tag=None, format="gz", zonegroups=[], metadata=None):
13
+ """Rebuild the internal timezone info in dateutil/zoneinfo/zoneinfo*tar*
14
+
15
+ filename is the timezone tarball from ``ftp.iana.org/tz``.
16
+
17
+ """
18
+ tmpdir = tempfile.mkdtemp()
19
+ zonedir = os.path.join(tmpdir, "zoneinfo")
20
+ moduledir = os.path.dirname(__file__)
21
+ try:
22
+ with TarFile.open(filename) as tf:
23
+ for name in zonegroups:
24
+ tf.extract(name, tmpdir)
25
+ filepaths = [os.path.join(tmpdir, n) for n in zonegroups]
26
+
27
+ _run_zic(zonedir, filepaths)
28
+
29
+ # write metadata file
30
+ with open(os.path.join(zonedir, METADATA_FN), 'w') as f:
31
+ json.dump(metadata, f, indent=4, sort_keys=True)
32
+ target = os.path.join(moduledir, ZONEFILENAME)
33
+ with TarFile.open(target, "w:%s" % format) as tf:
34
+ for entry in os.listdir(zonedir):
35
+ entrypath = os.path.join(zonedir, entry)
36
+ tf.add(entrypath, entry)
37
+ finally:
38
+ shutil.rmtree(tmpdir)
39
+
40
+
41
+ def _run_zic(zonedir, filepaths):
42
+ """Calls the ``zic`` compiler in a compatible way to get a "fat" binary.
43
+
44
+ Recent versions of ``zic`` default to ``-b slim``, while older versions
45
+ don't even have the ``-b`` option (but default to "fat" binaries). The
46
+ current version of dateutil does not support Version 2+ TZif files, which
47
+ causes problems when used in conjunction with "slim" binaries, so this
48
+ function is used to ensure that we always get a "fat" binary.
49
+ """
50
+
51
+ try:
52
+ help_text = check_output(["zic", "--help"])
53
+ except OSError as e:
54
+ _print_on_nosuchfile(e)
55
+ raise
56
+
57
+ if b"-b " in help_text:
58
+ bloat_args = ["-b", "fat"]
59
+ else:
60
+ bloat_args = []
61
+
62
+ check_call(["zic"] + bloat_args + ["-d", zonedir] + filepaths)
63
+
64
+
65
+ def _print_on_nosuchfile(e):
66
+ """Print helpful troubleshooting message
67
+
68
+ e is an exception raised by subprocess.check_call()
69
+
70
+ """
71
+ if e.errno == 2:
72
+ logging.error(
73
+ "Could not find zic. Perhaps you need to install "
74
+ "libc-bin or some other package that provides it, "
75
+ "or it's not in your PATH?")
pandas/_config/__init__.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ pandas._config is considered explicitly upstream of everything else in pandas,
3
+ should have no intra-pandas dependencies.
4
+
5
+ importing `dates` and `display` ensures that keys needed by _libs
6
+ are initialized.
7
+ """
8
+
9
+ __all__ = [
10
+ "config",
11
+ "describe_option",
12
+ "detect_console_encoding",
13
+ "get_option",
14
+ "option_context",
15
+ "options",
16
+ "reset_option",
17
+ "set_option",
18
+ ]
19
+ from pandas._config import config
20
+ from pandas._config import dates # pyright: ignore[reportUnusedImport] # noqa: F401
21
+ from pandas._config.config import (
22
+ _global_config,
23
+ describe_option,
24
+ get_option,
25
+ option_context,
26
+ options,
27
+ reset_option,
28
+ set_option,
29
+ )
30
+ from pandas._config.display import detect_console_encoding
31
+
32
+
33
+ def using_string_dtype() -> bool:
34
+ _mode_options = _global_config["future"]
35
+ return _mode_options["infer_string"]
36
+
37
+
38
+ def using_python_scalars() -> bool:
39
+ _mode_options = _global_config["future"]
40
+ return _mode_options["python_scalars"]
41
+
42
+
43
+ def is_nan_na() -> bool:
44
+ _mode_options = _global_config["future"]
45
+ return not _mode_options["distinguish_nan_and_na"]
pandas/_config/config.py ADDED
@@ -0,0 +1,954 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ The config module holds package-wide configurables and provides
3
+ a uniform API for working with them.
4
+
5
+ Overview
6
+ ========
7
+
8
+ This module supports the following requirements:
9
+ - options are referenced using keys in dot.notation, e.g. "x.y.option - z".
10
+ - keys are case-insensitive.
11
+ - functions should accept partial/regex keys, when unambiguous.
12
+ - options can be registered by modules at import time.
13
+ - options can be registered at init-time (via core.config_init)
14
+ - options have a default value, and (optionally) a description and
15
+ validation function associated with them.
16
+ - options can be deprecated, in which case referencing them
17
+ should produce a warning.
18
+ - deprecated options can optionally be rerouted to a replacement
19
+ so that accessing a deprecated option reroutes to a differently
20
+ named option.
21
+ - options can be reset to their default value.
22
+ - all option can be reset to their default value at once.
23
+ - all options in a certain sub - namespace can be reset at once.
24
+ - the user can set / get / reset or ask for the description of an option.
25
+ - a developer can register and mark an option as deprecated.
26
+ - you can register a callback to be invoked when the option value
27
+ is set or reset. Changing the stored value is considered misuse, but
28
+ is not verboten.
29
+
30
+ Implementation
31
+ ==============
32
+
33
+ - Data is stored using nested dictionaries, and should be accessed
34
+ through the provided API.
35
+
36
+ - "Registered options" and "Deprecated options" have metadata associated
37
+ with them, which are stored in auxiliary dictionaries keyed on the
38
+ fully-qualified key, e.g. "x.y.z.option".
39
+
40
+ - the config_init module is imported by the package's __init__.py file.
41
+ placing any register_option() calls there will ensure those options
42
+ are available as soon as pandas is loaded. If you use register_option
43
+ in a module, it will only be available after that module is imported,
44
+ which you should be aware of.
45
+
46
+ - `config_prefix` is a context_manager (for use with the `with` keyword)
47
+ which can save developers some typing, see the docstring.
48
+
49
+ """
50
+
51
+ from __future__ import annotations
52
+
53
+ from contextlib import contextmanager
54
+ import re
55
+ from typing import (
56
+ TYPE_CHECKING,
57
+ Any,
58
+ NamedTuple,
59
+ cast,
60
+ )
61
+ import warnings
62
+
63
+ from pandas._typing import F
64
+ from pandas.util._exceptions import find_stack_level
65
+
66
+ if TYPE_CHECKING:
67
+ from collections.abc import (
68
+ Callable,
69
+ Generator,
70
+ Sequence,
71
+ )
72
+
73
+
74
+ class DeprecatedOption(NamedTuple):
75
+ key: str
76
+ category: type[Warning]
77
+ msg: str | None
78
+ rkey: str | None
79
+ removal_ver: str | None
80
+
81
+
82
+ class RegisteredOption(NamedTuple):
83
+ key: str
84
+ defval: Any
85
+ doc: str
86
+ validator: Callable[[object], Any] | None
87
+ cb: Callable[[str], Any] | None
88
+
89
+
90
+ # holds deprecated option metadata
91
+ _deprecated_options: dict[str, DeprecatedOption] = {}
92
+
93
+ # holds registered option metadata
94
+ _registered_options: dict[str, RegisteredOption] = {}
95
+
96
+ # holds the current values for registered options
97
+ _global_config: dict[str, Any] = {}
98
+
99
+ # keys which have a special meaning
100
+ _reserved_keys: list[str] = ["all"]
101
+
102
+
103
+ class OptionError(AttributeError, KeyError):
104
+ """
105
+ Exception raised for pandas.options.
106
+
107
+ Backwards compatible with KeyError checks.
108
+
109
+ See Also
110
+ --------
111
+ options : Access and modify global pandas settings.
112
+
113
+ Examples
114
+ --------
115
+ >>> pd.options.context
116
+ Traceback (most recent call last):
117
+ OptionError: No such option
118
+ """
119
+
120
+ __module__ = "pandas.errors"
121
+
122
+
123
+ #
124
+ # User API
125
+
126
+
127
+ def _get_single_key(pat: str) -> str:
128
+ keys = _select_options(pat)
129
+ if len(keys) == 0:
130
+ _warn_if_deprecated(pat)
131
+ raise OptionError(f"No such keys(s): {pat!r}")
132
+ if len(keys) > 1:
133
+ raise OptionError("Pattern matched multiple keys")
134
+ key = keys[0]
135
+
136
+ _warn_if_deprecated(key)
137
+
138
+ key = _translate_key(key)
139
+
140
+ return key
141
+
142
+
143
+ def get_option(pat: str) -> Any:
144
+ """
145
+ Retrieve the value of the specified option.
146
+
147
+ This method allows users to query the current value of a given option
148
+ in the pandas configuration system. Options control various display,
149
+ performance, and behavior-related settings within pandas.
150
+
151
+ Parameters
152
+ ----------
153
+ pat : str
154
+ Regexp which should match a single option.
155
+
156
+ .. warning::
157
+
158
+ Partial matches are supported for convenience, but unless you use the
159
+ full option name (e.g. x.y.z.option_name), your code may break in future
160
+ versions if new options with similar names are introduced.
161
+
162
+ Returns
163
+ -------
164
+ Any
165
+ The value of the option.
166
+
167
+ Raises
168
+ ------
169
+ OptionError : if no such option exists
170
+
171
+ See Also
172
+ --------
173
+ set_option : Set the value of the specified option or options.
174
+ reset_option : Reset one or more options to their default value.
175
+ describe_option : Print the description for one or more registered options.
176
+
177
+ Notes
178
+ -----
179
+ For all available options, please view the :ref:`User Guide <options.available>`
180
+ or use ``pandas.describe_option()``.
181
+
182
+ Examples
183
+ --------
184
+ >>> pd.get_option("display.max_columns") # doctest: +SKIP
185
+ 4
186
+ """
187
+ key = _get_single_key(pat)
188
+
189
+ # walk the nested dict
190
+ root, k = _get_root(key)
191
+ return root[k]
192
+
193
+
194
+ def set_option(*args) -> None:
195
+ """
196
+ Set the value of the specified option or options.
197
+
198
+ This method allows fine-grained control over the behavior and display settings
199
+ of pandas. Options affect various functionalities such as output formatting,
200
+ display limits, and operational behavior. Settings can be modified at runtime
201
+ without requiring changes to global configurations or environment variables.
202
+
203
+ Parameters
204
+ ----------
205
+ *args : str | object | dict
206
+ Arguments provided in pairs, which will be interpreted as (pattern, value),
207
+ or as a single dictionary containing multiple option-value pairs.
208
+ pattern: str
209
+ Regexp which should match a single option
210
+ value: object
211
+ New value of option
212
+
213
+ .. warning::
214
+
215
+ Partial pattern matches are supported for convenience, but unless you
216
+ use the full option name (e.g. x.y.z.option_name), your code may break in
217
+ future versions if new options with similar names are introduced.
218
+
219
+ Returns
220
+ -------
221
+ None
222
+ No return value.
223
+
224
+ Raises
225
+ ------
226
+ ValueError if odd numbers of non-keyword arguments are provided
227
+ TypeError if keyword arguments are provided
228
+ OptionError if no such option exists
229
+
230
+ See Also
231
+ --------
232
+ get_option : Retrieve the value of the specified option.
233
+ reset_option : Reset one or more options to their default value.
234
+ describe_option : Print the description for one or more registered options.
235
+ option_context : Context manager to temporarily set options in a ``with``
236
+ statement.
237
+
238
+ Notes
239
+ -----
240
+ For all available options, please view the :ref:`User Guide <options.available>`
241
+ or use ``pandas.describe_option()``.
242
+
243
+ Examples
244
+ --------
245
+ Option-Value Pair Input:
246
+
247
+ >>> pd.set_option("display.max_columns", 4)
248
+ >>> df = pd.DataFrame([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])
249
+ >>> df
250
+ 0 1 ... 3 4
251
+ 0 1 2 ... 4 5
252
+ 1 6 7 ... 9 10
253
+ [2 rows x 5 columns]
254
+ >>> pd.reset_option("display.max_columns")
255
+
256
+ Dictionary Input:
257
+
258
+ >>> pd.set_option({"display.max_columns": 4, "display.precision": 1})
259
+ >>> df = pd.DataFrame([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])
260
+ >>> df
261
+ 0 1 ... 3 4
262
+ 0 1 2 ... 4 5
263
+ 1 6 7 ... 9 10
264
+ [2 rows x 5 columns]
265
+ >>> pd.reset_option("display.max_columns")
266
+ >>> pd.reset_option("display.precision")
267
+ """
268
+ # Handle dictionary input
269
+ if len(args) == 1 and isinstance(args[0], dict):
270
+ args = tuple(kv for item in args[0].items() for kv in item)
271
+
272
+ nargs = len(args)
273
+ if not nargs or nargs % 2 != 0:
274
+ raise ValueError("Must provide an even number of non-keyword arguments")
275
+
276
+ for k, v in zip(args[::2], args[1::2], strict=True):
277
+ key = _get_single_key(k)
278
+
279
+ opt = _get_registered_option(key)
280
+ if opt and opt.validator:
281
+ opt.validator(v)
282
+
283
+ # walk the nested dict
284
+ root, k_root = _get_root(key)
285
+ root[k_root] = v
286
+
287
+ if opt.cb:
288
+ opt.cb(key)
289
+
290
+
291
+ def describe_option(pat: str = "", _print_desc: bool = True) -> str | None:
292
+ """
293
+ Print the description for one or more registered options.
294
+
295
+ Call with no arguments to get a listing for all registered options.
296
+
297
+ Parameters
298
+ ----------
299
+ pat : str, default ""
300
+ String or string regexp pattern.
301
+ Empty string will return all options.
302
+ For regexp strings, all matching keys will have their description displayed.
303
+ _print_desc : bool, default True
304
+ If True (default) the description(s) will be printed to stdout.
305
+ Otherwise, the description(s) will be returned as a string
306
+ (for testing).
307
+
308
+ Returns
309
+ -------
310
+ None
311
+ If ``_print_desc=True``.
312
+ str
313
+ If the description(s) as a string if ``_print_desc=False``.
314
+
315
+ See Also
316
+ --------
317
+ get_option : Retrieve the value of the specified option.
318
+ set_option : Set the value of the specified option or options.
319
+ reset_option : Reset one or more options to their default value.
320
+
321
+ Notes
322
+ -----
323
+ For all available options, please view the
324
+ :ref:`User Guide <options.available>`.
325
+
326
+ Examples
327
+ --------
328
+ >>> pd.describe_option("display.max_columns") # doctest: +SKIP
329
+ display.max_columns : int
330
+ If max_cols is exceeded, switch to truncate view...
331
+ """
332
+ keys = _select_options(pat)
333
+ if len(keys) == 0:
334
+ raise OptionError(f"No such keys(s) for {pat=}")
335
+
336
+ s = "\n".join([_build_option_description(k) for k in keys])
337
+
338
+ if _print_desc:
339
+ print(s)
340
+ return None
341
+ return s
342
+
343
+
344
+ def reset_option(pat: str) -> None:
345
+ """
346
+ Reset one or more options to their default value.
347
+
348
+ This method resets the specified pandas option(s) back to their default
349
+ values. It allows partial string matching for convenience, but users should
350
+ exercise caution to avoid unintended resets due to changes in option names
351
+ in future versions.
352
+
353
+ Parameters
354
+ ----------
355
+ pat : str/regex
356
+ If specified only options matching ``pat*`` will be reset.
357
+ Pass ``"all"`` as argument to reset all options.
358
+
359
+ .. warning::
360
+
361
+ Partial matches are supported for convenience, but unless you
362
+ use the full option name (e.g. x.y.z.option_name), your code may break
363
+ in future versions if new options with similar names are introduced.
364
+
365
+ Returns
366
+ -------
367
+ None
368
+ No return value.
369
+
370
+ See Also
371
+ --------
372
+ get_option : Retrieve the value of the specified option.
373
+ set_option : Set the value of the specified option or options.
374
+ describe_option : Print the description for one or more registered options.
375
+
376
+ Notes
377
+ -----
378
+ For all available options, please view the
379
+ :ref:`User Guide <options.available>`.
380
+
381
+ Examples
382
+ --------
383
+ >>> pd.reset_option("display.max_columns") # doctest: +SKIP
384
+ """
385
+ keys = _select_options(pat)
386
+
387
+ if len(keys) == 0:
388
+ raise OptionError(f"No such keys(s) for {pat=}")
389
+
390
+ if len(keys) > 1 and len(pat) < 4 and pat != "all":
391
+ raise ValueError(
392
+ "You must specify at least 4 characters when "
393
+ "resetting multiple keys, use the special keyword "
394
+ '"all" to reset all the options to their default value'
395
+ )
396
+
397
+ for k in keys:
398
+ set_option(k, _registered_options[k].defval)
399
+
400
+
401
+ def get_default_val(pat: str):
402
+ key = _get_single_key(pat)
403
+ return _get_registered_option(key).defval
404
+
405
+
406
+ class DictWrapper:
407
+ """provide attribute-style access to a nested dict"""
408
+
409
+ d: dict[str, Any]
410
+
411
+ def __init__(self, d: dict[str, Any], prefix: str = "") -> None:
412
+ object.__setattr__(self, "d", d)
413
+ object.__setattr__(self, "prefix", prefix)
414
+
415
+ def __setattr__(self, key: str, val: Any) -> None:
416
+ prefix = object.__getattribute__(self, "prefix")
417
+ if prefix:
418
+ prefix += "."
419
+ prefix += key
420
+ # you can't set new keys
421
+ # can you can't overwrite subtrees
422
+ if key in self.d and not isinstance(self.d[key], dict):
423
+ set_option(prefix, val)
424
+ else:
425
+ raise OptionError("You can only set the value of existing options")
426
+
427
+ def __getattr__(self, key: str):
428
+ prefix = object.__getattribute__(self, "prefix")
429
+ if prefix:
430
+ prefix += "."
431
+ prefix += key
432
+ try:
433
+ v = object.__getattribute__(self, "d")[key]
434
+ except KeyError as err:
435
+ raise OptionError("No such option") from err
436
+ if isinstance(v, dict):
437
+ return DictWrapper(v, prefix)
438
+ else:
439
+ return get_option(prefix)
440
+
441
+ def __dir__(self) -> list[str]:
442
+ return list(self.d.keys())
443
+
444
+
445
+ options = DictWrapper(_global_config)
446
+ # DictWrapper defines a custom setattr
447
+ object.__setattr__(options, "__module__", "pandas")
448
+
449
+ #
450
+ # Functions for use by pandas developers, in addition to User - api
451
+
452
+
453
+ @contextmanager
454
+ def option_context(*args) -> Generator[None]:
455
+ """
456
+ Context manager to temporarily set options in a ``with`` statement.
457
+
458
+ This method allows users to set one or more pandas options temporarily
459
+ within a controlled block. The previous options' values are restored
460
+ once the block is exited. This is useful when making temporary adjustments
461
+ to pandas' behavior without affecting the global state.
462
+
463
+ Parameters
464
+ ----------
465
+ *args : str | object | dict
466
+ An even amount of arguments provided in pairs which will be
467
+ interpreted as (pattern, value) pairs. Alternatively, a single
468
+ dictionary of {pattern: value} may be provided.
469
+
470
+ Returns
471
+ -------
472
+ None
473
+ No return value.
474
+
475
+ Yields
476
+ ------
477
+ None
478
+ No yield value.
479
+
480
+ See Also
481
+ --------
482
+ get_option : Retrieve the value of the specified option.
483
+ set_option : Set the value of the specified option.
484
+ reset_option : Reset one or more options to their default value.
485
+ describe_option : Print the description for one or more registered options.
486
+
487
+ Notes
488
+ -----
489
+ For all available options, please view the :ref:`User Guide <options.available>`
490
+ or use ``pandas.describe_option()``.
491
+
492
+ Examples
493
+ --------
494
+ >>> from pandas import option_context
495
+ >>> with option_context("display.max_rows", 10, "display.max_columns", 5):
496
+ ... pass
497
+ >>> with option_context({"display.max_rows": 10, "display.max_columns": 5}):
498
+ ... pass
499
+ """
500
+ if len(args) == 1 and isinstance(args[0], dict):
501
+ args = tuple(kv for item in args[0].items() for kv in item)
502
+
503
+ if len(args) % 2 != 0 or len(args) < 2:
504
+ raise ValueError(
505
+ "Provide an even amount of arguments as "
506
+ "option_context(pat, val, pat, val...)."
507
+ )
508
+
509
+ ops = tuple(zip(args[::2], args[1::2], strict=True))
510
+ undo: tuple[tuple[Any, Any], ...] = ()
511
+ try:
512
+ undo = tuple((pat, get_option(pat)) for pat, val in ops)
513
+ for pat, val in ops:
514
+ set_option(pat, val)
515
+ yield
516
+ finally:
517
+ for pat, val in undo:
518
+ set_option(pat, val)
519
+
520
+
521
+ def register_option(
522
+ key: str,
523
+ defval: object,
524
+ doc: str = "",
525
+ validator: Callable[[object], Any] | None = None,
526
+ cb: Callable[[str], Any] | None = None,
527
+ ) -> None:
528
+ """
529
+ Register an option in the package-wide pandas config object
530
+
531
+ Parameters
532
+ ----------
533
+ key : str
534
+ Fully-qualified key, e.g. "x.y.option - z".
535
+ defval : object
536
+ Default value of the option.
537
+ doc : str
538
+ Description of the option.
539
+ validator : Callable, optional
540
+ Function of a single argument, should raise `ValueError` if
541
+ called with a value which is not a legal value for the option.
542
+ cb
543
+ a function of a single argument "key", which is called
544
+ immediately after an option value is set/reset. key is
545
+ the full name of the option.
546
+
547
+ Raises
548
+ ------
549
+ ValueError if `validator` is specified and `defval` is not a valid value.
550
+
551
+ """
552
+ import keyword
553
+ import tokenize
554
+
555
+ key = key.lower()
556
+
557
+ if key in _registered_options:
558
+ raise OptionError(f"Option '{key}' has already been registered")
559
+ if key in _reserved_keys:
560
+ raise OptionError(f"Option '{key}' is a reserved key")
561
+
562
+ # the default value should be legal
563
+ if validator:
564
+ validator(defval)
565
+
566
+ # walk the nested dict, creating dicts as needed along the path
567
+ path = key.split(".")
568
+
569
+ for k in path:
570
+ if not re.match("^" + tokenize.Name + "$", k):
571
+ raise ValueError(f"{k} is not a valid identifier")
572
+ if keyword.iskeyword(k):
573
+ raise ValueError(f"{k} is a python keyword")
574
+
575
+ cursor = _global_config
576
+ msg = "Path prefix to option '{option}' is already an option"
577
+
578
+ for i, p in enumerate(path[:-1]):
579
+ if not isinstance(cursor, dict):
580
+ raise OptionError(msg.format(option=".".join(path[:i])))
581
+ if p not in cursor:
582
+ cursor[p] = {}
583
+ cursor = cursor[p]
584
+
585
+ if not isinstance(cursor, dict):
586
+ raise OptionError(msg.format(option=".".join(path[:-1])))
587
+
588
+ cursor[path[-1]] = defval # initialize
589
+
590
+ # save the option metadata
591
+ _registered_options[key] = RegisteredOption(
592
+ key=key, defval=defval, doc=doc, validator=validator, cb=cb
593
+ )
594
+
595
+
596
+ def deprecate_option(
597
+ key: str,
598
+ category: type[Warning],
599
+ msg: str | None = None,
600
+ rkey: str | None = None,
601
+ removal_ver: str | None = None,
602
+ ) -> None:
603
+ """
604
+ Mark option `key` as deprecated, if code attempts to access this option,
605
+ a warning will be produced, using `msg` if given, or a default message
606
+ if not.
607
+ if `rkey` is given, any access to the key will be re-routed to `rkey`.
608
+
609
+ Neither the existence of `key` nor that if `rkey` is checked. If they
610
+ do not exist, any subsequence access will fail as usual, after the
611
+ deprecation warning is given.
612
+
613
+ Parameters
614
+ ----------
615
+ key : str
616
+ Name of the option to be deprecated.
617
+ must be a fully-qualified option name (e.g "x.y.z.rkey").
618
+ category : Warning
619
+ Warning class for the deprecation.
620
+ msg : str, optional
621
+ Warning message to output when the key is referenced.
622
+ if no message is given a default message will be emitted.
623
+ rkey : str, optional
624
+ Name of an option to reroute access to.
625
+ If specified, any referenced `key` will be
626
+ re-routed to `rkey` including set/get/reset.
627
+ rkey must be a fully-qualified option name (e.g "x.y.z.rkey").
628
+ used by the default message if no `msg` is specified.
629
+ removal_ver : str, optional
630
+ Specifies the version in which this option will
631
+ be removed. used by the default message if no `msg` is specified.
632
+
633
+ Raises
634
+ ------
635
+ OptionError
636
+ If the specified key has already been deprecated.
637
+ """
638
+ key = key.lower()
639
+
640
+ if key in _deprecated_options:
641
+ raise OptionError(f"Option '{key}' has already been defined as deprecated.")
642
+
643
+ _deprecated_options[key] = DeprecatedOption(key, category, msg, rkey, removal_ver)
644
+
645
+
646
+ #
647
+ # functions internal to the module
648
+
649
+
650
+ def _select_options(pat: str) -> list[str]:
651
+ """
652
+ returns a list of keys matching `pat`
653
+
654
+ if pat=="all", returns all registered options
655
+ """
656
+ # short-circuit for exact key
657
+ if pat in _registered_options:
658
+ return [pat]
659
+
660
+ # else look through all of them
661
+ keys = sorted(_registered_options.keys())
662
+ if pat == "all": # reserved key
663
+ return keys
664
+
665
+ return [k for k in keys if re.search(pat, k, re.I)]
666
+
667
+
668
+ def _get_root(key: str) -> tuple[dict[str, Any], str]:
669
+ path = key.split(".")
670
+ cursor = _global_config
671
+ for p in path[:-1]:
672
+ cursor = cursor[p]
673
+ return cursor, path[-1]
674
+
675
+
676
+ def _get_deprecated_option(key: str):
677
+ """
678
+ Retrieves the metadata for a deprecated option, if `key` is deprecated.
679
+
680
+ Returns
681
+ -------
682
+ DeprecatedOption (namedtuple) if key is deprecated, None otherwise
683
+ """
684
+ try:
685
+ d = _deprecated_options[key]
686
+ except KeyError:
687
+ return None
688
+ else:
689
+ return d
690
+
691
+
692
+ def _get_registered_option(key: str):
693
+ """
694
+ Retrieves the option metadata if `key` is a registered option.
695
+
696
+ Returns
697
+ -------
698
+ RegisteredOption (namedtuple) if key is deprecated, None otherwise
699
+ """
700
+ return _registered_options.get(key)
701
+
702
+
703
+ def _translate_key(key: str) -> str:
704
+ """
705
+ if `key` is deprecated and a replacement key defined, will return the
706
+ replacement key, otherwise returns `key` as-is
707
+ """
708
+ d = _get_deprecated_option(key)
709
+ if d:
710
+ return d.rkey or key
711
+ else:
712
+ return key
713
+
714
+
715
+ def _warn_if_deprecated(key: str) -> bool:
716
+ """
717
+ Checks if `key` is a deprecated option and if so, prints a warning.
718
+
719
+ Returns
720
+ -------
721
+ bool - True if `key` is deprecated, False otherwise.
722
+ """
723
+ d = _get_deprecated_option(key)
724
+ if d:
725
+ if d.msg:
726
+ warnings.warn(
727
+ d.msg,
728
+ d.category,
729
+ stacklevel=find_stack_level(),
730
+ )
731
+ else:
732
+ msg = f"'{key}' is deprecated"
733
+ if d.removal_ver:
734
+ msg += f" and will be removed in {d.removal_ver}"
735
+ if d.rkey:
736
+ msg += f", please use '{d.rkey}' instead."
737
+ else:
738
+ msg += ", please refrain from using it."
739
+
740
+ warnings.warn(
741
+ msg,
742
+ d.category,
743
+ stacklevel=find_stack_level(),
744
+ )
745
+ return True
746
+ return False
747
+
748
+
749
+ def _build_option_description(k: str) -> str:
750
+ """Builds a formatted description of a registered option and prints it"""
751
+ o = _get_registered_option(k)
752
+ d = _get_deprecated_option(k)
753
+
754
+ s = f"{k} "
755
+
756
+ if o.doc:
757
+ s += "\n".join(o.doc.strip().split("\n"))
758
+ else:
759
+ s += "No description available."
760
+
761
+ if o:
762
+ with warnings.catch_warnings():
763
+ warnings.simplefilter("ignore", FutureWarning)
764
+ warnings.simplefilter("ignore", DeprecationWarning)
765
+ s += f"\n [default: {o.defval}] [currently: {get_option(k)}]"
766
+
767
+ if d:
768
+ rkey = d.rkey or ""
769
+ s += "\n (Deprecated"
770
+ s += f", use `{rkey}` instead."
771
+ s += ")"
772
+
773
+ return s
774
+
775
+
776
+ # helpers
777
+
778
+
779
+ @contextmanager
780
+ def config_prefix(prefix: str) -> Generator[None]:
781
+ """
782
+ contextmanager for multiple invocations of API with a common prefix
783
+
784
+ supported API functions: (register / get / set )__option
785
+
786
+ Warning: This is not thread - safe, and won't work properly if you import
787
+ the API functions into your module using the "from x import y" construct.
788
+
789
+ Example
790
+ -------
791
+ import pandas._config.config as cf
792
+ with cf.config_prefix("display.font"):
793
+ cf.register_option("color", "red")
794
+ cf.register_option("size", " 5 pt")
795
+ cf.set_option(size, " 6 pt")
796
+ cf.get_option(size)
797
+ ...
798
+
799
+ etc'
800
+
801
+ will register options "display.font.color", "display.font.size", set the
802
+ value of "display.font.size"... and so on.
803
+ """
804
+ # Note: reset_option relies on set_option, and on key directly
805
+ # it does not fit in to this monkey-patching scheme
806
+
807
+ global register_option, get_option, set_option
808
+
809
+ def wrap(func: F) -> F:
810
+ def inner(key: str, *args, **kwds):
811
+ pkey = f"{prefix}.{key}"
812
+ return func(pkey, *args, **kwds)
813
+
814
+ return cast(F, inner)
815
+
816
+ _register_option = register_option
817
+ _get_option = get_option
818
+ _set_option = set_option
819
+ set_option = wrap(set_option)
820
+ get_option = wrap(get_option)
821
+ register_option = wrap(register_option)
822
+ try:
823
+ yield
824
+ finally:
825
+ set_option = _set_option
826
+ get_option = _get_option
827
+ register_option = _register_option
828
+
829
+
830
+ # These factories and methods are handy for use as the validator
831
+ # arg in register_option
832
+
833
+
834
+ def is_type_factory(_type: type[Any]) -> Callable[[Any], None]:
835
+ """
836
+
837
+ Parameters
838
+ ----------
839
+ `_type` - a type to be compared against (e.g. type(x) == `_type`)
840
+
841
+ Returns
842
+ -------
843
+ validator - a function of a single argument x , which raises
844
+ ValueError if type(x) is not equal to `_type`
845
+
846
+ """
847
+
848
+ def inner(x) -> None:
849
+ if type(x) != _type:
850
+ raise ValueError(f"Value must have type '{_type}'")
851
+
852
+ return inner
853
+
854
+
855
+ def is_instance_factory(_type: type | tuple[type, ...]) -> Callable[[Any], None]:
856
+ """
857
+
858
+ Parameters
859
+ ----------
860
+ `_type` - the type to be checked against
861
+
862
+ Returns
863
+ -------
864
+ validator - a function of a single argument x , which raises
865
+ ValueError if x is not an instance of `_type`
866
+
867
+ """
868
+ if isinstance(_type, tuple):
869
+ type_repr = "|".join(map(str, _type))
870
+ else:
871
+ type_repr = f"'{_type}'"
872
+
873
+ def inner(x) -> None:
874
+ if not isinstance(x, _type):
875
+ raise ValueError(f"Value must be an instance of {type_repr}")
876
+
877
+ return inner
878
+
879
+
880
+ def is_one_of_factory(legal_values: Sequence) -> Callable[[Any], None]:
881
+ callables = [c for c in legal_values if callable(c)]
882
+ legal_values = [c for c in legal_values if not callable(c)]
883
+
884
+ def inner(x) -> None:
885
+ if x not in legal_values:
886
+ if not any(c(x) for c in callables):
887
+ uvals = [str(lval) for lval in legal_values]
888
+ pp_values = "|".join(uvals)
889
+ msg = f"Value must be one of {pp_values}"
890
+ if len(callables):
891
+ msg += " or a callable"
892
+ raise ValueError(msg)
893
+
894
+ return inner
895
+
896
+
897
+ def is_nonnegative_int(value: object) -> None:
898
+ """
899
+ Verify that value is None or a positive int.
900
+
901
+ Parameters
902
+ ----------
903
+ value : None or int
904
+ The `value` to be checked.
905
+
906
+ Raises
907
+ ------
908
+ ValueError
909
+ When the value is not None or is a negative integer
910
+ """
911
+ if value is None:
912
+ return
913
+
914
+ elif isinstance(value, int):
915
+ if value >= 0:
916
+ return
917
+
918
+ msg = "Value must be a nonnegative integer or None"
919
+ raise ValueError(msg)
920
+
921
+
922
+ # common type validators, for convenience
923
+ # usage: register_option(... , validator = is_int)
924
+ is_int = is_type_factory(int)
925
+ is_bool = is_type_factory(bool)
926
+ is_float = is_type_factory(float)
927
+ is_str = is_type_factory(str)
928
+ is_text = is_instance_factory((str, bytes))
929
+
930
+
931
+ def is_callable(obj: object) -> bool:
932
+ """
933
+
934
+ Parameters
935
+ ----------
936
+ `obj` - the object to be checked
937
+
938
+ Returns
939
+ -------
940
+ validator - returns True if object is callable
941
+ raises ValueError otherwise.
942
+
943
+ """
944
+ if not callable(obj):
945
+ raise ValueError("Value must be a callable")
946
+ return True
947
+
948
+
949
+ # import set_module here would cause circular import
950
+ get_option.__module__ = "pandas"
951
+ set_option.__module__ = "pandas"
952
+ describe_option.__module__ = "pandas"
953
+ reset_option.__module__ = "pandas"
954
+ option_context.__module__ = "pandas"
pandas/_config/dates.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ config for datetime formatting
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from pandas._config import config as cf
8
+
9
+ pc_date_dayfirst_doc = """
10
+ : boolean
11
+ When True, prints and parses dates with the day first, eg 20/01/2005
12
+ """
13
+
14
+ pc_date_yearfirst_doc = """
15
+ : boolean
16
+ When True, prints and parses dates with the year first, eg 2005/01/20
17
+ """
18
+
19
+ with cf.config_prefix("display"):
20
+ # Needed upstream of `_libs` because these are used in tslibs.parsing
21
+ cf.register_option(
22
+ "date_dayfirst", False, pc_date_dayfirst_doc, validator=cf.is_bool
23
+ )
24
+ cf.register_option(
25
+ "date_yearfirst", False, pc_date_yearfirst_doc, validator=cf.is_bool
26
+ )
pandas/_config/display.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Unopinionated display configuration.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import locale
8
+ import sys
9
+
10
+ from pandas._config import config as cf
11
+
12
+ # -----------------------------------------------------------------------------
13
+ # Global formatting options
14
+ _initial_defencoding: str | None = None
15
+
16
+
17
+ def detect_console_encoding() -> str:
18
+ """
19
+ Try to find the most capable encoding supported by the console.
20
+ slightly modified from the way IPython handles the same issue.
21
+ """
22
+ global _initial_defencoding
23
+
24
+ encoding = None
25
+ try:
26
+ encoding = sys.stdout.encoding or sys.stdin.encoding
27
+ except (AttributeError, OSError):
28
+ pass
29
+
30
+ # try again for something better
31
+ if not encoding or "ascii" in encoding.lower():
32
+ try:
33
+ encoding = locale.getpreferredencoding()
34
+ except locale.Error:
35
+ # can be raised by locale.setlocale(), which is
36
+ # called by getpreferredencoding
37
+ # (on some systems, see stdlib locale docs)
38
+ pass
39
+
40
+ # when all else fails. this will usually be "ascii"
41
+ if not encoding or "ascii" in encoding.lower():
42
+ encoding = sys.getdefaultencoding()
43
+
44
+ # GH#3360, save the reported defencoding at import time
45
+ # MPL backends may change it. Make available for debugging.
46
+ if not _initial_defencoding:
47
+ _initial_defencoding = sys.getdefaultencoding()
48
+
49
+ return encoding
50
+
51
+
52
+ pc_encoding_doc = """
53
+ : str/unicode
54
+ Defaults to the detected encoding of the console.
55
+ Specifies the encoding to be used for strings returned by to_string,
56
+ these are generally strings meant to be displayed on the console.
57
+ """
58
+
59
+ with cf.config_prefix("display"):
60
+ cf.register_option(
61
+ "encoding", detect_console_encoding(), pc_encoding_doc, validator=cf.is_text
62
+ )
pandas/_config/localization.py ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Helpers for configuring locale settings.
3
+
4
+ Name `localization` is chosen to avoid overlap with builtin `locale` module.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from contextlib import contextmanager
10
+ import locale
11
+ import platform
12
+ import re
13
+ import subprocess
14
+ from typing import (
15
+ TYPE_CHECKING,
16
+ cast,
17
+ )
18
+
19
+ from pandas._config.config import options
20
+
21
+ if TYPE_CHECKING:
22
+ from collections.abc import Generator
23
+
24
+
25
+ @contextmanager
26
+ def set_locale(
27
+ new_locale: str | tuple[str, str], lc_var: int = locale.LC_ALL
28
+ ) -> Generator[str | tuple[str, str]]:
29
+ """
30
+ Context manager for temporarily setting a locale.
31
+
32
+ Parameters
33
+ ----------
34
+ new_locale : str or tuple
35
+ A string of the form <language_country>.<encoding>. For example to set
36
+ the current locale to US English with a UTF8 encoding, you would pass
37
+ "en_US.UTF-8".
38
+ lc_var : int, default `locale.LC_ALL`
39
+ The category of the locale being set.
40
+
41
+ Notes
42
+ -----
43
+ This is useful when you want to run a particular block of code under a
44
+ particular locale, without globally setting the locale. This probably isn't
45
+ thread-safe.
46
+ """
47
+ # getlocale is not always compliant with setlocale, use setlocale. GH#46595
48
+ current_locale = locale.setlocale(lc_var)
49
+
50
+ try:
51
+ locale.setlocale(lc_var, new_locale)
52
+ normalized_code, normalized_encoding = locale.getlocale()
53
+ if normalized_code is not None and normalized_encoding is not None:
54
+ yield f"{normalized_code}.{normalized_encoding}"
55
+ else:
56
+ yield new_locale
57
+ finally:
58
+ locale.setlocale(lc_var, current_locale)
59
+
60
+
61
+ def can_set_locale(lc: str, lc_var: int = locale.LC_ALL) -> bool:
62
+ """
63
+ Check to see if we can set a locale, and subsequently get the locale,
64
+ without raising an Exception.
65
+
66
+ Parameters
67
+ ----------
68
+ lc : str
69
+ The locale to attempt to set.
70
+ lc_var : int, default `locale.LC_ALL`
71
+ The category of the locale being set.
72
+
73
+ Returns
74
+ -------
75
+ bool
76
+ Whether the passed locale can be set
77
+ """
78
+ try:
79
+ with set_locale(lc, lc_var=lc_var):
80
+ pass
81
+ except (ValueError, locale.Error):
82
+ # horrible name for an Exception subclass
83
+ return False
84
+ else:
85
+ return True
86
+
87
+
88
+ def _valid_locales(locales: list[str] | str, normalize: bool) -> list[str]:
89
+ """
90
+ Return a list of normalized locales that do not throw an ``Exception``
91
+ when set.
92
+
93
+ Parameters
94
+ ----------
95
+ locales : str
96
+ A string where each locale is separated by a newline.
97
+ normalize : bool
98
+ Whether to call ``locale.normalize`` on each locale.
99
+
100
+ Returns
101
+ -------
102
+ valid_locales : list
103
+ A list of valid locales.
104
+ """
105
+ return [
106
+ loc
107
+ for loc in (
108
+ locale.normalize(loc.strip()) if normalize else loc.strip()
109
+ for loc in locales
110
+ )
111
+ if can_set_locale(loc)
112
+ ]
113
+
114
+
115
+ def get_locales(
116
+ prefix: str | None = None,
117
+ normalize: bool = True,
118
+ ) -> list[str]:
119
+ """
120
+ Get all the locales that are available on the system.
121
+
122
+ Parameters
123
+ ----------
124
+ prefix : str
125
+ If not ``None`` then return only those locales with the prefix
126
+ provided. For example to get all English language locales (those that
127
+ start with ``"en"``), pass ``prefix="en"``.
128
+ normalize : bool
129
+ Call ``locale.normalize`` on the resulting list of available locales.
130
+ If ``True``, only locales that can be set without throwing an
131
+ ``Exception`` are returned.
132
+
133
+ Returns
134
+ -------
135
+ locales : list of strings
136
+ A list of locale strings that can be set with ``locale.setlocale()``.
137
+ For example::
138
+
139
+ locale.setlocale(locale.LC_ALL, locale_string)
140
+
141
+ On error will return an empty list (no locale available, e.g. Windows)
142
+
143
+ """
144
+ if platform.system() in ("Linux", "Darwin"):
145
+ raw_locales = subprocess.check_output(["locale", "-a"])
146
+ else:
147
+ # Other platforms e.g. windows platforms don't define "locale -a"
148
+ # Note: is_platform_windows causes circular import here
149
+ return []
150
+
151
+ try:
152
+ # raw_locales is "\n" separated list of locales
153
+ # it may contain non-decodable parts, so split
154
+ # extract what we can and then rejoin.
155
+ split_raw_locales = raw_locales.split(b"\n")
156
+ out_locales = []
157
+ for x in split_raw_locales:
158
+ try:
159
+ out_locales.append(str(x, encoding=cast(str, options.display.encoding)))
160
+ except UnicodeError:
161
+ # 'locale -a' is used to populated 'raw_locales' and on
162
+ # Redhat 7 Linux (and maybe others) prints locale names
163
+ # using windows-1252 encoding. Bug only triggered by
164
+ # a few special characters and when there is an
165
+ # extensive list of installed locales.
166
+ out_locales.append(str(x, encoding="windows-1252"))
167
+
168
+ except TypeError:
169
+ pass
170
+
171
+ if prefix is None:
172
+ return _valid_locales(out_locales, normalize)
173
+
174
+ pattern = re.compile(f"{prefix}.*")
175
+ found = pattern.findall("\n".join(out_locales))
176
+ return _valid_locales(found, normalize)
pandas/_libs/__init__.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ __all__ = [
2
+ "Interval",
3
+ "NaT",
4
+ "NaTType",
5
+ "OutOfBoundsDatetime",
6
+ "Period",
7
+ "Timedelta",
8
+ "Timestamp",
9
+ "iNaT",
10
+ ]
11
+
12
+
13
+ # Below imports needs to happen first to ensure pandas top level
14
+ # module gets monkeypatched with the pandas_datetime_CAPI
15
+ # see pandas_datetime_exec in pd_datetime.c
16
+ import pandas._libs.pandas_parser # isort: skip # type: ignore[reportUnusedImport]
17
+ import pandas._libs.pandas_datetime # noqa: F401 # isort: skip # type: ignore[reportUnusedImport]
18
+ from pandas._libs.interval import Interval
19
+ from pandas._libs.tslibs import (
20
+ NaT,
21
+ NaTType,
22
+ OutOfBoundsDatetime,
23
+ Period,
24
+ Timedelta,
25
+ Timestamp,
26
+ iNaT,
27
+ )
pandas/_libs/algos.pyi ADDED
@@ -0,0 +1,443 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Any
2
+
3
+ import numpy as np
4
+
5
+ from pandas._typing import npt
6
+
7
+ class Infinity:
8
+ def __eq__(self, other) -> bool: ...
9
+ def __ne__(self, other) -> bool: ...
10
+ def __lt__(self, other) -> bool: ...
11
+ def __le__(self, other) -> bool: ...
12
+ def __gt__(self, other) -> bool: ...
13
+ def __ge__(self, other) -> bool: ...
14
+
15
+ class NegInfinity:
16
+ def __eq__(self, other) -> bool: ...
17
+ def __ne__(self, other) -> bool: ...
18
+ def __lt__(self, other) -> bool: ...
19
+ def __le__(self, other) -> bool: ...
20
+ def __gt__(self, other) -> bool: ...
21
+ def __ge__(self, other) -> bool: ...
22
+
23
+ def unique_deltas(
24
+ arr: np.ndarray, # const int64_t[:]
25
+ ) -> np.ndarray: ... # np.ndarray[np.int64, ndim=1]
26
+ def is_lexsorted(list_of_arrays: list[npt.NDArray[np.int64]]) -> bool: ...
27
+ def groupsort_indexer(
28
+ index: np.ndarray, # const int64_t[:]
29
+ ngroups: int,
30
+ ) -> tuple[
31
+ np.ndarray, # ndarray[int64_t, ndim=1]
32
+ np.ndarray, # ndarray[int64_t, ndim=1]
33
+ ]: ...
34
+ def kth_smallest(
35
+ arr: np.ndarray, # numeric[:]
36
+ k: int,
37
+ ) -> Any: ... # numeric
38
+
39
+ # ----------------------------------------------------------------------
40
+ # Pairwise correlation/covariance
41
+
42
+ def nancorr(
43
+ mat: npt.NDArray[np.float64], # const float64_t[:, :]
44
+ cov: bool = ...,
45
+ minp: int | None = ...,
46
+ ) -> npt.NDArray[np.float64]: ... # ndarray[float64_t, ndim=2]
47
+ def nancorr_spearman(
48
+ mat: npt.NDArray[np.float64], # ndarray[float64_t, ndim=2]
49
+ minp: int = ...,
50
+ ) -> npt.NDArray[np.float64]: ... # ndarray[float64_t, ndim=2]
51
+
52
+ # ----------------------------------------------------------------------
53
+
54
+ def validate_limit(nobs: int | None, limit=...) -> int: ...
55
+ def get_fill_indexer(
56
+ mask: npt.NDArray[np.bool_],
57
+ limit: int | None = None,
58
+ ) -> npt.NDArray[np.intp]: ...
59
+ def pad(
60
+ old: np.ndarray, # ndarray[numeric_object_t]
61
+ new: np.ndarray, # ndarray[numeric_object_t]
62
+ limit=...,
63
+ ) -> npt.NDArray[np.intp]: ... # np.ndarray[np.intp, ndim=1]
64
+ def pad_inplace(
65
+ values: np.ndarray, # numeric_object_t[:]
66
+ mask: np.ndarray, # uint8_t[:]
67
+ limit=...,
68
+ ) -> None: ...
69
+ def pad_2d_inplace(
70
+ values: np.ndarray, # numeric_object_t[:, :]
71
+ mask: np.ndarray, # const uint8_t[:, :]
72
+ limit=...,
73
+ ) -> None: ...
74
+ def backfill(
75
+ old: np.ndarray, # ndarray[numeric_object_t]
76
+ new: np.ndarray, # ndarray[numeric_object_t]
77
+ limit=...,
78
+ ) -> npt.NDArray[np.intp]: ... # np.ndarray[np.intp, ndim=1]
79
+ def backfill_inplace(
80
+ values: np.ndarray, # numeric_object_t[:]
81
+ mask: np.ndarray, # uint8_t[:]
82
+ limit=...,
83
+ ) -> None: ...
84
+ def backfill_2d_inplace(
85
+ values: np.ndarray, # numeric_object_t[:, :]
86
+ mask: np.ndarray, # const uint8_t[:, :]
87
+ limit=...,
88
+ ) -> None: ...
89
+ def is_monotonic(
90
+ arr: np.ndarray, # ndarray[numeric_object_t, ndim=1]
91
+ timelike: bool,
92
+ ) -> tuple[bool, bool, bool]: ...
93
+
94
+ # ----------------------------------------------------------------------
95
+ # rank_1d, rank_2d
96
+ # ----------------------------------------------------------------------
97
+
98
+ def rank_1d(
99
+ values: np.ndarray, # ndarray[numeric_object_t, ndim=1]
100
+ labels: np.ndarray | None = ..., # const int64_t[:]=None
101
+ is_datetimelike: bool = ...,
102
+ ties_method=...,
103
+ ascending: bool = ...,
104
+ pct: bool = ...,
105
+ na_option=...,
106
+ mask: npt.NDArray[np.bool_] | None = ...,
107
+ ) -> np.ndarray: ... # np.ndarray[float64_t, ndim=1]
108
+ def rank_2d(
109
+ in_arr: np.ndarray, # ndarray[numeric_object_t, ndim=2]
110
+ axis: int = ...,
111
+ is_datetimelike: bool = ...,
112
+ ties_method=...,
113
+ ascending: bool = ...,
114
+ na_option=...,
115
+ pct: bool = ...,
116
+ ) -> np.ndarray: ... # np.ndarray[float64_t, ndim=1]
117
+ def diff_2d(
118
+ arr: np.ndarray, # ndarray[diff_t, ndim=2]
119
+ out: np.ndarray, # ndarray[out_t, ndim=2]
120
+ periods: int,
121
+ axis: int,
122
+ datetimelike: bool = ...,
123
+ ) -> None: ...
124
+ def ensure_platform_int(arr: object) -> npt.NDArray[np.intp]: ...
125
+ def ensure_object(arr: object) -> npt.NDArray[np.object_]: ...
126
+ def ensure_float64(arr: object) -> npt.NDArray[np.float64]: ...
127
+ def ensure_int8(arr: object) -> npt.NDArray[np.int8]: ...
128
+ def ensure_int16(arr: object) -> npt.NDArray[np.int16]: ...
129
+ def ensure_int32(arr: object) -> npt.NDArray[np.int32]: ...
130
+ def ensure_int64(arr: object) -> npt.NDArray[np.int64]: ...
131
+ def ensure_uint64(arr: object) -> npt.NDArray[np.uint64]: ...
132
+ def take_1d_int8_int8(
133
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
134
+ ) -> None: ...
135
+ def take_1d_int8_int32(
136
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
137
+ ) -> None: ...
138
+ def take_1d_int8_int64(
139
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
140
+ ) -> None: ...
141
+ def take_1d_int8_float64(
142
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
143
+ ) -> None: ...
144
+ def take_1d_int16_int16(
145
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
146
+ ) -> None: ...
147
+ def take_1d_int16_int32(
148
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
149
+ ) -> None: ...
150
+ def take_1d_int16_int64(
151
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
152
+ ) -> None: ...
153
+ def take_1d_int16_float64(
154
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
155
+ ) -> None: ...
156
+ def take_1d_int32_int32(
157
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
158
+ ) -> None: ...
159
+ def take_1d_int32_int64(
160
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
161
+ ) -> None: ...
162
+ def take_1d_int32_float64(
163
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
164
+ ) -> None: ...
165
+ def take_1d_int64_int64(
166
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
167
+ ) -> None: ...
168
+ def take_1d_uint16_uint16(
169
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
170
+ ) -> None: ...
171
+ def take_1d_uint32_uint32(
172
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
173
+ ) -> None: ...
174
+ def take_1d_uint64_uint64(
175
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
176
+ ) -> None: ...
177
+ def take_1d_int64_float64(
178
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
179
+ ) -> None: ...
180
+ def take_1d_float32_float32(
181
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
182
+ ) -> None: ...
183
+ def take_1d_float32_float64(
184
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
185
+ ) -> None: ...
186
+ def take_1d_float64_float64(
187
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
188
+ ) -> None: ...
189
+ def take_1d_object_object(
190
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
191
+ ) -> None: ...
192
+ def take_1d_bool_bool(
193
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
194
+ ) -> None: ...
195
+ def take_1d_bool_object(
196
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
197
+ ) -> None: ...
198
+ def take_2d_axis0_int8_int8(
199
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
200
+ ) -> None: ...
201
+ def take_2d_axis0_int8_int32(
202
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
203
+ ) -> None: ...
204
+ def take_2d_axis0_int8_int64(
205
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
206
+ ) -> None: ...
207
+ def take_2d_axis0_int8_float64(
208
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
209
+ ) -> None: ...
210
+ def take_2d_axis0_int16_int16(
211
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
212
+ ) -> None: ...
213
+ def take_2d_axis0_int16_int32(
214
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
215
+ ) -> None: ...
216
+ def take_2d_axis0_int16_int64(
217
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
218
+ ) -> None: ...
219
+ def take_2d_axis0_int16_float64(
220
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
221
+ ) -> None: ...
222
+ def take_2d_axis0_int32_int32(
223
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
224
+ ) -> None: ...
225
+ def take_2d_axis0_int32_int64(
226
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
227
+ ) -> None: ...
228
+ def take_2d_axis0_int32_float64(
229
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
230
+ ) -> None: ...
231
+ def take_2d_axis0_int64_int64(
232
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
233
+ ) -> None: ...
234
+ def take_2d_axis0_int64_float64(
235
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
236
+ ) -> None: ...
237
+ def take_2d_axis0_uint16_uint16(
238
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
239
+ ) -> None: ...
240
+ def take_2d_axis0_uint32_uint32(
241
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
242
+ ) -> None: ...
243
+ def take_2d_axis0_uint64_uint64(
244
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
245
+ ) -> None: ...
246
+ def take_2d_axis0_float32_float32(
247
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
248
+ ) -> None: ...
249
+ def take_2d_axis0_float32_float64(
250
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
251
+ ) -> None: ...
252
+ def take_2d_axis0_float64_float64(
253
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
254
+ ) -> None: ...
255
+ def take_2d_axis0_object_object(
256
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
257
+ ) -> None: ...
258
+ def take_2d_axis0_bool_bool(
259
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
260
+ ) -> None: ...
261
+ def take_2d_axis0_bool_object(
262
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
263
+ ) -> None: ...
264
+ def take_2d_axis1_int8_int8(
265
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
266
+ ) -> None: ...
267
+ def take_2d_axis1_int8_int32(
268
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
269
+ ) -> None: ...
270
+ def take_2d_axis1_int8_int64(
271
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
272
+ ) -> None: ...
273
+ def take_2d_axis1_int8_float64(
274
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
275
+ ) -> None: ...
276
+ def take_2d_axis1_int16_int16(
277
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
278
+ ) -> None: ...
279
+ def take_2d_axis1_int16_int32(
280
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
281
+ ) -> None: ...
282
+ def take_2d_axis1_int16_int64(
283
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
284
+ ) -> None: ...
285
+ def take_2d_axis1_int16_float64(
286
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
287
+ ) -> None: ...
288
+ def take_2d_axis1_int32_int32(
289
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
290
+ ) -> None: ...
291
+ def take_2d_axis1_int32_int64(
292
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
293
+ ) -> None: ...
294
+ def take_2d_axis1_int32_float64(
295
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
296
+ ) -> None: ...
297
+ def take_2d_axis1_int64_int64(
298
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
299
+ ) -> None: ...
300
+ def take_2d_axis1_uint16_uint16(
301
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
302
+ ) -> None: ...
303
+ def take_2d_axis1_uint32_uint32(
304
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
305
+ ) -> None: ...
306
+ def take_2d_axis1_uint64_uint64(
307
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
308
+ ) -> None: ...
309
+ def take_2d_axis1_int64_float64(
310
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
311
+ ) -> None: ...
312
+ def take_2d_axis1_float32_float32(
313
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
314
+ ) -> None: ...
315
+ def take_2d_axis1_float32_float64(
316
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
317
+ ) -> None: ...
318
+ def take_2d_axis1_float64_float64(
319
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
320
+ ) -> None: ...
321
+ def take_2d_axis1_object_object(
322
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
323
+ ) -> None: ...
324
+ def take_2d_axis1_bool_bool(
325
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
326
+ ) -> None: ...
327
+ def take_2d_axis1_bool_object(
328
+ values: np.ndarray, indexer: npt.NDArray[np.intp], out: np.ndarray, fill_value=...
329
+ ) -> None: ...
330
+ def take_2d_multi_int8_int8(
331
+ values: np.ndarray,
332
+ indexer: tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]],
333
+ out: np.ndarray,
334
+ fill_value=...,
335
+ ) -> None: ...
336
+ def take_2d_multi_int8_int32(
337
+ values: np.ndarray,
338
+ indexer: tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]],
339
+ out: np.ndarray,
340
+ fill_value=...,
341
+ ) -> None: ...
342
+ def take_2d_multi_int8_int64(
343
+ values: np.ndarray,
344
+ indexer: tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]],
345
+ out: np.ndarray,
346
+ fill_value=...,
347
+ ) -> None: ...
348
+ def take_2d_multi_int8_float64(
349
+ values: np.ndarray,
350
+ indexer: tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]],
351
+ out: np.ndarray,
352
+ fill_value=...,
353
+ ) -> None: ...
354
+ def take_2d_multi_int16_int16(
355
+ values: np.ndarray,
356
+ indexer: tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]],
357
+ out: np.ndarray,
358
+ fill_value=...,
359
+ ) -> None: ...
360
+ def take_2d_multi_int16_int32(
361
+ values: np.ndarray,
362
+ indexer: tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]],
363
+ out: np.ndarray,
364
+ fill_value=...,
365
+ ) -> None: ...
366
+ def take_2d_multi_int16_int64(
367
+ values: np.ndarray,
368
+ indexer: tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]],
369
+ out: np.ndarray,
370
+ fill_value=...,
371
+ ) -> None: ...
372
+ def take_2d_multi_int16_float64(
373
+ values: np.ndarray,
374
+ indexer: tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]],
375
+ out: np.ndarray,
376
+ fill_value=...,
377
+ ) -> None: ...
378
+ def take_2d_multi_int32_int32(
379
+ values: np.ndarray,
380
+ indexer: tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]],
381
+ out: np.ndarray,
382
+ fill_value=...,
383
+ ) -> None: ...
384
+ def take_2d_multi_int32_int64(
385
+ values: np.ndarray,
386
+ indexer: tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]],
387
+ out: np.ndarray,
388
+ fill_value=...,
389
+ ) -> None: ...
390
+ def take_2d_multi_int32_float64(
391
+ values: np.ndarray,
392
+ indexer: tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]],
393
+ out: np.ndarray,
394
+ fill_value=...,
395
+ ) -> None: ...
396
+ def take_2d_multi_int64_float64(
397
+ values: np.ndarray,
398
+ indexer: tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]],
399
+ out: np.ndarray,
400
+ fill_value=...,
401
+ ) -> None: ...
402
+ def take_2d_multi_float32_float32(
403
+ values: np.ndarray,
404
+ indexer: tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]],
405
+ out: np.ndarray,
406
+ fill_value=...,
407
+ ) -> None: ...
408
+ def take_2d_multi_float32_float64(
409
+ values: np.ndarray,
410
+ indexer: tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]],
411
+ out: np.ndarray,
412
+ fill_value=...,
413
+ ) -> None: ...
414
+ def take_2d_multi_float64_float64(
415
+ values: np.ndarray,
416
+ indexer: tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]],
417
+ out: np.ndarray,
418
+ fill_value=...,
419
+ ) -> None: ...
420
+ def take_2d_multi_object_object(
421
+ values: np.ndarray,
422
+ indexer: tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]],
423
+ out: np.ndarray,
424
+ fill_value=...,
425
+ ) -> None: ...
426
+ def take_2d_multi_bool_bool(
427
+ values: np.ndarray,
428
+ indexer: tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]],
429
+ out: np.ndarray,
430
+ fill_value=...,
431
+ ) -> None: ...
432
+ def take_2d_multi_bool_object(
433
+ values: np.ndarray,
434
+ indexer: tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]],
435
+ out: np.ndarray,
436
+ fill_value=...,
437
+ ) -> None: ...
438
+ def take_2d_multi_int64_int64(
439
+ values: np.ndarray,
440
+ indexer: tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]],
441
+ out: np.ndarray,
442
+ fill_value=...,
443
+ ) -> None: ...
pandas/_libs/arrays.pyi ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from collections.abc import Sequence
2
+ from typing import Self
3
+
4
+ import numpy as np
5
+
6
+ from pandas._typing import (
7
+ AxisInt,
8
+ DtypeObj,
9
+ Shape,
10
+ )
11
+
12
+ class NDArrayBacked:
13
+ _dtype: DtypeObj
14
+ _ndarray: np.ndarray
15
+ def __init__(self, values: np.ndarray, dtype: DtypeObj) -> None: ...
16
+ @classmethod
17
+ def _simple_new(cls, values: np.ndarray, dtype: DtypeObj) -> Self: ...
18
+ def _from_backing_data(self, values: np.ndarray) -> Self: ...
19
+ def __setstate__(self, state) -> None: ...
20
+ def __len__(self) -> int: ...
21
+ @property
22
+ def shape(self) -> Shape: ...
23
+ @property
24
+ def ndim(self) -> int: ...
25
+ @property
26
+ def size(self) -> int: ...
27
+ @property
28
+ def nbytes(self) -> int: ...
29
+ def copy(self, order=...) -> Self: ...
30
+ def delete(self, loc, axis=...) -> Self: ...
31
+ def swapaxes(self, axis1, axis2) -> Self: ...
32
+ def repeat(self, repeats: int | Sequence[int], axis: int | None = ...) -> Self: ...
33
+ def reshape(self, *args, **kwargs) -> Self: ...
34
+ def ravel(self, order=...) -> Self: ...
35
+ @property
36
+ def T(self) -> Self: ...
37
+ @classmethod
38
+ def _concat_same_type(
39
+ cls, to_concat: Sequence[Self], axis: AxisInt = ...
40
+ ) -> Self: ...
pandas/_libs/byteswap.cpython-312-x86_64-linux-gnu.so ADDED
Binary file (49.7 kB). View file
 
pandas/_libs/byteswap.pyi ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ def read_float_with_byteswap(data: bytes, offset: int, byteswap: bool) -> float: ...
2
+ def read_double_with_byteswap(data: bytes, offset: int, byteswap: bool) -> float: ...
3
+ def read_uint16_with_byteswap(data: bytes, offset: int, byteswap: bool) -> int: ...
4
+ def read_uint32_with_byteswap(data: bytes, offset: int, byteswap: bool) -> int: ...
5
+ def read_uint64_with_byteswap(data: bytes, offset: int, byteswap: bool) -> int: ...
pandas/_libs/groupby.pyi ADDED
@@ -0,0 +1,234 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Literal
2
+
3
+ import numpy as np
4
+
5
+ from pandas._typing import npt
6
+
7
+ def group_median_float64(
8
+ out: np.ndarray, # ndarray[float64_t, ndim=2]
9
+ counts: npt.NDArray[np.int64],
10
+ values: np.ndarray, # ndarray[float64_t, ndim=2]
11
+ labels: npt.NDArray[np.int64],
12
+ min_count: int = ..., # Py_ssize_t
13
+ mask: np.ndarray | None = ...,
14
+ result_mask: np.ndarray | None = ...,
15
+ is_datetimelike: bool = ..., # bint
16
+ skipna: bool = ...,
17
+ ) -> None: ...
18
+ def group_cumprod(
19
+ out: np.ndarray, # float64_t[:, ::1]
20
+ values: np.ndarray, # const float64_t[:, :]
21
+ labels: np.ndarray, # const int64_t[:]
22
+ ngroups: int,
23
+ is_datetimelike: bool,
24
+ skipna: bool = ...,
25
+ mask: np.ndarray | None = ...,
26
+ result_mask: np.ndarray | None = ...,
27
+ ) -> None: ...
28
+ def group_cumsum(
29
+ out: np.ndarray, # int64float_t[:, ::1]
30
+ values: np.ndarray, # ndarray[int64float_t, ndim=2]
31
+ labels: np.ndarray, # const int64_t[:]
32
+ ngroups: int,
33
+ is_datetimelike: bool,
34
+ skipna: bool = ...,
35
+ mask: np.ndarray | None = ...,
36
+ result_mask: np.ndarray | None = ...,
37
+ ) -> None: ...
38
+ def group_shift_indexer(
39
+ out: np.ndarray, # int64_t[::1]
40
+ labels: np.ndarray, # const int64_t[:]
41
+ ngroups: int,
42
+ periods: int,
43
+ ) -> None: ...
44
+ def group_fillna_indexer(
45
+ out: np.ndarray, # ndarray[intp_t]
46
+ labels: np.ndarray, # ndarray[int64_t]
47
+ mask: npt.NDArray[np.uint8],
48
+ limit: int, # int64_t
49
+ compute_ffill: bool,
50
+ ngroups: int,
51
+ ) -> None: ...
52
+ def group_any_all(
53
+ out: np.ndarray, # uint8_t[::1]
54
+ values: np.ndarray, # const uint8_t[::1]
55
+ labels: np.ndarray, # const int64_t[:]
56
+ mask: np.ndarray, # const uint8_t[::1]
57
+ val_test: Literal["any", "all"],
58
+ skipna: bool,
59
+ result_mask: np.ndarray | None,
60
+ ) -> None: ...
61
+ def group_sum(
62
+ out: np.ndarray, # complexfloatingintuint_t[:, ::1]
63
+ counts: np.ndarray, # int64_t[::1]
64
+ values: np.ndarray, # ndarray[complexfloatingintuint_t, ndim=2]
65
+ labels: np.ndarray, # const intp_t[:]
66
+ mask: np.ndarray | None,
67
+ result_mask: np.ndarray | None = ...,
68
+ min_count: int = ...,
69
+ is_datetimelike: bool = ...,
70
+ initial: object = ...,
71
+ skipna: bool = ...,
72
+ ) -> None: ...
73
+ def group_prod(
74
+ out: np.ndarray, # int64float_t[:, ::1]
75
+ counts: np.ndarray, # int64_t[::1]
76
+ values: np.ndarray, # ndarray[int64float_t, ndim=2]
77
+ labels: np.ndarray, # const intp_t[:]
78
+ mask: np.ndarray | None,
79
+ result_mask: np.ndarray | None = ...,
80
+ min_count: int = ...,
81
+ skipna: bool = ...,
82
+ ) -> None: ...
83
+ def group_var(
84
+ out: np.ndarray, # floating[:, ::1]
85
+ counts: np.ndarray, # int64_t[::1]
86
+ values: np.ndarray, # ndarray[floating, ndim=2]
87
+ labels: np.ndarray, # const intp_t[:]
88
+ min_count: int = ..., # Py_ssize_t
89
+ ddof: int = ..., # int64_t
90
+ mask: np.ndarray | None = ...,
91
+ result_mask: np.ndarray | None = ...,
92
+ is_datetimelike: bool = ...,
93
+ name: str = ...,
94
+ skipna: bool = ...,
95
+ ) -> None: ...
96
+ def group_skew(
97
+ out: np.ndarray, # float64_t[:, ::1]
98
+ counts: np.ndarray, # int64_t[::1]
99
+ values: np.ndarray, # ndarray[float64_T, ndim=2]
100
+ labels: np.ndarray, # const intp_t[::1]
101
+ mask: np.ndarray | None = ...,
102
+ result_mask: np.ndarray | None = ...,
103
+ skipna: bool = ...,
104
+ ) -> None: ...
105
+ def group_kurt(
106
+ out: np.ndarray, # float64_t[:, ::1]
107
+ counts: np.ndarray, # int64_t[::1]
108
+ values: np.ndarray, # ndarray[float64_T, ndim=2]
109
+ labels: np.ndarray, # const intp_t[::1]
110
+ mask: np.ndarray | None = ...,
111
+ result_mask: np.ndarray | None = ...,
112
+ skipna: bool = ...,
113
+ ) -> None: ...
114
+ def group_mean(
115
+ out: np.ndarray, # floating[:, ::1]
116
+ counts: np.ndarray, # int64_t[::1]
117
+ values: np.ndarray, # ndarray[floating, ndim=2]
118
+ labels: np.ndarray, # const intp_t[:]
119
+ min_count: int = ..., # Py_ssize_t
120
+ is_datetimelike: bool = ..., # bint
121
+ mask: np.ndarray | None = ...,
122
+ result_mask: np.ndarray | None = ...,
123
+ skipna: bool = ...,
124
+ ) -> None: ...
125
+ def group_ohlc(
126
+ out: np.ndarray, # floatingintuint_t[:, ::1]
127
+ counts: np.ndarray, # int64_t[::1]
128
+ values: np.ndarray, # ndarray[floatingintuint_t, ndim=2]
129
+ labels: np.ndarray, # const intp_t[:]
130
+ min_count: int = ...,
131
+ mask: np.ndarray | None = ...,
132
+ result_mask: np.ndarray | None = ...,
133
+ ) -> None: ...
134
+ def group_quantile(
135
+ out: npt.NDArray[np.float64],
136
+ values: np.ndarray, # ndarray[numeric, ndim=1]
137
+ labels: npt.NDArray[np.intp],
138
+ mask: npt.NDArray[np.uint8],
139
+ qs: npt.NDArray[np.float64], # const
140
+ starts: npt.NDArray[np.int64],
141
+ ends: npt.NDArray[np.int64],
142
+ interpolation: Literal["linear", "lower", "higher", "nearest", "midpoint"],
143
+ result_mask: np.ndarray | None,
144
+ is_datetimelike: bool,
145
+ ) -> None: ...
146
+ def group_last(
147
+ out: np.ndarray, # rank_t[:, ::1]
148
+ counts: np.ndarray, # int64_t[::1]
149
+ values: np.ndarray, # ndarray[rank_t, ndim=2]
150
+ labels: np.ndarray, # const int64_t[:]
151
+ mask: npt.NDArray[np.bool_] | None,
152
+ result_mask: npt.NDArray[np.bool_] | None = ...,
153
+ min_count: int = ..., # Py_ssize_t
154
+ is_datetimelike: bool = ...,
155
+ skipna: bool = ...,
156
+ ) -> None: ...
157
+ def group_nth(
158
+ out: np.ndarray, # rank_t[:, ::1]
159
+ counts: np.ndarray, # int64_t[::1]
160
+ values: np.ndarray, # ndarray[rank_t, ndim=2]
161
+ labels: np.ndarray, # const int64_t[:]
162
+ mask: npt.NDArray[np.bool_] | None,
163
+ result_mask: npt.NDArray[np.bool_] | None = ...,
164
+ min_count: int = ..., # int64_t
165
+ rank: int = ..., # int64_t
166
+ is_datetimelike: bool = ...,
167
+ skipna: bool = ...,
168
+ ) -> None: ...
169
+ def group_rank(
170
+ out: np.ndarray, # float64_t[:, ::1]
171
+ values: np.ndarray, # ndarray[rank_t, ndim=2]
172
+ labels: np.ndarray, # const int64_t[:]
173
+ ngroups: int,
174
+ is_datetimelike: bool,
175
+ ties_method: Literal["average", "min", "max", "first", "dense"] = ...,
176
+ ascending: bool = ...,
177
+ pct: bool = ...,
178
+ na_option: Literal["keep", "top", "bottom"] = ...,
179
+ mask: npt.NDArray[np.bool_] | None = ...,
180
+ ) -> None: ...
181
+ def group_max(
182
+ out: np.ndarray, # groupby_t[:, ::1]
183
+ counts: np.ndarray, # int64_t[::1]
184
+ values: np.ndarray, # ndarray[groupby_t, ndim=2]
185
+ labels: np.ndarray, # const int64_t[:]
186
+ min_count: int = ...,
187
+ is_datetimelike: bool = ...,
188
+ mask: np.ndarray | None = ...,
189
+ result_mask: np.ndarray | None = ...,
190
+ skipna: bool = ...,
191
+ ) -> None: ...
192
+ def group_min(
193
+ out: np.ndarray, # groupby_t[:, ::1]
194
+ counts: np.ndarray, # int64_t[::1]
195
+ values: np.ndarray, # ndarray[groupby_t, ndim=2]
196
+ labels: np.ndarray, # const int64_t[:]
197
+ min_count: int = ...,
198
+ is_datetimelike: bool = ...,
199
+ mask: np.ndarray | None = ...,
200
+ result_mask: np.ndarray | None = ...,
201
+ skipna: bool = ...,
202
+ ) -> None: ...
203
+ def group_idxmin_idxmax(
204
+ out: npt.NDArray[np.intp],
205
+ counts: npt.NDArray[np.int64],
206
+ values: np.ndarray, # ndarray[groupby_t, ndim=2]
207
+ labels: npt.NDArray[np.intp],
208
+ min_count: int = ...,
209
+ is_datetimelike: bool = ...,
210
+ mask: np.ndarray | None = ...,
211
+ name: str = ...,
212
+ skipna: bool = ...,
213
+ result_mask: np.ndarray | None = ...,
214
+ ) -> None: ...
215
+ def group_cummin(
216
+ out: np.ndarray, # groupby_t[:, ::1]
217
+ values: np.ndarray, # ndarray[groupby_t, ndim=2]
218
+ labels: np.ndarray, # const int64_t[:]
219
+ ngroups: int,
220
+ is_datetimelike: bool,
221
+ mask: np.ndarray | None = ...,
222
+ result_mask: np.ndarray | None = ...,
223
+ skipna: bool = ...,
224
+ ) -> None: ...
225
+ def group_cummax(
226
+ out: np.ndarray, # groupby_t[:, ::1]
227
+ values: np.ndarray, # ndarray[groupby_t, ndim=2]
228
+ labels: np.ndarray, # const int64_t[:]
229
+ ngroups: int,
230
+ is_datetimelike: bool,
231
+ mask: np.ndarray | None = ...,
232
+ result_mask: np.ndarray | None = ...,
233
+ skipna: bool = ...,
234
+ ) -> None: ...
pandas/_libs/hashing.cpython-312-x86_64-linux-gnu.so ADDED
Binary file (78.6 kB). View file
 
pandas/_libs/hashing.pyi ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+
3
+ from pandas._typing import npt
4
+
5
+ def hash_object_array(
6
+ arr: npt.NDArray[np.object_],
7
+ key: str,
8
+ encoding: str = ...,
9
+ ) -> npt.NDArray[np.uint64]: ...
pandas/_libs/hashtable.pyi ADDED
@@ -0,0 +1,274 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from collections.abc import Hashable
2
+ from typing import (
3
+ Any,
4
+ Literal,
5
+ overload,
6
+ )
7
+
8
+ import numpy as np
9
+
10
+ from pandas._typing import npt
11
+
12
+ def unique_label_indices(
13
+ labels: np.ndarray, # const int64_t[:]
14
+ ) -> np.ndarray: ...
15
+
16
+ class Factorizer:
17
+ count: int
18
+ uniques: Any
19
+ def __init__(self, size_hint: int, uses_mask: bool = False) -> None: ...
20
+ def get_count(self) -> int: ...
21
+ def factorize(
22
+ self,
23
+ values: np.ndarray,
24
+ na_sentinel=...,
25
+ na_value=...,
26
+ mask=...,
27
+ ) -> npt.NDArray[np.intp]: ...
28
+ def hash_inner_join(
29
+ self, values: np.ndarray, mask=...
30
+ ) -> tuple[np.ndarray, np.ndarray]: ...
31
+
32
+ class ObjectFactorizer(Factorizer):
33
+ table: PyObjectHashTable
34
+ uniques: ObjectVector
35
+
36
+ class Int64Factorizer(Factorizer):
37
+ table: Int64HashTable
38
+ uniques: Int64Vector
39
+
40
+ class UInt64Factorizer(Factorizer):
41
+ table: UInt64HashTable
42
+ uniques: UInt64Vector
43
+
44
+ class Int32Factorizer(Factorizer):
45
+ table: Int32HashTable
46
+ uniques: Int32Vector
47
+
48
+ class UInt32Factorizer(Factorizer):
49
+ table: UInt32HashTable
50
+ uniques: UInt32Vector
51
+
52
+ class Int16Factorizer(Factorizer):
53
+ table: Int16HashTable
54
+ uniques: Int16Vector
55
+
56
+ class UInt16Factorizer(Factorizer):
57
+ table: UInt16HashTable
58
+ uniques: UInt16Vector
59
+
60
+ class Int8Factorizer(Factorizer):
61
+ table: Int8HashTable
62
+ uniques: Int8Vector
63
+
64
+ class UInt8Factorizer(Factorizer):
65
+ table: UInt8HashTable
66
+ uniques: UInt8Vector
67
+
68
+ class Float64Factorizer(Factorizer):
69
+ table: Float64HashTable
70
+ uniques: Float64Vector
71
+
72
+ class Float32Factorizer(Factorizer):
73
+ table: Float32HashTable
74
+ uniques: Float32Vector
75
+
76
+ class Complex64Factorizer(Factorizer):
77
+ table: Complex64HashTable
78
+ uniques: Complex64Vector
79
+
80
+ class Complex128Factorizer(Factorizer):
81
+ table: Complex128HashTable
82
+ uniques: Complex128Vector
83
+
84
+ class Int64Vector:
85
+ def __init__(self, *args) -> None: ...
86
+ def __len__(self) -> int: ...
87
+ def to_array(self) -> npt.NDArray[np.int64]: ...
88
+
89
+ class Int32Vector:
90
+ def __init__(self, *args) -> None: ...
91
+ def __len__(self) -> int: ...
92
+ def to_array(self) -> npt.NDArray[np.int32]: ...
93
+
94
+ class Int16Vector:
95
+ def __init__(self, *args) -> None: ...
96
+ def __len__(self) -> int: ...
97
+ def to_array(self) -> npt.NDArray[np.int16]: ...
98
+
99
+ class Int8Vector:
100
+ def __init__(self, *args) -> None: ...
101
+ def __len__(self) -> int: ...
102
+ def to_array(self) -> npt.NDArray[np.int8]: ...
103
+
104
+ class UInt64Vector:
105
+ def __init__(self, *args) -> None: ...
106
+ def __len__(self) -> int: ...
107
+ def to_array(self) -> npt.NDArray[np.uint64]: ...
108
+
109
+ class UInt32Vector:
110
+ def __init__(self, *args) -> None: ...
111
+ def __len__(self) -> int: ...
112
+ def to_array(self) -> npt.NDArray[np.uint32]: ...
113
+
114
+ class UInt16Vector:
115
+ def __init__(self, *args) -> None: ...
116
+ def __len__(self) -> int: ...
117
+ def to_array(self) -> npt.NDArray[np.uint16]: ...
118
+
119
+ class UInt8Vector:
120
+ def __init__(self, *args) -> None: ...
121
+ def __len__(self) -> int: ...
122
+ def to_array(self) -> npt.NDArray[np.uint8]: ...
123
+
124
+ class Float64Vector:
125
+ def __init__(self, *args) -> None: ...
126
+ def __len__(self) -> int: ...
127
+ def to_array(self) -> npt.NDArray[np.float64]: ...
128
+
129
+ class Float32Vector:
130
+ def __init__(self, *args) -> None: ...
131
+ def __len__(self) -> int: ...
132
+ def to_array(self) -> npt.NDArray[np.float32]: ...
133
+
134
+ class Complex128Vector:
135
+ def __init__(self, *args) -> None: ...
136
+ def __len__(self) -> int: ...
137
+ def to_array(self) -> npt.NDArray[np.complex128]: ...
138
+
139
+ class Complex64Vector:
140
+ def __init__(self, *args) -> None: ...
141
+ def __len__(self) -> int: ...
142
+ def to_array(self) -> npt.NDArray[np.complex64]: ...
143
+
144
+ class StringVector:
145
+ def __init__(self, *args) -> None: ...
146
+ def __len__(self) -> int: ...
147
+ def to_array(self) -> npt.NDArray[np.object_]: ...
148
+
149
+ class ObjectVector:
150
+ def __init__(self, *args) -> None: ...
151
+ def __len__(self) -> int: ...
152
+ def to_array(self) -> npt.NDArray[np.object_]: ...
153
+
154
+ class HashTable:
155
+ # NB: The base HashTable class does _not_ actually have these methods;
156
+ # we are putting them here for the sake of mypy to avoid
157
+ # reproducing them in each subclass below.
158
+ def __init__(self, size_hint: int = ..., uses_mask: bool = ...) -> None: ...
159
+ def __len__(self) -> int: ...
160
+ def __contains__(self, key: Hashable) -> bool: ...
161
+ def sizeof(self, deep: bool = ...) -> int: ...
162
+ def get_state(self) -> dict[str, int]: ...
163
+ # TODO: `val/key` type is subclass-specific
164
+ def get_item(self, val): ... # TODO: return type?
165
+ def set_item(self, key, val) -> None: ...
166
+ def get_na(self): ... # TODO: return type?
167
+ def set_na(self, val) -> None: ...
168
+ def map_locations(
169
+ self,
170
+ values: np.ndarray, # np.ndarray[subclass-specific]
171
+ mask: npt.NDArray[np.bool_] | None = ...,
172
+ ) -> None: ...
173
+ def lookup(
174
+ self,
175
+ values: np.ndarray, # np.ndarray[subclass-specific]
176
+ mask: npt.NDArray[np.bool_] | None = ...,
177
+ ) -> npt.NDArray[np.intp]: ...
178
+ def get_labels(
179
+ self,
180
+ values: np.ndarray, # np.ndarray[subclass-specific]
181
+ uniques, # SubclassTypeVector
182
+ count_prior: int = ...,
183
+ na_sentinel: int = ...,
184
+ na_value: object = ...,
185
+ mask=...,
186
+ ) -> npt.NDArray[np.intp]: ...
187
+ @overload
188
+ def unique(
189
+ self,
190
+ values: np.ndarray, # np.ndarray[subclass-specific]
191
+ *,
192
+ return_inverse: Literal[False] = ...,
193
+ mask: None = ...,
194
+ ) -> np.ndarray: ... # np.ndarray[subclass-specific]
195
+ @overload
196
+ def unique(
197
+ self,
198
+ values: np.ndarray, # np.ndarray[subclass-specific]
199
+ *,
200
+ return_inverse: Literal[True],
201
+ mask: None = ...,
202
+ ) -> tuple[np.ndarray, npt.NDArray[np.intp]]: ... # np.ndarray[subclass-specific]
203
+ @overload
204
+ def unique(
205
+ self,
206
+ values: np.ndarray, # np.ndarray[subclass-specific]
207
+ *,
208
+ return_inverse: Literal[False] = ...,
209
+ mask: npt.NDArray[np.bool_],
210
+ ) -> tuple[
211
+ np.ndarray,
212
+ npt.NDArray[np.bool_],
213
+ ]: ... # np.ndarray[subclass-specific]
214
+ def factorize(
215
+ self,
216
+ values: np.ndarray, # np.ndarray[subclass-specific]
217
+ na_sentinel: int = ...,
218
+ na_value: object = ...,
219
+ mask=...,
220
+ ignore_na: bool = True,
221
+ ) -> tuple[np.ndarray, npt.NDArray[np.intp]]: ... # np.ndarray[subclass-specific]
222
+ def hash_inner_join(
223
+ self, values: np.ndarray, mask=...
224
+ ) -> tuple[np.ndarray, np.ndarray]: ...
225
+
226
+ class Complex128HashTable(HashTable): ...
227
+ class Complex64HashTable(HashTable): ...
228
+ class Float64HashTable(HashTable): ...
229
+ class Float32HashTable(HashTable): ...
230
+
231
+ class Int64HashTable(HashTable):
232
+ # Only Int64HashTable has get_labels_groupby, map_keys_to_values
233
+ def get_labels_groupby(
234
+ self,
235
+ values: npt.NDArray[np.int64], # const int64_t[:]
236
+ ) -> tuple[npt.NDArray[np.intp], npt.NDArray[np.int64]]: ...
237
+ def map_keys_to_values(
238
+ self,
239
+ keys: npt.NDArray[np.int64],
240
+ values: npt.NDArray[np.int64], # const int64_t[:]
241
+ ) -> None: ...
242
+
243
+ class Int32HashTable(HashTable): ...
244
+ class Int16HashTable(HashTable): ...
245
+ class Int8HashTable(HashTable): ...
246
+ class UInt64HashTable(HashTable): ...
247
+ class UInt32HashTable(HashTable): ...
248
+ class UInt16HashTable(HashTable): ...
249
+ class UInt8HashTable(HashTable): ...
250
+ class StringHashTable(HashTable): ...
251
+ class PyObjectHashTable(HashTable): ...
252
+ class IntpHashTable(HashTable): ...
253
+
254
+ def duplicated(
255
+ values: np.ndarray,
256
+ keep: Literal["last", "first", False] = ...,
257
+ mask: npt.NDArray[np.bool_] | None = ...,
258
+ ) -> npt.NDArray[np.bool_]: ...
259
+ def mode(
260
+ values: np.ndarray, dropna: bool, mask: npt.NDArray[np.bool_] | None = ...
261
+ ) -> np.ndarray: ...
262
+ def value_count(
263
+ values: np.ndarray,
264
+ dropna: bool,
265
+ mask: npt.NDArray[np.bool_] | None = ...,
266
+ ) -> tuple[np.ndarray, npt.NDArray[np.int64], int]: ... # np.ndarray[same-as-values]
267
+
268
+ # arr and values should have same dtype
269
+ def ismember(
270
+ arr: np.ndarray,
271
+ values: np.ndarray,
272
+ ) -> npt.NDArray[np.bool_]: ...
273
+ def object_hash(obj) -> int: ...
274
+ def objects_are_equal(a, b) -> bool: ...
pandas/_libs/index.pyi ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+
3
+ from pandas._typing import npt
4
+
5
+ from pandas import (
6
+ Index,
7
+ MultiIndex,
8
+ )
9
+ from pandas.core.arrays import ExtensionArray
10
+
11
+ multiindex_nulls_shift: int
12
+
13
+ class IndexEngine:
14
+ over_size_threshold: bool
15
+ def __init__(self, values: np.ndarray) -> None: ...
16
+ def __contains__(self, val: object) -> bool: ...
17
+
18
+ # -> int | slice | np.ndarray[bool]
19
+ def get_loc(self, val: object) -> int | slice | np.ndarray: ...
20
+ def sizeof(self, deep: bool = ...) -> int: ...
21
+ def __sizeof__(self) -> int: ...
22
+ @property
23
+ def is_unique(self) -> bool: ...
24
+ @property
25
+ def is_monotonic_increasing(self) -> bool: ...
26
+ @property
27
+ def is_monotonic_decreasing(self) -> bool: ...
28
+ @property
29
+ def is_mapping_populated(self) -> bool: ...
30
+ def clear_mapping(self): ...
31
+ def get_indexer(self, values: np.ndarray) -> npt.NDArray[np.intp]: ...
32
+ def get_indexer_non_unique(
33
+ self,
34
+ targets: np.ndarray,
35
+ ) -> tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]]: ...
36
+
37
+ class MaskedIndexEngine(IndexEngine):
38
+ def __init__(self, values: object) -> None: ...
39
+ def get_indexer_non_unique(
40
+ self, targets: object
41
+ ) -> tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]]: ...
42
+
43
+ class Float64Engine(IndexEngine): ...
44
+ class Float32Engine(IndexEngine): ...
45
+ class Complex128Engine(IndexEngine): ...
46
+ class Complex64Engine(IndexEngine): ...
47
+ class Int64Engine(IndexEngine): ...
48
+ class Int32Engine(IndexEngine): ...
49
+ class Int16Engine(IndexEngine): ...
50
+ class Int8Engine(IndexEngine): ...
51
+ class UInt64Engine(IndexEngine): ...
52
+ class UInt32Engine(IndexEngine): ...
53
+ class UInt16Engine(IndexEngine): ...
54
+ class UInt8Engine(IndexEngine): ...
55
+ class ObjectEngine(IndexEngine): ...
56
+ class StringEngine(IndexEngine): ...
57
+ class DatetimeEngine(Int64Engine): ...
58
+ class TimedeltaEngine(DatetimeEngine): ...
59
+ class PeriodEngine(Int64Engine): ...
60
+ class BoolEngine(UInt8Engine): ...
61
+ class MaskedFloat64Engine(MaskedIndexEngine): ...
62
+ class MaskedFloat32Engine(MaskedIndexEngine): ...
63
+ class MaskedComplex128Engine(MaskedIndexEngine): ...
64
+ class MaskedComplex64Engine(MaskedIndexEngine): ...
65
+ class MaskedInt64Engine(MaskedIndexEngine): ...
66
+ class MaskedInt32Engine(MaskedIndexEngine): ...
67
+ class MaskedInt16Engine(MaskedIndexEngine): ...
68
+ class MaskedInt8Engine(MaskedIndexEngine): ...
69
+ class MaskedUInt64Engine(MaskedIndexEngine): ...
70
+ class MaskedUInt32Engine(MaskedIndexEngine): ...
71
+ class MaskedUInt16Engine(MaskedIndexEngine): ...
72
+ class MaskedUInt8Engine(MaskedIndexEngine): ...
73
+ class MaskedBoolEngine(MaskedUInt8Engine): ...
74
+
75
+ class StringObjectEngine(ObjectEngine):
76
+ def __init__(self, values: object, na_value) -> None: ...
77
+
78
+ class BaseMultiIndexCodesEngine:
79
+ levels: list[np.ndarray]
80
+ offsets: np.ndarray # np.ndarray[..., ndim=1]
81
+
82
+ def __init__(
83
+ self,
84
+ levels: list[Index], # all entries hashable
85
+ labels: list[np.ndarray], # all entries integer-dtyped
86
+ offsets: np.ndarray, # np.ndarray[..., ndim=1]
87
+ ) -> None: ...
88
+ def get_indexer(self, target: npt.NDArray[np.object_]) -> npt.NDArray[np.intp]: ...
89
+ def _extract_level_codes(self, target: MultiIndex) -> np.ndarray: ...
90
+
91
+ class ExtensionEngine:
92
+ def __init__(self, values: ExtensionArray) -> None: ...
93
+ def __contains__(self, val: object) -> bool: ...
94
+ def get_loc(self, val: object) -> int | slice | np.ndarray: ...
95
+ def get_indexer(self, values: np.ndarray) -> npt.NDArray[np.intp]: ...
96
+ def get_indexer_non_unique(
97
+ self,
98
+ targets: np.ndarray,
99
+ ) -> tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]]: ...
100
+ @property
101
+ def is_unique(self) -> bool: ...
102
+ @property
103
+ def is_monotonic_increasing(self) -> bool: ...
104
+ @property
105
+ def is_monotonic_decreasing(self) -> bool: ...
106
+ def sizeof(self, deep: bool = ...) -> int: ...
107
+ def clear_mapping(self): ...
pandas/_libs/indexing.cpython-312-x86_64-linux-gnu.so ADDED
Binary file (62.6 kB). View file
 
pandas/_libs/indexing.pyi ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import (
2
+ Generic,
3
+ TypeVar,
4
+ )
5
+
6
+ from pandas.core.indexing import IndexingMixin
7
+
8
+ _IndexingMixinT = TypeVar("_IndexingMixinT", bound=IndexingMixin)
9
+
10
+ class NDFrameIndexerBase(Generic[_IndexingMixinT]):
11
+ name: str
12
+ # in practice obj is either a DataFrame or a Series
13
+ obj: _IndexingMixinT
14
+
15
+ def __init__(self, name: str, obj: _IndexingMixinT) -> None: ...
16
+ @property
17
+ def ndim(self) -> int: ...
pandas/_libs/internals.pyi ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from collections.abc import (
2
+ Iterator,
3
+ Sequence,
4
+ )
5
+ from typing import (
6
+ Self,
7
+ final,
8
+ overload,
9
+ )
10
+ import weakref
11
+
12
+ import numpy as np
13
+
14
+ from pandas._typing import (
15
+ ArrayLike,
16
+ npt,
17
+ )
18
+
19
+ from pandas import Index
20
+ from pandas.core.internals.blocks import Block as B
21
+
22
+ def slice_len(slc: slice, objlen: int = ...) -> int: ...
23
+ def get_concat_blkno_indexers(
24
+ blknos_list: list[npt.NDArray[np.intp]],
25
+ ) -> list[tuple[npt.NDArray[np.intp], BlockPlacement]]: ...
26
+ def get_blkno_indexers(
27
+ blknos: np.ndarray, # int64_t[:]
28
+ group: bool = ...,
29
+ ) -> list[tuple[int, slice | np.ndarray]]: ...
30
+ def get_blkno_placements(
31
+ blknos: np.ndarray,
32
+ group: bool = ...,
33
+ ) -> Iterator[tuple[int, BlockPlacement]]: ...
34
+ def update_blklocs_and_blknos(
35
+ blklocs: npt.NDArray[np.intp],
36
+ blknos: npt.NDArray[np.intp],
37
+ loc: int,
38
+ nblocks: int,
39
+ ) -> tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]]: ...
40
+ @final
41
+ class BlockPlacement:
42
+ def __init__(self, val: int | slice | np.ndarray) -> None: ...
43
+ @property
44
+ def indexer(self) -> np.ndarray | slice: ...
45
+ @property
46
+ def as_array(self) -> np.ndarray: ...
47
+ @property
48
+ def as_slice(self) -> slice: ...
49
+ @property
50
+ def is_slice_like(self) -> bool: ...
51
+ @overload
52
+ def __getitem__(
53
+ self, loc: slice | Sequence[int] | npt.NDArray[np.intp]
54
+ ) -> BlockPlacement: ...
55
+ @overload
56
+ def __getitem__(self, loc: int) -> int: ...
57
+ def __iter__(self) -> Iterator[int]: ...
58
+ def __len__(self) -> int: ...
59
+ def delete(self, loc) -> BlockPlacement: ...
60
+ def add(self, other) -> BlockPlacement: ...
61
+ def append(self, others: list[BlockPlacement]) -> BlockPlacement: ...
62
+ def tile_for_unstack(self, factor: int) -> npt.NDArray[np.intp]: ...
63
+
64
+ class Block:
65
+ _mgr_locs: BlockPlacement
66
+ ndim: int
67
+ values: ArrayLike
68
+ refs: BlockValuesRefs
69
+ def __init__(
70
+ self,
71
+ values: ArrayLike,
72
+ placement: BlockPlacement,
73
+ ndim: int,
74
+ refs: BlockValuesRefs | None = ...,
75
+ ) -> None: ...
76
+ def slice_block_rows(self, slicer: slice) -> Self: ...
77
+
78
+ class BlockManager:
79
+ blocks: tuple[B, ...]
80
+ axes: list[Index]
81
+ _known_consolidated: bool
82
+ _is_consolidated: bool
83
+ _blknos: np.ndarray
84
+ _blklocs: np.ndarray
85
+ def __init__(
86
+ self, blocks: tuple[B, ...], axes: list[Index], verify_integrity=...
87
+ ) -> None: ...
88
+ def get_slice(self, slobj: slice, axis: int = ...) -> Self: ...
89
+ def _rebuild_blknos_and_blklocs(self) -> None: ...
90
+
91
+ class BlockValuesRefs:
92
+ referenced_blocks: list[weakref.ref]
93
+ def __init__(self, blk: Block | None = ...) -> None: ...
94
+ def add_reference(self, blk: Block) -> None: ...
95
+ def add_index_reference(self, index: Index) -> None: ...
96
+ def has_reference(self) -> bool: ...
pandas/_libs/interval.pyi ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import (
2
+ Any,
3
+ Generic,
4
+ TypeVar,
5
+ overload,
6
+ )
7
+
8
+ import numpy as np
9
+ import numpy.typing as npt
10
+
11
+ from pandas._typing import (
12
+ IntervalClosedType,
13
+ Timedelta,
14
+ Timestamp,
15
+ )
16
+
17
+ VALID_CLOSED: frozenset[str]
18
+
19
+ _OrderableScalarT = TypeVar("_OrderableScalarT", int, float)
20
+ _OrderableTimesT = TypeVar("_OrderableTimesT", Timestamp, Timedelta)
21
+ _OrderableT = TypeVar("_OrderableT", int, float, Timestamp, Timedelta)
22
+
23
+ class _LengthDescriptor:
24
+ @overload
25
+ def __get__(
26
+ self, instance: Interval[_OrderableScalarT], owner: Any
27
+ ) -> _OrderableScalarT: ...
28
+ @overload
29
+ def __get__(
30
+ self, instance: Interval[_OrderableTimesT], owner: Any
31
+ ) -> Timedelta: ...
32
+
33
+ class _MidDescriptor:
34
+ @overload
35
+ def __get__(self, instance: Interval[_OrderableScalarT], owner: Any) -> float: ...
36
+ @overload
37
+ def __get__(
38
+ self, instance: Interval[_OrderableTimesT], owner: Any
39
+ ) -> _OrderableTimesT: ...
40
+
41
+ class IntervalMixin:
42
+ @property
43
+ def closed_left(self) -> bool: ...
44
+ @property
45
+ def closed_right(self) -> bool: ...
46
+ @property
47
+ def open_left(self) -> bool: ...
48
+ @property
49
+ def open_right(self) -> bool: ...
50
+ @property
51
+ def is_empty(self) -> bool: ...
52
+ def _check_closed_matches(self, other: IntervalMixin, name: str = ...) -> None: ...
53
+
54
+ class Interval(IntervalMixin, Generic[_OrderableT]):
55
+ @property
56
+ def left(self: Interval[_OrderableT]) -> _OrderableT: ...
57
+ @property
58
+ def right(self: Interval[_OrderableT]) -> _OrderableT: ...
59
+ @property
60
+ def closed(self) -> IntervalClosedType: ...
61
+ mid: _MidDescriptor
62
+ length: _LengthDescriptor
63
+ def __init__(
64
+ self,
65
+ left: _OrderableT,
66
+ right: _OrderableT,
67
+ closed: IntervalClosedType = ...,
68
+ ) -> None: ...
69
+ def __hash__(self) -> int: ...
70
+ @overload
71
+ def __contains__(
72
+ self: Interval[Timedelta], key: Timedelta | Interval[Timedelta]
73
+ ) -> bool: ...
74
+ @overload
75
+ def __contains__(
76
+ self: Interval[Timestamp], key: Timestamp | Interval[Timestamp]
77
+ ) -> bool: ...
78
+ @overload
79
+ def __contains__(
80
+ self: Interval[_OrderableScalarT],
81
+ key: _OrderableScalarT | Interval[_OrderableScalarT],
82
+ ) -> bool: ...
83
+ @overload
84
+ def __add__(
85
+ self: Interval[_OrderableTimesT], y: Timedelta
86
+ ) -> Interval[_OrderableTimesT]: ...
87
+ @overload
88
+ def __add__(
89
+ self: Interval[int], y: _OrderableScalarT
90
+ ) -> Interval[_OrderableScalarT]: ...
91
+ @overload
92
+ def __add__(self: Interval[float], y: float) -> Interval[float]: ...
93
+ @overload
94
+ def __radd__(
95
+ self: Interval[_OrderableTimesT], y: Timedelta
96
+ ) -> Interval[_OrderableTimesT]: ...
97
+ @overload
98
+ def __radd__(
99
+ self: Interval[int], y: _OrderableScalarT
100
+ ) -> Interval[_OrderableScalarT]: ...
101
+ @overload
102
+ def __radd__(self: Interval[float], y: float) -> Interval[float]: ...
103
+ @overload
104
+ def __sub__(
105
+ self: Interval[_OrderableTimesT], y: Timedelta
106
+ ) -> Interval[_OrderableTimesT]: ...
107
+ @overload
108
+ def __sub__(
109
+ self: Interval[int], y: _OrderableScalarT
110
+ ) -> Interval[_OrderableScalarT]: ...
111
+ @overload
112
+ def __sub__(self: Interval[float], y: float) -> Interval[float]: ...
113
+ @overload
114
+ def __rsub__(
115
+ self: Interval[_OrderableTimesT], y: Timedelta
116
+ ) -> Interval[_OrderableTimesT]: ...
117
+ @overload
118
+ def __rsub__(
119
+ self: Interval[int], y: _OrderableScalarT
120
+ ) -> Interval[_OrderableScalarT]: ...
121
+ @overload
122
+ def __rsub__(self: Interval[float], y: float) -> Interval[float]: ...
123
+ @overload
124
+ def __mul__(
125
+ self: Interval[int], y: _OrderableScalarT
126
+ ) -> Interval[_OrderableScalarT]: ...
127
+ @overload
128
+ def __mul__(self: Interval[float], y: float) -> Interval[float]: ...
129
+ @overload
130
+ def __rmul__(
131
+ self: Interval[int], y: _OrderableScalarT
132
+ ) -> Interval[_OrderableScalarT]: ...
133
+ @overload
134
+ def __rmul__(self: Interval[float], y: float) -> Interval[float]: ...
135
+ @overload
136
+ def __truediv__(
137
+ self: Interval[int], y: _OrderableScalarT
138
+ ) -> Interval[_OrderableScalarT]: ...
139
+ @overload
140
+ def __truediv__(self: Interval[float], y: float) -> Interval[float]: ...
141
+ @overload
142
+ def __floordiv__(
143
+ self: Interval[int], y: _OrderableScalarT
144
+ ) -> Interval[_OrderableScalarT]: ...
145
+ @overload
146
+ def __floordiv__(self: Interval[float], y: float) -> Interval[float]: ...
147
+ def overlaps(self: Interval[_OrderableT], other: Interval[_OrderableT]) -> bool: ...
148
+
149
+ def intervals_to_interval_bounds(
150
+ intervals: np.ndarray, validate_closed: bool = ...
151
+ ) -> tuple[np.ndarray, np.ndarray, IntervalClosedType]: ...
152
+
153
+ class IntervalTree(IntervalMixin):
154
+ def __init__(
155
+ self,
156
+ left: np.ndarray,
157
+ right: np.ndarray,
158
+ closed: IntervalClosedType = ...,
159
+ leaf_size: int = ...,
160
+ ) -> None: ...
161
+ @property
162
+ def mid(self) -> np.ndarray: ...
163
+ @property
164
+ def length(self) -> np.ndarray: ...
165
+ def get_indexer(self, target) -> npt.NDArray[np.intp]: ...
166
+ def get_indexer_non_unique(
167
+ self, target
168
+ ) -> tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]]: ...
169
+ _na_count: int
170
+ @property
171
+ def is_overlapping(self) -> bool: ...
172
+ @property
173
+ def is_monotonic_increasing(self) -> bool: ...
174
+ def clear_mapping(self) -> None: ...
pandas/_libs/join.pyi ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+
3
+ from pandas._typing import npt
4
+
5
+ def inner_join(
6
+ left: np.ndarray, # const intp_t[:]
7
+ right: np.ndarray, # const intp_t[:]
8
+ max_groups: int,
9
+ sort: bool = ...,
10
+ ) -> tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]]: ...
11
+ def left_outer_join(
12
+ left: np.ndarray, # const intp_t[:]
13
+ right: np.ndarray, # const intp_t[:]
14
+ max_groups: int,
15
+ sort: bool = ...,
16
+ ) -> tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]]: ...
17
+ def full_outer_join(
18
+ left: np.ndarray, # const intp_t[:]
19
+ right: np.ndarray, # const intp_t[:]
20
+ max_groups: int,
21
+ ) -> tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]]: ...
22
+ def ffill_indexer(
23
+ indexer: np.ndarray, # const intp_t[:]
24
+ ) -> npt.NDArray[np.intp]: ...
25
+ def left_join_indexer_unique(
26
+ left: np.ndarray, # ndarray[join_t]
27
+ right: np.ndarray, # ndarray[join_t]
28
+ ) -> npt.NDArray[np.intp]: ...
29
+ def left_join_indexer(
30
+ left: np.ndarray, # ndarray[join_t]
31
+ right: np.ndarray, # ndarray[join_t]
32
+ ) -> tuple[
33
+ np.ndarray, # np.ndarray[join_t]
34
+ npt.NDArray[np.intp],
35
+ npt.NDArray[np.intp],
36
+ ]: ...
37
+ def inner_join_indexer(
38
+ left: np.ndarray, # ndarray[join_t]
39
+ right: np.ndarray, # ndarray[join_t]
40
+ ) -> tuple[
41
+ np.ndarray, # np.ndarray[join_t]
42
+ npt.NDArray[np.intp],
43
+ npt.NDArray[np.intp],
44
+ ]: ...
45
+ def outer_join_indexer(
46
+ left: np.ndarray, # ndarray[join_t]
47
+ right: np.ndarray, # ndarray[join_t]
48
+ ) -> tuple[
49
+ np.ndarray, # np.ndarray[join_t]
50
+ npt.NDArray[np.intp],
51
+ npt.NDArray[np.intp],
52
+ ]: ...
53
+ def asof_join_backward_on_X_by_Y(
54
+ left_values: np.ndarray, # ndarray[numeric_t]
55
+ right_values: np.ndarray, # ndarray[numeric_t]
56
+ left_by_values: np.ndarray, # const int64_t[:]
57
+ right_by_values: np.ndarray, # const int64_t[:]
58
+ allow_exact_matches: bool = ...,
59
+ tolerance: np.number | float | None = ...,
60
+ use_hashtable: bool = ...,
61
+ ) -> tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]]: ...
62
+ def asof_join_forward_on_X_by_Y(
63
+ left_values: np.ndarray, # ndarray[numeric_t]
64
+ right_values: np.ndarray, # ndarray[numeric_t]
65
+ left_by_values: np.ndarray, # const int64_t[:]
66
+ right_by_values: np.ndarray, # const int64_t[:]
67
+ allow_exact_matches: bool = ...,
68
+ tolerance: np.number | float | None = ...,
69
+ use_hashtable: bool = ...,
70
+ ) -> tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]]: ...
71
+ def asof_join_nearest_on_X_by_Y(
72
+ left_values: np.ndarray, # ndarray[numeric_t]
73
+ right_values: np.ndarray, # ndarray[numeric_t]
74
+ left_by_values: np.ndarray, # const int64_t[:]
75
+ right_by_values: np.ndarray, # const int64_t[:]
76
+ allow_exact_matches: bool = ...,
77
+ tolerance: np.number | float | None = ...,
78
+ use_hashtable: bool = ...,
79
+ ) -> tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]]: ...
pandas/_libs/json.cpython-312-x86_64-linux-gnu.so ADDED
Binary file (68.7 kB). View file
 
pandas/_libs/json.pyi ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from collections.abc import Callable
2
+ from typing import (
3
+ Any,
4
+ )
5
+
6
+ def ujson_dumps(
7
+ obj: Any,
8
+ ensure_ascii: bool = ...,
9
+ double_precision: int = ...,
10
+ indent: int = ...,
11
+ orient: str = ...,
12
+ date_unit: str = ...,
13
+ iso_dates: bool = ...,
14
+ default_handler: None
15
+ | Callable[[Any], str | float | bool | list | dict | None] = ...,
16
+ ) -> str: ...
17
+ def ujson_loads(
18
+ s: str,
19
+ precise_float: bool = ...,
20
+ numpy: bool = ...,
21
+ dtype: None = ...,
22
+ labelled: bool = ...,
23
+ ) -> Any: ...
pandas/_libs/lib.pyi ADDED
@@ -0,0 +1,238 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # TODO(npdtypes): Many types specified here can be made more specific/accurate;
2
+ # the more specific versions are specified in comments
3
+ from collections.abc import (
4
+ Callable,
5
+ Generator,
6
+ Hashable,
7
+ )
8
+ from decimal import Decimal
9
+ from typing import (
10
+ Any,
11
+ Final,
12
+ Literal,
13
+ TypeAlias,
14
+ TypeGuard,
15
+ overload,
16
+ )
17
+
18
+ import numpy as np
19
+
20
+ from pandas._typing import (
21
+ ArrayLike,
22
+ DtypeObj,
23
+ npt,
24
+ )
25
+
26
+ # placeholder until we can specify np.ndarray[object, ndim=2]
27
+ ndarray_obj_2d = np.ndarray
28
+
29
+ from enum import Enum
30
+
31
+ class _NoDefault(Enum):
32
+ no_default = ...
33
+
34
+ no_default: Final = _NoDefault.no_default
35
+ NoDefault: TypeAlias = Literal[_NoDefault.no_default]
36
+
37
+ i8max: int
38
+ u8max: int
39
+
40
+ def is_np_dtype(dtype: object, kinds: str | None = ...) -> TypeGuard[np.dtype]: ...
41
+ def item_from_zerodim(val: object) -> object: ...
42
+ def infer_dtype(value: object, skipna: bool = ...) -> str: ...
43
+ def is_iterator(obj: object) -> bool: ...
44
+ def is_scalar(val: object) -> bool: ...
45
+ def is_list_like(obj: object, allow_sets: bool = ...) -> bool: ...
46
+ def is_pyarrow_array(obj: object) -> bool: ...
47
+ def is_decimal(obj: object) -> TypeGuard[Decimal]: ...
48
+ def is_complex(obj: object) -> TypeGuard[complex]: ...
49
+ def is_bool(obj: object) -> TypeGuard[bool | np.bool_]: ...
50
+ def is_integer(obj: object) -> TypeGuard[int | np.integer]: ...
51
+ def is_int_or_none(obj) -> bool: ...
52
+ def is_float(obj: object) -> TypeGuard[float]: ...
53
+ def is_interval_array(values: np.ndarray) -> bool: ...
54
+ def is_datetime64_array(values: np.ndarray, skipna: bool = True) -> bool: ...
55
+ def is_timedelta_or_timedelta64_array(
56
+ values: np.ndarray, skipna: bool = True
57
+ ) -> bool: ...
58
+ def is_datetime_with_singletz_array(values: np.ndarray) -> bool: ...
59
+ def is_time_array(values: np.ndarray, skipna: bool = ...): ...
60
+ def is_date_array(values: np.ndarray, skipna: bool = ...): ...
61
+ def is_datetime_array(values: np.ndarray, skipna: bool = ...): ...
62
+ def is_string_array(values: np.ndarray, skipna: bool = ...): ...
63
+ def is_float_array(values: np.ndarray, skipna: bool = ...): ...
64
+ def is_integer_array(values: np.ndarray, skipna: bool = ...): ...
65
+ def is_bool_array(values: np.ndarray, skipna: bool = ...): ...
66
+ def fast_multiget(
67
+ mapping: dict,
68
+ keys: np.ndarray, # object[:]
69
+ default=...,
70
+ ) -> ArrayLike: ...
71
+ def fast_unique_multiple_list_gen(gen: Generator, sort: bool = ...) -> list: ...
72
+ @overload
73
+ def map_infer(
74
+ arr: np.ndarray,
75
+ f: Callable[[Any], Any],
76
+ *,
77
+ convert: Literal[False],
78
+ ignore_na: bool = ...,
79
+ ) -> np.ndarray: ...
80
+ @overload
81
+ def map_infer(
82
+ arr: np.ndarray,
83
+ f: Callable[[Any], Any],
84
+ *,
85
+ convert: bool = ...,
86
+ ignore_na: bool = ...,
87
+ ) -> ArrayLike: ...
88
+ @overload
89
+ def maybe_convert_objects(
90
+ objects: npt.NDArray[np.object_],
91
+ *,
92
+ try_float: bool = ...,
93
+ safe: bool = ...,
94
+ convert_numeric: bool = ...,
95
+ convert_non_numeric: Literal[False] = ...,
96
+ convert_to_nullable_dtype: Literal[False] = ...,
97
+ dtype_if_all_nat: DtypeObj | None = ...,
98
+ ) -> npt.NDArray[np.object_ | np.number]: ...
99
+ @overload
100
+ def maybe_convert_objects(
101
+ objects: npt.NDArray[np.object_],
102
+ *,
103
+ try_float: bool = ...,
104
+ safe: bool = ...,
105
+ convert_numeric: bool = ...,
106
+ convert_non_numeric: bool = ...,
107
+ convert_to_nullable_dtype: Literal[True] = ...,
108
+ dtype_if_all_nat: DtypeObj | None = ...,
109
+ ) -> ArrayLike: ...
110
+ @overload
111
+ def maybe_convert_objects(
112
+ objects: npt.NDArray[np.object_],
113
+ *,
114
+ try_float: bool = ...,
115
+ safe: bool = ...,
116
+ convert_numeric: bool = ...,
117
+ convert_non_numeric: bool = ...,
118
+ convert_to_nullable_dtype: bool = ...,
119
+ dtype_if_all_nat: DtypeObj | None = ...,
120
+ ) -> ArrayLike: ...
121
+ @overload
122
+ def maybe_convert_numeric(
123
+ values: npt.NDArray[np.object_],
124
+ na_values: set,
125
+ convert_empty: bool = ...,
126
+ coerce_numeric: bool = ...,
127
+ convert_to_masked_nullable: Literal[False] = ...,
128
+ ) -> tuple[np.ndarray, None]: ...
129
+ @overload
130
+ def maybe_convert_numeric(
131
+ values: npt.NDArray[np.object_],
132
+ na_values: set,
133
+ convert_empty: bool = ...,
134
+ coerce_numeric: bool = ...,
135
+ *,
136
+ convert_to_masked_nullable: Literal[True],
137
+ ) -> tuple[np.ndarray, np.ndarray]: ...
138
+
139
+ # TODO: restrict `arr`?
140
+ def ensure_string_array(
141
+ arr,
142
+ na_value: object = ...,
143
+ convert_na_value: bool = ...,
144
+ copy: bool = ...,
145
+ skipna: bool = ...,
146
+ ) -> npt.NDArray[np.object_]: ...
147
+ def convert_nans_to_NA(
148
+ arr: npt.NDArray[np.object_],
149
+ ) -> npt.NDArray[np.object_]: ...
150
+ def fast_zip(ndarrays: list) -> npt.NDArray[np.object_]: ...
151
+
152
+ # TODO: can we be more specific about rows?
153
+ def to_object_array_tuples(rows: object) -> ndarray_obj_2d: ...
154
+ def tuples_to_object_array(
155
+ tuples: npt.NDArray[np.object_],
156
+ ) -> ndarray_obj_2d: ...
157
+
158
+ # TODO: can we be more specific about rows?
159
+ def to_object_array(rows: object, min_width: int = ...) -> ndarray_obj_2d: ...
160
+ def dicts_to_array(dicts: list, columns: list) -> ndarray_obj_2d: ...
161
+ def maybe_booleans_to_slice(
162
+ mask: npt.NDArray[np.uint8],
163
+ ) -> slice | npt.NDArray[np.uint8]: ...
164
+ def maybe_indices_to_slice(
165
+ indices: npt.NDArray[np.intp],
166
+ max_len: int,
167
+ ) -> slice | npt.NDArray[np.intp]: ...
168
+ def is_all_arraylike(obj: list) -> bool: ...
169
+
170
+ # -----------------------------------------------------------------
171
+ # Functions which in reality take memoryviews
172
+
173
+ def memory_usage_of_objects(arr: np.ndarray) -> int: ... # object[:] # np.int64
174
+ @overload
175
+ def map_infer_mask(
176
+ arr: np.ndarray,
177
+ f: Callable[[Any], Any],
178
+ mask: np.ndarray, # const uint8_t[:]
179
+ *,
180
+ convert: Literal[False],
181
+ na_value: Any = ...,
182
+ dtype: np.dtype = ...,
183
+ ) -> np.ndarray: ...
184
+ @overload
185
+ def map_infer_mask(
186
+ arr: np.ndarray,
187
+ f: Callable[[Any], Any],
188
+ mask: np.ndarray, # const uint8_t[:]
189
+ *,
190
+ convert: bool = ...,
191
+ na_value: Any = ...,
192
+ dtype: np.dtype = ...,
193
+ ) -> ArrayLike: ...
194
+ def indices_fast(
195
+ index: npt.NDArray[np.intp],
196
+ labels: np.ndarray, # const int64_t[:]
197
+ keys: list,
198
+ sorted_labels: list[npt.NDArray[np.int64]],
199
+ ) -> dict[Hashable, npt.NDArray[np.intp]]: ...
200
+ def generate_slices(
201
+ labels: np.ndarray,
202
+ ngroups: int, # const intp_t[:]
203
+ ) -> tuple[npt.NDArray[np.int64], npt.NDArray[np.int64]]: ...
204
+ def count_level_2d(
205
+ mask: np.ndarray, # ndarray[uint8_t, ndim=2, cast=True],
206
+ labels: np.ndarray, # const intp_t[:]
207
+ max_bin: int,
208
+ ) -> np.ndarray: ... # np.ndarray[np.int64, ndim=2]
209
+ def get_level_sorter(
210
+ codes: np.ndarray, # const int64_t[:]
211
+ starts: np.ndarray, # const intp_t[:]
212
+ ) -> np.ndarray: ... # np.ndarray[np.intp, ndim=1]
213
+ def generate_bins_dt64(
214
+ values: npt.NDArray[np.int64],
215
+ binner: np.ndarray, # const int64_t[:]
216
+ closed: object = ...,
217
+ hasnans: bool = ...,
218
+ ) -> np.ndarray: ... # np.ndarray[np.int64, ndim=1]
219
+ def array_equivalent_object(
220
+ left: npt.NDArray[np.object_],
221
+ right: npt.NDArray[np.object_],
222
+ ) -> bool: ...
223
+ def has_infs(arr: np.ndarray) -> bool: ... # const floating[:]
224
+ def has_only_ints_or_nan(arr: np.ndarray) -> bool: ... # const floating[:]
225
+ def get_reverse_indexer(
226
+ indexer: np.ndarray, # const intp_t[:]
227
+ length: int,
228
+ ) -> npt.NDArray[np.intp]: ...
229
+ def is_bool_list(obj: list) -> bool: ...
230
+ def dtypes_all_equal(types: list[DtypeObj]) -> bool: ...
231
+ def is_range_indexer(
232
+ left: np.ndarray,
233
+ n: int, # np.ndarray[np.int64, ndim=1]
234
+ ) -> bool: ...
235
+ def is_sequence_range(
236
+ sequence: np.ndarray,
237
+ step: int, # np.ndarray[np.int64, ndim=1]
238
+ ) -> bool: ...
pandas/_libs/missing.pyi ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from numpy import typing as npt
3
+
4
+ class NAType:
5
+ def __new__(cls, *args, **kwargs): ...
6
+
7
+ NA: NAType
8
+
9
+ def is_matching_na(
10
+ left: object, right: object, nan_matches_none: bool = ...
11
+ ) -> bool: ...
12
+ def isposinf_scalar(val: object) -> bool: ...
13
+ def isneginf_scalar(val: object) -> bool: ...
14
+ def checknull(val: object) -> bool: ...
15
+ def isnaobj(arr: np.ndarray) -> npt.NDArray[np.bool_]: ...
16
+ def is_numeric_na(values: np.ndarray) -> npt.NDArray[np.bool_]: ...
17
+ def is_pdna_or_none(values: np.ndarray) -> npt.NDArray[np.bool_]: ...
pandas/_libs/ops.pyi ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from collections.abc import (
2
+ Callable,
3
+ Iterable,
4
+ )
5
+ from typing import (
6
+ Any,
7
+ Literal,
8
+ TypeAlias,
9
+ overload,
10
+ )
11
+
12
+ import numpy as np
13
+
14
+ from pandas._typing import npt
15
+
16
+ _BinOp: TypeAlias = Callable[[Any, Any], Any]
17
+ _BoolOp: TypeAlias = Callable[[Any, Any], bool]
18
+
19
+ def scalar_compare(
20
+ values: np.ndarray, # object[:]
21
+ val: object,
22
+ op: _BoolOp, # {operator.eq, operator.ne, ...}
23
+ ) -> npt.NDArray[np.bool_]: ...
24
+ def vec_compare(
25
+ left: npt.NDArray[np.object_],
26
+ right: npt.NDArray[np.object_],
27
+ op: _BoolOp, # {operator.eq, operator.ne, ...}
28
+ ) -> npt.NDArray[np.bool_]: ...
29
+ def scalar_binop(
30
+ values: np.ndarray, # object[:]
31
+ val: object,
32
+ op: _BinOp, # binary operator
33
+ ) -> np.ndarray: ...
34
+ def vec_binop(
35
+ left: np.ndarray, # object[:]
36
+ right: np.ndarray, # object[:]
37
+ op: _BinOp, # binary operator
38
+ ) -> np.ndarray: ...
39
+ @overload
40
+ def maybe_convert_bool(
41
+ arr: npt.NDArray[np.object_],
42
+ true_values: Iterable | None = None,
43
+ false_values: Iterable | None = None,
44
+ convert_to_masked_nullable: Literal[False] = ...,
45
+ ) -> tuple[np.ndarray, None]: ...
46
+ @overload
47
+ def maybe_convert_bool(
48
+ arr: npt.NDArray[np.object_],
49
+ true_values: Iterable = ...,
50
+ false_values: Iterable = ...,
51
+ *,
52
+ convert_to_masked_nullable: Literal[True],
53
+ ) -> tuple[np.ndarray, np.ndarray]: ...
pandas/_libs/ops_dispatch.cpython-312-x86_64-linux-gnu.so ADDED
Binary file (57.9 kB). View file
 
pandas/_libs/ops_dispatch.pyi ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ import numpy as np
2
+
3
+ def maybe_dispatch_ufunc_to_dunder_op(
4
+ self, ufunc: np.ufunc, method: str, *inputs, **kwargs
5
+ ): ...
pandas/_libs/pandas_datetime.cpython-312-x86_64-linux-gnu.so ADDED
Binary file (39.6 kB). View file
 
pandas/_libs/pandas_parser.cpython-312-x86_64-linux-gnu.so ADDED
Binary file (43.8 kB). View file
 
pandas/_libs/parsers.pyi ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from collections.abc import Hashable
2
+ from typing import (
3
+ Literal,
4
+ )
5
+
6
+ import numpy as np
7
+
8
+ from pandas._typing import (
9
+ ArrayLike,
10
+ Dtype,
11
+ npt,
12
+ )
13
+
14
+ STR_NA_VALUES: set[str]
15
+ DEFAULT_BUFFER_HEURISTIC: int
16
+
17
+ def sanitize_objects(
18
+ values: npt.NDArray[np.object_],
19
+ na_values: set,
20
+ ) -> int: ...
21
+
22
+ class TextReader:
23
+ unnamed_cols: set[str]
24
+ table_width: int # int64_t
25
+ leading_cols: int # int64_t
26
+ header: list[list[int]] # non-negative integers
27
+ def __init__(
28
+ self,
29
+ source,
30
+ delimiter: bytes | str = ..., # single-character only
31
+ header=...,
32
+ header_start: int = ..., # int64_t
33
+ header_end: int = ..., # uint64_t
34
+ index_col=...,
35
+ names=...,
36
+ tokenize_chunksize: int = ..., # int64_t
37
+ delim_whitespace: bool = ...,
38
+ converters=...,
39
+ skipinitialspace: bool = ...,
40
+ escapechar: bytes | str | None = ..., # single-character only
41
+ doublequote: bool = ...,
42
+ quotechar: str | bytes | None = ..., # at most 1 character
43
+ quoting: int = ...,
44
+ lineterminator: bytes | str | None = ..., # at most 1 character
45
+ comment=...,
46
+ decimal: bytes | str = ..., # single-character only
47
+ thousands: bytes | str | None = ..., # single-character only
48
+ dtype: Dtype | dict[Hashable, Dtype] = ...,
49
+ usecols=...,
50
+ error_bad_lines: bool = ...,
51
+ warn_bad_lines: bool = ...,
52
+ na_filter: bool = ...,
53
+ na_values=...,
54
+ na_fvalues=...,
55
+ keep_default_na: bool = ...,
56
+ true_values=...,
57
+ false_values=...,
58
+ allow_leading_cols: bool = ...,
59
+ skiprows=...,
60
+ skipfooter: int = ..., # int64_t
61
+ verbose: bool = ...,
62
+ float_precision: Literal["round_trip", "legacy", "high"] | None = ...,
63
+ skip_blank_lines: bool = ...,
64
+ encoding_errors: bytes | str = ...,
65
+ ) -> None: ...
66
+ def set_noconvert(self, i: int) -> None: ...
67
+ def remove_noconvert(self, i: int) -> None: ...
68
+ def close(self) -> None: ...
69
+ def read(self, rows: int | None = ...) -> dict[int, ArrayLike]: ...
70
+ def read_low_memory(self, rows: int | None) -> list[dict[int, ArrayLike]]: ...
71
+
72
+ # _maybe_upcast, na_values are only exposed for testing
73
+ na_values: dict
74
+
75
+ def _maybe_upcast(
76
+ arr, use_dtype_backend: bool = ..., dtype_backend: str = ...
77
+ ) -> np.ndarray: ...
pandas/_libs/properties.cpython-312-x86_64-linux-gnu.so ADDED
Binary file (79.5 kB). View file
 
pandas/_libs/properties.pyi ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from collections.abc import Sequence
2
+ from typing import (
3
+ overload,
4
+ )
5
+
6
+ from pandas._typing import (
7
+ AnyArrayLike,
8
+ DataFrame,
9
+ Index,
10
+ Series,
11
+ )
12
+
13
+ # note: this is a lie to make type checkers happy (they special
14
+ # case property). cache_readonly uses attribute names similar to
15
+ # property (fget) but it does not provide fset and fdel.
16
+ cache_readonly = property
17
+
18
+ class AxisProperty:
19
+ axis: int
20
+ def __init__(self, axis: int = ..., doc: str = ...) -> None: ...
21
+ @overload
22
+ def __get__(self, obj: DataFrame | Series, type) -> Index: ...
23
+ @overload
24
+ def __get__(self, obj: None, type) -> AxisProperty: ...
25
+ def __set__(
26
+ self, obj: DataFrame | Series, value: AnyArrayLike | Sequence
27
+ ) -> None: ...
pandas/_libs/reshape.pyi ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+
3
+ from pandas._typing import npt
4
+
5
+ def unstack(
6
+ values: np.ndarray, # reshape_t[:, :]
7
+ mask: np.ndarray, # const uint8_t[:]
8
+ stride: int,
9
+ length: int,
10
+ width: int,
11
+ new_values: np.ndarray, # reshape_t[:, :]
12
+ new_mask: np.ndarray, # uint8_t[:, :]
13
+ ) -> None: ...
14
+ def explode(
15
+ values: npt.NDArray[np.object_],
16
+ ) -> tuple[npt.NDArray[np.object_], npt.NDArray[np.int64]]: ...
pandas/_libs/sas.pyi ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ from pandas.io.sas.sas7bdat import SAS7BDATReader
2
+
3
+ class Parser:
4
+ def __init__(self, parser: SAS7BDATReader) -> None: ...
5
+ def read(self, nrows: int) -> None: ...
6
+
7
+ def get_subheader_index(signature: bytes) -> int: ...
pandas/_libs/sparse.pyi ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Self
2
+
3
+ import numpy as np
4
+
5
+ from pandas._typing import (
6
+ TakeIndexer,
7
+ npt,
8
+ )
9
+
10
+ class SparseIndex:
11
+ length: int
12
+ npoints: int
13
+ def __init__(self) -> None: ...
14
+ @property
15
+ def ngaps(self) -> int: ...
16
+ @property
17
+ def nbytes(self) -> int: ...
18
+ @property
19
+ def indices(self) -> npt.NDArray[np.int32]: ...
20
+ def equals(self, other) -> bool: ...
21
+ def lookup(self, index: int) -> np.int32: ...
22
+ def lookup_array(self, indexer: npt.NDArray[np.int32]) -> npt.NDArray[np.int32]: ...
23
+ def to_int_index(self) -> IntIndex: ...
24
+ def to_block_index(self) -> BlockIndex: ...
25
+ def intersect(self, y_: SparseIndex) -> Self: ...
26
+ def make_union(self, y_: SparseIndex) -> Self: ...
27
+
28
+ class IntIndex(SparseIndex):
29
+ indices: npt.NDArray[np.int32]
30
+ def __init__(
31
+ self, length: int, indices: TakeIndexer, check_integrity: bool = ...
32
+ ) -> None: ...
33
+
34
+ class BlockIndex(SparseIndex):
35
+ nblocks: int
36
+ blocs: np.ndarray
37
+ blengths: np.ndarray
38
+ def __init__(
39
+ self, length: int, blocs: np.ndarray, blengths: np.ndarray
40
+ ) -> None: ...
41
+
42
+ # Override to have correct parameters
43
+ def intersect(self, other: SparseIndex) -> Self: ...
44
+ def make_union(self, y: SparseIndex) -> Self: ...
45
+
46
+ def make_mask_object_ndarray(
47
+ arr: npt.NDArray[np.object_], fill_value
48
+ ) -> npt.NDArray[np.bool_]: ...
49
+ def get_blocks(
50
+ indices: npt.NDArray[np.int32],
51
+ ) -> tuple[npt.NDArray[np.int32], npt.NDArray[np.int32]]: ...
pandas/_libs/testing.pyi ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from collections.abc import Mapping
2
+
3
+ def assert_dict_equal(a: Mapping, b: Mapping, compare_keys: bool = ...) -> bool: ...
4
+ def assert_almost_equal(
5
+ a,
6
+ b,
7
+ rtol: float = ...,
8
+ atol: float = ...,
9
+ check_dtype: bool = ...,
10
+ obj=...,
11
+ lobj=...,
12
+ robj=...,
13
+ index_values=...,
14
+ ) -> bool: ...
pandas/_libs/tslib.pyi ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import tzinfo
2
+
3
+ import numpy as np
4
+
5
+ from pandas._typing import npt
6
+
7
+ def format_array_from_datetime(
8
+ values: npt.NDArray[np.int64],
9
+ tz: tzinfo | None = ...,
10
+ format: str | None = ...,
11
+ na_rep: str | float = ...,
12
+ reso: int = ..., # NPY_DATETIMEUNIT
13
+ ) -> npt.NDArray[np.object_]: ...
14
+ def first_non_null(values: np.ndarray) -> int: ...
15
+ def array_to_datetime(
16
+ values: npt.NDArray[np.object_],
17
+ errors: str = ...,
18
+ dayfirst: bool = ...,
19
+ yearfirst: bool = ...,
20
+ utc: bool = ...,
21
+ creso: int = ...,
22
+ unit_for_numerics: str | None = ...,
23
+ ) -> tuple[np.ndarray, tzinfo | None]: ...
24
+
25
+ # returned ndarray may be object dtype or datetime64[ns]
26
+
27
+ def array_to_datetime_with_tz(
28
+ values: npt.NDArray[np.object_],
29
+ tz: tzinfo,
30
+ dayfirst: bool,
31
+ yearfirst: bool,
32
+ creso: int,
33
+ ) -> npt.NDArray[np.int64]: ...
pandas/_libs/tslibs/__init__.py ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ __all__ = [
2
+ "BaseOffset",
3
+ "Day",
4
+ "IncompatibleFrequency",
5
+ "NaT",
6
+ "NaTType",
7
+ "OutOfBoundsDatetime",
8
+ "OutOfBoundsTimedelta",
9
+ "Period",
10
+ "Resolution",
11
+ "Tick",
12
+ "Timedelta",
13
+ "Timestamp",
14
+ "add_overflowsafe",
15
+ "astype_overflowsafe",
16
+ "delta_to_nanoseconds",
17
+ "dt64arr_to_periodarr",
18
+ "dtypes",
19
+ "get_resolution",
20
+ "get_supported_dtype",
21
+ "get_unit_from_dtype",
22
+ "guess_datetime_format",
23
+ "iNaT",
24
+ "ints_to_pydatetime",
25
+ "ints_to_pytimedelta",
26
+ "is_date_array_normalized",
27
+ "is_supported_dtype",
28
+ "is_unitless",
29
+ "localize_pydatetime",
30
+ "nat_strings",
31
+ "normalize_i8_timestamps",
32
+ "periods_per_day",
33
+ "periods_per_second",
34
+ "to_offset",
35
+ "tz_compare",
36
+ "tz_convert_from_utc",
37
+ "tz_convert_from_utc_single",
38
+ ]
39
+
40
+ from pandas._libs.tslibs import dtypes
41
+ from pandas._libs.tslibs.conversion import localize_pydatetime
42
+ from pandas._libs.tslibs.dtypes import (
43
+ Resolution,
44
+ periods_per_day,
45
+ periods_per_second,
46
+ )
47
+ from pandas._libs.tslibs.nattype import (
48
+ NaT,
49
+ NaTType,
50
+ iNaT,
51
+ nat_strings,
52
+ )
53
+ from pandas._libs.tslibs.np_datetime import (
54
+ OutOfBoundsDatetime,
55
+ OutOfBoundsTimedelta,
56
+ add_overflowsafe,
57
+ astype_overflowsafe,
58
+ get_supported_dtype,
59
+ is_supported_dtype,
60
+ is_unitless,
61
+ py_get_unit_from_dtype as get_unit_from_dtype,
62
+ )
63
+ from pandas._libs.tslibs.offsets import (
64
+ BaseOffset,
65
+ Day,
66
+ Tick,
67
+ to_offset,
68
+ )
69
+ from pandas._libs.tslibs.parsing import guess_datetime_format
70
+ from pandas._libs.tslibs.period import (
71
+ IncompatibleFrequency,
72
+ Period,
73
+ )
74
+ from pandas._libs.tslibs.timedeltas import (
75
+ Timedelta,
76
+ delta_to_nanoseconds,
77
+ ints_to_pytimedelta,
78
+ )
79
+ from pandas._libs.tslibs.timestamps import Timestamp
80
+ from pandas._libs.tslibs.timezones import tz_compare
81
+ from pandas._libs.tslibs.tzconversion import tz_convert_from_utc_single
82
+ from pandas._libs.tslibs.vectorized import (
83
+ dt64arr_to_periodarr,
84
+ get_resolution,
85
+ ints_to_pydatetime,
86
+ is_date_array_normalized,
87
+ normalize_i8_timestamps,
88
+ tz_convert_from_utc,
89
+ )
pandas/_libs/tslibs/base.cpython-312-x86_64-linux-gnu.so ADDED
Binary file (54.1 kB). View file
 
pandas/_libs/tslibs/ccalendar.cpython-312-x86_64-linux-gnu.so ADDED
Binary file (90.8 kB). View file