test_misc.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. import calendar
  2. from datetime import datetime
  3. import locale
  4. import unicodedata
  5. import numpy as np
  6. import pytest
  7. import pandas as pd
  8. from pandas import (
  9. DatetimeIndex,
  10. Index,
  11. Timedelta,
  12. Timestamp,
  13. date_range,
  14. offsets,
  15. )
  16. import pandas._testing as tm
  17. from pandas.core.arrays import DatetimeArray
  18. from pandas.tseries.frequencies import to_offset
  19. class TestDatetime64:
  20. def test_no_millisecond_field(self):
  21. msg = "type object 'DatetimeIndex' has no attribute 'millisecond'"
  22. with pytest.raises(AttributeError, match=msg):
  23. DatetimeIndex.millisecond
  24. msg = "'DatetimeIndex' object has no attribute 'millisecond'"
  25. with pytest.raises(AttributeError, match=msg):
  26. DatetimeIndex([]).millisecond
  27. def test_datetimeindex_accessors(self):
  28. dti_naive = date_range(freq="D", start=datetime(1998, 1, 1), periods=365)
  29. # GH#13303
  30. dti_tz = date_range(
  31. freq="D", start=datetime(1998, 1, 1), periods=365, tz="US/Eastern"
  32. )
  33. for dti in [dti_naive, dti_tz]:
  34. assert dti.year[0] == 1998
  35. assert dti.month[0] == 1
  36. assert dti.day[0] == 1
  37. assert dti.hour[0] == 0
  38. assert dti.minute[0] == 0
  39. assert dti.second[0] == 0
  40. assert dti.microsecond[0] == 0
  41. assert dti.dayofweek[0] == 3
  42. assert dti.dayofyear[0] == 1
  43. assert dti.dayofyear[120] == 121
  44. assert dti.isocalendar().week[0] == 1
  45. assert dti.isocalendar().week[120] == 18
  46. assert dti.quarter[0] == 1
  47. assert dti.quarter[120] == 2
  48. assert dti.days_in_month[0] == 31
  49. assert dti.days_in_month[90] == 30
  50. assert dti.is_month_start[0]
  51. assert not dti.is_month_start[1]
  52. assert dti.is_month_start[31]
  53. assert dti.is_quarter_start[0]
  54. assert dti.is_quarter_start[90]
  55. assert dti.is_year_start[0]
  56. assert not dti.is_year_start[364]
  57. assert not dti.is_month_end[0]
  58. assert dti.is_month_end[30]
  59. assert not dti.is_month_end[31]
  60. assert dti.is_month_end[364]
  61. assert not dti.is_quarter_end[0]
  62. assert not dti.is_quarter_end[30]
  63. assert dti.is_quarter_end[89]
  64. assert dti.is_quarter_end[364]
  65. assert not dti.is_year_end[0]
  66. assert dti.is_year_end[364]
  67. assert len(dti.year) == 365
  68. assert len(dti.month) == 365
  69. assert len(dti.day) == 365
  70. assert len(dti.hour) == 365
  71. assert len(dti.minute) == 365
  72. assert len(dti.second) == 365
  73. assert len(dti.microsecond) == 365
  74. assert len(dti.dayofweek) == 365
  75. assert len(dti.dayofyear) == 365
  76. assert len(dti.isocalendar()) == 365
  77. assert len(dti.quarter) == 365
  78. assert len(dti.is_month_start) == 365
  79. assert len(dti.is_month_end) == 365
  80. assert len(dti.is_quarter_start) == 365
  81. assert len(dti.is_quarter_end) == 365
  82. assert len(dti.is_year_start) == 365
  83. assert len(dti.is_year_end) == 365
  84. dti.name = "name"
  85. # non boolean accessors -> return Index
  86. for accessor in DatetimeArray._field_ops:
  87. res = getattr(dti, accessor)
  88. assert len(res) == 365
  89. assert isinstance(res, Index)
  90. assert res.name == "name"
  91. # boolean accessors -> return array
  92. for accessor in DatetimeArray._bool_ops:
  93. res = getattr(dti, accessor)
  94. assert len(res) == 365
  95. assert isinstance(res, np.ndarray)
  96. # test boolean indexing
  97. res = dti[dti.is_quarter_start]
  98. exp = dti[[0, 90, 181, 273]]
  99. tm.assert_index_equal(res, exp)
  100. res = dti[dti.is_leap_year]
  101. exp = DatetimeIndex([], freq="D", tz=dti.tz, name="name")
  102. tm.assert_index_equal(res, exp)
  103. def test_datetimeindex_accessors2(self):
  104. dti = date_range(freq="BQ-FEB", start=datetime(1998, 1, 1), periods=4)
  105. assert sum(dti.is_quarter_start) == 0
  106. assert sum(dti.is_quarter_end) == 4
  107. assert sum(dti.is_year_start) == 0
  108. assert sum(dti.is_year_end) == 1
  109. def test_datetimeindex_accessors3(self):
  110. # Ensure is_start/end accessors throw ValueError for CustomBusinessDay,
  111. bday_egypt = offsets.CustomBusinessDay(weekmask="Sun Mon Tue Wed Thu")
  112. dti = date_range(datetime(2013, 4, 30), periods=5, freq=bday_egypt)
  113. msg = "Custom business days is not supported by is_month_start"
  114. with pytest.raises(ValueError, match=msg):
  115. dti.is_month_start
  116. def test_datetimeindex_accessors4(self):
  117. dti = DatetimeIndex(["2000-01-01", "2000-01-02", "2000-01-03"])
  118. assert dti.is_month_start[0] == 1
  119. def test_datetimeindex_accessors5(self):
  120. freq_m = to_offset("M")
  121. bm = to_offset("BM")
  122. qfeb = to_offset("Q-FEB")
  123. qsfeb = to_offset("QS-FEB")
  124. bq = to_offset("BQ")
  125. bqs_apr = to_offset("BQS-APR")
  126. as_nov = to_offset("AS-NOV")
  127. tests = [
  128. (freq_m.is_month_start(Timestamp("2013-06-01")), 1),
  129. (bm.is_month_start(Timestamp("2013-06-01")), 0),
  130. (freq_m.is_month_start(Timestamp("2013-06-03")), 0),
  131. (bm.is_month_start(Timestamp("2013-06-03")), 1),
  132. (qfeb.is_month_end(Timestamp("2013-02-28")), 1),
  133. (qfeb.is_quarter_end(Timestamp("2013-02-28")), 1),
  134. (qfeb.is_year_end(Timestamp("2013-02-28")), 1),
  135. (qfeb.is_month_start(Timestamp("2013-03-01")), 1),
  136. (qfeb.is_quarter_start(Timestamp("2013-03-01")), 1),
  137. (qfeb.is_year_start(Timestamp("2013-03-01")), 1),
  138. (qsfeb.is_month_end(Timestamp("2013-03-31")), 1),
  139. (qsfeb.is_quarter_end(Timestamp("2013-03-31")), 0),
  140. (qsfeb.is_year_end(Timestamp("2013-03-31")), 0),
  141. (qsfeb.is_month_start(Timestamp("2013-02-01")), 1),
  142. (qsfeb.is_quarter_start(Timestamp("2013-02-01")), 1),
  143. (qsfeb.is_year_start(Timestamp("2013-02-01")), 1),
  144. (bq.is_month_end(Timestamp("2013-06-30")), 0),
  145. (bq.is_quarter_end(Timestamp("2013-06-30")), 0),
  146. (bq.is_year_end(Timestamp("2013-06-30")), 0),
  147. (bq.is_month_end(Timestamp("2013-06-28")), 1),
  148. (bq.is_quarter_end(Timestamp("2013-06-28")), 1),
  149. (bq.is_year_end(Timestamp("2013-06-28")), 0),
  150. (bqs_apr.is_month_end(Timestamp("2013-06-30")), 0),
  151. (bqs_apr.is_quarter_end(Timestamp("2013-06-30")), 0),
  152. (bqs_apr.is_year_end(Timestamp("2013-06-30")), 0),
  153. (bqs_apr.is_month_end(Timestamp("2013-06-28")), 1),
  154. (bqs_apr.is_quarter_end(Timestamp("2013-06-28")), 1),
  155. (bqs_apr.is_year_end(Timestamp("2013-03-29")), 1),
  156. (as_nov.is_year_start(Timestamp("2013-11-01")), 1),
  157. (as_nov.is_year_end(Timestamp("2013-10-31")), 1),
  158. (Timestamp("2012-02-01").days_in_month, 29),
  159. (Timestamp("2013-02-01").days_in_month, 28),
  160. ]
  161. for ts, value in tests:
  162. assert ts == value
  163. def test_datetimeindex_accessors6(self):
  164. # GH 6538: Check that DatetimeIndex and its TimeStamp elements
  165. # return the same weekofyear accessor close to new year w/ tz
  166. dates = ["2013/12/29", "2013/12/30", "2013/12/31"]
  167. dates = DatetimeIndex(dates, tz="Europe/Brussels")
  168. expected = [52, 1, 1]
  169. assert dates.isocalendar().week.tolist() == expected
  170. assert [d.weekofyear for d in dates] == expected
  171. # GH 12806
  172. # error: Unsupported operand types for + ("List[None]" and "List[str]")
  173. @pytest.mark.parametrize(
  174. "time_locale", [None] + tm.get_locales() # type: ignore[operator]
  175. )
  176. def test_datetime_name_accessors(self, time_locale):
  177. # Test Monday -> Sunday and January -> December, in that sequence
  178. if time_locale is None:
  179. # If the time_locale is None, day-name and month_name should
  180. # return the english attributes
  181. expected_days = [
  182. "Monday",
  183. "Tuesday",
  184. "Wednesday",
  185. "Thursday",
  186. "Friday",
  187. "Saturday",
  188. "Sunday",
  189. ]
  190. expected_months = [
  191. "January",
  192. "February",
  193. "March",
  194. "April",
  195. "May",
  196. "June",
  197. "July",
  198. "August",
  199. "September",
  200. "October",
  201. "November",
  202. "December",
  203. ]
  204. else:
  205. with tm.set_locale(time_locale, locale.LC_TIME):
  206. expected_days = calendar.day_name[:]
  207. expected_months = calendar.month_name[1:]
  208. # GH#11128
  209. dti = date_range(freq="D", start=datetime(1998, 1, 1), periods=365)
  210. english_days = [
  211. "Monday",
  212. "Tuesday",
  213. "Wednesday",
  214. "Thursday",
  215. "Friday",
  216. "Saturday",
  217. "Sunday",
  218. ]
  219. for day, name, eng_name in zip(range(4, 11), expected_days, english_days):
  220. name = name.capitalize()
  221. assert dti.day_name(locale=time_locale)[day] == name
  222. assert dti.day_name(locale=None)[day] == eng_name
  223. ts = Timestamp(datetime(2016, 4, day))
  224. assert ts.day_name(locale=time_locale) == name
  225. dti = dti.append(DatetimeIndex([pd.NaT]))
  226. assert np.isnan(dti.day_name(locale=time_locale)[-1])
  227. ts = Timestamp(pd.NaT)
  228. assert np.isnan(ts.day_name(locale=time_locale))
  229. # GH#12805
  230. dti = date_range(freq="M", start="2012", end="2013")
  231. result = dti.month_name(locale=time_locale)
  232. expected = Index([month.capitalize() for month in expected_months])
  233. # work around different normalization schemes
  234. # https://github.com/pandas-dev/pandas/issues/22342
  235. result = result.str.normalize("NFD")
  236. expected = expected.str.normalize("NFD")
  237. tm.assert_index_equal(result, expected)
  238. for date, expected in zip(dti, expected_months):
  239. result = date.month_name(locale=time_locale)
  240. expected = expected.capitalize()
  241. result = unicodedata.normalize("NFD", result)
  242. expected = unicodedata.normalize("NFD", result)
  243. assert result == expected
  244. dti = dti.append(DatetimeIndex([pd.NaT]))
  245. assert np.isnan(dti.month_name(locale=time_locale)[-1])
  246. def test_nanosecond_field(self):
  247. dti = DatetimeIndex(np.arange(10))
  248. expected = Index(np.arange(10, dtype=np.int32))
  249. tm.assert_index_equal(dti.nanosecond, expected)
  250. def test_iter_readonly():
  251. # GH#28055 ints_to_pydatetime with readonly array
  252. arr = np.array([np.datetime64("2012-02-15T12:00:00.000000000")])
  253. arr.setflags(write=False)
  254. dti = pd.to_datetime(arr)
  255. list(dti)
  256. def test_add_timedelta_preserves_freq():
  257. # GH#37295 should hold for any DTI with freq=None or Tick freq
  258. tz = "Canada/Eastern"
  259. dti = date_range(
  260. start=Timestamp("2019-03-26 00:00:00-0400", tz=tz),
  261. end=Timestamp("2020-10-17 00:00:00-0400", tz=tz),
  262. freq="D",
  263. )
  264. result = dti + Timedelta(days=1)
  265. assert result.freq == dti.freq