docs: def text_to_edtf_date(text):
      """
      Return EDTF string equivalent of a given natural language date string.
  
      The approach here is to parse the text twice, with different default
      dates. Then compare the results to see what differs - the parts that
      differ are undefined.
      """
      if not text:
          return
  
      t = text.lower()
      result = ''
  
      for reject_re in REJECT_RULES:
          if re.match(reject_re, t):
              return
  
      # matches on '1800s'. Needs to happen before is_decade.
      could_be_century = re.findall(r'(\d{2}00)s', t)
      # matches on '1800s' and '1910s'. Removes the 's'.
      # Needs to happen before is_uncertain because e.g. "1860s?"
      t, is_decade = re.subn(r'(\d{3}0)s', r'\1', t)
  
      # detect approximation signifiers
      # a few 'circa' abbreviations just before the year
      is_approximate = re.findall(r'\b(ca?\.?) ?\d{4}', t)
      # the word 'circa' anywhere
      is_approximate = is_approximate or re.findall(r'\bcirca\b', t)
      # the word 'approx'/'around'/'about' anywhere
      is_approximate = is_approximate or \
                       re.findall(r'\b(approx|around|about)', t)
      # a ~ before a year-ish number
      is_approximate = is_approximate or re.findall(r'\b~\d{4}', t)
      # a ~ at the beginning
      is_approximate = is_approximate or re.findall(r'^~', t)
  
      # detect uncertainty signifiers
      t, is_uncertain = re.subn(r'(\d{4})\?', r'\1', t)
      # the words uncertain/maybe/guess anywhere
      is_uncertain = is_uncertain or re.findall(
          r'\b(uncertain|possibly|maybe|guess)', t)
  
      # detect century forms
      is_century = re.findall(CENTURY_RE, t)
  
      # detect CE/BCE year form
      is_ce = re.findall(CE_RE, t)
      if is_century:
          result = "%02dxx" % (int(is_century[0][0]) - 1,)
          is_approximate = is_approximate or \
                           re.findall(r'\b(ca?\.?) ?' + CENTURY_RE, t)
          is_uncertain = is_uncertain or re.findall(CENTURY_RE + r'\?', t)
  
          try:
              is_bc = is_century[0][-1] in ("bc", "bce")
              if is_bc:
                  result = "-%s" % result
          except IndexError:
              pass
  
      elif is_ce:
          result = "%04d" % (int(is_ce[0][0]))
          is_approximate = is_approximate or \
                           re.findall(r'\b(ca?\.?) ?' + CE_RE, t)
          is_uncertain = is_uncertain or re.findall(CE_RE + r'\?', t)
  
          try:
              is_bc = is_ce[0][-1] in ("bc", "bce")
              if is_bc:
                  result = "-%s" % result
          except IndexError:
              pass
  
      else:
          # try dateutil.parse
  
          try:
              # parse twice, using different defaults to see what was
              # parsed and what was guessed.
              dt1 = parse(
                  t,
                  dayfirst=appsettings.DAY_FIRST,
                  yearfirst=False,
                  fuzzy=True,  # force a match, even if it's default date
                  default=DEFAULT_DATE_1
              )
  
              dt2 = parse(
                  t,
                  dayfirst=appsettings.DAY_FIRST,
                  yearfirst=False,
                  fuzzy=True,  # force a match, even if it's default date
                  default=DEFAULT_DATE_2
              )
  
          except ValueError:
              return
  
          if dt1.date() == DEFAULT_DATE_1.date() and \
                  dt2.date() == DEFAULT_DATE_2.date():
              # couldn't parse anything - defaults are untouched.
              return
  
          date1 = dt1.isoformat()[:10]
          date2 = dt2.isoformat()[:10]
  
          # guess precision of 'unspecified' characters to use
          mentions_year = re.findall(r'\byear\b.+(in|during)\b', t)
          mentions_month = re.findall(r'\bmonth\b.+(in|during)\b', t)
          mentions_day = re.findall(r'\bday\b.+(in|during)\b', t)
  
          for i in xrange(len(date1)):
              # if the given year could be a century (e.g. '1800s') then use
              # approximate/uncertain markers to decide whether we treat it as
              # a century or a decade.
              if i == 2 and could_be_century and \
                  not (is_approximate or is_uncertain):
                  result += 'x'
              elif i == 3 and is_decade > 0:
                  if mentions_year:
                      result += 'u'  # year precision
                  else:
                      result += 'x'  # decade precision
              elif date1[i] == date2[i]:
                  # since both attempts at parsing produced the same result
                  # it must be parsed value, not a default
                  result += date1[i]
              else:
                  # different values were produced, meaning that it's likely
                  # a default. Use 'unspecified'
                  result += "u"
  
          # strip off unknown chars from end of string - except the first 4
  
          for i in reversed(xrange(len(result))):
              if result[i] not in ('u', 'x', '-'):
                  smallest_length = 4
  
                  if mentions_month:
                      smallest_length = 7
                  if mentions_day:
                      smallest_length = 10
  
                  limit = max(smallest_length, i + 1)
                  result = result[:limit]
                  break
  
          # check for seasons
          if "spring" in t:
              result = result[:4] + "-21" + result[7:]
          elif "summer" in t:
              result = result[:4] + "-22" + result[7:]
          elif "autumn" in t or "fall" in t:
              result = result[:4] + "-23" + result[7:]
          elif "winter" in t:
              result = result[:4] + "-24" + result[7:]
  
              # end dateutil post-parsing
  
      if is_uncertain:
          result += "?"
  
      if is_approximate:
          result += "~"
  
      # weed out bad parses
      if result.startswith("uu-uu"):
          return None
  
      return result