test_np_datetime.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. import numpy as np
  2. import pytest
  3. from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
  4. from pandas._libs.tslibs.np_datetime import (
  5. OutOfBoundsDatetime,
  6. OutOfBoundsTimedelta,
  7. astype_overflowsafe,
  8. is_unitless,
  9. py_get_unit_from_dtype,
  10. py_td64_to_tdstruct,
  11. )
  12. import pandas._testing as tm
  13. def test_is_unitless():
  14. dtype = np.dtype("M8[ns]")
  15. assert not is_unitless(dtype)
  16. dtype = np.dtype("datetime64")
  17. assert is_unitless(dtype)
  18. dtype = np.dtype("m8[ns]")
  19. assert not is_unitless(dtype)
  20. dtype = np.dtype("timedelta64")
  21. assert is_unitless(dtype)
  22. msg = "dtype must be datetime64 or timedelta64"
  23. with pytest.raises(ValueError, match=msg):
  24. is_unitless(np.dtype(np.int64))
  25. msg = "Argument 'dtype' has incorrect type"
  26. with pytest.raises(TypeError, match=msg):
  27. is_unitless("foo")
  28. def test_get_unit_from_dtype():
  29. # datetime64
  30. assert py_get_unit_from_dtype(np.dtype("M8[Y]")) == NpyDatetimeUnit.NPY_FR_Y.value
  31. assert py_get_unit_from_dtype(np.dtype("M8[M]")) == NpyDatetimeUnit.NPY_FR_M.value
  32. assert py_get_unit_from_dtype(np.dtype("M8[W]")) == NpyDatetimeUnit.NPY_FR_W.value
  33. # B has been deprecated and removed -> no 3
  34. assert py_get_unit_from_dtype(np.dtype("M8[D]")) == NpyDatetimeUnit.NPY_FR_D.value
  35. assert py_get_unit_from_dtype(np.dtype("M8[h]")) == NpyDatetimeUnit.NPY_FR_h.value
  36. assert py_get_unit_from_dtype(np.dtype("M8[m]")) == NpyDatetimeUnit.NPY_FR_m.value
  37. assert py_get_unit_from_dtype(np.dtype("M8[s]")) == NpyDatetimeUnit.NPY_FR_s.value
  38. assert py_get_unit_from_dtype(np.dtype("M8[ms]")) == NpyDatetimeUnit.NPY_FR_ms.value
  39. assert py_get_unit_from_dtype(np.dtype("M8[us]")) == NpyDatetimeUnit.NPY_FR_us.value
  40. assert py_get_unit_from_dtype(np.dtype("M8[ns]")) == NpyDatetimeUnit.NPY_FR_ns.value
  41. assert py_get_unit_from_dtype(np.dtype("M8[ps]")) == NpyDatetimeUnit.NPY_FR_ps.value
  42. assert py_get_unit_from_dtype(np.dtype("M8[fs]")) == NpyDatetimeUnit.NPY_FR_fs.value
  43. assert py_get_unit_from_dtype(np.dtype("M8[as]")) == NpyDatetimeUnit.NPY_FR_as.value
  44. # timedelta64
  45. assert py_get_unit_from_dtype(np.dtype("m8[Y]")) == NpyDatetimeUnit.NPY_FR_Y.value
  46. assert py_get_unit_from_dtype(np.dtype("m8[M]")) == NpyDatetimeUnit.NPY_FR_M.value
  47. assert py_get_unit_from_dtype(np.dtype("m8[W]")) == NpyDatetimeUnit.NPY_FR_W.value
  48. # B has been deprecated and removed -> no 3
  49. assert py_get_unit_from_dtype(np.dtype("m8[D]")) == NpyDatetimeUnit.NPY_FR_D.value
  50. assert py_get_unit_from_dtype(np.dtype("m8[h]")) == NpyDatetimeUnit.NPY_FR_h.value
  51. assert py_get_unit_from_dtype(np.dtype("m8[m]")) == NpyDatetimeUnit.NPY_FR_m.value
  52. assert py_get_unit_from_dtype(np.dtype("m8[s]")) == NpyDatetimeUnit.NPY_FR_s.value
  53. assert py_get_unit_from_dtype(np.dtype("m8[ms]")) == NpyDatetimeUnit.NPY_FR_ms.value
  54. assert py_get_unit_from_dtype(np.dtype("m8[us]")) == NpyDatetimeUnit.NPY_FR_us.value
  55. assert py_get_unit_from_dtype(np.dtype("m8[ns]")) == NpyDatetimeUnit.NPY_FR_ns.value
  56. assert py_get_unit_from_dtype(np.dtype("m8[ps]")) == NpyDatetimeUnit.NPY_FR_ps.value
  57. assert py_get_unit_from_dtype(np.dtype("m8[fs]")) == NpyDatetimeUnit.NPY_FR_fs.value
  58. assert py_get_unit_from_dtype(np.dtype("m8[as]")) == NpyDatetimeUnit.NPY_FR_as.value
  59. def test_td64_to_tdstruct():
  60. val = 12454636234 # arbitrary value
  61. res1 = py_td64_to_tdstruct(val, NpyDatetimeUnit.NPY_FR_ns.value)
  62. exp1 = {
  63. "days": 0,
  64. "hrs": 0,
  65. "min": 0,
  66. "sec": 12,
  67. "ms": 454,
  68. "us": 636,
  69. "ns": 234,
  70. "seconds": 12,
  71. "microseconds": 454636,
  72. "nanoseconds": 234,
  73. }
  74. assert res1 == exp1
  75. res2 = py_td64_to_tdstruct(val, NpyDatetimeUnit.NPY_FR_us.value)
  76. exp2 = {
  77. "days": 0,
  78. "hrs": 3,
  79. "min": 27,
  80. "sec": 34,
  81. "ms": 636,
  82. "us": 234,
  83. "ns": 0,
  84. "seconds": 12454,
  85. "microseconds": 636234,
  86. "nanoseconds": 0,
  87. }
  88. assert res2 == exp2
  89. res3 = py_td64_to_tdstruct(val, NpyDatetimeUnit.NPY_FR_ms.value)
  90. exp3 = {
  91. "days": 144,
  92. "hrs": 3,
  93. "min": 37,
  94. "sec": 16,
  95. "ms": 234,
  96. "us": 0,
  97. "ns": 0,
  98. "seconds": 13036,
  99. "microseconds": 234000,
  100. "nanoseconds": 0,
  101. }
  102. assert res3 == exp3
  103. # Note this out of bounds for nanosecond Timedelta
  104. res4 = py_td64_to_tdstruct(val, NpyDatetimeUnit.NPY_FR_s.value)
  105. exp4 = {
  106. "days": 144150,
  107. "hrs": 21,
  108. "min": 10,
  109. "sec": 34,
  110. "ms": 0,
  111. "us": 0,
  112. "ns": 0,
  113. "seconds": 76234,
  114. "microseconds": 0,
  115. "nanoseconds": 0,
  116. }
  117. assert res4 == exp4
  118. class TestAstypeOverflowSafe:
  119. def test_pass_non_dt64_array(self):
  120. # check that we raise, not segfault
  121. arr = np.arange(5)
  122. dtype = np.dtype("M8[ns]")
  123. msg = (
  124. "astype_overflowsafe values.dtype and dtype must be either "
  125. "both-datetime64 or both-timedelta64"
  126. )
  127. with pytest.raises(TypeError, match=msg):
  128. astype_overflowsafe(arr, dtype, copy=True)
  129. with pytest.raises(TypeError, match=msg):
  130. astype_overflowsafe(arr, dtype, copy=False)
  131. def test_pass_non_dt64_dtype(self):
  132. # check that we raise, not segfault
  133. arr = np.arange(5, dtype="i8").view("M8[D]")
  134. dtype = np.dtype("m8[ns]")
  135. msg = (
  136. "astype_overflowsafe values.dtype and dtype must be either "
  137. "both-datetime64 or both-timedelta64"
  138. )
  139. with pytest.raises(TypeError, match=msg):
  140. astype_overflowsafe(arr, dtype, copy=True)
  141. with pytest.raises(TypeError, match=msg):
  142. astype_overflowsafe(arr, dtype, copy=False)
  143. def test_astype_overflowsafe_dt64(self):
  144. dtype = np.dtype("M8[ns]")
  145. dt = np.datetime64("2262-04-05", "D")
  146. arr = dt + np.arange(10, dtype="m8[D]")
  147. # arr.astype silently overflows, so this
  148. wrong = arr.astype(dtype)
  149. roundtrip = wrong.astype(arr.dtype)
  150. assert not (wrong == roundtrip).all()
  151. msg = "Out of bounds nanosecond timestamp"
  152. with pytest.raises(OutOfBoundsDatetime, match=msg):
  153. astype_overflowsafe(arr, dtype)
  154. # But converting to microseconds is fine, and we match numpy's results.
  155. dtype2 = np.dtype("M8[us]")
  156. result = astype_overflowsafe(arr, dtype2)
  157. expected = arr.astype(dtype2)
  158. tm.assert_numpy_array_equal(result, expected)
  159. def test_astype_overflowsafe_td64(self):
  160. dtype = np.dtype("m8[ns]")
  161. dt = np.datetime64("2262-04-05", "D")
  162. arr = dt + np.arange(10, dtype="m8[D]")
  163. arr = arr.view("m8[D]")
  164. # arr.astype silently overflows, so this
  165. wrong = arr.astype(dtype)
  166. roundtrip = wrong.astype(arr.dtype)
  167. assert not (wrong == roundtrip).all()
  168. msg = r"Cannot convert 106752 days to timedelta64\[ns\] without overflow"
  169. with pytest.raises(OutOfBoundsTimedelta, match=msg):
  170. astype_overflowsafe(arr, dtype)
  171. # But converting to microseconds is fine, and we match numpy's results.
  172. dtype2 = np.dtype("m8[us]")
  173. result = astype_overflowsafe(arr, dtype2)
  174. expected = arr.astype(dtype2)
  175. tm.assert_numpy_array_equal(result, expected)
  176. def test_astype_overflowsafe_disallow_rounding(self):
  177. arr = np.array([-1500, 1500], dtype="M8[ns]")
  178. dtype = np.dtype("M8[us]")
  179. msg = "Cannot losslessly cast '-1500 ns' to us"
  180. with pytest.raises(ValueError, match=msg):
  181. astype_overflowsafe(arr, dtype, round_ok=False)
  182. result = astype_overflowsafe(arr, dtype, round_ok=True)
  183. expected = arr.astype(dtype)
  184. tm.assert_numpy_array_equal(result, expected)