def merge_date_ranges(dates):
    if not dates:
        return
    sorted_dates = sorted([sorted(t) for t in dates])
    saved = list(sorted_dates[0])
    for st, en in sorted_dates:
        if st < MIN_PERIOD_DATE or st > MAX_PERIOD_DATE:
            raise ValueError("start date %s is out of bounds" % str(st))
        if en < MIN_PERIOD_DATE or en > MAX_PERIOD_DATE:
            raise ValueError("end date %s is out of bounds" % str(en))
        if st <= saved[1]:
            if saved[0] == MIN_PERIOD_DATE:
                saved[0] = st
            if MAX_PERIOD_DATE in (en, saved[1]):
                saved[1] = min(saved[1], en)
            else:
                saved[1] = max(saved[1], en)
        else:
            yield tuple(saved)
            saved[0] = st
            saved[1] = en
    yield tuple(saved)