{"action":"created","comment":{"url":"https://api.github.com/repos/Microsoft/Recognizers-Text/pulls/comments/195236293","pull_request_review_id":128565039,"id":195236293,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDE5NTIzNjI5Mw==","diff_hunk":"@@ -0,0 +1,768 @@\n+from abc import ABC, abstractmethod\n+from typing import List, Optional, Pattern, Dict\n+from datetime import datetime, timedelta\n+import calendar\n+from datedelta import datedelta\n+import regex\n+\n+from recognizers_text.extractor import ExtractResult\n+from recognizers_text.utilities import RegExpUtility\n+from recognizers_number import BaseNumberExtractor, BaseNumberParser\n+from recognizers_number.number import Constants as NumberConstants\n+from .constants import Constants, TimeTypeConstants\n+from .extractors import DateTimeExtractor\n+from .parsers import DateTimeParser, DateTimeParseResult\n+from .utilities import DateTimeUtilityConfiguration, Token, merge_all_tokens, get_tokens_from_regex, DateUtils, AgoLaterUtil, FormatUtil, DateTimeResolutionResult, DayOfWeek, AgoLaterMode\n+\n+class DateExtractorConfiguration(ABC):\n+    @property\n+    @abstractmethod\n+    def date_regex_list(self) -> List[Pattern]:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def implicit_date_list(self) -> List[Pattern]:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def month_end(self) -> Pattern:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def of_month(self) -> Pattern:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def date_unit_regex(self) -> Pattern:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def for_the_regex(self) -> Pattern:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def week_day_and_day_of_month_regex(self) -> Pattern:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def relative_month_regex(self) -> Pattern:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def week_day_regex(self) -> Pattern:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def day_of_week(self) -> Dict[str, int]:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def ordinal_extractor(self) -> BaseNumberExtractor:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def integer_extractor(self) -> BaseNumberExtractor:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def number_parser(self) -> BaseNumberParser:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def duration_extractor(self) -> DateTimeExtractor:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def utility_configuration(self) -> DateTimeUtilityConfiguration:\n+        raise NotImplementedError\n+\n+class BaseDateExtractor(DateTimeExtractor):\n+    @property\n+    def extractor_type_name(self) -> str:\n+        return Constants.SYS_DATETIME_DATE\n+\n+    def __init__(self, config: DateExtractorConfiguration):\n+        self.config = config\n+\n+    def extract(self, source: str, reference: datetime = None) -> List[ExtractResult]:\n+        if reference is None:\n+            reference = datetime.now()\n+\n+        tokens = self.basic_regex_match(source)\n+        tokens.extend(self.implicit_date(source))\n+        tokens.extend(self.number_with_month(source, reference))\n+        tokens.extend(self.duration_with_before_and_after(source, reference))\n+\n+        result = merge_all_tokens(tokens, source, self.extractor_type_name)\n+        return result\n+\n+    def basic_regex_match(self, source: str) -> List[Token]:\n+        ret: List[Token] = list()\n+        for regexp in self.config.date_regex_list:\n+            ret.extend(get_tokens_from_regex(regexp, source))\n+\n+        return ret\n+\n+    def implicit_date(self, source: str) -> List[Token]:\n+        ret: List[Token] = list()\n+        for regexp in self.config.implicit_date_list:\n+            ret.extend(get_tokens_from_regex(regexp, source))\n+\n+        return ret\n+\n+    def number_with_month(self, source: str, reference: datetime) -> List[Token]:\n+        ret: List[Token] = list()\n+        extract_results = self.config.ordinal_extractor.extract(source)\n+        extract_results.extend(self.config.integer_extractor.extract(source))\n+\n+        for result in extract_results:\n+            num = int(self.config.number_parser.parse(result).value)\n+            if num < 1 or num > 31:\n+                continue\n+            if result.start >= 0:\n+                front_string = source[0:result.start or 0]\n+                match = regex.search(self.config.month_end, front_string)\n+                if match is not None:\n+                    ret.append(Token(match.start(), match.end() + result.length))\n+                    continue\n+\n+                # handling cases like 'for the 25th'\n+                matches = regex.finditer(self.config.for_the_regex, source)\n+                is_found = False\n+                for match_case in matches:\n+                    if match_case is not None:\n+                        ordinal_num = RegExpUtility.get_group(match_case, 'DayOfMonth')\n+                        if ordinal_num == result.text:\n+                            length = len(RegExpUtility.get_group(match_case, 'end'))\n+                            ret.append(Token(match_case.start(), match_case.end() - length))\n+                            is_found = True\n+\n+                if is_found:\n+                    continue\n+\n+                # handling cases like 'Thursday the 21st', which both 'Thursday' and '21st' refer to a same date\n+                matches = regex.finditer(self.config.week_day_and_day_of_month_regex, source)\n+                for match_case in matches:\n+                    if match_case is not None:\n+                        ordinal_num = RegExpUtility.get_group(match_case, 'DayOfMonth')\n+                        if ordinal_num == result.text:\n+                            month = reference.month\n+                            year = reference.year\n+\n+                            # get week of day for the ordinal number which is regarded as a date of reference month\n+                            date = DateUtils.safe_create_from_min_value(year, month, num)\n+                            num_week_day_str: str = calendar.day_name[date.weekday()].lower()\n+\n+                            # get week day from text directly, compare it with the weekday generated above\n+                            # to see whether they refer to a same week day\n+                            extracted_week_day_str = RegExpUtility.get_group(match_case, 'weekday').lower()\n+                            if (date != DateUtils.min_value and\n+                                    self.config.day_of_week[num_week_day_str] ==\n+                                    self.config.day_of_week[extracted_week_day_str]):\n+                                ret.append(\n+                                    Token(match_case.start(), match_case.end()))\n+                                is_found = True\n+\n+                if is_found:\n+                    continue\n+\n+                # handling cases like '20th of next month'\n+                suffix_str: str = source[result.start + result.length:].lower()\n+                match = regex.match(self.config.relative_month_regex, suffix_str.strip())\n+                space_len = len(suffix_str) - len(suffix_str.strip())\n+\n+                if match is not None and match.start() == 0:\n+                    ret.append(\n+                        Token(result.start, result.start + result.length + space_len + len(match.group())))\n+\n+                # handling cases like 'second Sunday'\n+                match = regex.match(\n+                    self.config.week_day_regex, suffix_str.strip())\n+                if (match is not None and match.start() == 0 and\n+                        num >= 1 and num <= 5 and\n+                        result.type == NumberConstants.SYS_NUM_ORDINAL):\n+                    week_day_str = RegExpUtility.get_group(match, 'weekday')\n+                    if week_day_str in self.config.day_of_week:\n+                        ret.append(\n+                            Token(result.start, result.start + result.length + space_len + len(match.group())))\n+\n+            if result.start + result.length < len(source):\n+                after_string = source[result.start + result.length:]\n+                match = regex.match(self.config.of_month, after_string)\n+                if match is not None:\n+                    ret.append(Token(result.start, result.start + result.length + len(match.group())))\n+\n+        return ret\n+\n+    def duration_with_before_and_after(self, source: str, reference: datetime) -> List[Token]:\n+        ret: List[Token] = list()\n+        duration_results = self.config.duration_extractor.extract(source, reference)\n+\n+        for result in duration_results:\n+            match = regex.search(self.config.date_unit_regex, result.text)\n+            if match is None:\n+                continue\n+\n+            ret = AgoLaterUtil.extractor_duration_with_before_and_after(source, result, ret, self.config.utility_configuration)\n+\n+        return ret\n+\n+class DateParserConfiguration(ABC):\n+    @property\n+    @abstractmethod\n+    def ordinal_extractor(self) -> BaseNumberExtractor:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def integer_extractor(self) -> BaseNumberExtractor:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def cardinal_extractor(self) -> BaseNumberExtractor:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def duration_extractor(self) -> DateTimeExtractor:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def duration_parser(self) -> DateTimeParser:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def number_parser(self) -> BaseNumberParser:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def month_of_year(self) -> Dict[str, int]:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def day_of_month(self) -> Dict[str, int]:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def day_of_week(self) -> Dict[str, int]:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def unit_map(self) -> Dict[str, str]:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def cardinal_map(self) -> Dict[str, int]:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def date_regex(self) -> List[Pattern]:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def on_regex(self) -> Pattern:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def special_day_regex(self) -> Pattern:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def next_regex(self) -> Pattern:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def unit_regex(self) -> Pattern:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def month_regex(self) -> Pattern:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def week_day_regex(self) -> Pattern:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def last_regex(self) -> Pattern:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def this_regex(self) -> Pattern:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def week_day_of_month_regex(self) -> Pattern:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def for_the_regex(self) -> Pattern:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def week_day_and_day_of_month_regex(self) -> Pattern:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def relative_month_regex(self) -> Pattern:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def utility_configuration(self) -> DateTimeUtilityConfiguration:\n+        raise NotImplementedError\n+\n+    @property\n+    @abstractmethod\n+    def date_token_prefix(self) -> str:\n+        raise NotImplementedError\n+\n+    @abstractmethod\n+    def get_swift_day(self, source: str) -> int:\n+        raise NotImplementedError\n+\n+    @abstractmethod\n+    def get_swift_month(self, source: str) -> int:\n+        raise NotImplementedError\n+\n+    @abstractmethod\n+    def is_cardinal_last(self, source: str) -> bool:\n+        raise NotImplementedError\n+\n+class BaseDateParser(DateTimeParser):\n+    @property\n+    def parser_type_name(self) -> str:\n+        return Constants.SYS_DATETIME_DATE\n+\n+    def __init__(self, config: DateParserConfiguration):\n+        self.config = config\n+\n+    def parse(self, source: ExtractResult, reference: datetime = None) -> Optional[DateTimeParseResult]:\n+        if reference is None:\n+            reference = datetime.now()\n+        result_value: DateTimeParseResult = None\n+        if source.type is self.parser_type_name:\n+            source_text = source.text.lower()\n+            inner_result = self.parse_basic_regex_match(source_text, reference)\n+            if not inner_result.success:\n+                inner_result = self.parse_implicit_date(source_text, reference)\n+            if not inner_result.success:\n+                inner_result = self.parse_weekday_of_month(source_text, reference)\n+            if not inner_result.success:\n+                inner_result = self.parser_duration_with_ago_and_later(source_text, reference)\n+            if not inner_result.success:\n+                inner_result = self.parse_number_with_month(source_text, reference)\n+            if not inner_result.success:\n+                inner_result = self.parse_single_number(source_text, reference)\n+            if inner_result.success:\n+                inner_result.future_resolution: Dict[str, str] = dict()\n+                inner_result.future_resolution[TimeTypeConstants.DATE] = FormatUtil.format_date(inner_result.future_value)\n+                inner_result.past_resolution: Dict[str, str] = dict()\n+                inner_result.past_resolution[TimeTypeConstants.DATE] = FormatUtil.format_date(inner_result.past_value)\n+                result_value = inner_result\n+\n+        result = DateTimeParseResult(source)\n+        result.value = result_value\n+        result.timex_str = result_value.timex if result_value is not None else ''\n+        result.resolution_str = ''\n+\n+        return result\n+\n+    def parse_basic_regex_match(self, source: str, reference: datetime) -> DateTimeParseResult:\n+        trimmed_source = source.strip()\n+        result = DateTimeResolutionResult()\n+        for regexp in self.config.date_regex:\n+            offset = 0\n+            match = regex.search(regexp, trimmed_source)\n+            if match is None:\n+                match = regex.search(regexp, self.config.date_token_prefix + trimmed_source)\n+                offset = len(self.config.date_token_prefix)\n+            if match and match.start() == offset and len(match.group()) == len(trimmed_source):\n+                result = self.match_to_date(match, reference)\n+                break\n+\n+        return result\n+\n+    def match_to_date(self, match, reference: datetime)-> DateTimeResolutionResult:\n+        result = DateTimeResolutionResult()\n+        year_str = RegExpUtility.get_group(match, 'year')\n+        month_str = RegExpUtility.get_group(match, 'month')\n+        day_str = RegExpUtility.get_group(match, 'day')\n+        month = 0\n+        day = 0\n+        year = 0\n+        if month_str in self.config.month_of_year and day_str in self.config.day_of_month:\n+            month = self.config.month_of_year.get(month_str)\n+            day = self.config.day_of_month.get(day_str)\n+            if year_str:\n+                year = int(year_str) if year_str.isnumeric() else 0\n+                if year < 100 and year >= 90:\n+                    year += 1900\n+                elif year < 100 and year < 20:\n+                    year += 2000\n+\n+        no_year = False\n+        if year == 0:\n+            year = reference.year\n+            result.timex = FormatUtil.luis_date(-1, month, day)\n+            no_year = True\n+        else:\n+            result.timex = FormatUtil.luis_date(year, month, day)\n+\n+        future_date = DateUtils.safe_create_from_min_value(year, month, day)\n+        past_date = DateUtils.safe_create_from_min_value(year, month, day)\n+        if no_year and future_date < reference:\n+            future_date = DateUtils.safe_create_from_min_value(year + 1, month, day)\n+        if no_year and past_date >= reference:\n+            past_date = DateUtils.safe_create_from_min_value(year - 1, month, day)\n+\n+        result.future_value = future_date\n+        result.past_value = past_date\n+        result.success = True\n+        return result\n+\n+    def parse_implicit_date(self, source: str, reference: datetime) -> DateTimeParseResult:\n+        trimmed_source = source.strip()\n+        result = DateTimeResolutionResult()\n+\n+        # handle \"on 12\"\n+        match = regex.search(self.config.on_regex, self.config.date_token_prefix + trimmed_source)\n+        if match and match.start() == len(self.config.date_token_prefix) and len(match.group()) == len(trimmed_source):\n+            day = 0\n+            month = reference.month\n+            year = reference.year\n+            day_str = match.group('day')\n+            day = self.config.day_of_month.get(day_str)\n+\n+            result.timex = FormatUtil.luis_date(-1, -1, day)\n+\n+            try_str = FormatUtil.luis_date(year, month, day)\n+            try_date = datetime.strptime(try_str, '%Y-%m-%d')\n+            future_date: datetime\n+            past_date: datetime\n+\n+            if try_date:\n+                future_date = DateUtils.safe_create_from_min_value(year, month, day)\n+                past_date = DateUtils.safe_create_from_min_value(year, month, day)\n+                if future_date < reference:\n+                    future_date += datedelta(months=1)\n+\n+                if past_date >= reference:\n+                    past_date += datedelta(months=-1)\n+            else:\n+                future_date = DateUtils.safe_create_from_min_value(year, month + 1, day)\n+                past_date = DateUtils.safe_create_from_min_value(year, month - 1, day)\n+\n+            result.future_value = future_date\n+            result.past_value = past_date\n+            result.success = True\n+            return result\n+\n+        # handle \"today\", \"the day before yesterday\"\n+        match = regex.match(self.config.special_day_regex, trimmed_source)\n+        if match and match.start() == 0 and len(match.group()) == len(trimmed_source):\n+            swift = self.config.get_swift_day(match.group())\n+            value = reference + timedelta(days=swift)\n+            result.timex = FormatUtil.luis_date_from_datetime(value)\n+            result.future_value = value\n+            result.past_value = value\n+            result.success = True\n+            return result\n+\n+        # handle \"next Sunday\"\n+        match = regex.match(self.config.next_regex, trimmed_source)\n+        if match and match.start() == 0 and len(match.group()) == len(trimmed_source):\n+            weekday_str = match.group('weekday')\n+            value = DateUtils.next(reference, self.config.day_of_week.get(weekday_str))\n+\n+            result.timex = FormatUtil.luis_date_from_datetime(value)\n+            result.future_value = value\n+            result.past_value = value\n+            result.success = True\n+            return result\n+\n+        # handle \"this Friday\"\n+        match = regex.match(self.config.this_regex, trimmed_source)\n+        if match and match.start() == 0 and len(match.group()) == len(trimmed_source):\n+            weekday_str = match.group('weekday')\n+            value = DateUtils.this(reference, self.config.day_of_week.get(weekday_str))\n+\n+            result.timex = FormatUtil.luis_date_from_datetime(value)\n+            result.future_value = value\n+            result.past_value = value\n+            result.success = True\n+            return result\n+\n+        # handle \"last Friday\", \"last mon\"\n+        match = regex.match(self.config.last_regex, trimmed_source)\n+        if match and match.start() == 0 and len(match.group()) == len(trimmed_source):\n+            weekday_str = match.group('weekday')\n+            value = DateUtils.last(reference, self.config.day_of_week.get(weekday_str))\n+\n+            result.timex = FormatUtil.luis_date_from_datetime(value)\n+            result.future_value = value\n+            result.past_value = value\n+            result.success = True\n+            return result\n+\n+        # handle \"Friday\"\n+        match = regex.match(self.config.week_day_regex, trimmed_source)\n+        if match and match.start() == 0 and len(match.group()) == len(trimmed_source):\n+            weekday_str = match.group('weekday')\n+            weekday = self.config.day_of_week.get(weekday_str)\n+            value = DateUtils.this(reference, weekday)\n+\n+            if weekday < int(DayOfWeek.Monday):\n+                weekday = int(DayOfWeek.Sunday)\n+            if weekday < reference.isoweekday():\n+                value = DateUtils.next(reference, weekday)\n+            result.timex = 'XXXX-WXX-' + str(weekday)\n+            future_date = value\n+            past_date = value\n+            if future_date < reference:\n+                future_date += timedelta(weeks=1)\n+            if past_date >= reference:\n+                past_date -= timedelta(weeks=1)\n+\n+            result.future_value = future_date\n+            result.past_value = past_date\n+            result.success = True\n+            return result\n+\n+        # handle \"for the 27th.\"\n+        match = regex.match(self.config.for_the_regex, trimmed_source)\n+        if match:\n+            day_str = match.group('DayOfMonth')\n+            er = ExtractResult.get_from_text(day_str)\n+            day = int(self.config.number_parser.parse(er).value)\n+\n+            month = reference.month\n+            year = reference.year\n+\n+            result.timex = FormatUtil.luis_date(-1, -1, day)\n+            date = datetime(year, month, day)\n+            result.future_value = date\n+            result.past_value = date\n+            result.success = True\n+\n+            return result\n+\n+        # handling cases like 'Thursday the 21st', which both 'Thursday' and '21st' refer to a same date\n+        match = regex.match(self.config.week_day_and_day_of_month_regex, trimmed_source)\n+        if match:\n+            day_str = match.group('DayOfMonth')\n+            er = ExtractResult.get_from_text(day_str)\n+            day = int(self.config.number_parser.parse(er).value)\n+            month = reference.month\n+            year = reference.year\n+\n+            # the validity of the phrase is guaranteed in the Date Extractor\n+            result.timex = FormatUtil.luis_date(year, month, day)\n+            date = datetime(year, month, day)\n+            result.future_value = date\n+            result.past_value = date\n+            result.success = True\n+\n+            return result\n+\n+        return result\n+\n+    def parse_weekday_of_month(self, source: str, reference: datetime) -> DateTimeParseResult:\n+        trimmed_source = source.strip()\n+        result = DateTimeResolutionResult()\n+        match = regex.match(self.config.week_day_of_month_regex, trimmed_source)\n+        if not match:\n+            return result\n+        cardinal_str = RegExpUtility.get_group(match, 'cardinal')\n+        weekday_str = RegExpUtility.get_group(match, 'weekday')\n+        month_str = RegExpUtility.get_group(match, 'month')\n+        no_year = False\n+        cardinal = 5 if self.config.is_cardinal_last(cardinal_str) else self.config.cardinal_map.get(cardinal_str)\n+        weekday = self.config.day_of_week.get(weekday_str)\n+        month = reference.month\n+        year = reference.year\n+\n+        if not month_str:\n+            swift = self.config.get_swift_month(trimmed_source)\n+            temp = reference.replace(month=reference.month + swift)\n+            month = temp.month\n+            year = temp.year\n+        else:\n+            month = self.config.month_of_year.get(month_str)\n+            no_year = True\n+\n+        value = self._compute_date(cardinal, weekday, month, year)\n+\n+        if value.month != month:\n+            cardinal -= 1\n+            value = value.replace(day=value.day - 7)\n+\n+        future_date = value\n+        past_date = value\n+        if no_year and future_date < reference:\n+            future_date = self._compute_date(cardinal, weekday, month, year + 1)\n+            if future_date.month != month:\n+                future_date = future_date.replace(day=future_date.day - 7)\n+\n+        if no_year and past_date >= reference:\n+            past_date = self._compute_date(cardinal, weekday, month, year - 1)\n+            if past_date.month != month:\n+                past_date = past_date.replace(day=past_date.date - 7)\n+\n+        result.timex = '-'.join(['XXXX', FormatUtil.to_str(month, 2), 'WXX', str(weekday), '#' + str(cardinal)])\n+        result.future_value = future_date\n+        result.past_value = past_date\n+        result.success = True\n+        return result\n+\n+    def _compute_date(self, cardinal: int, weekday: DayOfWeek, month: int, year: int):\n+        first_day = datetime(year, month, 1)","path":"Python/libraries/recognizers-date-time/recognizers_date_time/date_time/base_date.py","position":691,"original_position":654,"commit_id":"e252eedfc05334bb7d731e74f7077e680e2dabf7","original_commit_id":"00f11e7391134c7e23a612776f25efc44d7fccb1","user":{"login":"enzocanoo","id":12394167,"node_id":"MDQ6VXNlcjEyMzk0MTY3","avatar_url":"https://avatars1.githubusercontent.com/u/12394167?v=4","gravatar_id":"","url":"https://api.github.com/users/enzocanoo","html_url":"https://github.com/enzocanoo","followers_url":"https://api.github.com/users/enzocanoo/followers","following_url":"https://api.github.com/users/enzocanoo/following{/other_user}","gists_url":"https://api.github.com/users/enzocanoo/gists{/gist_id}","starred_url":"https://api.github.com/users/enzocanoo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/enzocanoo/subscriptions","organizations_url":"https://api.github.com/users/enzocanoo/orgs","repos_url":"https://api.github.com/users/enzocanoo/repos","events_url":"https://api.github.com/users/enzocanoo/events{/privacy}","received_events_url":"https://api.github.com/users/enzocanoo/received_events","type":"User","site_admin":false},"body":"Python does not have the concept of private variables. However, there is a convention to treat functions with an underscore as a non-public functions. [(more information)](https://docs.python.org/3.5/tutorial/classes.html#private-variables)","created_at":"2018-06-13T21:07:16Z","updated_at":"2018-06-13T21:07:16Z","html_url":"https://github.com/Microsoft/Recognizers-Text/pull/616#discussion_r195236293","pull_request_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/pulls/616","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/Microsoft/Recognizers-Text/pulls/comments/195236293"},"html":{"href":"https://github.com/Microsoft/Recognizers-Text/pull/616#discussion_r195236293"},"pull_request":{"href":"https://api.github.com/repos/Microsoft/Recognizers-Text/pulls/616"}},"in_reply_to_id":194969155},"pull_request":{"url":"https://api.github.com/repos/Microsoft/Recognizers-Text/pulls/616","id":194425433,"node_id":"MDExOlB1bGxSZXF1ZXN0MTk0NDI1NDMz","html_url":"https://github.com/Microsoft/Recognizers-Text/pull/616","diff_url":"https://github.com/Microsoft/Recognizers-Text/pull/616.diff","patch_url":"https://github.com/Microsoft/Recognizers-Text/pull/616.patch","issue_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/issues/616","number":616,"state":"open","locked":false,"title":"[Python] Port Datetime Recognizer to Python","user":{"login":"amitstein","id":4796555,"node_id":"MDQ6VXNlcjQ3OTY1NTU=","avatar_url":"https://avatars0.githubusercontent.com/u/4796555?v=4","gravatar_id":"","url":"https://api.github.com/users/amitstein","html_url":"https://github.com/amitstein","followers_url":"https://api.github.com/users/amitstein/followers","following_url":"https://api.github.com/users/amitstein/following{/other_user}","gists_url":"https://api.github.com/users/amitstein/gists{/gist_id}","starred_url":"https://api.github.com/users/amitstein/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/amitstein/subscriptions","organizations_url":"https://api.github.com/users/amitstein/orgs","repos_url":"https://api.github.com/users/amitstein/repos","events_url":"https://api.github.com/users/amitstein/events{/privacy}","received_events_url":"https://api.github.com/users/amitstein/received_events","type":"User","site_admin":false},"body":"This PR includes Datetime Recognizer for English ported to python.","created_at":"2018-06-12T22:29:09Z","updated_at":"2018-06-13T21:07:16Z","closed_at":null,"merged_at":null,"merge_commit_sha":"0a694a7482c3b5a31cd471dc94891f5291923df3","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/pulls/616/commits","review_comments_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/pulls/616/comments","review_comment_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/pulls/comments{/number}","comments_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/issues/616/comments","statuses_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/statuses/e252eedfc05334bb7d731e74f7077e680e2dabf7","head":{"label":"southworkscom:python-datetime","ref":"python-datetime","sha":"e252eedfc05334bb7d731e74f7077e680e2dabf7","user":{"login":"southworkscom","id":2643983,"node_id":"MDEyOk9yZ2FuaXphdGlvbjI2NDM5ODM=","avatar_url":"https://avatars0.githubusercontent.com/u/2643983?v=4","gravatar_id":"","url":"https://api.github.com/users/southworkscom","html_url":"https://github.com/southworkscom","followers_url":"https://api.github.com/users/southworkscom/followers","following_url":"https://api.github.com/users/southworkscom/following{/other_user}","gists_url":"https://api.github.com/users/southworkscom/gists{/gist_id}","starred_url":"https://api.github.com/users/southworkscom/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/southworkscom/subscriptions","organizations_url":"https://api.github.com/users/southworkscom/orgs","repos_url":"https://api.github.com/users/southworkscom/repos","events_url":"https://api.github.com/users/southworkscom/events{/privacy}","received_events_url":"https://api.github.com/users/southworkscom/received_events","type":"Organization","site_admin":false},"repo":{"id":98216957,"node_id":"MDEwOlJlcG9zaXRvcnk5ODIxNjk1Nw==","name":"Recognizers-Text","full_name":"southworkscom/Recognizers-Text","owner":{"login":"southworkscom","id":2643983,"node_id":"MDEyOk9yZ2FuaXphdGlvbjI2NDM5ODM=","avatar_url":"https://avatars0.githubusercontent.com/u/2643983?v=4","gravatar_id":"","url":"https://api.github.com/users/southworkscom","html_url":"https://github.com/southworkscom","followers_url":"https://api.github.com/users/southworkscom/followers","following_url":"https://api.github.com/users/southworkscom/following{/other_user}","gists_url":"https://api.github.com/users/southworkscom/gists{/gist_id}","starred_url":"https://api.github.com/users/southworkscom/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/southworkscom/subscriptions","organizations_url":"https://api.github.com/users/southworkscom/orgs","repos_url":"https://api.github.com/users/southworkscom/repos","events_url":"https://api.github.com/users/southworkscom/events{/privacy}","received_events_url":"https://api.github.com/users/southworkscom/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/southworkscom/Recognizers-Text","description":"Microsoft.Recognizers.Text.Number provides robust recognition and resolution of numbers and date/time expressed in multiple languages. Full support for English, Spanish, and Chinese. Partial for French and Portuguese. Contributions are greatly welcome.","fork":true,"url":"https://api.github.com/repos/southworkscom/Recognizers-Text","forks_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/forks","keys_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/keys{/key_id}","collaborators_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/teams","hooks_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/hooks","issue_events_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/issues/events{/number}","events_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/events","assignees_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/assignees{/user}","branches_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/branches{/branch}","tags_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/tags","blobs_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/git/refs{/sha}","trees_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/git/trees{/sha}","statuses_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/statuses/{sha}","languages_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/languages","stargazers_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/stargazers","contributors_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/contributors","subscribers_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/subscribers","subscription_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/subscription","commits_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/commits{/sha}","git_commits_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/git/commits{/sha}","comments_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/comments{/number}","issue_comment_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/issues/comments{/number}","contents_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/contents/{+path}","compare_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/compare/{base}...{head}","merges_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/merges","archive_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/downloads","issues_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/issues{/number}","pulls_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/pulls{/number}","milestones_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/milestones{/number}","notifications_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/labels{/name}","releases_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/releases{/id}","deployments_url":"https://api.github.com/repos/southworkscom/Recognizers-Text/deployments","created_at":"2017-07-24T17:31:17Z","updated_at":"2018-06-12T11:52:48Z","pushed_at":"2018-06-13T21:04:40Z","git_url":"git://github.com/southworkscom/Recognizers-Text.git","ssh_url":"git@github.com:southworkscom/Recognizers-Text.git","clone_url":"https://github.com/southworkscom/Recognizers-Text.git","svn_url":"https://github.com/southworkscom/Recognizers-Text","homepage":"","size":11729,"stargazers_count":0,"watchers_count":0,"language":"C#","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"archived":false,"open_issues_count":2,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"forks":1,"open_issues":2,"watchers":0,"default_branch":"master"}},"base":{"label":"Microsoft:master","ref":"master","sha":"8ad662d72950b1e883ac614be0e0dfa6e20757ea","user":{"login":"Microsoft","id":6154722,"node_id":"MDEyOk9yZ2FuaXphdGlvbjYxNTQ3MjI=","avatar_url":"https://avatars2.githubusercontent.com/u/6154722?v=4","gravatar_id":"","url":"https://api.github.com/users/Microsoft","html_url":"https://github.com/Microsoft","followers_url":"https://api.github.com/users/Microsoft/followers","following_url":"https://api.github.com/users/Microsoft/following{/other_user}","gists_url":"https://api.github.com/users/Microsoft/gists{/gist_id}","starred_url":"https://api.github.com/users/Microsoft/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Microsoft/subscriptions","organizations_url":"https://api.github.com/users/Microsoft/orgs","repos_url":"https://api.github.com/users/Microsoft/repos","events_url":"https://api.github.com/users/Microsoft/events{/privacy}","received_events_url":"https://api.github.com/users/Microsoft/received_events","type":"Organization","site_admin":false},"repo":{"id":88544417,"node_id":"MDEwOlJlcG9zaXRvcnk4ODU0NDQxNw==","name":"Recognizers-Text","full_name":"Microsoft/Recognizers-Text","owner":{"login":"Microsoft","id":6154722,"node_id":"MDEyOk9yZ2FuaXphdGlvbjYxNTQ3MjI=","avatar_url":"https://avatars2.githubusercontent.com/u/6154722?v=4","gravatar_id":"","url":"https://api.github.com/users/Microsoft","html_url":"https://github.com/Microsoft","followers_url":"https://api.github.com/users/Microsoft/followers","following_url":"https://api.github.com/users/Microsoft/following{/other_user}","gists_url":"https://api.github.com/users/Microsoft/gists{/gist_id}","starred_url":"https://api.github.com/users/Microsoft/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Microsoft/subscriptions","organizations_url":"https://api.github.com/users/Microsoft/orgs","repos_url":"https://api.github.com/users/Microsoft/repos","events_url":"https://api.github.com/users/Microsoft/events{/privacy}","received_events_url":"https://api.github.com/users/Microsoft/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/Microsoft/Recognizers-Text","description":"Microsoft.Recognizers.Text provides recognition and resolution of numbers, units, and date/time expressed in multiple languages (Chinese, English, French, Spanish, and Portuguese. Partial support for German). Contributions are greatly welcome! Packages are available at https://www.nuget.org/profiles/Recognizers.Text and https://www.npmjs.com/~recognizers.text","fork":false,"url":"https://api.github.com/repos/Microsoft/Recognizers-Text","forks_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/forks","keys_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/teams","hooks_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/hooks","issue_events_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/issues/events{/number}","events_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/events","assignees_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/assignees{/user}","branches_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/branches{/branch}","tags_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/tags","blobs_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/git/refs{/sha}","trees_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/statuses/{sha}","languages_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/languages","stargazers_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/stargazers","contributors_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/contributors","subscribers_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/subscribers","subscription_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/subscription","commits_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/commits{/sha}","git_commits_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/git/commits{/sha}","comments_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/comments{/number}","issue_comment_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/issues/comments{/number}","contents_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/contents/{+path}","compare_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/merges","archive_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/downloads","issues_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/issues{/number}","pulls_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/pulls{/number}","milestones_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/milestones{/number}","notifications_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/labels{/name}","releases_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/releases{/id}","deployments_url":"https://api.github.com/repos/Microsoft/Recognizers-Text/deployments","created_at":"2017-04-17T19:45:47Z","updated_at":"2018-06-13T18:01:55Z","pushed_at":"2018-06-13T20:35:30Z","git_url":"git://github.com/Microsoft/Recognizers-Text.git","ssh_url":"git@github.com:Microsoft/Recognizers-Text.git","clone_url":"https://github.com/Microsoft/Recognizers-Text.git","svn_url":"https://github.com/Microsoft/Recognizers-Text","homepage":"","size":11268,"stargazers_count":160,"watchers_count":160,"language":"C#","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":82,"mirror_url":null,"archived":false,"open_issues_count":61,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"forks":82,"open_issues":61,"watchers":160,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/Microsoft/Recognizers-Text/pulls/616"},"html":{"href":"https://github.com/Microsoft/Recognizers-Text/pull/616"},"issue":{"href":"https://api.github.com/repos/Microsoft/Recognizers-Text/issues/616"},"comments":{"href":"https://api.github.com/repos/Microsoft/Recognizers-Text/issues/616/comments"},"review_comments":{"href":"https://api.github.com/repos/Microsoft/Recognizers-Text/pulls/616/comments"},"review_comment":{"href":"https://api.github.com/repos/Microsoft/Recognizers-Text/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/Microsoft/Recognizers-Text/pulls/616/commits"},"statuses":{"href":"https://api.github.com/repos/Microsoft/Recognizers-Text/statuses/e252eedfc05334bb7d731e74f7077e680e2dabf7"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"enzocanoo"}}