From 4ed17251197877012248fc81812ad3af7ef366e3 Mon Sep 17 00:00:00 2001
From: Tulir Asokan <tulir@maunium.net>
Date: Wed, 25 Mar 2020 23:11:10 +0200
Subject: [PATCH] Add support for AM/PM in US and UK locales

---
 reminder/locale_util.py | 28 ++++++++++++++++++++++++----
 reminder/locales.py     | 14 ++++++++++----
 2 files changed, 34 insertions(+), 8 deletions(-)

diff --git a/reminder/locale_util.py b/reminder/locale_util.py
index 1d93dcc..dd5462f 100644
--- a/reminder/locale_util.py
+++ b/reminder/locale_util.py
@@ -13,7 +13,7 @@
 #
 # You should have received a copy of the GNU Affero General Public License
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
-from typing import NamedTuple, Union, Pattern, Dict, Type, Optional, TYPE_CHECKING
+from typing import NamedTuple, Union, Pattern, Match, Dict, Type, Optional, TYPE_CHECKING
 from datetime import datetime
 from abc import ABC, abstractmethod
 import re
@@ -73,11 +73,31 @@ class RegexMatcher(Matcher):
     def match(self, val: str, start: int = 0) -> Optional[MatcherReturn]:
         match = self.regex.match(val, pos=start)
         if match and match.end() > 0:
-            return MatcherReturn(params={key: self.value_type(value)
-                                         for key, value in match.groupdict().items() if value},
-                                 end=match.end())
+            return self._convert_match(match)
         return None
 
+    def _convert_match(self, match: Match) -> MatcherReturn:
+        return MatcherReturn(params=self._convert_groups(match.groupdict()),
+                             end=match.end())
+
+    def _convert_groups(self, groups: Dict[str, str]) -> 'RelativeDeltaParams':
+        return {key: self.value_type(value) for key, value in groups.items() if value}
+
+
+class TimeMatcher(RegexMatcher):
+    def _convert_match(self, match: Match) -> MatcherReturn:
+        groups = match.groupdict()
+        try:
+            meridiem = groups.pop("meridiem").lower()
+        except KeyError:
+            meridiem = None
+        params = self._convert_groups(groups)
+        if meridiem == "pm":
+            params["hour"] += 12
+        elif meridiem == "am" and params["hour"] == 12:
+            params["hour"] = 0
+        return MatcherReturn(params=params, end=match.end())
+
 
 class WeekdayMatcher(Matcher):
     regex: Pattern
diff --git a/reminder/locales.py b/reminder/locales.py
index 989abda..1e2cac4 100644
--- a/reminder/locales.py
+++ b/reminder/locales.py
@@ -15,7 +15,7 @@
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 from dateutil.relativedelta import MO, TU, WE, TH, FR, SA, SU
 
-from .locale_util import Locales, Locale, RegexMatcher, WeekdayMatcher
+from .locale_util import Locales, Locale, RegexMatcher, WeekdayMatcher, TimeMatcher
 
 locales: Locales = {}
 
@@ -47,15 +47,21 @@ locales["en_iso"] = Locale(
     time=RegexMatcher(r"\s?(?:at\s)?"
                       r"(?P<hour>\d{2})"
                       r"[:.](?P<minute>\d{2})"
-                      r"(?:[:.](?P<second>\d{2}))?")
+                      r"(?:[:.](?P<second>\d{2}))?"),
 )
 
+time_12_en = TimeMatcher(r"\s?(?:at\s)?"
+                         r"(?P<hour>\d{2})"
+                         r"(?:[:.](?P<minute>\d{2}))?"
+                         r"(?:[:.](?P<second>\d{2}))?"
+                         r"(?:\s(?P<meridiem>a\.?m|p\.?m)\.?)?")
+
 locales["en_us"] = locales["en_iso"].replace(
-    name="English (US)",
+    name="English (US)", time=time_12_en,
     date=RegexMatcher(r"(?P<month>\d{1,2})/(?P<day>\d{1,2})(?:/(?P<year>\d{4}))?"))
 
 locales["en_uk"] = locales["en_iso"].replace(
-    name="English (UK)",
+    name="English (UK)", time=time_12_en,
     date=RegexMatcher(r"(?P<day>\d{1,2})/(?P<month>\d{1,2})(?:/(?P<year>\d{4}))?"))
 
 td_sep_fi = r"(?:[\s,]{1,3}(?:ja\s)?)"
-- 
GitLab