Source code for timelinelib.config.dateformatparser
# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 Rickard Lindberg, Roger Lindberg
#
# This file is part of Timeline.
#
# Timeline is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Timeline is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Timeline. If not, see <http://www.gnu.org/licenses/>.
"""
The DateFormatParser is used define how a date string shall be parsed, given a pattern
describing the date format.
The date format string shall contain three regions (YEAR, MONTH, DAY) with two region
separators in between.
:doc:`Tests are found here <unit_config_dateformatparser>`.
"""
from timelinelib.calendar.gregorian.dateformatter import GregorianDateFormatter
REGION_START_POS = 0
REGION_LENGTH = 1
REGION_SEPARATOR = 2
REGION_TYPE = 3
YEAR = GregorianDateFormatter.YEAR
MONTH = GregorianDateFormatter.MONTH
DAY = GregorianDateFormatter.DAY
ERROR_TEXT = _("""\
Invalid Date Format:
The format should contain
one year placeholder = yyyy
one month placeholder = mmm or mm
one day placeholder = dd
two placeholders for separators between year, month and day
Separators can't contain the letters y, m or d
Example of valid formats:
yyyy-mm-dd
dd/mm/yyyy
mmm/dd-yyyy
""")
[docs]class DateFormatParser:
""" """
[docs] def get_error_text(self):
"""Returns a text that describes how a valid date format string shall be constructed."""
return ERROR_TEXT
[docs] def is_valid(self, date_format):
"""Return True if the given date_format is a valid format."""
try:
self.parse(date_format)
return True
except ValueError:
return False
[docs] def parse(self, date_format):
"""Parse the date format string, and retrieve region information."""
self.regions = self._to_regions(date_format)
return self
[docs] def get_separators(self):
"""Return a 2 item tuple with the region separators."""
return self.regions[1][REGION_SEPARATOR], self.regions[2][REGION_SEPARATOR]
[docs] def get_region_order(self):
"""Return the index of the year, mont and day region orders."""
return self._get_region_type_index(YEAR), self._get_region_type_index(MONTH), self._get_region_type_index(DAY)
[docs] def use_abbreviated_month_names(self):
"""
If the length of the MONTH region contains 3 characters abbreviated month
names shall be used.
"""
for i in range(len(self.regions)):
if self.regions[i][REGION_TYPE] == MONTH:
return self.regions[i][REGION_LENGTH] == 3
return False
def _get_region_type_index(self, region_type):
for i in range(len(self.regions)):
if self.regions[i][REGION_TYPE] == region_type:
return i
def _to_regions(self, date_format):
fmt = date_format.lower()
self._assert_leading_char_is_a_date_placeholder(date_format)
self._assert_trailing_char_is_a_date_placeholder(date_format)
fmt, regions = self._get_regions(fmt, date_format)
self._get_separators_placeholders(fmt, regions)
return regions
def _assert_leading_char_is_a_date_placeholder(self, date_format):
if date_format[0] not in ("y", "m", "d"):
raise ValueError()
def _assert_trailing_char_is_a_date_placeholder(self, date_format):
if date_format[-1] not in ("y", "m", "d"):
raise ValueError()
def _get_separators_placeholders(self, fmt, regions):
separator1_length = regions[1][REGION_START_POS] - regions[0][REGION_LENGTH]
regions[1][REGION_SEPARATOR] = fmt[:separator1_length]
regions[2][REGION_SEPARATOR] = fmt[separator1_length:]
def _get_regions(self, fmt, date_format):
def getKey(item):
return item[0]
fmt, region0 = self._get_and_remove_year_region(fmt, date_format)
fmt, region1 = self._get_and_remove_month_region(fmt, date_format)
fmt, region2 = self._get_and_remove_day_region(fmt, date_format)
regions = [region0, region1, region2]
if "y" in fmt or "m" in fmt or "d" in fmt:
raise ValueError()
regions.sort(key=getKey)
return fmt, regions
def _get_and_remove_year_region(self, fmt, date_format):
return self._get_and_remove_region(fmt, date_format, "yyyy", YEAR)
def _get_and_remove_month_region(self, fmt, date_format):
if "mmm" in fmt:
return self._get_and_remove_region(fmt, date_format, "mmm", MONTH)
elif "mm" in fmt:
return self._get_and_remove_region(fmt, date_format, "mm", MONTH)
else:
raise ValueError()
def _get_and_remove_day_region(self, fmt, date_format):
return self._get_and_remove_region(fmt, date_format, "dd", DAY)
def _get_and_remove_region(self, fmt, date_format, placeholder, region_type):
if placeholder in fmt:
fmt = fmt.replace(placeholder, "", 1)
index = date_format.lower().index(placeholder)
return fmt, [index, len(placeholder), "", region_type]
else:
raise ValueError()