Skip to main content

qif2json.ts

This is qif2json.ts is a minor refactor to bring Typescript support to the qif2json package available on NPM. It is the basic QIF parsing without any other requirements from Node (fs) or NPM (iconv, jschardet). This means that it is possible to do the QIF parsing in the browser.

gist link

Attachment Type Size
qif2json.ts video/MP2T 3.9KiB
readme.md text/markdown 594.0B

qif2json.ts – video/MP2T, 3.9KiB

/* Adapted by Jason Schwarzenberger
 * for Typescript from
 * qif2json
 * https://github.com/spmason/qif2json
 *
 * Copyright (c) 2012 Steve Mason
 * Licensed under the MIT license.
 */

function parseDate(str: string, format: string) {
  const [day, month, year]: any[] = str.replace(' ', '').split(/[^0-9]/);
  const output: {
    year: number;
    month: string;
    day: string;
  } = { year: 0, month: '00', day: '00' };
  const yearNow = new Date().getFullYear();
  const yearInt = parseInt(year, 10);
  const year1900 = 1900 + yearInt;
  const year2000 = 2000 + yearInt;

  output.day = day.length < 2 ? `0${day}` : day;
  output.month = month.length < 2 ? `0${month}` : day;
  output.year = year;

  if (year.length <= 2) {
    output.year = year2000 > yearNow ? year1900 : year2000;
  }

  if (format === 'us') {
    return `${output.year}-${output.day}-${output.month}`;
  }
  return `${output.year}-${output.month}-${output.day}`;
}

export interface QifOptions {
  dateFormat?: string;
  ignoreType?: boolean;
}

export interface Qif {
  transactions: Transaction[];
  type: string;
}

export interface Division {
  amount: number;
  category: string;
  description: string;
  subcategory: string;
}

export interface Transaction {
  date: string;
  amount: number;
  number: string;
  memo: string;
  address: string[];
  payee: string;
  category: string;
  subcategory: string;
  clearedStatus: string;
  division: Division[];
  [key: string]: any;
}

export const parser = (qif: string, options: QifOptions = <QifOptions>{ ignoreType: false }): Qif => {
  const lines = qif.split('\n');
  let line = lines.shift();
  let transaction: Transaction = <Transaction>{};
  let division: Division = <Division>{};
  const typeArray = /!Type:([^$]*)$/.exec(line!.trim()) || [];

  if (!typeArray || !typeArray.length) {
    if (!options.ignoreType) {
      throw new Error('File does not appear to be a valid qif file: ' + line);
    }
    typeArray[1] = line!.trim();
  }

  const type = typeArray[1];
  const transactions: Transaction[] = [];

  // tslint:disable-next-line: no-conditional-assignment
  while ((line = lines.shift())) {
    line = line.trim();
    if (line === '^') {
      transactions.push(transaction);
      transaction = <Transaction>{};
      continue;
    }
    switch (line[0]) {
      case 'D':
        transaction.date = parseDate(line.substring(1), options.dateFormat || '');
        break;
      case 'T':
        transaction.amount = parseFloat(line.substring(1).replace(',', ''));
        break;
      case 'N':
        transaction.number = line.substring(1);
        break;
      case 'M':
        transaction.memo = line.substring(1);
        break;
      case 'A':
        transaction.address = (transaction.address || []).concat(line.substring(1));
        break;
      case 'P':
        transaction.payee = line.substring(1).replace(/&amp;/g, '&');
        break;
      case 'L':
        const lArray = line.substring(1).split(':');
        transaction.category = lArray[0];
        if (lArray[1] !== undefined) {
          transaction.subcategory = lArray[1];
        }
        break;
      case 'C':
        transaction.clearedStatus = line.substring(1);
        break;
      case 'S':
        const sArray = line.substring(1).split(':');
        division.category = sArray[0];
        if (sArray[1] !== undefined) {
          division.subcategory = sArray[1];
        }
        break;
      case 'E':
        division.description = line.substring(1);
        break;
      case '$':
        division.amount = parseFloat(line.substring(1));
        if (!(transaction.division instanceof Array)) {
          transaction.division = [];
        }
        transaction.division.push(division);
        division = <Division>{};

        break;

      default:
        throw new Error('Unknown Detail Code: ' + line[0]);
    }
  }

  if (Object.keys(transaction).length) {
    transactions.push(transaction);
  }

  return <Qif>{
    transactions,
    type
  };
};


readme.md – text/markdown, 594.0B

This is qif2json.ts is a minor refactor to bring Typescript support to the qif2json package available on NPM. It is the basic QIF parsing without any other requirements from Node (fs) or NPM (iconv, jschardet). This means that it is possible to do the QIF parsing in the browser.

I’ve written my own QIF parser on StackBlitz which is an improvement in terms of QIF support. Perhaps not as great performance, meh. Can see what it does using this demo.