/**
 * Calculates the Luhn checksum of a string of digits.
 *
 * @param {string|number} str
 *
 * @return {number}
 */
const luhn = str => {
  let v = 0;
  let sum = 0;

  str += '';

  for (var i = 0, l = str.length; i < l; i++) {
    v = str[i];
    v *= 2 - i % 2;
    if (v > 9) {
      v -= 9;
    }
    sum += v;
  }

  return Math.ceil(sum / 10) * 10 - sum;
};

/**
 * Test if the input parameters are a valid date or not.
 *
 * @param {int} year
 * @param {int} month
 * @param {int} day
 *
 * @return {boolean}
 */
const testDate = (year, month, day) => {
  month -= 1;
  const date = new Date(year, month, day);
  return !('' + date.getFullYear() !== year || date.getMonth() !== month || date.getDate() !== day);
};

/**
 * Parse a Swedish social security number and get the parts.
 *
 * @param {string|int} ssn
 *
 * @return {object}
 */
const getParts = (ssn) => {
  ssn += '';

  const reg = /^(\d{2}){0,1}(\d{2})(\d{2})(\d{2})([\-|\+]{0,1})?(\d{3})(\d{0,1})$/;
  const match = reg.exec(ssn);

  if (!match) {
    return {};
  }

  let century = match[1];
  let year = match[2];
  let month = match[3];
  let day = match[4];
  let sep = match[5];
  let num = match[6];
  let check = match[7];

  if (typeof century === 'undefined' || !century.length) {
    const d = new Date;
    let baseYear = 0;

    if (sep === '+') {
      baseYear = d.getFullYear() - 100;
    } else {
      baseYear = d.getFullYear();
    }

    century = ('' + (baseYear - ((baseYear - year) % 100))).substr(0, 2);
  }

  if (!sep) {
    sep = '-';
  }

  // Set the right separator to match the full year.
  // >= 100 should use + and < 100 should use -
  const yearDiff = (new Date().getFullYear()) - parseInt(century + year, 10);
  if (sep === '-' && yearDiff >= 100) {
    sep = '+';
  } else if (sep === '+' && yearDiff < 100) {
    sep = '-';
  }

  return {
    'century': century,
    'year': year,
    'month': month,
    'day': day,
    'sep': sep,
    'num': num,
    'check': check
  };
}

module.exports = {
  /**
   * Format a Swedish social security number as one of the official formats,
   * A long format or a short format.
   *
   * If the input number could not be parsed a empty string will be returned.
   *
   * @param {string} ssn
   * @param {boolean} longFormat
   *
   * @return {string}
   */
  format(ssn, longFormat) {
    if (!this.valid(ssn)) {
      return '';
    }

    const parts = getParts(ssn);

    if (longFormat) {
      return `${parts.century}${parts.year}${parts.month}${parts.day}${parts.num}${parts.check}`;
    }

    return `${parts.year}${parts.month}${parts.day}${parts.sep}${parts.num}${parts.check}`;
  },

  /**
   * Get age from a Swedish social security number.
   *
   * @param {string|int} ssn
   * @param {boolean} includeCoordinationNumber
   *
   * @return {int}
   */
  getAge(ssn, includeCoordinationNumber) {
    if (!this.valid(ssn, includeCoordinationNumber)) {
      return 0;
    }

    const parts = getParts(ssn);
    let day = +parts.day;

    if (includeCoordinationNumber && day >= 61 && day < 91) {
      day -= 60;
    }

    return Math.floor((Date.now() - new Date(parts.century + parts.year, parts.month, day).getTime()) / 3.15576e+10);
  },

  /**
   * Check if a Swedish social security number is for a female.
   *
   * @param {string|number} ssn
   * @param {boolean} includeCoordinationNumber
   *
   * @throws Error when input value is not valid.
   *
   * @return {boolean}
   */
  isFemale(ssn, includeCoordinationNumber) {
    return !this.isMale(ssn, includeCoordinationNumber)
  },

  /**
   * Check if a Swedish social security number is for a male.
   *
   * @param {string|number} ssn
   * @param {boolean} includeCoordinationNumber
   *
   * @throws Error when input value is not valid.
   *
   * @return {boolean}
   */
  isMale(ssn, includeCoordinationNumber) {
    if (!this.valid(ssn, includeCoordinationNumber)) {
      throw new Error('Invalid swedish social security number');
    }

    const parts = getParts(ssn);
    const sexDigit = parts['num'].substr(-1);

    return sexDigit % 2 === 1;
  },

  /**
   * Validate a Swedish social security number.
   *
   * @param {string|number} str
   * @param {boolean} includeCoordinationNumber
   *
   * @return {boolean}
   */
  valid(ssn, includeCoordinationNumber = true) {
    if (typeof ssn !== 'number' && typeof ssn !== 'string') {
      return false;
    }

    const parts = getParts(ssn);
    if (!Object.keys(parts).length) {
      return false;
    }

    const valid = luhn(parts.year + parts.month + parts.day + parts.num) === +parts.check && !!parts.check;

    if (valid && testDate(parts.century + parts.year, +parts.month, +parts.day)) {
      return valid;
    }

    if (!includeCoordinationNumber) {
      return false;
    }

    return valid && testDate(parts.century + parts.year, +parts.month, +parts.day - 60);
  }
};

@@ -1382,16 +1382,33 @@
  else %7B%0A
+      sep = '-';%0A
       ba
@@ -1433,16 +1433,16 @@
 Year();%0A
-
     %7D%0A%0A 
@@ -1521,164 +1521,24 @@
 %0A  %7D
-%0A%0A  if (!sep) %7B%0A    sep = '-';%0A  %7D%0A%0A  // Set the right separator to match the full year.%0A  // %3E= 100 should use + and %3C 100 should use -%0A  const yearDiff = 
+ else %7B%0A    if (
 (new
@@ -1594,50 +1594,18 @@
  10)
-;%0A  if (sep === '-' && yearDiff %3E=
+ %3C
  100) %7B%0A
     
@@ -1592,32 +1592,34 @@
 r, 10) %3C 100) %7B%0A
+  
     sep = '+';%0A 
@@ -1617,12 +1617,14 @@
  = '
-+
+-
 ';%0A
+  
   %7D 
@@ -1632,59 +1632,32 @@
 lse 
-if (
+%7B%0A      
 sep =
-==
  '+'
- && yearDiff %3C 100) %7B%0A    sep = '-';
+;%0A    %7D
 %0A  %7D

Improve format