datetimes.py 60 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679
  1. # pylint: disable=E1101
  2. from __future__ import division
  3. from datetime import datetime, time, timedelta
  4. import operator
  5. import warnings
  6. import numpy as np
  7. from pandas._libs import (
  8. Timestamp, index as libindex, join as libjoin, lib, tslib as libts)
  9. from pandas._libs.tslibs import ccalendar, fields, parsing, timezones
  10. import pandas.compat as compat
  11. from pandas.util._decorators import Appender, Substitution, cache_readonly
  12. from pandas.core.dtypes.common import (
  13. _NS_DTYPE, ensure_int64, is_float, is_integer, is_list_like, is_scalar,
  14. is_string_like)
  15. import pandas.core.dtypes.concat as _concat
  16. from pandas.core.dtypes.dtypes import DatetimeTZDtype
  17. from pandas.core.dtypes.missing import isna
  18. from pandas.core.accessor import delegate_names
  19. from pandas.core.arrays.datetimes import (
  20. DatetimeArray, _to_M8, tz_to_dtype, validate_tz_from_dtype)
  21. from pandas.core.base import _shared_docs
  22. import pandas.core.common as com
  23. from pandas.core.indexes.base import Index
  24. from pandas.core.indexes.datetimelike import (
  25. DatetimeIndexOpsMixin, DatetimelikeDelegateMixin, ea_passthrough)
  26. from pandas.core.indexes.numeric import Int64Index
  27. from pandas.core.ops import get_op_result_name
  28. import pandas.core.tools.datetimes as tools
  29. from pandas.tseries import offsets
  30. from pandas.tseries.frequencies import Resolution, to_offset
  31. from pandas.tseries.offsets import CDay, prefix_mapping
  32. def _new_DatetimeIndex(cls, d):
  33. """ This is called upon unpickling, rather than the default which doesn't
  34. have arguments and breaks __new__ """
  35. if "data" in d and not isinstance(d["data"], DatetimeIndex):
  36. # Avoid need to verify integrity by calling simple_new directly
  37. data = d.pop("data")
  38. result = cls._simple_new(data, **d)
  39. else:
  40. with warnings.catch_warnings():
  41. # we ignore warnings from passing verify_integrity=False
  42. # TODO: If we knew what was going in to **d, we might be able to
  43. # go through _simple_new instead
  44. warnings.simplefilter("ignore")
  45. result = cls.__new__(cls, verify_integrity=False, **d)
  46. return result
  47. class DatetimeDelegateMixin(DatetimelikeDelegateMixin):
  48. # Most attrs are dispatched via datetimelike_{ops,methods}
  49. # Some are "raw" methods, the result is not not re-boxed in an Index
  50. # We also have a few "extra" attrs, which may or may not be raw,
  51. # which we we dont' want to expose in the .dt accessor.
  52. _extra_methods = [
  53. 'to_period',
  54. 'to_perioddelta',
  55. 'to_julian_date',
  56. ]
  57. _extra_raw_methods = [
  58. 'to_pydatetime',
  59. '_local_timestamps',
  60. '_has_same_tz',
  61. ]
  62. _extra_raw_properties = [
  63. '_box_func',
  64. 'tz', 'tzinfo',
  65. ]
  66. _delegated_properties = (
  67. DatetimeArray._datetimelike_ops + _extra_raw_properties
  68. )
  69. _delegated_methods = (
  70. DatetimeArray._datetimelike_methods + _extra_methods +
  71. _extra_raw_methods
  72. )
  73. _raw_properties = {
  74. 'date',
  75. 'time',
  76. 'timetz',
  77. } | set(DatetimeArray._bool_ops) | set(_extra_raw_properties)
  78. _raw_methods = set(_extra_raw_methods)
  79. _delegate_class = DatetimeArray
  80. @delegate_names(DatetimeArray,
  81. DatetimeDelegateMixin._delegated_properties,
  82. typ="property")
  83. @delegate_names(DatetimeArray,
  84. DatetimeDelegateMixin._delegated_methods,
  85. typ="method", overwrite=False)
  86. class DatetimeIndex(DatetimeIndexOpsMixin, Int64Index, DatetimeDelegateMixin):
  87. """
  88. Immutable ndarray of datetime64 data, represented internally as int64, and
  89. which can be boxed to Timestamp objects that are subclasses of datetime and
  90. carry metadata such as frequency information.
  91. Parameters
  92. ----------
  93. data : array-like (1-dimensional), optional
  94. Optional datetime-like data to construct index with
  95. copy : bool
  96. Make a copy of input ndarray
  97. freq : string or pandas offset object, optional
  98. One of pandas date offset strings or corresponding objects. The string
  99. 'infer' can be passed in order to set the frequency of the index as the
  100. inferred frequency upon creation
  101. start : starting value, datetime-like, optional
  102. If data is None, start is used as the start point in generating regular
  103. timestamp data.
  104. .. deprecated:: 0.24.0
  105. periods : int, optional, > 0
  106. Number of periods to generate, if generating index. Takes precedence
  107. over end argument
  108. .. deprecated:: 0.24.0
  109. end : end time, datetime-like, optional
  110. If periods is none, generated index will extend to first conforming
  111. time on or just past end argument
  112. .. deprecated:: 0.24.0
  113. closed : string or None, default None
  114. Make the interval closed with respect to the given frequency to
  115. the 'left', 'right', or both sides (None)
  116. .. deprecated:: 0.24. 0
  117. tz : pytz.timezone or dateutil.tz.tzfile
  118. ambiguous : 'infer', bool-ndarray, 'NaT', default 'raise'
  119. When clocks moved backward due to DST, ambiguous times may arise.
  120. For example in Central European Time (UTC+01), when going from 03:00
  121. DST to 02:00 non-DST, 02:30:00 local time occurs both at 00:30:00 UTC
  122. and at 01:30:00 UTC. In such a situation, the `ambiguous` parameter
  123. dictates how ambiguous times should be handled.
  124. - 'infer' will attempt to infer fall dst-transition hours based on
  125. order
  126. - bool-ndarray where True signifies a DST time, False signifies a
  127. non-DST time (note that this flag is only applicable for ambiguous
  128. times)
  129. - 'NaT' will return NaT where there are ambiguous times
  130. - 'raise' will raise an AmbiguousTimeError if there are ambiguous times
  131. name : object
  132. Name to be stored in the index
  133. dayfirst : bool, default False
  134. If True, parse dates in `data` with the day first order
  135. yearfirst : bool, default False
  136. If True parse dates in `data` with the year first order
  137. Attributes
  138. ----------
  139. year
  140. month
  141. day
  142. hour
  143. minute
  144. second
  145. microsecond
  146. nanosecond
  147. date
  148. time
  149. timetz
  150. dayofyear
  151. weekofyear
  152. week
  153. dayofweek
  154. weekday
  155. quarter
  156. tz
  157. freq
  158. freqstr
  159. is_month_start
  160. is_month_end
  161. is_quarter_start
  162. is_quarter_end
  163. is_year_start
  164. is_year_end
  165. is_leap_year
  166. inferred_freq
  167. Methods
  168. -------
  169. normalize
  170. strftime
  171. snap
  172. tz_convert
  173. tz_localize
  174. round
  175. floor
  176. ceil
  177. to_period
  178. to_perioddelta
  179. to_pydatetime
  180. to_series
  181. to_frame
  182. month_name
  183. day_name
  184. Notes
  185. -----
  186. To learn more about the frequency strings, please see `this link
  187. <http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.
  188. Creating a DatetimeIndex based on `start`, `periods`, and `end` has
  189. been deprecated in favor of :func:`date_range`.
  190. See Also
  191. ---------
  192. Index : The base pandas Index type.
  193. TimedeltaIndex : Index of timedelta64 data.
  194. PeriodIndex : Index of Period data.
  195. to_datetime : Convert argument to datetime.
  196. date_range : Create a fixed-frequency DatetimeIndex.
  197. """
  198. _typ = 'datetimeindex'
  199. _join_precedence = 10
  200. def _join_i8_wrapper(joinf, **kwargs):
  201. return DatetimeIndexOpsMixin._join_i8_wrapper(joinf, dtype='M8[ns]',
  202. **kwargs)
  203. _inner_indexer = _join_i8_wrapper(libjoin.inner_join_indexer_int64)
  204. _outer_indexer = _join_i8_wrapper(libjoin.outer_join_indexer_int64)
  205. _left_indexer = _join_i8_wrapper(libjoin.left_join_indexer_int64)
  206. _left_indexer_unique = _join_i8_wrapper(
  207. libjoin.left_join_indexer_unique_int64, with_indexers=False)
  208. _engine_type = libindex.DatetimeEngine
  209. _tz = None
  210. _freq = None
  211. _comparables = ['name', 'freqstr', 'tz']
  212. _attributes = ['name', 'tz', 'freq']
  213. # dummy attribute so that datetime.__eq__(DatetimeArray) defers
  214. # by returning NotImplemented
  215. timetuple = None
  216. _is_numeric_dtype = False
  217. _infer_as_myclass = True
  218. # some things like freq inference make use of these attributes.
  219. _bool_ops = DatetimeArray._bool_ops
  220. _object_ops = DatetimeArray._object_ops
  221. _field_ops = DatetimeArray._field_ops
  222. _datetimelike_ops = DatetimeArray._datetimelike_ops
  223. _datetimelike_methods = DatetimeArray._datetimelike_methods
  224. # --------------------------------------------------------------------
  225. # Constructors
  226. def __new__(cls, data=None,
  227. freq=None, start=None, end=None, periods=None, tz=None,
  228. normalize=False, closed=None, ambiguous='raise',
  229. dayfirst=False, yearfirst=False, dtype=None,
  230. copy=False, name=None, verify_integrity=None):
  231. if verify_integrity is not None:
  232. warnings.warn("The 'verify_integrity' argument is deprecated, "
  233. "will be removed in a future version.",
  234. FutureWarning, stacklevel=2)
  235. else:
  236. verify_integrity = True
  237. if data is None:
  238. dtarr = DatetimeArray._generate_range(
  239. start, end, periods,
  240. freq=freq, tz=tz, normalize=normalize,
  241. closed=closed, ambiguous=ambiguous)
  242. warnings.warn("Creating a DatetimeIndex by passing range "
  243. "endpoints is deprecated. Use "
  244. "`pandas.date_range` instead.",
  245. FutureWarning, stacklevel=2)
  246. return cls._simple_new(
  247. dtarr._data, freq=dtarr.freq, tz=dtarr.tz, name=name)
  248. if is_scalar(data):
  249. raise TypeError("{cls}() must be called with a "
  250. "collection of some kind, {data} was passed"
  251. .format(cls=cls.__name__, data=repr(data)))
  252. # - Cases checked above all return/raise before reaching here - #
  253. if name is None and hasattr(data, 'name'):
  254. name = data.name
  255. dtarr = DatetimeArray._from_sequence(
  256. data, dtype=dtype, copy=copy, tz=tz, freq=freq,
  257. dayfirst=dayfirst, yearfirst=yearfirst, ambiguous=ambiguous,
  258. int_as_wall_time=True)
  259. subarr = cls._simple_new(dtarr, name=name,
  260. freq=dtarr.freq, tz=dtarr.tz)
  261. return subarr
  262. @classmethod
  263. def _simple_new(cls, values, name=None, freq=None, tz=None, dtype=None):
  264. """
  265. we require the we have a dtype compat for the values
  266. if we are passed a non-dtype compat, then coerce using the constructor
  267. """
  268. if isinstance(values, DatetimeArray):
  269. if tz:
  270. tz = validate_tz_from_dtype(dtype, tz)
  271. dtype = DatetimeTZDtype(tz=tz)
  272. elif dtype is None:
  273. dtype = _NS_DTYPE
  274. values = DatetimeArray(values, freq=freq, dtype=dtype)
  275. tz = values.tz
  276. freq = values.freq
  277. values = values._data
  278. # DatetimeArray._simple_new will accept either i8 or M8[ns] dtypes
  279. if isinstance(values, DatetimeIndex):
  280. values = values._data
  281. dtype = tz_to_dtype(tz)
  282. dtarr = DatetimeArray._simple_new(values, freq=freq, dtype=dtype)
  283. assert isinstance(dtarr, DatetimeArray)
  284. result = object.__new__(cls)
  285. result._data = dtarr
  286. result.name = name
  287. # For groupby perf. See note in indexes/base about _index_data
  288. result._index_data = dtarr._data
  289. result._reset_identity()
  290. return result
  291. # --------------------------------------------------------------------
  292. def __array__(self, dtype=None):
  293. if (dtype is None and isinstance(self._data, DatetimeArray)
  294. and getattr(self.dtype, 'tz', None)):
  295. msg = (
  296. "Converting timezone-aware DatetimeArray to timezone-naive "
  297. "ndarray with 'datetime64[ns]' dtype. In the future, this "
  298. "will return an ndarray with 'object' dtype where each "
  299. "element is a 'pandas.Timestamp' with the correct 'tz'.\n\t"
  300. "To accept the future behavior, pass 'dtype=object'.\n\t"
  301. "To keep the old behavior, pass 'dtype=\"datetime64[ns]\"'."
  302. )
  303. warnings.warn(msg, FutureWarning, stacklevel=3)
  304. dtype = 'M8[ns]'
  305. return np.asarray(self._data, dtype=dtype)
  306. @property
  307. def dtype(self):
  308. return self._data.dtype
  309. @property
  310. def tz(self):
  311. # GH 18595
  312. return self._data.tz
  313. @tz.setter
  314. def tz(self, value):
  315. # GH 3746: Prevent localizing or converting the index by setting tz
  316. raise AttributeError("Cannot directly set timezone. Use tz_localize() "
  317. "or tz_convert() as appropriate")
  318. tzinfo = tz
  319. @cache_readonly
  320. def _is_dates_only(self):
  321. """Return a boolean if we are only dates (and don't have a timezone)"""
  322. from pandas.io.formats.format import _is_dates_only
  323. return _is_dates_only(self.values) and self.tz is None
  324. def __reduce__(self):
  325. # we use a special reudce here because we need
  326. # to simply set the .tz (and not reinterpret it)
  327. d = dict(data=self._data)
  328. d.update(self._get_attributes_dict())
  329. return _new_DatetimeIndex, (self.__class__, d), None
  330. def __setstate__(self, state):
  331. """Necessary for making this object picklable"""
  332. if isinstance(state, dict):
  333. super(DatetimeIndex, self).__setstate__(state)
  334. elif isinstance(state, tuple):
  335. # < 0.15 compat
  336. if len(state) == 2:
  337. nd_state, own_state = state
  338. data = np.empty(nd_state[1], dtype=nd_state[2])
  339. np.ndarray.__setstate__(data, nd_state)
  340. freq = own_state[1]
  341. tz = timezones.tz_standardize(own_state[2])
  342. dtype = tz_to_dtype(tz)
  343. dtarr = DatetimeArray._simple_new(data, freq=freq, dtype=dtype)
  344. self.name = own_state[0]
  345. else: # pragma: no cover
  346. data = np.empty(state)
  347. np.ndarray.__setstate__(data, state)
  348. dtarr = DatetimeArray(data)
  349. self._data = dtarr
  350. self._reset_identity()
  351. else:
  352. raise Exception("invalid pickle state")
  353. _unpickle_compat = __setstate__
  354. def _convert_for_op(self, value):
  355. """ Convert value to be insertable to ndarray """
  356. if self._has_same_tz(value):
  357. return _to_M8(value)
  358. raise ValueError('Passed item and index have different timezone')
  359. def _maybe_update_attributes(self, attrs):
  360. """ Update Index attributes (e.g. freq) depending on op """
  361. freq = attrs.get('freq', None)
  362. if freq is not None:
  363. # no need to infer if freq is None
  364. attrs['freq'] = 'infer'
  365. return attrs
  366. # --------------------------------------------------------------------
  367. # Rendering Methods
  368. def _mpl_repr(self):
  369. # how to represent ourselves to matplotlib
  370. return libts.ints_to_pydatetime(self.asi8, self.tz)
  371. def _format_native_types(self, na_rep='NaT', date_format=None, **kwargs):
  372. from pandas.io.formats.format import _get_format_datetime64_from_values
  373. fmt = _get_format_datetime64_from_values(self, date_format)
  374. return libts.format_array_from_datetime(self.asi8,
  375. tz=self.tz,
  376. format=fmt,
  377. na_rep=na_rep)
  378. @property
  379. def _formatter_func(self):
  380. from pandas.io.formats.format import _get_format_datetime64
  381. formatter = _get_format_datetime64(is_dates_only=self._is_dates_only)
  382. return lambda x: "'%s'" % formatter(x, tz=self.tz)
  383. # --------------------------------------------------------------------
  384. # Set Operation Methods
  385. def union(self, other):
  386. """
  387. Specialized union for DatetimeIndex objects. If combine
  388. overlapping ranges with the same DateOffset, will be much
  389. faster than Index.union
  390. Parameters
  391. ----------
  392. other : DatetimeIndex or array-like
  393. Returns
  394. -------
  395. y : Index or DatetimeIndex
  396. """
  397. self._assert_can_do_setop(other)
  398. if len(other) == 0 or self.equals(other) or len(self) == 0:
  399. return super(DatetimeIndex, self).union(other)
  400. if not isinstance(other, DatetimeIndex):
  401. try:
  402. other = DatetimeIndex(other)
  403. except TypeError:
  404. pass
  405. this, other = self._maybe_utc_convert(other)
  406. if this._can_fast_union(other):
  407. return this._fast_union(other)
  408. else:
  409. result = Index.union(this, other)
  410. if isinstance(result, DatetimeIndex):
  411. # TODO: we shouldn't be setting attributes like this;
  412. # in all the tests this equality already holds
  413. result._data._dtype = this.dtype
  414. if (result.freq is None and
  415. (this.freq is not None or other.freq is not None)):
  416. result.freq = to_offset(result.inferred_freq)
  417. return result
  418. def union_many(self, others):
  419. """
  420. A bit of a hack to accelerate unioning a collection of indexes
  421. """
  422. this = self
  423. for other in others:
  424. if not isinstance(this, DatetimeIndex):
  425. this = Index.union(this, other)
  426. continue
  427. if not isinstance(other, DatetimeIndex):
  428. try:
  429. other = DatetimeIndex(other)
  430. except TypeError:
  431. pass
  432. this, other = this._maybe_utc_convert(other)
  433. if this._can_fast_union(other):
  434. this = this._fast_union(other)
  435. else:
  436. dtype = this.dtype
  437. this = Index.union(this, other)
  438. if isinstance(this, DatetimeIndex):
  439. # TODO: we shouldn't be setting attributes like this;
  440. # in all the tests this equality already holds
  441. this._data._dtype = dtype
  442. return this
  443. def _can_fast_union(self, other):
  444. if not isinstance(other, DatetimeIndex):
  445. return False
  446. freq = self.freq
  447. if freq is None or freq != other.freq:
  448. return False
  449. if not self.is_monotonic or not other.is_monotonic:
  450. return False
  451. if len(self) == 0 or len(other) == 0:
  452. return True
  453. # to make our life easier, "sort" the two ranges
  454. if self[0] <= other[0]:
  455. left, right = self, other
  456. else:
  457. left, right = other, self
  458. right_start = right[0]
  459. left_end = left[-1]
  460. # Only need to "adjoin", not overlap
  461. try:
  462. return (right_start == left_end + freq) or right_start in left
  463. except (ValueError):
  464. # if we are comparing a freq that does not propagate timezones
  465. # this will raise
  466. return False
  467. def _fast_union(self, other):
  468. if len(other) == 0:
  469. return self.view(type(self))
  470. if len(self) == 0:
  471. return other.view(type(self))
  472. # to make our life easier, "sort" the two ranges
  473. if self[0] <= other[0]:
  474. left, right = self, other
  475. else:
  476. left, right = other, self
  477. left_end = left[-1]
  478. right_end = right[-1]
  479. # TODO: consider re-implementing freq._should_cache for fastpath
  480. # concatenate dates
  481. if left_end < right_end:
  482. loc = right.searchsorted(left_end, side='right')
  483. right_chunk = right.values[loc:]
  484. dates = _concat._concat_compat((left.values, right_chunk))
  485. return self._shallow_copy(dates)
  486. else:
  487. return left
  488. def _wrap_setop_result(self, other, result):
  489. name = get_op_result_name(self, other)
  490. return self._shallow_copy(result, name=name, freq=None, tz=self.tz)
  491. def intersection(self, other, sort=False):
  492. """
  493. Specialized intersection for DatetimeIndex objects. May be much faster
  494. than Index.intersection
  495. Parameters
  496. ----------
  497. other : DatetimeIndex or array-like
  498. sort : False or None, default False
  499. Sort the resulting index if possible.
  500. .. versionadded:: 0.24.0
  501. .. versionchanged:: 0.24.1
  502. Changed the default to ``False`` to match the behaviour
  503. from before 0.24.0.
  504. Returns
  505. -------
  506. y : Index or DatetimeIndex
  507. """
  508. self._validate_sort_keyword(sort)
  509. self._assert_can_do_setop(other)
  510. if self.equals(other):
  511. return self._get_reconciled_name_object(other)
  512. if not isinstance(other, DatetimeIndex):
  513. try:
  514. other = DatetimeIndex(other)
  515. except (TypeError, ValueError):
  516. pass
  517. result = Index.intersection(self, other, sort=sort)
  518. if isinstance(result, DatetimeIndex):
  519. if result.freq is None:
  520. result.freq = to_offset(result.inferred_freq)
  521. return result
  522. elif (other.freq is None or self.freq is None or
  523. other.freq != self.freq or
  524. not other.freq.isAnchored() or
  525. (not self.is_monotonic or not other.is_monotonic)):
  526. result = Index.intersection(self, other, sort=sort)
  527. # Invalidate the freq of `result`, which may not be correct at
  528. # this point, depending on the values.
  529. result.freq = None
  530. result = self._shallow_copy(result._values, name=result.name,
  531. tz=result.tz, freq=None)
  532. if result.freq is None:
  533. result.freq = to_offset(result.inferred_freq)
  534. return result
  535. if len(self) == 0:
  536. return self
  537. if len(other) == 0:
  538. return other
  539. # to make our life easier, "sort" the two ranges
  540. if self[0] <= other[0]:
  541. left, right = self, other
  542. else:
  543. left, right = other, self
  544. end = min(left[-1], right[-1])
  545. start = right[0]
  546. if end < start:
  547. return type(self)(data=[])
  548. else:
  549. lslice = slice(*left.slice_locs(start, end))
  550. left_chunk = left.values[lslice]
  551. return self._shallow_copy(left_chunk)
  552. # --------------------------------------------------------------------
  553. def _get_time_micros(self):
  554. values = self.asi8
  555. if self.tz is not None and not timezones.is_utc(self.tz):
  556. values = self._data._local_timestamps()
  557. return fields.get_time_micros(values)
  558. def to_series(self, keep_tz=None, index=None, name=None):
  559. """
  560. Create a Series with both index and values equal to the index keys
  561. useful with map for returning an indexer based on an index
  562. Parameters
  563. ----------
  564. keep_tz : optional, defaults False
  565. Return the data keeping the timezone.
  566. If keep_tz is True:
  567. If the timezone is not set, the resulting
  568. Series will have a datetime64[ns] dtype.
  569. Otherwise the Series will have an datetime64[ns, tz] dtype; the
  570. tz will be preserved.
  571. If keep_tz is False:
  572. Series will have a datetime64[ns] dtype. TZ aware
  573. objects will have the tz removed.
  574. .. versionchanged:: 0.24
  575. The default value will change to True in a future release.
  576. You can set ``keep_tz=True`` to already obtain the future
  577. behaviour and silence the warning.
  578. index : Index, optional
  579. index of resulting Series. If None, defaults to original index
  580. name : string, optional
  581. name of resulting Series. If None, defaults to name of original
  582. index
  583. Returns
  584. -------
  585. Series
  586. """
  587. from pandas import Series
  588. if index is None:
  589. index = self._shallow_copy()
  590. if name is None:
  591. name = self.name
  592. if keep_tz is None and self.tz is not None:
  593. warnings.warn("The default of the 'keep_tz' keyword will change "
  594. "to True in a future release. You can set "
  595. "'keep_tz=True' to obtain the future behaviour and "
  596. "silence this warning.", FutureWarning, stacklevel=2)
  597. keep_tz = False
  598. elif keep_tz is False:
  599. warnings.warn("Specifying 'keep_tz=False' is deprecated and this "
  600. "option will be removed in a future release. If "
  601. "you want to remove the timezone information, you "
  602. "can do 'idx.tz_convert(None)' before calling "
  603. "'to_series'.", FutureWarning, stacklevel=2)
  604. if keep_tz and self.tz is not None:
  605. # preserve the tz & copy
  606. values = self.copy(deep=True)
  607. else:
  608. values = self.values.copy()
  609. return Series(values, index=index, name=name)
  610. def snap(self, freq='S'):
  611. """
  612. Snap time stamps to nearest occurring frequency
  613. """
  614. # Superdumb, punting on any optimizing
  615. freq = to_offset(freq)
  616. snapped = np.empty(len(self), dtype=_NS_DTYPE)
  617. for i, v in enumerate(self):
  618. s = v
  619. if not freq.onOffset(s):
  620. t0 = freq.rollback(s)
  621. t1 = freq.rollforward(s)
  622. if abs(s - t0) < abs(t1 - s):
  623. s = t0
  624. else:
  625. s = t1
  626. snapped[i] = s
  627. # we know it conforms; skip check
  628. return DatetimeIndex._simple_new(snapped, freq=freq)
  629. # TODO: what about self.name? tz? if so, use shallow_copy?
  630. def join(self, other, how='left', level=None, return_indexers=False,
  631. sort=False):
  632. """
  633. See Index.join
  634. """
  635. if (not isinstance(other, DatetimeIndex) and len(other) > 0 and
  636. other.inferred_type not in ('floating', 'integer', 'mixed-integer',
  637. 'mixed-integer-float', 'mixed')):
  638. try:
  639. other = DatetimeIndex(other)
  640. except (TypeError, ValueError):
  641. pass
  642. this, other = self._maybe_utc_convert(other)
  643. return Index.join(this, other, how=how, level=level,
  644. return_indexers=return_indexers, sort=sort)
  645. def _maybe_utc_convert(self, other):
  646. this = self
  647. if isinstance(other, DatetimeIndex):
  648. if self.tz is not None:
  649. if other.tz is None:
  650. raise TypeError('Cannot join tz-naive with tz-aware '
  651. 'DatetimeIndex')
  652. elif other.tz is not None:
  653. raise TypeError('Cannot join tz-naive with tz-aware '
  654. 'DatetimeIndex')
  655. if not timezones.tz_compare(self.tz, other.tz):
  656. this = self.tz_convert('UTC')
  657. other = other.tz_convert('UTC')
  658. return this, other
  659. def _wrap_joined_index(self, joined, other):
  660. name = get_op_result_name(self, other)
  661. if (isinstance(other, DatetimeIndex) and
  662. self.freq == other.freq and
  663. self._can_fast_union(other)):
  664. joined = self._shallow_copy(joined)
  665. joined.name = name
  666. return joined
  667. else:
  668. tz = getattr(other, 'tz', None)
  669. return self._simple_new(joined, name, tz=tz)
  670. def _parsed_string_to_bounds(self, reso, parsed):
  671. """
  672. Calculate datetime bounds for parsed time string and its resolution.
  673. Parameters
  674. ----------
  675. reso : Resolution
  676. Resolution provided by parsed string.
  677. parsed : datetime
  678. Datetime from parsed string.
  679. Returns
  680. -------
  681. lower, upper: pd.Timestamp
  682. """
  683. if reso == 'year':
  684. return (Timestamp(datetime(parsed.year, 1, 1), tz=self.tz),
  685. Timestamp(datetime(parsed.year, 12, 31, 23,
  686. 59, 59, 999999), tz=self.tz))
  687. elif reso == 'month':
  688. d = ccalendar.get_days_in_month(parsed.year, parsed.month)
  689. return (Timestamp(datetime(parsed.year, parsed.month, 1),
  690. tz=self.tz),
  691. Timestamp(datetime(parsed.year, parsed.month, d, 23,
  692. 59, 59, 999999), tz=self.tz))
  693. elif reso == 'quarter':
  694. qe = (((parsed.month - 1) + 2) % 12) + 1 # two months ahead
  695. d = ccalendar.get_days_in_month(parsed.year, qe) # at end of month
  696. return (Timestamp(datetime(parsed.year, parsed.month, 1),
  697. tz=self.tz),
  698. Timestamp(datetime(parsed.year, qe, d, 23, 59,
  699. 59, 999999), tz=self.tz))
  700. elif reso == 'day':
  701. st = datetime(parsed.year, parsed.month, parsed.day)
  702. return (Timestamp(st, tz=self.tz),
  703. Timestamp(Timestamp(st + offsets.Day(),
  704. tz=self.tz).value - 1))
  705. elif reso == 'hour':
  706. st = datetime(parsed.year, parsed.month, parsed.day,
  707. hour=parsed.hour)
  708. return (Timestamp(st, tz=self.tz),
  709. Timestamp(Timestamp(st + offsets.Hour(),
  710. tz=self.tz).value - 1))
  711. elif reso == 'minute':
  712. st = datetime(parsed.year, parsed.month, parsed.day,
  713. hour=parsed.hour, minute=parsed.minute)
  714. return (Timestamp(st, tz=self.tz),
  715. Timestamp(Timestamp(st + offsets.Minute(),
  716. tz=self.tz).value - 1))
  717. elif reso == 'second':
  718. st = datetime(parsed.year, parsed.month, parsed.day,
  719. hour=parsed.hour, minute=parsed.minute,
  720. second=parsed.second)
  721. return (Timestamp(st, tz=self.tz),
  722. Timestamp(Timestamp(st + offsets.Second(),
  723. tz=self.tz).value - 1))
  724. elif reso == 'microsecond':
  725. st = datetime(parsed.year, parsed.month, parsed.day,
  726. parsed.hour, parsed.minute, parsed.second,
  727. parsed.microsecond)
  728. return (Timestamp(st, tz=self.tz), Timestamp(st, tz=self.tz))
  729. else:
  730. raise KeyError
  731. def _partial_date_slice(self, reso, parsed, use_lhs=True, use_rhs=True):
  732. is_monotonic = self.is_monotonic
  733. if (is_monotonic and reso in ['day', 'hour', 'minute', 'second'] and
  734. self._resolution >= Resolution.get_reso(reso)):
  735. # These resolution/monotonicity validations came from GH3931,
  736. # GH3452 and GH2369.
  737. # See also GH14826
  738. raise KeyError
  739. if reso == 'microsecond':
  740. # _partial_date_slice doesn't allow microsecond resolution, but
  741. # _parsed_string_to_bounds allows it.
  742. raise KeyError
  743. t1, t2 = self._parsed_string_to_bounds(reso, parsed)
  744. stamps = self.asi8
  745. if is_monotonic:
  746. # we are out of range
  747. if (len(stamps) and ((use_lhs and t1.value < stamps[0] and
  748. t2.value < stamps[0]) or
  749. ((use_rhs and t1.value > stamps[-1] and
  750. t2.value > stamps[-1])))):
  751. raise KeyError
  752. # a monotonic (sorted) series can be sliced
  753. left = stamps.searchsorted(
  754. t1.value, side='left') if use_lhs else None
  755. right = stamps.searchsorted(
  756. t2.value, side='right') if use_rhs else None
  757. return slice(left, right)
  758. lhs_mask = (stamps >= t1.value) if use_lhs else True
  759. rhs_mask = (stamps <= t2.value) if use_rhs else True
  760. # try to find a the dates
  761. return (lhs_mask & rhs_mask).nonzero()[0]
  762. def _maybe_promote(self, other):
  763. if other.inferred_type == 'date':
  764. other = DatetimeIndex(other)
  765. return self, other
  766. def get_value(self, series, key):
  767. """
  768. Fast lookup of value from 1-dimensional ndarray. Only use this if you
  769. know what you're doing
  770. """
  771. if isinstance(key, datetime):
  772. # needed to localize naive datetimes
  773. if self.tz is not None:
  774. if key.tzinfo is not None:
  775. key = Timestamp(key).tz_convert(self.tz)
  776. else:
  777. key = Timestamp(key).tz_localize(self.tz)
  778. return self.get_value_maybe_box(series, key)
  779. if isinstance(key, time):
  780. locs = self.indexer_at_time(key)
  781. return series.take(locs)
  782. try:
  783. return com.maybe_box(self, Index.get_value(self, series, key),
  784. series, key)
  785. except KeyError:
  786. try:
  787. loc = self._get_string_slice(key)
  788. return series[loc]
  789. except (TypeError, ValueError, KeyError):
  790. pass
  791. try:
  792. return self.get_value_maybe_box(series, key)
  793. except (TypeError, ValueError, KeyError):
  794. raise KeyError(key)
  795. def get_value_maybe_box(self, series, key):
  796. # needed to localize naive datetimes
  797. if self.tz is not None:
  798. key = Timestamp(key)
  799. if key.tzinfo is not None:
  800. key = key.tz_convert(self.tz)
  801. else:
  802. key = key.tz_localize(self.tz)
  803. elif not isinstance(key, Timestamp):
  804. key = Timestamp(key)
  805. values = self._engine.get_value(com.values_from_object(series),
  806. key, tz=self.tz)
  807. return com.maybe_box(self, values, series, key)
  808. def get_loc(self, key, method=None, tolerance=None):
  809. """
  810. Get integer location for requested label
  811. Returns
  812. -------
  813. loc : int
  814. """
  815. if tolerance is not None:
  816. # try converting tolerance now, so errors don't get swallowed by
  817. # the try/except clauses below
  818. tolerance = self._convert_tolerance(tolerance, np.asarray(key))
  819. if isinstance(key, datetime):
  820. # needed to localize naive datetimes
  821. if key.tzinfo is None:
  822. key = Timestamp(key, tz=self.tz)
  823. else:
  824. key = Timestamp(key).tz_convert(self.tz)
  825. return Index.get_loc(self, key, method, tolerance)
  826. elif isinstance(key, timedelta):
  827. # GH#20464
  828. raise TypeError("Cannot index {cls} with {other}"
  829. .format(cls=type(self).__name__,
  830. other=type(key).__name__))
  831. if isinstance(key, time):
  832. if method is not None:
  833. raise NotImplementedError('cannot yet lookup inexact labels '
  834. 'when key is a time object')
  835. return self.indexer_at_time(key)
  836. try:
  837. return Index.get_loc(self, key, method, tolerance)
  838. except (KeyError, ValueError, TypeError):
  839. try:
  840. return self._get_string_slice(key)
  841. except (TypeError, KeyError, ValueError):
  842. pass
  843. try:
  844. stamp = Timestamp(key)
  845. if stamp.tzinfo is not None and self.tz is not None:
  846. stamp = stamp.tz_convert(self.tz)
  847. else:
  848. stamp = stamp.tz_localize(self.tz)
  849. return Index.get_loc(self, stamp, method, tolerance)
  850. except KeyError:
  851. raise KeyError(key)
  852. except ValueError as e:
  853. # list-like tolerance size must match target index size
  854. if 'list-like' in str(e):
  855. raise e
  856. raise KeyError(key)
  857. def _maybe_cast_slice_bound(self, label, side, kind):
  858. """
  859. If label is a string, cast it to datetime according to resolution.
  860. Parameters
  861. ----------
  862. label : object
  863. side : {'left', 'right'}
  864. kind : {'ix', 'loc', 'getitem'}
  865. Returns
  866. -------
  867. label : object
  868. Notes
  869. -----
  870. Value of `side` parameter should be validated in caller.
  871. """
  872. assert kind in ['ix', 'loc', 'getitem', None]
  873. if is_float(label) or isinstance(label, time) or is_integer(label):
  874. self._invalid_indexer('slice', label)
  875. if isinstance(label, compat.string_types):
  876. freq = getattr(self, 'freqstr',
  877. getattr(self, 'inferred_freq', None))
  878. _, parsed, reso = parsing.parse_time_string(label, freq)
  879. lower, upper = self._parsed_string_to_bounds(reso, parsed)
  880. # lower, upper form the half-open interval:
  881. # [parsed, parsed + 1 freq)
  882. # because label may be passed to searchsorted
  883. # the bounds need swapped if index is reverse sorted and has a
  884. # length > 1 (is_monotonic_decreasing gives True for empty
  885. # and length 1 index)
  886. if self._is_strictly_monotonic_decreasing and len(self) > 1:
  887. return upper if side == 'left' else lower
  888. return lower if side == 'left' else upper
  889. else:
  890. return label
  891. def _get_string_slice(self, key, use_lhs=True, use_rhs=True):
  892. freq = getattr(self, 'freqstr',
  893. getattr(self, 'inferred_freq', None))
  894. _, parsed, reso = parsing.parse_time_string(key, freq)
  895. loc = self._partial_date_slice(reso, parsed, use_lhs=use_lhs,
  896. use_rhs=use_rhs)
  897. return loc
  898. def slice_indexer(self, start=None, end=None, step=None, kind=None):
  899. """
  900. Return indexer for specified label slice.
  901. Index.slice_indexer, customized to handle time slicing.
  902. In addition to functionality provided by Index.slice_indexer, does the
  903. following:
  904. - if both `start` and `end` are instances of `datetime.time`, it
  905. invokes `indexer_between_time`
  906. - if `start` and `end` are both either string or None perform
  907. value-based selection in non-monotonic cases.
  908. """
  909. # For historical reasons DatetimeIndex supports slices between two
  910. # instances of datetime.time as if it were applying a slice mask to
  911. # an array of (self.hour, self.minute, self.seconds, self.microsecond).
  912. if isinstance(start, time) and isinstance(end, time):
  913. if step is not None and step != 1:
  914. raise ValueError('Must have step size of 1 with time slices')
  915. return self.indexer_between_time(start, end)
  916. if isinstance(start, time) or isinstance(end, time):
  917. raise KeyError('Cannot mix time and non-time slice keys')
  918. try:
  919. return Index.slice_indexer(self, start, end, step, kind=kind)
  920. except KeyError:
  921. # For historical reasons DatetimeIndex by default supports
  922. # value-based partial (aka string) slices on non-monotonic arrays,
  923. # let's try that.
  924. if ((start is None or isinstance(start, compat.string_types)) and
  925. (end is None or isinstance(end, compat.string_types))):
  926. mask = True
  927. if start is not None:
  928. start_casted = self._maybe_cast_slice_bound(
  929. start, 'left', kind)
  930. mask = start_casted <= self
  931. if end is not None:
  932. end_casted = self._maybe_cast_slice_bound(
  933. end, 'right', kind)
  934. mask = (self <= end_casted) & mask
  935. indexer = mask.nonzero()[0][::step]
  936. if len(indexer) == len(self):
  937. return slice(None)
  938. else:
  939. return indexer
  940. else:
  941. raise
  942. # --------------------------------------------------------------------
  943. # Wrapping DatetimeArray
  944. # Compat for frequency inference, see GH#23789
  945. _is_monotonic_increasing = Index.is_monotonic_increasing
  946. _is_monotonic_decreasing = Index.is_monotonic_decreasing
  947. _is_unique = Index.is_unique
  948. _timezone = cache_readonly(DatetimeArray._timezone.fget)
  949. is_normalized = cache_readonly(DatetimeArray.is_normalized.fget)
  950. _resolution = cache_readonly(DatetimeArray._resolution.fget)
  951. strftime = ea_passthrough(DatetimeArray.strftime)
  952. _has_same_tz = ea_passthrough(DatetimeArray._has_same_tz)
  953. @property
  954. def offset(self):
  955. """
  956. get/set the frequency of the instance
  957. """
  958. msg = ('{cls}.offset has been deprecated and will be removed '
  959. 'in a future version; use {cls}.freq instead.'
  960. .format(cls=type(self).__name__))
  961. warnings.warn(msg, FutureWarning, stacklevel=2)
  962. return self.freq
  963. @offset.setter
  964. def offset(self, value):
  965. """
  966. get/set the frequency of the instance
  967. """
  968. msg = ('{cls}.offset has been deprecated and will be removed '
  969. 'in a future version; use {cls}.freq instead.'
  970. .format(cls=type(self).__name__))
  971. warnings.warn(msg, FutureWarning, stacklevel=2)
  972. self.freq = value
  973. def __getitem__(self, key):
  974. result = self._data.__getitem__(key)
  975. if is_scalar(result):
  976. return result
  977. elif result.ndim > 1:
  978. # To support MPL which performs slicing with 2 dim
  979. # even though it only has 1 dim by definition
  980. assert isinstance(result, np.ndarray), result
  981. return result
  982. return type(self)(result, name=self.name)
  983. @property
  984. def _box_func(self):
  985. return lambda x: Timestamp(x, tz=self.tz)
  986. # --------------------------------------------------------------------
  987. @Substitution(klass='DatetimeIndex')
  988. @Appender(_shared_docs['searchsorted'])
  989. def searchsorted(self, value, side='left', sorter=None):
  990. if isinstance(value, (np.ndarray, Index)):
  991. value = np.array(value, dtype=_NS_DTYPE, copy=False)
  992. else:
  993. value = _to_M8(value, tz=self.tz)
  994. return self.values.searchsorted(value, side=side)
  995. def is_type_compatible(self, typ):
  996. return typ == self.inferred_type or typ == 'datetime'
  997. @property
  998. def inferred_type(self):
  999. # b/c datetime is represented as microseconds since the epoch, make
  1000. # sure we can't have ambiguous indexing
  1001. return 'datetime64'
  1002. @property
  1003. def is_all_dates(self):
  1004. return True
  1005. def insert(self, loc, item):
  1006. """
  1007. Make new Index inserting new item at location
  1008. Parameters
  1009. ----------
  1010. loc : int
  1011. item : object
  1012. if not either a Python datetime or a numpy integer-like, returned
  1013. Index dtype will be object rather than datetime.
  1014. Returns
  1015. -------
  1016. new_index : Index
  1017. """
  1018. if is_scalar(item) and isna(item):
  1019. # GH 18295
  1020. item = self._na_value
  1021. freq = None
  1022. if isinstance(item, (datetime, np.datetime64)):
  1023. self._assert_can_do_op(item)
  1024. if not self._has_same_tz(item) and not isna(item):
  1025. raise ValueError(
  1026. 'Passed item and index have different timezone')
  1027. # check freq can be preserved on edge cases
  1028. if self.size and self.freq is not None:
  1029. if ((loc == 0 or loc == -len(self)) and
  1030. item + self.freq == self[0]):
  1031. freq = self.freq
  1032. elif (loc == len(self)) and item - self.freq == self[-1]:
  1033. freq = self.freq
  1034. item = _to_M8(item, tz=self.tz)
  1035. try:
  1036. new_dates = np.concatenate((self[:loc].asi8, [item.view(np.int64)],
  1037. self[loc:].asi8))
  1038. return self._shallow_copy(new_dates, freq=freq)
  1039. except (AttributeError, TypeError):
  1040. # fall back to object index
  1041. if isinstance(item, compat.string_types):
  1042. return self.astype(object).insert(loc, item)
  1043. raise TypeError(
  1044. "cannot insert DatetimeIndex with incompatible label")
  1045. def delete(self, loc):
  1046. """
  1047. Make a new DatetimeIndex with passed location(s) deleted.
  1048. Parameters
  1049. ----------
  1050. loc: int, slice or array of ints
  1051. Indicate which sub-arrays to remove.
  1052. Returns
  1053. -------
  1054. new_index : DatetimeIndex
  1055. """
  1056. new_dates = np.delete(self.asi8, loc)
  1057. freq = None
  1058. if is_integer(loc):
  1059. if loc in (0, -len(self), -1, len(self) - 1):
  1060. freq = self.freq
  1061. else:
  1062. if is_list_like(loc):
  1063. loc = lib.maybe_indices_to_slice(
  1064. ensure_int64(np.array(loc)), len(self))
  1065. if isinstance(loc, slice) and loc.step in (1, None):
  1066. if (loc.start in (0, None) or loc.stop in (len(self), None)):
  1067. freq = self.freq
  1068. return self._shallow_copy(new_dates, freq=freq)
  1069. def indexer_at_time(self, time, asof=False):
  1070. """
  1071. Returns index locations of index values at particular time of day
  1072. (e.g. 9:30AM).
  1073. Parameters
  1074. ----------
  1075. time : datetime.time or string
  1076. datetime.time or string in appropriate format ("%H:%M", "%H%M",
  1077. "%I:%M%p", "%I%M%p", "%H:%M:%S", "%H%M%S", "%I:%M:%S%p",
  1078. "%I%M%S%p").
  1079. Returns
  1080. -------
  1081. values_at_time : array of integers
  1082. See Also
  1083. --------
  1084. indexer_between_time, DataFrame.at_time
  1085. """
  1086. from dateutil.parser import parse
  1087. if asof:
  1088. raise NotImplementedError("'asof' argument is not supported")
  1089. if isinstance(time, compat.string_types):
  1090. time = parse(time).time()
  1091. if time.tzinfo:
  1092. # TODO
  1093. raise NotImplementedError("argument 'time' with timezone info is "
  1094. "not supported")
  1095. time_micros = self._get_time_micros()
  1096. micros = _time_to_micros(time)
  1097. return (micros == time_micros).nonzero()[0]
  1098. def indexer_between_time(self, start_time, end_time, include_start=True,
  1099. include_end=True):
  1100. """
  1101. Return index locations of values between particular times of day
  1102. (e.g., 9:00-9:30AM).
  1103. Parameters
  1104. ----------
  1105. start_time, end_time : datetime.time, str
  1106. datetime.time or string in appropriate format ("%H:%M", "%H%M",
  1107. "%I:%M%p", "%I%M%p", "%H:%M:%S", "%H%M%S", "%I:%M:%S%p",
  1108. "%I%M%S%p").
  1109. include_start : boolean, default True
  1110. include_end : boolean, default True
  1111. Returns
  1112. -------
  1113. values_between_time : array of integers
  1114. See Also
  1115. --------
  1116. indexer_at_time, DataFrame.between_time
  1117. """
  1118. start_time = tools.to_time(start_time)
  1119. end_time = tools.to_time(end_time)
  1120. time_micros = self._get_time_micros()
  1121. start_micros = _time_to_micros(start_time)
  1122. end_micros = _time_to_micros(end_time)
  1123. if include_start and include_end:
  1124. lop = rop = operator.le
  1125. elif include_start:
  1126. lop = operator.le
  1127. rop = operator.lt
  1128. elif include_end:
  1129. lop = operator.lt
  1130. rop = operator.le
  1131. else:
  1132. lop = rop = operator.lt
  1133. if start_time <= end_time:
  1134. join_op = operator.and_
  1135. else:
  1136. join_op = operator.or_
  1137. mask = join_op(lop(start_micros, time_micros),
  1138. rop(time_micros, end_micros))
  1139. return mask.nonzero()[0]
  1140. DatetimeIndex._add_comparison_ops()
  1141. DatetimeIndex._add_numeric_methods_disabled()
  1142. DatetimeIndex._add_logical_methods_disabled()
  1143. DatetimeIndex._add_datetimelike_methods()
  1144. def date_range(start=None, end=None, periods=None, freq=None, tz=None,
  1145. normalize=False, name=None, closed=None, **kwargs):
  1146. """
  1147. Return a fixed frequency DatetimeIndex.
  1148. Parameters
  1149. ----------
  1150. start : str or datetime-like, optional
  1151. Left bound for generating dates.
  1152. end : str or datetime-like, optional
  1153. Right bound for generating dates.
  1154. periods : integer, optional
  1155. Number of periods to generate.
  1156. freq : str or DateOffset, default 'D'
  1157. Frequency strings can have multiples, e.g. '5H'. See
  1158. :ref:`here <timeseries.offset_aliases>` for a list of
  1159. frequency aliases.
  1160. tz : str or tzinfo, optional
  1161. Time zone name for returning localized DatetimeIndex, for example
  1162. 'Asia/Hong_Kong'. By default, the resulting DatetimeIndex is
  1163. timezone-naive.
  1164. normalize : bool, default False
  1165. Normalize start/end dates to midnight before generating date range.
  1166. name : str, default None
  1167. Name of the resulting DatetimeIndex.
  1168. closed : {None, 'left', 'right'}, optional
  1169. Make the interval closed with respect to the given frequency to
  1170. the 'left', 'right', or both sides (None, the default).
  1171. **kwargs
  1172. For compatibility. Has no effect on the result.
  1173. Returns
  1174. -------
  1175. rng : DatetimeIndex
  1176. See Also
  1177. --------
  1178. pandas.DatetimeIndex : An immutable container for datetimes.
  1179. pandas.timedelta_range : Return a fixed frequency TimedeltaIndex.
  1180. pandas.period_range : Return a fixed frequency PeriodIndex.
  1181. pandas.interval_range : Return a fixed frequency IntervalIndex.
  1182. Notes
  1183. -----
  1184. Of the four parameters ``start``, ``end``, ``periods``, and ``freq``,
  1185. exactly three must be specified. If ``freq`` is omitted, the resulting
  1186. ``DatetimeIndex`` will have ``periods`` linearly spaced elements between
  1187. ``start`` and ``end`` (closed on both sides).
  1188. To learn more about the frequency strings, please see `this link
  1189. <http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.
  1190. Examples
  1191. --------
  1192. **Specifying the values**
  1193. The next four examples generate the same `DatetimeIndex`, but vary
  1194. the combination of `start`, `end` and `periods`.
  1195. Specify `start` and `end`, with the default daily frequency.
  1196. >>> pd.date_range(start='1/1/2018', end='1/08/2018')
  1197. DatetimeIndex(['2018-01-01', '2018-01-02', '2018-01-03', '2018-01-04',
  1198. '2018-01-05', '2018-01-06', '2018-01-07', '2018-01-08'],
  1199. dtype='datetime64[ns]', freq='D')
  1200. Specify `start` and `periods`, the number of periods (days).
  1201. >>> pd.date_range(start='1/1/2018', periods=8)
  1202. DatetimeIndex(['2018-01-01', '2018-01-02', '2018-01-03', '2018-01-04',
  1203. '2018-01-05', '2018-01-06', '2018-01-07', '2018-01-08'],
  1204. dtype='datetime64[ns]', freq='D')
  1205. Specify `end` and `periods`, the number of periods (days).
  1206. >>> pd.date_range(end='1/1/2018', periods=8)
  1207. DatetimeIndex(['2017-12-25', '2017-12-26', '2017-12-27', '2017-12-28',
  1208. '2017-12-29', '2017-12-30', '2017-12-31', '2018-01-01'],
  1209. dtype='datetime64[ns]', freq='D')
  1210. Specify `start`, `end`, and `periods`; the frequency is generated
  1211. automatically (linearly spaced).
  1212. >>> pd.date_range(start='2018-04-24', end='2018-04-27', periods=3)
  1213. DatetimeIndex(['2018-04-24 00:00:00', '2018-04-25 12:00:00',
  1214. '2018-04-27 00:00:00'],
  1215. dtype='datetime64[ns]', freq=None)
  1216. **Other Parameters**
  1217. Changed the `freq` (frequency) to ``'M'`` (month end frequency).
  1218. >>> pd.date_range(start='1/1/2018', periods=5, freq='M')
  1219. DatetimeIndex(['2018-01-31', '2018-02-28', '2018-03-31', '2018-04-30',
  1220. '2018-05-31'],
  1221. dtype='datetime64[ns]', freq='M')
  1222. Multiples are allowed
  1223. >>> pd.date_range(start='1/1/2018', periods=5, freq='3M')
  1224. DatetimeIndex(['2018-01-31', '2018-04-30', '2018-07-31', '2018-10-31',
  1225. '2019-01-31'],
  1226. dtype='datetime64[ns]', freq='3M')
  1227. `freq` can also be specified as an Offset object.
  1228. >>> pd.date_range(start='1/1/2018', periods=5, freq=pd.offsets.MonthEnd(3))
  1229. DatetimeIndex(['2018-01-31', '2018-04-30', '2018-07-31', '2018-10-31',
  1230. '2019-01-31'],
  1231. dtype='datetime64[ns]', freq='3M')
  1232. Specify `tz` to set the timezone.
  1233. >>> pd.date_range(start='1/1/2018', periods=5, tz='Asia/Tokyo')
  1234. DatetimeIndex(['2018-01-01 00:00:00+09:00', '2018-01-02 00:00:00+09:00',
  1235. '2018-01-03 00:00:00+09:00', '2018-01-04 00:00:00+09:00',
  1236. '2018-01-05 00:00:00+09:00'],
  1237. dtype='datetime64[ns, Asia/Tokyo]', freq='D')
  1238. `closed` controls whether to include `start` and `end` that are on the
  1239. boundary. The default includes boundary points on either end.
  1240. >>> pd.date_range(start='2017-01-01', end='2017-01-04', closed=None)
  1241. DatetimeIndex(['2017-01-01', '2017-01-02', '2017-01-03', '2017-01-04'],
  1242. dtype='datetime64[ns]', freq='D')
  1243. Use ``closed='left'`` to exclude `end` if it falls on the boundary.
  1244. >>> pd.date_range(start='2017-01-01', end='2017-01-04', closed='left')
  1245. DatetimeIndex(['2017-01-01', '2017-01-02', '2017-01-03'],
  1246. dtype='datetime64[ns]', freq='D')
  1247. Use ``closed='right'`` to exclude `start` if it falls on the boundary.
  1248. >>> pd.date_range(start='2017-01-01', end='2017-01-04', closed='right')
  1249. DatetimeIndex(['2017-01-02', '2017-01-03', '2017-01-04'],
  1250. dtype='datetime64[ns]', freq='D')
  1251. """
  1252. if freq is None and com._any_none(periods, start, end):
  1253. freq = 'D'
  1254. dtarr = DatetimeArray._generate_range(
  1255. start=start, end=end, periods=periods,
  1256. freq=freq, tz=tz, normalize=normalize,
  1257. closed=closed, **kwargs)
  1258. return DatetimeIndex._simple_new(
  1259. dtarr, tz=dtarr.tz, freq=dtarr.freq, name=name)
  1260. def bdate_range(start=None, end=None, periods=None, freq='B', tz=None,
  1261. normalize=True, name=None, weekmask=None, holidays=None,
  1262. closed=None, **kwargs):
  1263. """
  1264. Return a fixed frequency DatetimeIndex, with business day as the default
  1265. frequency
  1266. Parameters
  1267. ----------
  1268. start : string or datetime-like, default None
  1269. Left bound for generating dates.
  1270. end : string or datetime-like, default None
  1271. Right bound for generating dates.
  1272. periods : integer, default None
  1273. Number of periods to generate.
  1274. freq : string or DateOffset, default 'B' (business daily)
  1275. Frequency strings can have multiples, e.g. '5H'.
  1276. tz : string or None
  1277. Time zone name for returning localized DatetimeIndex, for example
  1278. Asia/Beijing.
  1279. normalize : bool, default False
  1280. Normalize start/end dates to midnight before generating date range.
  1281. name : string, default None
  1282. Name of the resulting DatetimeIndex.
  1283. weekmask : string or None, default None
  1284. Weekmask of valid business days, passed to ``numpy.busdaycalendar``,
  1285. only used when custom frequency strings are passed. The default
  1286. value None is equivalent to 'Mon Tue Wed Thu Fri'.
  1287. .. versionadded:: 0.21.0
  1288. holidays : list-like or None, default None
  1289. Dates to exclude from the set of valid business days, passed to
  1290. ``numpy.busdaycalendar``, only used when custom frequency strings
  1291. are passed.
  1292. .. versionadded:: 0.21.0
  1293. closed : string, default None
  1294. Make the interval closed with respect to the given frequency to
  1295. the 'left', 'right', or both sides (None).
  1296. **kwargs
  1297. For compatibility. Has no effect on the result.
  1298. Returns
  1299. -------
  1300. DatetimeIndex
  1301. Notes
  1302. -----
  1303. Of the four parameters: ``start``, ``end``, ``periods``, and ``freq``,
  1304. exactly three must be specified. Specifying ``freq`` is a requirement
  1305. for ``bdate_range``. Use ``date_range`` if specifying ``freq`` is not
  1306. desired.
  1307. To learn more about the frequency strings, please see `this link
  1308. <http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.
  1309. Examples
  1310. --------
  1311. Note how the two weekend days are skipped in the result.
  1312. >>> pd.bdate_range(start='1/1/2018', end='1/08/2018')
  1313. DatetimeIndex(['2018-01-01', '2018-01-02', '2018-01-03', '2018-01-04',
  1314. '2018-01-05', '2018-01-08'],
  1315. dtype='datetime64[ns]', freq='B')
  1316. """
  1317. if freq is None:
  1318. msg = 'freq must be specified for bdate_range; use date_range instead'
  1319. raise TypeError(msg)
  1320. if is_string_like(freq) and freq.startswith('C'):
  1321. try:
  1322. weekmask = weekmask or 'Mon Tue Wed Thu Fri'
  1323. freq = prefix_mapping[freq](holidays=holidays, weekmask=weekmask)
  1324. except (KeyError, TypeError):
  1325. msg = 'invalid custom frequency string: {freq}'.format(freq=freq)
  1326. raise ValueError(msg)
  1327. elif holidays or weekmask:
  1328. msg = ('a custom frequency string is required when holidays or '
  1329. 'weekmask are passed, got frequency {freq}').format(freq=freq)
  1330. raise ValueError(msg)
  1331. return date_range(start=start, end=end, periods=periods,
  1332. freq=freq, tz=tz, normalize=normalize, name=name,
  1333. closed=closed, **kwargs)
  1334. def cdate_range(start=None, end=None, periods=None, freq='C', tz=None,
  1335. normalize=True, name=None, closed=None, **kwargs):
  1336. """
  1337. Return a fixed frequency DatetimeIndex, with CustomBusinessDay as the
  1338. default frequency
  1339. .. deprecated:: 0.21.0
  1340. Parameters
  1341. ----------
  1342. start : string or datetime-like, default None
  1343. Left bound for generating dates
  1344. end : string or datetime-like, default None
  1345. Right bound for generating dates
  1346. periods : integer, default None
  1347. Number of periods to generate
  1348. freq : string or DateOffset, default 'C' (CustomBusinessDay)
  1349. Frequency strings can have multiples, e.g. '5H'
  1350. tz : string, default None
  1351. Time zone name for returning localized DatetimeIndex, for example
  1352. Asia/Beijing
  1353. normalize : bool, default False
  1354. Normalize start/end dates to midnight before generating date range
  1355. name : string, default None
  1356. Name of the resulting DatetimeIndex
  1357. weekmask : string, Default 'Mon Tue Wed Thu Fri'
  1358. weekmask of valid business days, passed to ``numpy.busdaycalendar``
  1359. holidays : list
  1360. list/array of dates to exclude from the set of valid business days,
  1361. passed to ``numpy.busdaycalendar``
  1362. closed : string, default None
  1363. Make the interval closed with respect to the given frequency to
  1364. the 'left', 'right', or both sides (None)
  1365. Notes
  1366. -----
  1367. Of the three parameters: ``start``, ``end``, and ``periods``, exactly two
  1368. must be specified.
  1369. To learn more about the frequency strings, please see `this link
  1370. <http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.
  1371. Returns
  1372. -------
  1373. rng : DatetimeIndex
  1374. """
  1375. warnings.warn("cdate_range is deprecated and will be removed in a future "
  1376. "version, instead use pd.bdate_range(..., freq='{freq}')"
  1377. .format(freq=freq), FutureWarning, stacklevel=2)
  1378. if freq == 'C':
  1379. holidays = kwargs.pop('holidays', [])
  1380. weekmask = kwargs.pop('weekmask', 'Mon Tue Wed Thu Fri')
  1381. freq = CDay(holidays=holidays, weekmask=weekmask)
  1382. return date_range(start=start, end=end, periods=periods, freq=freq,
  1383. tz=tz, normalize=normalize, name=name,
  1384. closed=closed, **kwargs)
  1385. def _time_to_micros(time):
  1386. seconds = time.hour * 60 * 60 + 60 * time.minute + time.second
  1387. return 1000000 * seconds + time.microsecond