## Story

Your company migrated the last 20 years of it's *very important data* to a new platform, in multiple phases. However, something went wrong: some of the essential time-stamps were messed up! It looks like that some servers were set to use the `dd/mm/yyyy` date format, while others were using the `mm/dd/yyyy` format during the migration. Unfortunately, the original database got corrupted in the process and there are no backups available... Now it's up to you to assess the damage.

## Task

You will receive a list of records as strings in the form of `[start_date, end_date]` given in the ISO `yyyy-mm-dd` format, and your task is to count how many of these records are: 
* **correct**: there can be nothing wrong with the dates, the month/day cannot be mixed up, or it would not make a valid timestamp in any other way; e.g. `["2015-04-04", "2015-05-13"]`
* **recoverable**: invalid in its current form, but the original timestamp can be recovered, because there is only one valid combination possible; e.g. `["2011-10-08", "2011-08-14"]`
* **uncertain**: one or both dates are ambiguous, and they may generate multiple valid timestamps, so the original cannot be retrieved; e.g. `["2002-02-07", "2002-12-10"]`

**Note:** the original records always defined a *non-negative* duration

Return your findings in an array: `[ correct_count, recoverable_count, uncertain_count ]`

## Examples

---

## My other katas

If you enjoyed this kata then please try [my other katas](https://www.codewars.com/collections/katas-created-by-anter69)! :-)

Difficulty: introductory

Starter code:

def check_dates(records):
	

Solutions:
def candidates(ymd):
    y, m, d = ymd.split('-')
    return {ymd, f'{y}-{d}-{m}'}

def check_dates(records):
    result = [0, 0, 0]
    for start, end in records:
        xs = [(dt1, dt2) for dt1 in candidates(start) for dt2 in candidates(end)
              if dt1 <= dt2 and dt1[5:7] <= '12' >= dt2[5:7]]
        i = 2 if len(xs) > 1 else xs[0] != (start, end)
        result[i] += 1  # 2: uncertain, 1(True): recoverable, 0(False): correct
    return result

from itertools import product

def disambiguate(timestamp):
    t = timestamp.split("-")
    return (f"{t[0]}-{t[1+i]}-{t[2-i]}" for i in (0, 1) if (t[1+i] != t[2-i] or i) and t[1+i] < "13")

def check_dates(records):
    result = [0, 0, 0]
    for record in records:
        valid = [list(p) for p in product(*(disambiguate(timestamp) for timestamp in record)) if p[0] <= p[1]]
        result[2 if len(valid) != 1 else 0 if record in valid else 1] += 1
    return result

from itertools import product

def check_dates(records):
    correct, recoverable, uncertain = 0, 0, 0
    for begin, end in records:
        parsings = [(d1,d2) for d1,d2 in product(parse(begin), parse(end)) if d1 <= d2]
        if len(parsings) != 1: uncertain += 1
        elif parsings[0] == (begin,end): correct += 1
        else: recoverable += 1
    return [correct, recoverable, uncertain]

def parse(date):
    y,m,d = date.split("-")
    if d <= "12" and m != d: yield "-".join([y,d,m])
    if m <= "12": yield date

from datetime import datetime as dt
from itertools import product, starmap
from collections import defaultdict


CORRECT, RECOVER, UNSURE = range(3)
TIMESTR_MODELS = ('%Y-%m-%d','%Y-%d-%m')            # wanted, inverted


def check_dates(records):
    out = [0]*3
    for r in records: out[check(*r)] += 1
    return out
    
def check(start, finish):
    cndsS, cndsF = getPossibleDates(start), getPossibleDates(finish)
    areOK = [ i1+i2 for(d1,i1),(d2,i2) in product(cndsS, cndsF) if d1<=d2 ]
    
    return ( UNSURE  if len(areOK)>1 else
             RECOVER if areOK[0] else
             CORRECT )

def getPossibleDates(s):
    cnds = defaultdict(lambda:1)
    for i,model in enumerate(TIMESTR_MODELS):
        try: cnds[dt.strptime(s, model)] *= i
        except ValueError: pass
    return cnds.items()

from datetime import date
from itertools import product


def check_dates(records):    
    correct, recoverable, uncertain = 0, 0, 0
    parse = lambda s: date( *[int(d) for d in s.split("-")] )
    
    for date_pair in records:
        start_date, end_date = map(parse, date_pair)
        orig_duration_valid = (end_date - start_date).days >= 0
        
        poss_start_dates = set( [start_date] + ([date(start_date.year, start_date.day, start_date.month)] if start_date.day <= 12 else []) )
        poss_end_dates   = set( [ end_date ] + ([date(end_date.year,   end_date.day,   end_date.month)  ] if end_date.day   <= 12 else []) )
        
        only_one_possible = sum( (poss_end - poss_start).days >= 0 for poss_start, poss_end in product(poss_start_dates, poss_end_dates) ) == 1
        
        if orig_duration_valid and only_one_possible:
            correct += 1
        elif only_one_possible:
            recoverable += 1
        else:
            uncertain += 1
    
    return [ correct, recoverable, uncertain ]

from datetime import datetime
from itertools import product

FORMATS = ["%Y-%m-%d", "%Y-%d-%m"]

def check_dates(records):
    correct     = 0
    recoverable = 0
    uncertain   = 0
    for start, end in records:
        good = set()
        right = False
        for i, (start_format, end_format) in enumerate(product(FORMATS, FORMATS)):
            try:
                start_date = datetime.strptime(start, start_format)
                end_date   = datetime.strptime(end,   end_format)
                if end_date>=start_date:
                    good.add((start_date, end_date))
                    if i == 0:
                        right = True
            except ValueError as e:
                pass
        if len(good) > 1:
            uncertain += 1
        elif len(good)==1:
            if right:
                correct += 1
            else:
                recoverable += 1
    return [correct, recoverable, uncertain]

from datetime import date

def less(d1, d2):
    if d1 == None or d2 == None:
        return 0
    return 1 if d1 <= d2 else 0

def check_dates(records):
    print(records)
    correct = recoverable = uncertain = 0
    for interval in records:
        start = interval[0].split('-')
        end = interval[1].split('-')
        try:
            a, b, c = map(int, (start[0], start[1], start[2]))
            start1 = date(a, b, c)
        except:
            start1 = None
        try:
            a, b, c = map(int, (start[0], start[2], start[1]))
            start2 = date(a, b, c)
        except:
            start2 = None
        try:
            a, b, c = map(int, (end[0], end[1], end[2]))
            end1 = date(a, b, c)
        except:
            end1 = None
        try:
            a, b, c = map(int, (end[0], end[2], end[1]))
            end2 = date(a, b, c)
        except:
            end2 = None
        
        
        combinations = less(start1, end1) + less(start1, end2) + less(start2, end1) + less(start2, end2)
        if start1 == start2:
            combinations -= 1
        if end1 == end2:
            combinations -= 1
        if start1 == start2 and end1 == end2:
            combinations -= 1

        if less(start1, end1) == 1 and combinations == 1:
            correct += 1
        elif combinations == 1:
            recoverable += 1
        else:
            uncertain += 1
        
    return [correct, recoverable, uncertain]

from datetime import date

def less(d1, d2):
    if d1 == None or d2 == None:
        return 0
    
    return 1 if d1 <= d2 else 0

def check_dates(records):
    correct = recoverable = uncertain = 0
    for interval in records:
        start = interval[0].split('-')
        end = interval[1].split('-')
        try:
            start1 = date(int(start[0]), int(start[1].lstrip('0')), int(start[2].lstrip('0')))
        except:
            start1 = None
        try:
            start2 = date(int(start[0]), int(start[2].lstrip('0')), int(start[1].lstrip('0')))
        except:
            start2 = None
        try:
            end1 = date(int(end[0]), int(end[1].lstrip('0')), int(end[2].lstrip('0')))
        except:
            end1 = None
        try:
            end2 = date(int(end[0]), int(end[2].lstrip('0')), int(end[1].lstrip('0')))
        except:
            end2 = None
        
        combinations = less(start1, end1) + less(start1, end2) + less(start2, end1) + less(start2, end2)
        if start1 == start2:
            combinations -= 1
        if end1 == end2:
            combinations -= 1
        if start1 == start2 and end1 == end2:
            combinations -= 1

        if less(start1, end1) == 1 and combinations == 1:
            correct += 1
        elif combinations == 1:
            recoverable += 1
        else:
            uncertain += 1
        
    return [correct, recoverable, uncertain]

def check_dates(records):
    correct = 0; recoverable = 0; uncertain = 0
    for rec in records:
        rec = DateRange(*rec)
        if rec.correct: correct += 1
        elif rec.recoverable: recoverable += 1
        elif rec.uncertain: uncertain += 1
    return [correct,recoverable,uncertain]

class Date:
    def __init__(self,date):
        self.date = [int(n) for n in date.split('-')]
        if 12<self.date[1]: self.date[2], self.date[1] = self.date[1:]; self.corrected = True
        else: self.corrected = False
        self.ambiguous = self.date[1]<13 and self.date[2]<13 and self.date[1]!=self.date[2]
    def swap(self):
        date = self.date[:]; date[2], date[1] = date[1:]
        return Date('-'.join([str(d) for d in date]))
    def __le__(self,other):
        if self.date[0]!=other.date[0]: return self.date[0]<other.date[0]
        if self.date[1]!=other.date[1]: return self.date[1]<other.date[1]
        return self.date[2]<=other.date[2]

class DateRange:
    def __init__(self,start,end):
        self.start = Date(start); self.end = Date(end)
        self.correct = None; self.recoverable = None; self.uncertain = None
        self.ambiguous = self.start.ambiguous or self.end.ambiguous
        if not self.ambiguous and self.start<=self.end:
            if self.start.corrected or self.end.corrected: self.recoverable = True
            else: self.correct = True
        else:
            self.start = [self.start]; self.end = [self.end]
            if self.start[0].ambiguous: self.start.append(self.start[0].swap())
            if self.end[0].ambiguous: self.end.append(self.end[0].swap())
            self.ranges = [(self.start[0],self.end[0])]
            if self.end[0].ambiguous: self.ranges.append((self.start[0],self.end[1]))
            if self.start[0].ambiguous:
                self.ranges.append((self.start[1],self.end[0]))
                if self.end[0].ambiguous: self.ranges.append((self.start[1],self.end[1]))
            self.ranges = [r for r in self.ranges if r[0]<=r[1]]
            if len(self.ranges)==1:
                if self.start[0]==self.ranges[0][0] and self.end[0]==self.ranges[0][1]: self.correct = True
                else: self.recoverable = True
            else: self.uncertain = True


def check_dates(A):
    Z=[0, 0, 0]
    for X in A:
        y0,m0,d0=map(int,X[0].split('-'))
        y1,m1,d1=map(int,X[1].split('-'))
        
        if y0<y1 and 12<d0 and 12<d1:Z[0]+=1    
        elif y0<y1 and m0==d0 and 12<d1:Z[0]+=1    
        elif y0<y1 and m0==d0 and m1==d1:Z[0]+=1    
        elif (m1==d1)and(12<m0 or 12<d0):Z[0]+=1    
        elif y0==y1 and m0==d0 and(12<m1 or 12<d1):Z[0]+=1   
        elif y0==y1 and m0==d0 and m1==d1:Z[0]+=1    
        elif y0==y1 and 12<d0 and 12<d1:Z[0]+=1
        elif y0==y1 and m0<=m1<d0<=12<d1:Z[0]+=1
        elif y0==y1 and m0<m1==d1<d0:Z[0]+=1
        elif y0==y1 and m0==d1<m1<d0:Z[0]+=1 
        elif y0==y1 and d1<m0<m1<=d0:Z[0]+=1                
        elif y0==y1 and d1<m0==d0<m1:Z[0]+=1    
        
        elif y0==y1 and (m0<=12<d0) and (m1<=12<d1):Z[1]+=1   
        elif y0==y1 and m1<=m0<=d1<=12<d0:Z[1]+=1     
        elif y0==y1 and d0<=m1<m0<=12<d1:Z[1]+=1     
        elif y0==y1 and d0<m1==d1<m0:Z[1]+=1    
        elif y0==y1 and d0==d1<m1<m0:Z[1]+=1        
        elif y0==y1 and m1<m0<d0==d1:Z[1]+=1    
        elif y0==y1 and m1<m0==d0<d1:Z[1]+=1   
        elif y0==y1 and m1<=m0<d1<d0:Z[1]+=1 
        elif y0==y1 and m1<d0<d1<=m0:Z[1]+=1
        elif y0==y1 and m1==d0<d1<m0:Z[1]+=1
        elif y0==y1 and d1<d0<m1<=m0:Z[1]+=1
        
        else:Z[2]+=1
    return Z