|
21 | 21 |
|
22 | 22 | from __future__ import print_function |
23 | 23 |
|
24 | | -from io import StringIO |
25 | 24 | import json as _json |
| 25 | +import re |
26 | 26 | import warnings |
| 27 | +from io import StringIO |
27 | 28 | from typing import Optional, Union |
28 | 29 | from urllib.parse import quote as urlencode |
29 | 30 |
|
30 | 31 | import pandas as pd |
| 32 | +import pytz |
31 | 33 | import requests |
32 | 34 |
|
33 | 35 | from . import utils, cache |
| 36 | +from .const import _BASE_URL_, _ROOT_URL_ |
34 | 37 | from .data import YfData |
35 | 38 | from .exceptions import YFEarningsDateMissing |
36 | 39 | from .scrapers.analysis import Analysis |
37 | 40 | from .scrapers.fundamentals import Fundamentals |
| 41 | +from .scrapers.history import PriceHistory |
38 | 42 | from .scrapers.holders import Holders |
39 | 43 | from .scrapers.quote import Quote, FastInfo |
40 | | -from .scrapers.history import PriceHistory |
41 | | - |
42 | | -from .const import _BASE_URL_, _ROOT_URL_ |
43 | 44 |
|
44 | 45 |
|
45 | 46 | class TickerBase: |
@@ -534,6 +535,15 @@ def get_earnings_dates(self, limit=12, proxy=None) -> Optional[pd.DataFrame]: |
534 | 535 |
|
535 | 536 | logger = utils.get_yf_logger() |
536 | 537 |
|
| 538 | + ticker_tz = "" |
| 539 | + |
| 540 | + def get_ticker_tz(): |
| 541 | + nonlocal ticker_tz |
| 542 | + if ticker_tz == "": |
| 543 | + self._quote.proxy = proxy or self.proxy |
| 544 | + ticker_tz = self._get_ticker_tz(proxy=proxy, timeout=30) |
| 545 | + return ticker_tz |
| 546 | + |
537 | 547 | page_size = min(limit, 100) # YF caps at 100, don't go higher |
538 | 548 | page_offset = 0 |
539 | 549 | dates = None |
@@ -589,20 +599,20 @@ def get_earnings_dates(self, limit=12, proxy=None) -> Optional[pd.DataFrame]: |
589 | 599 |
|
590 | 600 | # Parse earnings date string |
591 | 601 | cn = "Earnings Date" |
592 | | - # - remove AM/PM and timezone from date string |
593 | | - tzinfo = dates[cn].str.extract('([AP]M[a-zA-Z]*)$') |
594 | | - dates[cn] = dates[cn].replace(' [AP]M[a-zA-Z]*$', '', regex=True) |
595 | | - # - split AM/PM from timezone |
596 | | - tzinfo = tzinfo[0].str.extract('([AP]M)([a-zA-Z]*)', expand=True) |
597 | | - tzinfo.columns = ["AM/PM", "TZ"] |
598 | | - # - combine and parse |
599 | | - dates[cn] = dates[cn] + ' ' + tzinfo["AM/PM"] |
600 | | - dates[cn] = pd.to_datetime(dates[cn], format="%b %d, %Y, %I %p") |
601 | | - # - instead of attempting decoding of ambiguous timezone abbreviation, just use 'info': |
602 | | - self._quote.proxy = proxy or self.proxy |
603 | | - tz = self._get_ticker_tz(proxy=proxy, timeout=30) |
604 | | - dates[cn] = dates[cn].dt.tz_localize(tz) |
605 | 602 |
|
| 603 | + def map_date(time_str: str): |
| 604 | + tz_match = re.search('([AP]M)([a-zA-Z]*)$', time_str) |
| 605 | + tz_str = tz_match.group(2).strip() |
| 606 | + # - remove AM/PM and timezone from date string |
| 607 | + time_str = time_str.replace(tz_str, "") |
| 608 | + try: |
| 609 | + tz = pytz.timezone(tz_str) |
| 610 | + except pytz.UnknownTimeZoneError: |
| 611 | + tz = get_ticker_tz() |
| 612 | + |
| 613 | + return pd.to_datetime(time_str, format="%b %d, %Y, %I %p").tz_localize(tz) |
| 614 | + |
| 615 | + dates[cn] = dates[cn].map(map_date) |
606 | 616 | dates = dates.set_index("Earnings Date") |
607 | 617 |
|
608 | 618 | self._earnings_dates[limit] = dates |
|
0 commit comments