## 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)! :-)

Source: codewars | Difficulty: EASY

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
	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')

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]


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