text: # -*- coding: utf-8 -*-
  #
  # This file is part of INSPIRE.
  # Copyright (C) 2016 CERN.
  #
  # INSPIRE 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.
  #
  # INSPIRE 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 INSPIRE. If not, see <http://www.gnu.org/licenses/>.
  #
  # In applying this license, CERN does not waive the privileges and immunities
  # granted to it by virtue of its status as an Intergovernmental Organization
  # or submit itself to any jurisdiction.
  
  """Helpers for handling dates before 1900."""
  
  import six
  import re
  import time
  
  from datetime import date as real_date
  from datetime import datetime as real_datetime
  
  from flask_babelex import format_datetime as babel_format_datetime
  from flask_babelex import gettext as _
  
  # This library does not support strftime's "%s" or "%y" format strings.
  # Allowed if there's an even number of "%"s because they are escaped.
  _illegal_formatting = re.compile(r"((^|[^%])(%%)*%[sy])")
  
  DATE_FORMATS_YEAR = ["%Y", "%y"]
  DATE_FORMATS_MONTH = [
      "%Y-%m", "%Y %b", "%b %Y", "%Y %B", "%B %Y",
      "%y-%m", "%y %b", "%b %y", "%y %B", "%B %y",
  ]
  DATE_FORMATS_FULL = [
      "%Y-%m-%d", "%d %m %Y", "%x", "%d %b %Y",
      "%d %B %Y", "%d %b %y", "%d %B %y",
  ]
  
  
  class date(real_date):
  
      def strftime(self, fmt):
          return strftime(fmt, self)
  
  
  class datetime(real_datetime):
  
      def strftime(self, fmt):
          return strftime(fmt, self)
  
      @classmethod
      def combine(self, date, time):
          return self(date.year, date.month, date.day, time.hour, time.minute,
                      time.second, time.microsecond, time.tzinfo)
  
      def __add__(self, other):
          d = real_datetime.combine(self, self.timetz())
          d += other
          return self.combine(d, d.timetz())
  
      def date(self):
          return date(self.year, self.month, self.day)
  
      @staticmethod
      def strptime(date_string, format):
          return datetime(*(time.strptime(date_string, format)[0:6]))
  
  
  def _findall(text, substr):
      # Also finds overlaps
      sites = []
      i = 0
      while True:
          j = text.find(substr, i)
          if j == -1:
              break
          sites.append(j)
          i = j + 1
      return sites
  
  
  def strftime(fmt, dt):
      if not isinstance(dt, real_date):
          dt = datetime(dt.tm_year, dt.tm_mon, dt.tm_mday, dt.tm_hour, dt.tm_min,
                        dt.tm_sec)
      if dt.year >= 1900:
          return time.strftime(fmt, dt.timetuple())
      illegal_formatting = _illegal_formatting.search(fmt)
      if illegal_formatting:
          raise TypeError("strftime of dates before 1900 does not handle %s" %
                          illegal_formatting.group(0))
  
      year = dt.year
      # For every non-leap year century, advance by
      # 6 years to get into the 28-year repeat cycle
      delta = 2000 - year
      off = 6 * (delta // 100 + delta // 400)
      year = year + off
  
      # Move to around the year 2000
      year = year + ((2000 - year) // 28) * 28
      timetuple = dt.timetuple()
      s1 = time.strftime(fmt, (year,) + timetuple[1:])
      sites1 = _findall(s1, str(year))
  
      s2 = time.strftime(fmt, (year + 28,) + timetuple[1:])
      sites2 = _findall(s2, str(year + 28))
  
      sites = []
      for site in sites1:
          if site in sites2:
              sites.append(site)
  
      s = s1
      syear = "%04d" % (dt.year,)
      for site in sites:
          s = s[:site] + syear + s[site + 4:]
      return s
  
  
  def strptime(date_string, fmt):
      return real_datetime(*(time.strptime(date_string, fmt)[:6]))
  
  
  def create_valid_date(date, date_format_full="%Y-%m-%d",
                        date_format_month="%Y-%m", date_format_year="%Y"):
      """Iterate over possible formats and return a valid date if found."""
      valid_date = None
      date = six.text_type(date)
      for format in DATE_FORMATS_FULL:
          try:
              valid_date = strftime(date_format_full, (strptime(date, format)))
              break
          except ValueError:
              pass
      else:
          for format in DATE_FORMATS_MONTH:
              try:
                  if date.count('-') > 1:
                      date = "-".join(date.split('-')[:2])
                  valid_date = strftime(date_format_month, (strptime(date, format)))
                  break
              except ValueError:
                  pass
          else:
              for format in DATE_FORMATS_YEAR:
                  try:
                      if date.count('-') > 0:
                          date = date.split('-')[0]
                      valid_date = strftime(date_format_year, (strptime(date, format)))
                      break
                  except ValueError:
                      pass
      return valid_date
  
  
  def convert_datestruct_to_dategui(datestruct, ln=None, output_format="d MMM Y, H:mm"):
      """Convert: (2005, 11, 16, 15, 11, 44, 2, 320, 0) => '16 nov 2005, 15:11'
      Month is internationalized
      """
      assert ln is None, 'setting language is not supported'
      try:
          if datestruct[0] and datestruct[1] and datestruct[2]:
              dt = datetime.fromtimestamp(time.mktime(datestruct))
              return babel_format_datetime(dt, output_format)
          else:
              raise ValueError
      except:  # noqa todo: implement proper exception handling (E722 do not use bare except)
          return _("N/A")
  
  
  def create_datestruct(datetext):
      """
      Create a datestruct out of a date text in format YYYY-MM-dd
      :param datetext: date from record
      :type datetext: str
      :returns: tuple of 1 or more integers, up to max (year, month, day).
          Otherwise None.
      """
      if isinstance(datetext, int):
          datetext = unicode(datetext)
      if not datetext or not isinstance(datetext, six.string_types):
          return None
      datetext = datetext.strip()
      return tuple([int(component) for component in datetext.split('-')])
  