|
22 | 22 | from __future__ import print_function |
23 | 23 |
|
24 | 24 | import json as _json |
25 | | -import re |
26 | 25 | import warnings |
27 | 26 | from io import StringIO |
28 | 27 | from typing import Optional, Union |
29 | 28 | from urllib.parse import quote as urlencode |
30 | 29 |
|
31 | 30 | import pandas as pd |
32 | | -import pytz |
33 | 31 | import requests |
34 | 32 |
|
35 | 33 | from . import utils, cache |
@@ -599,20 +597,58 @@ def get_ticker_tz(): |
599 | 597 |
|
600 | 598 | # Parse earnings date string |
601 | 599 | cn = "Earnings Date" |
| 600 | + # - remove AM/PM and timezone from date string |
| 601 | + tzinfo = dates[cn].str.extract('([AP]M[a-zA-Z]*)$') |
| 602 | + dates[cn] = dates[cn].replace(' [AP]M[a-zA-Z]*$', '', regex=True) |
| 603 | + # - split AM/PM from timezone |
| 604 | + tzinfo = tzinfo[0].str.extract('([AP]M)([a-zA-Z]*)', expand=True) |
| 605 | + tzinfo.columns = ["AM/PM", "TZ"] |
| 606 | + # - combine and parse |
| 607 | + dates[cn] = dates[cn] + ' ' + tzinfo["AM/PM"] |
| 608 | + dates[cn] = pd.to_datetime(dates[cn], format="%b %d, %Y, %I %p") |
| 609 | + |
| 610 | + # Try to remap all ambiguous timezone values: |
| 611 | + tzinfo['TZ'] = tzinfo['TZ'].str.replace('BST', 'Europe/London') |
| 612 | + tzinfo['TZ'] = tzinfo['TZ'].str.replace('GMT', 'Europe/London') |
| 613 | + if '.' not in self.ticker: |
| 614 | + tzinfo['TZ'] = tzinfo['TZ'].str.replace('EST', 'America/New_York') |
| 615 | + elif self.ticker.endswith(".AX"): |
| 616 | + tzinfo['TZ'] = tzinfo['TZ'].str.replace('EST', 'Australia/Sydney') |
| 617 | + tzinfo['TZ'] = tzinfo['TZ'].str.replace('MST', 'America/Denver') |
| 618 | + tzinfo['TZ'] = tzinfo['TZ'].str.replace('PST', 'America/Los_Angeles') |
| 619 | + if'.' not in self.ticker: |
| 620 | + tzinfo['TZ'] = tzinfo['TZ'].str.replace('CST', 'America/Chicago') |
| 621 | + else: |
| 622 | + # Revisit if Cuba get a stock exchange |
| 623 | + tzinfo['TZ'] = tzinfo['TZ'].str.replace('CST', 'Asia/Shanghai') |
| 624 | + if self.ticker.endswith('.TA'): |
| 625 | + tzinfo['TZ'] = tzinfo['TZ'].str.replace('IST', 'Asia/Jerusalem') |
| 626 | + elif self.ticker.endswith('.IR'): |
| 627 | + tzinfo['TZ'] = tzinfo['TZ'].str.replace('IST', 'Europe/Dublin') |
| 628 | + elif self.ticker.endswith('.NS'): |
| 629 | + tzinfo['TZ'] = tzinfo['TZ'].str.replace('IST', 'Asia/Kolkata') |
| 630 | + |
| 631 | + # But in case still ambiguity that pytz cannot parse, have a backup: |
| 632 | + self._quote.proxy = proxy or self.proxy |
| 633 | + tz_backup = self._get_ticker_tz(proxy=proxy, timeout=30) |
602 | 634 |
|
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, "") |
| 635 | + if len(tzinfo['TZ'].unique())==1: |
608 | 636 | 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) |
| 637 | + dates[cn] = dates[cn].dt.tz_localize(tzinfo['TZ'].iloc[0]) |
| 638 | + except Exception: |
| 639 | + dates[cn] = dates[cn].dt.tz_localize(tz_backup) |
| 640 | + else: |
| 641 | + dates2 = [] |
| 642 | + for i in range(len(dates)): |
| 643 | + dt = dates[cn].iloc[i] |
| 644 | + tz = tzinfo['TZ'].iloc[i] |
| 645 | + try: |
| 646 | + dt = dt.tz_localize(tz) |
| 647 | + except Exception: |
| 648 | + dt = dt.tz_localize(tz_backup) |
| 649 | + dates2.append(dt) |
| 650 | + dates[cn] = pd.to_datetime(dates2) |
614 | 651 |
|
615 | | - dates[cn] = dates[cn].map(map_date) |
616 | 652 | dates = dates.set_index("Earnings Date") |
617 | 653 |
|
618 | 654 | self._earnings_dates[limit] = dates |
|
0 commit comments