PYield 0.19.2__tar.gz → 0.19.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. {pyield-0.19.2 → pyield-0.19.3}/PKG-INFO +1 -1
  2. pyield-0.19.3/pyield/__about__.py +1 -0
  3. {pyield-0.19.2 → pyield-0.19.3}/pyield/bday.py +16 -56
  4. {pyield-0.19.2 → pyield-0.19.3}/pyield/bonds/bond_tools.py +1 -1
  5. {pyield-0.19.2 → pyield-0.19.3}/pyield/bonds/lft.py +2 -2
  6. {pyield-0.19.2 → pyield-0.19.3}/pyield/bonds/ltn.py +4 -4
  7. {pyield-0.19.2 → pyield-0.19.3}/pyield/bonds/ntnb.py +12 -12
  8. {pyield-0.19.2 → pyield-0.19.3}/pyield/bonds/ntnf.py +13 -13
  9. {pyield-0.19.2 → pyield-0.19.3}/pyield/data_sources/anbima.py +3 -3
  10. {pyield-0.19.2 → pyield-0.19.3}/pyield/data_sources/di.py +5 -5
  11. {pyield-0.19.2 → pyield-0.19.3}/pyield/data_sources/futures_data/__init__.py +1 -1
  12. {pyield-0.19.2 → pyield-0.19.3}/pyield/data_sources/indicators.py +1 -1
  13. pyield-0.19.3/pyield/date_converter.py +82 -0
  14. {pyield-0.19.2 → pyield-0.19.3}/pyproject.toml +1 -1
  15. pyield-0.19.2/pyield/__about__.py +0 -1
  16. pyield-0.19.2/pyield/date_converter.py +0 -65
  17. {pyield-0.19.2 → pyield-0.19.3}/LICENSE +0 -0
  18. {pyield-0.19.2 → pyield-0.19.3}/README.md +0 -0
  19. {pyield-0.19.2 → pyield-0.19.3}/pyield/__init__.py +0 -0
  20. {pyield-0.19.2 → pyield-0.19.3}/pyield/bonds/__init__.py +0 -0
  21. {pyield-0.19.2 → pyield-0.19.3}/pyield/calculators.py +0 -0
  22. {pyield-0.19.2 → pyield-0.19.3}/pyield/data_sources/__init__.py +0 -0
  23. {pyield-0.19.2 → pyield-0.19.3}/pyield/data_sources/data_cache.py +0 -0
  24. {pyield-0.19.2 → pyield-0.19.3}/pyield/data_sources/futures_data/common.py +0 -0
  25. {pyield-0.19.2 → pyield-0.19.3}/pyield/data_sources/futures_data/historical.py +0 -0
  26. {pyield-0.19.2 → pyield-0.19.3}/pyield/data_sources/futures_data/intraday.py +0 -0
  27. {pyield-0.19.2 → pyield-0.19.3}/pyield/data_sources/futures_data/xml.py +0 -0
  28. {pyield-0.19.2 → pyield-0.19.3}/pyield/data_sources/projections.py +0 -0
  29. {pyield-0.19.2 → pyield-0.19.3}/pyield/holidays/__init__.py +0 -0
  30. {pyield-0.19.2 → pyield-0.19.3}/pyield/holidays/br_holidays_new.txt +0 -0
  31. {pyield-0.19.2 → pyield-0.19.3}/pyield/holidays/br_holidays_old.txt +0 -0
  32. {pyield-0.19.2 → pyield-0.19.3}/pyield/holidays/holidays_manager.py +0 -0
  33. {pyield-0.19.2 → pyield-0.19.3}/pyield/interpolator.py +0 -0
  34. {pyield-0.19.2 → pyield-0.19.3}/pyield/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PYield
3
- Version: 0.19.2
3
+ Version: 0.19.3
4
4
  Summary: A Python library for analysis of fixed income instruments in Brazil
5
5
  Keywords: fixed-income, brazil, finance, analysis, bonds
6
6
  Author-Email: Carlos Carvalho <cr.cj@outlook.com>
@@ -0,0 +1 @@
1
+ __version__ = "0.19.3"
@@ -1,14 +1,11 @@
1
- import datetime as dt
2
1
  from typing import Literal, overload
3
2
 
4
3
  import numpy as np
5
4
  import pandas as pd
6
5
 
6
+ from . import date_converter as dc
7
7
  from . import holidays
8
-
9
- type ScalarDateTypes = str | np.datetime64 | pd.Timestamp | dt.datetime | dt.date
10
- type PandasArrayDateTypes = pd.Series | pd.DatetimeIndex | pd.Index
11
- type ArrayDateTypes = PandasArrayDateTypes | np.ndarray | list | tuple
8
+ from .date_converter import ArrayDateTypes, ScalarDateTypes
12
9
 
13
10
  type ArrayIntTypes = np.ndarray | pd.Series | list | tuple
14
11
  type ScalarIntTypes = int | np.integer
@@ -17,44 +14,6 @@ type ScalarIntTypes = int | np.integer
17
14
  br_holidays = holidays.BrHolidays()
18
15
 
19
16
 
20
- @overload
21
- def _convert_input_dates(dates: ScalarDateTypes) -> pd.Timestamp: ...
22
- @overload
23
- def _convert_input_dates(dates: ArrayDateTypes) -> pd.Series: ...
24
-
25
-
26
- def _convert_input_dates(
27
- dates: ScalarDateTypes | ArrayDateTypes,
28
- ) -> pd.Timestamp | pd.Series:
29
- result = pd.to_datetime(dates, dayfirst=True)
30
- if isinstance(result, (pd.Timestamp, dt.date, dt.datetime, np.datetime64)):
31
- result = pd.Timestamp(result).normalize()
32
- elif isinstance(result, (pd.Series, pd.DatetimeIndex)):
33
- result = pd.Series(result).astype("datetime64[ns]")
34
- else:
35
- raise ValueError("Invalid date input type.")
36
-
37
- return result
38
-
39
-
40
- def _convert_to_numpy_date(
41
- dates: pd.Timestamp | pd.Series,
42
- ) -> np.datetime64 | np.ndarray:
43
- """
44
- Converts the input dates to a numpy datetime64[D] format.
45
-
46
- Args:
47
- dates (Timestamp | Series): A single date or a Series of dates.
48
-
49
- Returns:
50
- np.datetime64 | np.ndarray: The input dates in a numpy datetime64[D] format.
51
- """
52
- if isinstance(dates, pd.Timestamp):
53
- return np.datetime64(dates, "D")
54
- else:
55
- return dates.to_numpy().astype("datetime64[D]")
56
-
57
-
58
17
  @overload
59
18
  def offset(
60
19
  dates: ScalarDateTypes,
@@ -188,14 +147,14 @@ def offset(
188
147
  information on error handling and behavior, refer to the `numpy.busday_offset`
189
148
  documentation: https://numpy.org/doc/stable/reference/generated/numpy.busday_offset.html
190
149
  """
191
- normalized_dates = _convert_input_dates(dates)
150
+ converted_dates = dc.convert_input_dates(dates)
192
151
 
193
152
  selected_holidays = br_holidays.get_applicable_holidays(
194
- normalized_dates, holiday_list
153
+ converted_dates, holiday_list
195
154
  )
196
- selected_holidays_np = _convert_to_numpy_date(selected_holidays)
155
+ selected_holidays_np = dc.convert_to_numpy_date(selected_holidays)
197
156
 
198
- dates_np = _convert_to_numpy_date(normalized_dates)
157
+ dates_np = dc.convert_to_numpy_date(converted_dates)
199
158
  offsetted_dates_np = np.busday_offset(
200
159
  dates_np, offsets=offset, roll=roll, holidays=selected_holidays_np
201
160
  )
@@ -304,18 +263,18 @@ def count(
304
263
  1 19
305
264
  dtype: Int64
306
265
  """
307
- normalized_start = _convert_input_dates(start)
308
- normalized_end = _convert_input_dates(end)
266
+ converted_start = dc.convert_input_dates(start)
267
+ converted_end = dc.convert_input_dates(end)
309
268
 
310
269
  # Determine which list of holidays to use
311
270
  selected_holidays = br_holidays.get_applicable_holidays(
312
- normalized_start, holiday_list
271
+ converted_start, holiday_list
313
272
  )
314
- selected_holidays_np = _convert_to_numpy_date(selected_holidays)
273
+ selected_holidays_np = dc.convert_to_numpy_date(selected_holidays)
315
274
 
316
275
  # Convert inputs to numpy datetime64[D] before calling numpy.busday_count
317
- start_np = _convert_to_numpy_date(normalized_start)
318
- end_np = _convert_to_numpy_date(normalized_end)
276
+ start_np = dc.convert_to_numpy_date(converted_start)
277
+ end_np = dc.convert_to_numpy_date(converted_end)
319
278
 
320
279
  result_np = np.busday_count(start_np, end_np, holidays=selected_holidays_np)
321
280
  if isinstance(result_np, np.integer):
@@ -371,11 +330,12 @@ def generate(
371
330
  https://pandas.pydata.org/docs/reference/api/pandas.bdate_range.html.
372
331
  """
373
332
  if start:
374
- converted_start = _convert_input_dates(start)
333
+ converted_start = dc.convert_input_dates(start)
375
334
  else:
376
335
  converted_start = pd.Timestamp.today()
336
+
377
337
  if end:
378
- converted_end = _convert_input_dates(end)
338
+ converted_end = dc.convert_input_dates(end)
379
339
  else:
380
340
  converted_end = pd.Timestamp.today()
381
341
 
@@ -410,6 +370,6 @@ def is_business_day(date: ScalarDateTypes) -> bool:
410
370
  >>> bday.is_business_day("25-12-2023") # Christmas
411
371
  False
412
372
  """
413
- converted_date = _convert_input_dates(date)
373
+ converted_date = dc.convert_input_dates(date)
414
374
  shifted_date = offset(converted_date, 0) # Shift the date if it is not a bus. day
415
375
  return converted_date == shifted_date
@@ -77,7 +77,7 @@ def di_spreads(reference_date: str | pd.Timestamp) -> pd.DataFrame:
77
77
  bond type and maturity date.
78
78
  """
79
79
  # Fetch DI rates for the reference date
80
- reference_date = dc.convert_date(reference_date)
80
+ reference_date = dc.convert_input_dates(reference_date)
81
81
  df_di = di.data(reference_date, adj_expirations=True)
82
82
  if "SettlementRate" not in df_di.columns:
83
83
  raise ValueError("DI rates data is missing the 'SettlementRate' column.")
@@ -62,8 +62,8 @@ def quotation(
62
62
  98.9645
63
63
  """
64
64
  # Validate and normalize dates
65
- settlement = dc.convert_date(settlement)
66
- maturity = dc.convert_date(maturity)
65
+ settlement = dc.convert_input_dates(settlement)
66
+ maturity = dc.convert_input_dates(maturity)
67
67
 
68
68
  # The number of bdays between settlement (inclusive) and the maturity (exclusive)
69
69
  bdays = bday.count(settlement, maturity)
@@ -66,8 +66,8 @@ def price(
66
66
  """
67
67
 
68
68
  # Validate and normalize dates
69
- settlement = dc.convert_date(settlement)
70
- maturity = dc.convert_date(maturity)
69
+ settlement = dc.convert_input_dates(settlement)
70
+ maturity = dc.convert_input_dates(maturity)
71
71
 
72
72
  # Calculate the number of business days between settlement and cash flow dates
73
73
  bdays = bday.count(settlement, maturity)
@@ -138,8 +138,8 @@ def historical_premium(
138
138
  If the data is not available, returns NaN.
139
139
  """
140
140
  # Convert input dates to a consistent format
141
- reference_date = dc.convert_date(reference_date)
142
- maturity = dc.convert_date(maturity)
141
+ reference_date = dc.convert_input_dates(reference_date)
142
+ maturity = dc.convert_input_dates(maturity)
143
143
 
144
144
  # Retrieve LTN rates for the reference date
145
145
  df_anbima = rates(reference_date)
@@ -99,8 +99,8 @@ def _generate_coupon_dates_map(
99
99
  pd.Series: Series of coupon dates within the specified range.
100
100
  """
101
101
  # Validate and normalize dates
102
- start = dc.convert_date(start)
103
- end = dc.convert_date(end)
102
+ start = dc.convert_input_dates(start)
103
+ end = dc.convert_input_dates(end)
104
104
 
105
105
  # Initialize the first coupon date based on the reference date
106
106
  reference_year = start.year
@@ -138,8 +138,8 @@ def payment_dates(
138
138
  pd.Series: Series of coupon dates within the specified range.
139
139
  """
140
140
  # Validate and normalize dates
141
- settlement = dc.convert_date(settlement)
142
- maturity = dc.convert_date(maturity)
141
+ settlement = dc.convert_input_dates(settlement)
142
+ maturity = dc.convert_input_dates(maturity)
143
143
  _check_maturities(maturity)
144
144
 
145
145
  # Check if maturity date is after the start date
@@ -180,8 +180,8 @@ def cash_flows(
180
180
  - CashFlow: Cash flow value for the bond
181
181
  """
182
182
  # Validate and normalize dates
183
- settlement = dc.convert_date(settlement)
184
- maturity = dc.convert_date(maturity)
183
+ settlement = dc.convert_input_dates(settlement)
184
+ maturity = dc.convert_input_dates(maturity)
185
185
 
186
186
  # Get the coupon dates between the settlement and maturity dates
187
187
  p_dates = payment_dates(settlement, maturity)
@@ -225,8 +225,8 @@ def quotation(
225
225
  99.5341
226
226
  """
227
227
  # Validate and normalize dates
228
- settlement = dc.convert_date(settlement)
229
- maturity = dc.convert_date(maturity)
228
+ settlement = dc.convert_input_dates(settlement)
229
+ maturity = dc.convert_input_dates(maturity)
230
230
  _check_maturities(maturity)
231
231
 
232
232
  cf_df = cash_flows(settlement, maturity)
@@ -306,7 +306,7 @@ def spot_rates(
306
306
  - SpotRate: The real spot rate for the bond.
307
307
  """
308
308
  # Process and validate the input data
309
- settlement = dc.convert_date(settlement)
309
+ settlement = dc.convert_input_dates(settlement)
310
310
  maturities = pd.to_datetime(maturities, errors="raise", dayfirst=True)
311
311
  _check_maturities(maturities)
312
312
 
@@ -401,7 +401,7 @@ def bei_rates(
401
401
  settlement date to the bond's maturity.
402
402
  """
403
403
  # Normalize input dates
404
- settlement = dc.convert_date(settlement)
404
+ settlement = dc.convert_input_dates(settlement)
405
405
  ntnb_maturities = pd.to_datetime(ntnb_maturities, errors="coerce", dayfirst=True)
406
406
 
407
407
  # Calculate Real Interest Rate (RIR)
@@ -434,8 +434,8 @@ def duration(
434
434
  maturity: str | pd.Timestamp,
435
435
  rate: float,
436
436
  ) -> float:
437
- settlement = dc.convert_date(settlement)
438
- maturity = dc.convert_date(maturity)
437
+ settlement = dc.convert_input_dates(settlement)
438
+ maturity = dc.convert_input_dates(maturity)
439
439
 
440
440
  df = cash_flows(settlement, maturity)
441
441
  df["BY"] = bday.count(settlement, df["PaymentDate"]) / 252
@@ -85,8 +85,8 @@ def coupon_dates(
85
85
  (exclusive) and maturity (inclusive) dates.
86
86
  """
87
87
  # Validate and normalize dates
88
- settlement = dc.convert_date(settlement)
89
- maturity = dc.convert_date(maturity)
88
+ settlement = dc.convert_input_dates(settlement)
89
+ maturity = dc.convert_input_dates(maturity)
90
90
 
91
91
  # Check if the maturity date is valid
92
92
  _check_maturity_date(maturity)
@@ -131,8 +131,8 @@ def cash_flows(
131
131
  pd.DataFrame: DataFrame with columns "PaymentDate" and "CashFlow".
132
132
  """
133
133
  # Validate input dates
134
- settlement = dc.convert_date(settlement)
135
- maturity = dc.convert_date(maturity)
134
+ settlement = dc.convert_input_dates(settlement)
135
+ maturity = dc.convert_input_dates(maturity)
136
136
  _check_maturity_date(maturity)
137
137
 
138
138
  # Get the coupon payment dates between the settlement and maturity dates
@@ -221,7 +221,7 @@ def spot_rates( # noqa
221
221
  pd.DataFrame: DataFrame with columns "MaturityDate" and "SpotRate"
222
222
  """
223
223
  # Process and validate the input data
224
- settlement = dc.convert_date(settlement)
224
+ settlement = dc.convert_input_dates(settlement)
225
225
 
226
226
  # Create flat forward interpolators for LTN and NTN-F rates
227
227
  ltn_rate_interpolator = it.Interpolator(
@@ -379,8 +379,8 @@ def di_net_spread( # noqa
379
379
  float: The net DI spread in basis points.
380
380
  """
381
381
  # Create an interpolator for the DI rates using the flat-forward method
382
- settlement = dc.convert_date(settlement)
383
- ntnf_maturity = dc.convert_date(ntnf_maturity)
382
+ settlement = dc.convert_input_dates(settlement)
383
+ ntnf_maturity = dc.convert_input_dates(ntnf_maturity)
384
384
 
385
385
  ff_interpolator = it.Interpolator(
386
386
  "flat_forward",
@@ -420,8 +420,8 @@ def premium(
420
420
  di_expirations: pd.Series,
421
421
  di_rates: pd.Series,
422
422
  ) -> float:
423
- ntnf_maturity = dc.convert_date(ntnf_maturity)
424
- settlement = dc.convert_date(settlement)
423
+ ntnf_maturity = dc.convert_input_dates(ntnf_maturity)
424
+ settlement = dc.convert_input_dates(settlement)
425
425
 
426
426
  df = cash_flows(settlement, ntnf_maturity, adj_payment_dates=True)
427
427
  df["BDays"] = bday.count(settlement, df["PaymentDate"])
@@ -460,8 +460,8 @@ def historical_premium(
460
460
  reference_date: str | pd.Timestamp,
461
461
  maturity: str | pd.Timestamp, # noqa
462
462
  ) -> float:
463
- reference_date = dc.convert_date(reference_date)
464
- maturity = dc.convert_date(maturity)
463
+ reference_date = dc.convert_input_dates(reference_date)
464
+ maturity = dc.convert_input_dates(maturity)
465
465
 
466
466
  df_ntnf = rates(reference_date)
467
467
  if df_ntnf.empty:
@@ -503,8 +503,8 @@ def duration(
503
503
  maturity: str | pd.Timestamp,
504
504
  rate: float,
505
505
  ) -> float:
506
- settlement = dc.convert_date(settlement)
507
- maturity = dc.convert_date(maturity)
506
+ settlement = dc.convert_input_dates(settlement)
507
+ maturity = dc.convert_input_dates(maturity)
508
508
 
509
509
  df = cash_flows(settlement, maturity)
510
510
  df["BY"] = bday.count(settlement, df["PaymentDate"]) / 252
@@ -110,7 +110,7 @@ def data(
110
110
  bond_type: str | None = None,
111
111
  ) -> pd.DataFrame:
112
112
  # Normalize the reference date
113
- reference_date = dc.convert_date(reference_date)
113
+ reference_date = dc.convert_input_dates(reference_date)
114
114
  file_content = _get_file_content(reference_date)
115
115
 
116
116
  if not file_content:
@@ -134,7 +134,7 @@ def rates(
134
134
  ) -> pd.DataFrame:
135
135
  df = get_anbima_dataframe()
136
136
 
137
- reference_date = dc.convert_date(reference_date)
137
+ reference_date = dc.convert_input_dates(reference_date)
138
138
  df.query("ReferenceDate == @reference_date", inplace=True)
139
139
 
140
140
  if df.empty:
@@ -159,7 +159,7 @@ def pre_maturities(
159
159
  reference_date: str | pd.Timestamp,
160
160
  ) -> pd.Series:
161
161
  df = get_anbima_dataframe()
162
- reference_date = dc.convert_date(reference_date)
162
+ reference_date = dc.convert_input_dates(reference_date)
163
163
  df.query("ReferenceDate == @reference_date", inplace=True)
164
164
  df.query("BondType in ['LTN', 'NTN-F']", inplace=True)
165
165
  maturity_dates = df["MaturityDate"].drop_duplicates()
@@ -30,7 +30,7 @@ def data(
30
30
  expiration dates and selected rates. The DataFrame is sorted by the
31
31
  expiration date.
32
32
  """
33
- trade_date = dc.convert_date(trade_date)
33
+ trade_date = dc.convert_input_dates(trade_date)
34
34
  df = get_di_dataframe()
35
35
  df.query("TradeDate == @trade_date", inplace=True)
36
36
 
@@ -86,7 +86,7 @@ def expirations(
86
86
  Returns:
87
87
  pd.Series: A Series of unique expiration dates for DI contracts.
88
88
  """
89
- trade_date = dc.convert_date(trade_date)
89
+ trade_date = dc.convert_input_dates(trade_date)
90
90
  df = data(trade_date, adj_expirations, prefixed_filter)
91
91
  df = df.drop_duplicates(subset=["ExpirationDate"], ignore_index=True)
92
92
  return df["ExpirationDate"]
@@ -118,7 +118,7 @@ def rate(
118
118
  if the rate cannot be determined.
119
119
  """
120
120
  # Convert input dates to consistent format
121
- trade_date = dc.convert_date(trade_date)
121
+ trade_date = dc.convert_input_dates(trade_date)
122
122
  # Adjust expiration date to the nearest business day
123
123
  expiration = bday.offset(expiration, 0)
124
124
 
@@ -180,10 +180,10 @@ def trade_dates(
180
180
  """
181
181
  df = get_di_dataframe()
182
182
  if start_date:
183
- start_date = dc.convert_date(start_date)
183
+ start_date = dc.convert_input_dates(start_date)
184
184
  df = df.query("TradeDate >= @start_date")
185
185
  if end_date:
186
- end_date = dc.convert_date(end_date)
186
+ end_date = dc.convert_input_dates(end_date)
187
187
  df = df.query("TradeDate <= @end_date")
188
188
 
189
189
  df = df.drop_duplicates(subset=["TradeDate"], ignore_index=True)
@@ -47,7 +47,7 @@ def futures(
47
47
  if contract_code not in SUPPORTED_FUTURES:
48
48
  raise ValueError("Futures contract not supported.")
49
49
 
50
- trade_date = dc.convert_date(trade_date)
50
+ trade_date = dc.convert_input_dates(trade_date)
51
51
 
52
52
  # First, try to fetch historical data for the specified date
53
53
  df = fetch_historical_df(contract_code, trade_date)
@@ -13,7 +13,7 @@ def indicator(
13
13
  indicator_type: Literal["IPCA_MR", "SELIC_TARGET", "SELIC_OVER", "DI", "VNA_LFT"],
14
14
  reference_date: str | pd.Timestamp,
15
15
  ) -> float:
16
- reference_date = dc.convert_date(reference_date)
16
+ reference_date = dc.convert_input_dates(reference_date)
17
17
  ind_type = str(indicator_type).upper()
18
18
  if ind_type == "IPCA_MR":
19
19
  return ipca_monthly_rate(reference_date)
@@ -0,0 +1,82 @@
1
+ import datetime as dt
2
+ import re
3
+ from typing import overload
4
+
5
+ import numpy as np
6
+ import pandas as pd
7
+
8
+ type ScalarDateTypes = str | np.datetime64 | pd.Timestamp | dt.datetime | dt.date
9
+ type PandasArrayDateTypes = pd.Series | pd.DatetimeIndex | pd.Index
10
+ type ArrayDateTypes = PandasArrayDateTypes | np.ndarray | list | tuple
11
+
12
+
13
+ def validate_year_format(value):
14
+ """If the input date is a string, it validates the year format."""
15
+ if not isinstance(value, str):
16
+ return
17
+
18
+ value = value.strip().lower()
19
+ if value in {"today", "now"}:
20
+ return
21
+
22
+ if re.match(r"^\d{4}-", value):
23
+ error_msg = f"Invalid format: {value}. Day first format is required. Example: '31-05-2024'." # noqa
24
+ raise ValueError(error_msg)
25
+
26
+
27
+ def check_first_value(dates):
28
+ match dates:
29
+ case None:
30
+ raise ValueError("Date cannot be None.")
31
+ case str():
32
+ validate_year_format(dates)
33
+ case list() | tuple() | np.ndarray() | pd.Series():
34
+ validate_year_format(dates[0])
35
+
36
+
37
+ @overload
38
+ def convert_input_dates(dates: ScalarDateTypes) -> pd.Timestamp: ...
39
+ @overload
40
+ def convert_input_dates(dates: ArrayDateTypes) -> pd.Series: ...
41
+
42
+
43
+ def convert_input_dates(
44
+ dates: ScalarDateTypes | ArrayDateTypes,
45
+ ) -> pd.Timestamp | pd.Series:
46
+ check_first_value(dates)
47
+
48
+ result = pd.to_datetime(dates, dayfirst=True)
49
+
50
+ match result:
51
+ case pd.Timestamp():
52
+ result = result.normalize()
53
+ case dt.datetime() | dt.date() | np.datetime64():
54
+ result = pd.Timestamp(result).normalize()
55
+ case pd.Series():
56
+ result = result.astype("datetime64[ns]")
57
+ case pd.DatetimeIndex():
58
+ result = pd.Series(result).astype("datetime64[ns]")
59
+ case None:
60
+ raise ValueError("Date cannot be None.")
61
+ case _:
62
+ raise ValueError("Invalid date input type.")
63
+
64
+ return result
65
+
66
+
67
+ def convert_to_numpy_date(
68
+ dates: pd.Timestamp | pd.Series,
69
+ ) -> np.datetime64 | np.ndarray:
70
+ """
71
+ Converts the input dates to a numpy datetime64[D] format.
72
+
73
+ Args:
74
+ dates (Timestamp | Series): A single date or a Series of dates.
75
+
76
+ Returns:
77
+ np.datetime64 | np.ndarray: The input dates in a numpy datetime64[D] format.
78
+ """
79
+ if isinstance(dates, pd.Timestamp):
80
+ return np.datetime64(dates, "D")
81
+ else:
82
+ return dates.to_numpy().astype("datetime64[D]")
@@ -32,7 +32,7 @@ dependencies = [
32
32
  "types-requests",
33
33
  ]
34
34
  dynamic = []
35
- version = "0.19.2"
35
+ version = "0.19.3"
36
36
 
37
37
  [project.license]
38
38
  file = "LICENSE"
@@ -1 +0,0 @@
1
- __version__ = "0.19.2"
@@ -1,65 +0,0 @@
1
- import datetime as dt
2
- import re
3
-
4
- import numpy as np
5
- import pandas as pd
6
-
7
-
8
- def _starts_with_year(date_str: str) -> bool:
9
- """Check if the date string starts with a four-digit year."""
10
- if date_str in {"today", "now"}:
11
- return False
12
- else:
13
- return bool(re.match(r"^\d{4}-", date_str))
14
-
15
-
16
- def convert_date(
17
- input_date: str | pd.Timestamp | np.datetime64 | dt.date | dt.datetime,
18
- ) -> pd.Timestamp:
19
- """
20
- Convert a date to pandas Timestamp adjusted to midnight.
21
-
22
- Args:
23
- reference_date (str | pd.Timestamp | np.datetime64): The date to convert.
24
- If str, it should be with day first format (e.g. "31-05-2024").
25
-
26
- Returns:
27
- pd.Timestamp: A normalized pandas Timestamp.
28
-
29
- Raises:
30
- ValueError: If the input date format is invalid.
31
-
32
- Notes:
33
- - Normalization means setting the time component of the timestamp to midnight.
34
- - Business day calculations consider Brazilian holidays.
35
-
36
- Examples:
37
- >>> convert_date("31-05-2024")
38
- Timestamp('2024-05-31 00:00:00')
39
- """
40
- match input_date:
41
- case None:
42
- raise ValueError("Date cannot be None.")
43
- case str():
44
- if _starts_with_year(input_date):
45
- raise ValueError(
46
- f"Invalid date format: {input_date}. Day first format is required (e.g. '31-05-2024')." # noqa
47
- )
48
- # Parse the date string with day first format
49
- output_date = pd.to_datetime(input_date, dayfirst=True)
50
- case pd.Timestamp():
51
- # Use Timestamp as is
52
- output_date = input_date
53
- case np.datetime64():
54
- # Convert numpy datetime to pandas Timestamp
55
- output_date = pd.Timestamp(input_date)
56
- case dt.date():
57
- # Convert datetime.date to pandas Timestamp
58
- output_date = pd.Timestamp(input_date)
59
- case dt.datetime():
60
- # Convert datetime.datetime to pandas Timestamp
61
- output_date = pd.Timestamp(input_date)
62
- case _:
63
- raise ValueError(f"Date format not recognized: {input_date}")
64
- # Normalize the final date to midnight
65
- return output_date.normalize()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes