From db770cfb98070414907e6fe72fe2b6c6d394df3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Deuchnord?= Date: Mon, 7 Mar 2022 13:39:00 +0100 Subject: [PATCH] fix: use Babel to translate the dates --- Dockerfile | 3 +- Makefile | 2 +- Pipfile | 2 +- Pipfile.lock | 32 +++++----- _kosmorro/dumper.py | 105 +++++++++++---------------------- _kosmorro/exceptions.py | 5 +- _kosmorro/locales/messages.pot | 72 +++++++++++----------- _kosmorro/main.py | 9 +-- setup.py | 1 + tests/dates.py | 8 +-- tests/general.py | 4 +- tests/position.py | 26 ++++---- tests/timezone.py | 12 ++-- tests/utils.py | 4 +- 14 files changed, 128 insertions(+), 157 deletions(-) diff --git a/Dockerfile b/Dockerfile index 49557aa..0765773 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,7 +18,6 @@ COPY _kosmorro/ _kosmorro/ COPY kosmorro . # Compile the translations -RUN pip install Babel COPY setup.py setup.py COPY setup.cfg setup.cfg COPY README.md README.md @@ -27,7 +26,7 @@ RUN python setup.py compile_catalog # Clean the image RUN rm setup.py setup.cfg README.md && \ rm _kosmorro/locales/messages.pot _kosmorro/locales/*/LC_MESSAGES/messages.po && \ - pip uninstall --yes Babel pipenv + pip uninstall --yes pipenv USER kosmorro diff --git a/Makefile b/Makefile index 0b3792c..24c3637 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ tests: echo; \ fi - pipenv run python3 -m pytest tests/*.py + LANG=C pipenv run python3 -m pytest tests/*.py .PHONY: build build: manpage diff --git a/Pipfile b/Pipfile index 0d73dcc..5f4d225 100644 --- a/Pipfile +++ b/Pipfile @@ -4,7 +4,6 @@ url = "https://pypi.org/simple" verify_ssl = true [dev-packages] -babel = "*" black = "*" pytest = "*" aurornis = "*" @@ -14,6 +13,7 @@ tabulate = "*" termcolor = "*" kosmorrolib = ">=1.0.0,<2.0.0" python-dateutil = "*" +babel = ">=2.9.0,<3.0.0" [requires] python_version = "3" diff --git a/Pipfile.lock b/Pipfile.lock index 7553526..be90307 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "3ff7290b32da63ca9585dbe947830ae1d1e4692f0ca9a0faeaa3601d7f9c4b8b" + "sha256": "7c87215e821ad4c88dadc31960030f52957e2eaa41d451bfa3a53feb91b8d856" }, "pipfile-spec": 6, "requires": { @@ -16,6 +16,14 @@ ] }, "default": { + "babel": { + "hashes": [ + "sha256:ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9", + "sha256:bc0c176f9f6a994582230df350aa6e05ba2ebe4b3ac317eab29d9be5d2768da0" + ], + "index": "pypi", + "version": "==2.9.1" + }, "certifi": { "hashes": [ "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872", @@ -69,6 +77,13 @@ "index": "pypi", "version": "==2.8.2" }, + "pytz": { + "hashes": [ + "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c", + "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326" + ], + "version": "==2021.3" + }, "sgp4": { "hashes": [ "sha256:05f22b4fd91861ee2e0e03a528c1aef11a8b263a60ab5f723d32cdb65dac3eaa", @@ -155,14 +170,6 @@ "index": "pypi", "version": "==1.2.0" }, - "babel": { - "hashes": [ - "sha256:ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9", - "sha256:bc0c176f9f6a994582230df350aa6e05ba2ebe4b3ac317eab29d9be5d2768da0" - ], - "index": "pypi", - "version": "==2.9.1" - }, "black": { "hashes": [ "sha256:07e5c049442d7ca1a2fc273c79d1aecbbf1bc858f62e8184abe1ad175c4f7cc2", @@ -263,13 +270,6 @@ "index": "pypi", "version": "==7.0.1" }, - "pytz": { - "hashes": [ - "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c", - "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326" - ], - "version": "==2021.3" - }, "tomli": { "hashes": [ "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", diff --git a/_kosmorro/dumper.py b/_kosmorro/dumper.py index 5838241..6758501 100644 --- a/_kosmorro/dumper.py +++ b/_kosmorro/dumper.py @@ -25,7 +25,7 @@ import subprocess import shutil from pathlib import Path -import kosmorrolib +from babel.dates import format_date, format_time from tabulate import tabulate from termcolor import colored @@ -70,7 +70,7 @@ class Dumper(ABC): self.show_graph = show_graph def get_date_as_string(self, capitalized: bool = False) -> str: - date = self.date.strftime(FULL_DATE_FORMAT) + date = format_date(self.date, "full") if capitalized: return "".join([date[0].upper(), date[1:]]) @@ -181,35 +181,21 @@ class TextDumper(Dumper): name = self.style(object_name, "th") - if ephemeris.rise_time is not None: - time_fmt = ( - TIME_FORMAT - if ephemeris.rise_time.day == self.date.day - else SHORT_DATETIME_FORMAT - ) - planet_rise = ephemeris.rise_time.strftime(time_fmt) - else: - planet_rise = "-" - - if ephemeris.culmination_time is not None: - time_fmt = ( - TIME_FORMAT - if ephemeris.culmination_time.day == self.date.day - else SHORT_DATETIME_FORMAT - ) - planet_culmination = ephemeris.culmination_time.strftime(time_fmt) - else: - planet_culmination = "-" - - if ephemeris.set_time is not None: - time_fmt = ( - TIME_FORMAT - if ephemeris.set_time.day == self.date.day - else SHORT_DATETIME_FORMAT - ) - planet_set = ephemeris.set_time.strftime(time_fmt) - else: - planet_set = "-" + planet_rise = ( + "-" + if ephemeris.rise_time is None + else format_time(ephemeris.rise_time, "short") + ) + planet_culmination = ( + "-" + if ephemeris.culmination_time is None + else format_time(ephemeris.culmination_time, "short") + ) + planet_set = ( + "-" + if ephemeris.set_time is None + else format_time(ephemeris.set_time, "short") + ) data.append([name, planet_rise, planet_culmination, planet_set]) @@ -234,14 +220,9 @@ class TextDumper(Dumper): if description is None: continue - time_fmt = ( - TIME_FORMAT - if event.start_time.day == self.date.day - else SHORT_DATETIME_FORMAT - ) data.append( [ - self.style(event.start_time.strftime(time_fmt), "th"), + self.style(format_time(event.start_time, "short"), "th"), description, ] ) @@ -262,8 +243,8 @@ class TextDumper(Dumper): "{next_moon_phase} on {next_moon_phase_date} at {next_moon_phase_time}" ).format( next_moon_phase=_(strings.from_moon_phase(moon_phase.get_next_phase())), - next_moon_phase_date=moon_phase.next_phase_date.strftime(FULL_DATE_FORMAT), - next_moon_phase_time=moon_phase.next_phase_date.strftime(TIME_FORMAT), + next_moon_phase_date=format_date(moon_phase.next_phase_date, "full"), + next_moon_phase_time=format_time(moon_phase.next_phase_date, "short"), ) return "\n".join([current_moon_phase, new_moon_phase]) @@ -384,35 +365,21 @@ class _LatexDumper(Dumper): if self.ephemerides is not None: for ephemeris in self.ephemerides: - if ephemeris.rise_time is not None: - time_fmt = ( - TIME_FORMAT - if ephemeris.rise_time.day == self.date.day - else SHORT_DATETIME_FORMAT - ) - aster_rise = ephemeris.rise_time.strftime(time_fmt) - else: - aster_rise = "-" - - if ephemeris.culmination_time is not None: - time_fmt = ( - TIME_FORMAT - if ephemeris.culmination_time.day == self.date.day - else SHORT_DATETIME_FORMAT - ) - aster_culmination = ephemeris.culmination_time.strftime(time_fmt) - else: - aster_culmination = "-" - - if ephemeris.set_time is not None: - time_fmt = ( - TIME_FORMAT - if ephemeris.set_time.day == self.date.day - else SHORT_DATETIME_FORMAT - ) - aster_set = ephemeris.set_time.strftime(time_fmt) - else: - aster_set = "-" + aster_rise = ( + "-" + if ephemeris.rise_time is None + else format_time(ephemeris.rise_time, "short") + ) + aster_culmination = ( + "-" + if ephemeris.culmination_time is None + else format_time(ephemeris.culmination_time, "short") + ) + aster_set = ( + "-" + if ephemeris.set_time is None + else format_time(ephemeris.set_time, "short") + ) if not self.show_graph: object_name = strings.from_object(ephemeris.object.identifier) @@ -476,7 +443,7 @@ class _LatexDumper(Dumper): continue latex.append( - r"\event{%s}{%s}" % (event.start_time.strftime(TIME_FORMAT), event_name) + r"\event{%s}{%s}" % (format_time(event.start_time, "short"), event_name) ) return "".join(latex) diff --git a/_kosmorro/exceptions.py b/_kosmorro/exceptions.py index 88ae9b8..25b82b8 100644 --- a/_kosmorro/exceptions.py +++ b/_kosmorro/exceptions.py @@ -17,6 +17,7 @@ # along with this program. If not, see . from datetime import date +from babel.dates import format_date from _kosmorro.i18n.utils import _, SHORT_DATE_FORMAT @@ -34,8 +35,8 @@ class OutOfRangeDateError(RuntimeError): self.msg = _( "The date must be between {minimum_date} and {maximum_date}" ).format( - minimum_date=min_date.strftime(SHORT_DATE_FORMAT), - maximum_date=max_date.strftime(SHORT_DATE_FORMAT), + minimum_date=format_date(min_date, "long"), + maximum_date=format_date(max_date, "long"), ) diff --git a/_kosmorro/locales/messages.pot b/_kosmorro/locales/messages.pot index 75ee03a..b40fd9b 100644 --- a/_kosmorro/locales/messages.pot +++ b/_kosmorro/locales/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2022-02-16 13:58+0100\n" +"POT-Creation-Date: 2022-03-07 16:45+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -39,64 +39,64 @@ msgstr "" msgid "Note: All the hours are given in the UTC{offset} timezone." msgstr "" -#: _kosmorro/dumper.py:219 _kosmorro/dumper.py:352 +#: _kosmorro/dumper.py:205 _kosmorro/dumper.py:333 msgid "Object" msgstr "" -#: _kosmorro/dumper.py:220 _kosmorro/dumper.py:353 +#: _kosmorro/dumper.py:206 _kosmorro/dumper.py:334 msgid "Rise time" msgstr "" -#: _kosmorro/dumper.py:221 _kosmorro/dumper.py:355 +#: _kosmorro/dumper.py:207 _kosmorro/dumper.py:336 msgid "Culmination time" msgstr "" -#: _kosmorro/dumper.py:222 _kosmorro/dumper.py:357 +#: _kosmorro/dumper.py:208 _kosmorro/dumper.py:338 msgid "Set time" msgstr "" -#: _kosmorro/dumper.py:253 +#: _kosmorro/dumper.py:234 msgid "Moon phase is unavailable for this date." msgstr "" -#: _kosmorro/dumper.py:257 _kosmorro/dumper.py:361 +#: _kosmorro/dumper.py:238 _kosmorro/dumper.py:342 msgid "Moon phase:" msgstr "" -#: _kosmorro/dumper.py:261 +#: _kosmorro/dumper.py:242 msgid "{next_moon_phase} on {next_moon_phase_date} at {next_moon_phase_time}" msgstr "" -#: _kosmorro/dumper.py:325 +#: _kosmorro/dumper.py:306 msgid "Overview of your sky" msgstr "" -#: _kosmorro/dumper.py:333 +#: _kosmorro/dumper.py:314 msgid "" "This document summarizes the ephemerides and the events of {date}. It " "aims to help you to prepare your observation session. All the hours are " "given in {timezone}." msgstr "" -#: _kosmorro/dumper.py:343 +#: _kosmorro/dumper.py:324 msgid "" "Don't forget to check the weather forecast before you go out with your " "equipment." msgstr "" -#: _kosmorro/dumper.py:350 +#: _kosmorro/dumper.py:331 msgid "Ephemerides of the day" msgstr "" -#: _kosmorro/dumper.py:359 +#: _kosmorro/dumper.py:340 msgid "hours" msgstr "" -#: _kosmorro/dumper.py:366 +#: _kosmorro/dumper.py:347 msgid "Expected events" msgstr "" -#: _kosmorro/dumper.py:520 +#: _kosmorro/dumper.py:487 msgid "" "Building PDF was not possible, because some dependencies are not " "installed.\n" @@ -104,7 +104,7 @@ msgid "" "pdf/ for more information." msgstr "" -#: _kosmorro/dumper.py:573 +#: _kosmorro/dumper.py:540 #, python-format msgid "" "An error occurred during the compilation of the PDF.\n" @@ -112,102 +112,102 @@ msgid "" "share the content of the log file at /tmp/kosmorro-%s.log" msgstr "" -#: _kosmorro/exceptions.py:34 +#: _kosmorro/exceptions.py:35 msgid "The date must be between {minimum_date} and {maximum_date}" msgstr "" -#: _kosmorro/main.py:62 +#: _kosmorro/main.py:63 msgid "" "Save the planet and paper!\n" "Consider printing your PDF document only if really necessary, and use the" " other side of the sheet." msgstr "" -#: _kosmorro/main.py:71 +#: _kosmorro/main.py:72 msgid "" "PDF output will not contain the ephemerides, because you didn't provide " "the observation coordinates." msgstr "" -#: _kosmorro/main.py:116 +#: _kosmorro/main.py:117 msgid "The file could not be saved in \"{path}\": {error}" msgstr "" -#: _kosmorro/main.py:130 +#: _kosmorro/main.py:131 msgid "Please provide a file path to export in this format (--output)." msgstr "" -#: _kosmorro/main.py:163 -msgid "Moon phase can only be displayed between {min_date} and {max_date}" +#: _kosmorro/main.py:164 +msgid "Moon phase can only be computed between {min_date} and {max_date}" msgstr "" -#: _kosmorro/main.py:202 +#: _kosmorro/main.py:203 msgid "Running on Python {python_version} with Kosmorrolib v{kosmorrolib_version}" msgstr "" -#: _kosmorro/main.py:215 +#: _kosmorro/main.py:216 msgid "" "Compute the ephemerides and the events for a given date and a given " "position on Earth." msgstr "" -#: _kosmorro/main.py:218 +#: _kosmorro/main.py:219 msgid "" "By default, only the events will be computed for today.\n" "To compute also the ephemerides, latitude and longitude arguments are " "needed." msgstr "" -#: _kosmorro/main.py:231 +#: _kosmorro/main.py:232 msgid "Show the program version" msgstr "" -#: _kosmorro/main.py:239 +#: _kosmorro/main.py:240 msgid "The format to output the information to" msgstr "" -#: _kosmorro/main.py:246 +#: _kosmorro/main.py:247 msgid "" "The observer's latitude on Earth. Can also be set in the " "KOSMORRO_LATITUDE environment variable." msgstr "" -#: _kosmorro/main.py:256 +#: _kosmorro/main.py:257 msgid "" "The observer's longitude on Earth. Can also be set in the " "KOSMORRO_LONGITUDE environment variable." msgstr "" -#: _kosmorro/main.py:266 +#: _kosmorro/main.py:267 msgid "" "The date for which the ephemerides must be calculated. Can be in the " "YYYY-MM-DD format or an interval in the \"[+-]YyMmDd\" format (with Y, M," " and D numbers). Defaults to current date." msgstr "" -#: _kosmorro/main.py:277 +#: _kosmorro/main.py:278 msgid "" "The timezone to display the hours in (e.g. 2 for UTC+2 or -3 for UTC-3). " "Can also be set in the KOSMORRO_TIMEZONE environment variable." msgstr "" -#: _kosmorro/main.py:286 +#: _kosmorro/main.py:287 msgid "Disable the colors in the console." msgstr "" -#: _kosmorro/main.py:293 +#: _kosmorro/main.py:294 msgid "" "A file to export the output to. If not given, the standard output is " "used. This argument is needed for PDF format." msgstr "" -#: _kosmorro/main.py:302 +#: _kosmorro/main.py:303 msgid "" "Do not generate a graph to represent the rise and set times in the PDF " "format." msgstr "" -#: _kosmorro/main.py:310 +#: _kosmorro/main.py:311 msgid "Show debugging messages" msgstr "" diff --git a/_kosmorro/main.py b/_kosmorro/main.py index 6b36682..18bd52f 100644 --- a/_kosmorro/main.py +++ b/_kosmorro/main.py @@ -19,6 +19,7 @@ import argparse import sys +from babel.dates import format_date from kosmorrolib import Position, get_ephemerides, get_events, get_moon_phase from kosmorrolib.__version__ import __version__ as kosmorrolib_version from kosmorrolib.exceptions import OutOfRangeDateError @@ -161,10 +162,10 @@ def get_information( print( colored( _( - "Moon phase can only be displayed between {min_date} and {max_date}" + "Moon phase can only be computed between {min_date} and {max_date}" ).format( - min_date=error.min_date.strftime(SHORT_DATE_FORMAT), - max_date=error.max_date.strftime(SHORT_DATE_FORMAT), + min_date=format_date(error.min_date, "long"), + max_date=format_date(error.max_date, "long"), ), "yellow", ) @@ -218,7 +219,7 @@ def get_args(output_formats: [str]): epilog=_( "By default, only the events will be computed for today.\n" "To compute also the ephemerides, latitude and longitude arguments are needed." - ).format(date=today.strftime(dumper.FULL_DATE_FORMAT)), + ), ) parser.add_argument( diff --git a/setup.py b/setup.py index ee01c8f..8cafe4f 100644 --- a/setup.py +++ b/setup.py @@ -47,6 +47,7 @@ setup( "tabulate", "termcolor", "python-dateutil", + "babel", ], classifiers=[ "Development Status :: 3 - Alpha", diff --git a/tests/dates.py b/tests/dates.py index fc7ccaf..2e6f0a9 100644 --- a/tests/dates.py +++ b/tests/dates.py @@ -10,13 +10,13 @@ def test_with_date(): assert ( result.stdout - == """Monday January 27, 2020 + == """Monday, January 27, 2020 Moon phase: New Moon -First Quarter on Sunday February 02, 2020 at 01:41 +First Quarter on Sunday, February 2, 2020 at 1:41 AM Expected events: -20:00 Venus and Neptune are in conjunction +8:00 PM Venus and Neptune are in conjunction Note: All the hours are given in UTC. """ @@ -48,5 +48,5 @@ def test_with_out_of_range_dates(): assert not result.is_successful() assert ( result.stdout - == "Moon phase can only be displayed between Aug 09, 1899 and Sep 26, 2053\n" + == "Moon phase can only be computed between August 9, 1899 and September 26, 2053\n" ) diff --git a/tests/general.py b/tests/general.py index d2cf96f..c7f8462 100644 --- a/tests/general.py +++ b/tests/general.py @@ -8,6 +8,7 @@ from .utils import ( NEXT_MOON_PHASE_PATTERN, ) from datetime import date +from babel.dates import format_date def test_run_without_argument(): @@ -15,9 +16,10 @@ def test_run_without_argument(): assert result.is_successful() stdout = result.stdout.split("\n") + print(stdout) # It always starts with the current date, an empty line and the current and next Moon date: - assert stdout[0] == date.today().strftime("%A %B %d, %Y") + assert stdout[0] == format_date(date.today(), "full") assert stdout[1] == "" assert CURRENT_MOON_PHASE_PATTERN.match(stdout[2]) assert NEXT_MOON_PHASE_PATTERN.match(stdout[3]) diff --git a/tests/position.py b/tests/position.py index 918f87b..1912d1a 100644 --- a/tests/position.py +++ b/tests/position.py @@ -10,26 +10,26 @@ def check_command_return(result): assert result.is_successful() assert ( result.stdout - == """Monday January 27, 2020 + == """Monday, January 27, 2020 Object Rise time Culmination time Set time -------- ----------- ------------------ ---------- -Sun 07:31 12:01 16:30 -Moon 09:06 14:09 19:13 -Mercury 08:10 12:49 17:28 -Venus 09:01 14:35 20:10 -Mars 04:19 08:23 12:28 -Jupiter 06:15 10:18 14:21 -Saturn 06:56 11:09 15:22 -Uranus 10:21 17:25 00:33 -Neptune 09:01 14:36 20:10 -Pluto 06:57 11:04 15:11 +Sun 7:31 AM 12:01 PM 4:30 PM +Moon 9:06 AM 2:09 PM 7:13 PM +Mercury 8:10 AM 12:49 PM 5:28 PM +Venus 9:01 AM 2:35 PM 8:10 PM +Mars 4:19 AM 8:23 AM 12:28 PM +Jupiter 6:15 AM 10:18 AM 2:21 PM +Saturn 6:56 AM 11:09 AM 3:22 PM +Uranus 10:21 AM 5:25 PM 12:33 AM +Neptune 9:01 AM 2:36 PM 8:10 PM +Pluto 6:57 AM 11:04 AM 3:11 PM Moon phase: New Moon -First Quarter on Sunday February 02, 2020 at 01:41 +First Quarter on Sunday, February 2, 2020 at 1:41 AM Expected events: -20:00 Venus and Neptune are in conjunction +8:00 PM Venus and Neptune are in conjunction Note: All the hours are given in UTC. """ diff --git a/tests/timezone.py b/tests/timezone.py index e7e8c80..4972fd4 100644 --- a/tests/timezone.py +++ b/tests/timezone.py @@ -10,13 +10,13 @@ def check_command_return_t_plus_one(result): assert result.is_successful() assert ( result.stdout - == """Monday January 27, 2020 + == """Monday, January 27, 2020 Moon phase: New Moon -First Quarter on Sunday February 02, 2020 at 02:41 +First Quarter on Sunday, February 2, 2020 at 2:41 AM Expected events: -21:00 Venus and Neptune are in conjunction +9:00 PM Venus and Neptune are in conjunction Note: All the hours are given in the UTC+1 timezone. """ @@ -27,13 +27,13 @@ def check_command_return_t_minus_one(result): assert result.is_successful() assert ( result.stdout - == """Monday January 27, 2020 + == """Monday, January 27, 2020 Moon phase: New Moon -First Quarter on Sunday February 02, 2020 at 00:41 +First Quarter on Sunday, February 2, 2020 at 12:41 AM Expected events: -19:00 Venus and Neptune are in conjunction +7:00 PM Venus and Neptune are in conjunction Note: All the hours are given in the UTC-1 timezone. """ diff --git a/tests/utils.py b/tests/utils.py index 38373d8..2264f9f 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -24,10 +24,10 @@ NEXT_MOON_PHASE_PATTERN = re.compile( r"(Full Moon)|(Waning Gibbous)|" r"(Last Quarter)|(Waning Crescent)" r") " - r"on ((Monday)|(Tuesday)|(Wednesday)|(Thursday)|(Friday)|(Saturday)|(Sunday)) " + r"on ((Monday)|(Tuesday)|(Wednesday)|(Thursday)|(Friday)|(Saturday)|(Sunday)), " r"((January)|(February)|(March)|(April)|(May)|(June)|" r"(July)|(August)|(September)|(October)|(November)|(December)) " - r"[0-9]{2}, [0-9]{4} at [0-9]{2}:[0-9]{2}$" + r"[0-9]{1,2}, [0-9]{4} at [0-9]{2}:[0-9]{2} [AP]M$" )