From ec88a7b0384f31c81064b91a8019c7fc290a6af5 Mon Sep 17 00:00:00 2001 From: Hyan Mandian <5044101+hyanmandian@users.noreply.github.com> Date: Fri, 11 Sep 2026 17:32:07 -0300 Subject: [PATCH 01/13] fix(currency): parse real decimal/thousands separators and never format non-finite values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit parseCurrency now reads the last "," or "." followed by up to precision digits as the decimal separator and every other one as a thousands separator, via the new shared parseDecimal internal, instead of always dividing by 100 as if the input were cents. parseCurrency preserves a leading "-" instead of dropping the sign. formatCurrency(NaN) / formatCurrency(Infinity) -> "" instead of "NaN"/"∞". Both clamp precision to 0-100 (the range Intl.NumberFormat accepts) via the new clampPrecision internal, and parseCurrency gains a { precision } option. perf: cache Intl.NumberFormat instances per (symbol, precision) pair. --- .../clamp-precision/clamp-precision.test.ts | 34 +++++++++ .../clamp-precision/clamp-precision.ts | 31 ++++++++ .../parse-decimal/parse-decimal.test.ts | 54 ++++++++++++++ src/_internals/parse-decimal/parse-decimal.ts | 55 ++++++++++++++ src/format-currency/format-currency.test.ts | 43 +++++++++++ src/format-currency/format-currency.ts | 73 +++++++++++++++---- src/parse-currency/parse-currency.test.ts | 67 +++++++++++++++++ src/parse-currency/parse-currency.ts | 33 +++++++-- 8 files changed, 368 insertions(+), 22 deletions(-) create mode 100644 src/_internals/clamp-precision/clamp-precision.test.ts create mode 100644 src/_internals/clamp-precision/clamp-precision.ts create mode 100644 src/_internals/parse-decimal/parse-decimal.test.ts create mode 100644 src/_internals/parse-decimal/parse-decimal.ts diff --git a/src/_internals/clamp-precision/clamp-precision.test.ts b/src/_internals/clamp-precision/clamp-precision.test.ts new file mode 100644 index 00000000..844f8564 --- /dev/null +++ b/src/_internals/clamp-precision/clamp-precision.test.ts @@ -0,0 +1,34 @@ +import { describe, expect, test } from "../test/runtime"; +import { clampPrecision } from "./clamp-precision"; + +describe("clampPrecision", () => { + test("should default to 2", () => { + expect(clampPrecision()).toBe(2); + expect(clampPrecision(undefined)).toBe(2); + }); + + test("should default to 2 when it is not a finite number", () => { + expect(clampPrecision(Number.NaN)).toBe(2); + expect(clampPrecision(Number.POSITIVE_INFINITY)).toBe(2); + // @ts-expect-error + expect(clampPrecision("3")).toBe(2); + }); + + test("should keep valid precisions", () => { + expect(clampPrecision(0)).toBe(0); + expect(clampPrecision(3)).toBe(3); + expect(clampPrecision(20)).toBe(20); + expect(clampPrecision(20)).toBe(20); + }); + + test("should clamp out of range precisions", () => { + expect(clampPrecision(-1)).toBe(0); + expect(clampPrecision(-100)).toBe(0); + expect(clampPrecision(21)).toBe(20); + expect(clampPrecision(1000)).toBe(20); + }); + + test("should truncate fractional precisions", () => { + expect(clampPrecision(2.9)).toBe(2); + }); +}); diff --git a/src/_internals/clamp-precision/clamp-precision.ts b/src/_internals/clamp-precision/clamp-precision.ts new file mode 100644 index 00000000..4060090b --- /dev/null +++ b/src/_internals/clamp-precision/clamp-precision.ts @@ -0,0 +1,31 @@ +export const DEFAULT_PRECISION = 2; + +export const MIN_PRECISION = 0; + +export const MAX_PRECISION = 20; + +/** + * Clamps a fraction-digits precision into the range accepted by `Intl.NumberFormat`. + * + * `Intl.NumberFormat` throws a `RangeError` when `minimumFractionDigits` / + * `maximumFractionDigits` fall outside of the range the engine accepts. ES2023 raised the + * upper bound from 20 to 100, but Node 20 still enforces 20, so 20 is the portable limit + * across every supported runtime and every precision coming from user land is normalized + * to it before reaching `Intl`. + * + * @param {number} [precision] - The desired precision. Defaults to 2 when omitted or not a finite number. + * @returns {number} An integer between 0 and 20. + * + * @example + * ```typescript + * clampPrecision(); // 2 + * clampPrecision(3); // 3 + * clampPrecision(-1); // 0 + * clampPrecision(21); // 20 + * ``` + */ +export const clampPrecision = (precision?: number): number => { + if (typeof precision !== "number" || !Number.isFinite(precision)) return DEFAULT_PRECISION; + + return Math.min(MAX_PRECISION, Math.max(MIN_PRECISION, Math.trunc(precision))); +}; diff --git a/src/_internals/parse-decimal/parse-decimal.test.ts b/src/_internals/parse-decimal/parse-decimal.test.ts new file mode 100644 index 00000000..7fc82d85 --- /dev/null +++ b/src/_internals/parse-decimal/parse-decimal.test.ts @@ -0,0 +1,54 @@ +import { describe, expect, test } from "../test/runtime"; +import { parseDecimal } from "./parse-decimal"; + +describe("parseDecimal", () => { + test("should read the last short separator as the decimal separator", () => { + expect(parseDecimal("R$ 1.234,56")).toBe(1234.56); + expect(parseDecimal("1234,56")).toBe(1234.56); + expect(parseDecimal("12.34")).toBe(12.34); + expect(parseDecimal("1,5")).toBe(1.5); + }); + + test("should read a long run after the separator as thousands", () => { + expect(parseDecimal("R$ 1.234")).toBe(1234); + expect(parseDecimal("1.000.000")).toBe(1000000); + }); + + test("should read a value without separators as whole units by default", () => { + expect(parseDecimal("1234")).toBe(1234); + }); + + test("should read a value without separators as minor units when asked", () => { + expect(parseDecimal("1234", { minorUnits: 2 })).toBe(12.34); + expect(parseDecimal("1234", { minorUnits: 3 })).toBe(1.234); + }); + + test("should honor maxFractionDigits", () => { + expect(parseDecimal("1,001")).toBe(1001); + expect(parseDecimal("1,001", { maxFractionDigits: 3 })).toBe(1.001); + }); + + test("should preserve a minus sign written before the first digit", () => { + expect(parseDecimal("-R$ 1,00")).toBe(-1); + expect(parseDecimal("R$ -0,50")).toBe(-0.5); + expect(parseDecimal("-1234")).toBe(-1234); + }); + + test("should normalize negative zero", () => { + expect(Object.is(parseDecimal("-0,00"), 0)).toBe(true); + expect(Object.is(parseDecimal("-0"), 0)).toBe(true); + }); + + test("should return 0 when there is nothing to read", () => { + expect(parseDecimal("")).toBe(0); + // @ts-expect-error + expect(parseDecimal(null)).toBe(0); + expect(parseDecimal("R$")).toBe(0); + expect(parseDecimal("-")).toBe(0); + }); + + test("should handle a trailing separator", () => { + expect(parseDecimal("1.234,")).toBe(1234); + expect(parseDecimal(",50")).toBe(0.5); + }); +}); diff --git a/src/_internals/parse-decimal/parse-decimal.ts b/src/_internals/parse-decimal/parse-decimal.ts new file mode 100644 index 00000000..c4517b0e --- /dev/null +++ b/src/_internals/parse-decimal/parse-decimal.ts @@ -0,0 +1,55 @@ +export type ParseDecimalOptions = { + /** How many trailing digits of a value written without any separator are minor units, so `2` reads "1234" as 12.34 (default: `0`). */ + minorUnits?: number; + /** Longest run of digits a decimal separator may introduce; a longer run makes it a thousands separator (default: `2`). */ + maxFractionDigits?: number; +}; + +/** + * Reads a decimal number out of a string written in the Brazilian convention. + * + * The last `,` or `.` followed by 1 to `maxFractionDigits` digits is the decimal separator; + * every other `,` or `.` is a thousands separator and is dropped. A value written without any + * separator is read as minor units, i.e. divided by `10 ** minorUnits`. A `-` written before + * the first digit makes the result negative, and negative zero is normalized to zero. + * + * @param {string} value - The string to read. + * @param {ParseDecimalOptions} [options] - Optional reading options. + * @param {number} [options.minorUnits] - Minor unit digits of a value written without separators. Defaults to 0. + * @param {number} [options.maxFractionDigits] - Longest fraction a decimal separator may introduce. Defaults to 2. + * @returns {number} The parsed number, or 0 when the value holds no digits. + * + * @example + * ```typescript + * parseDecimal("R$ 1.234,56"); // 1234.56 + * parseDecimal("R$ 1.234"); // 1234 + * parseDecimal("1234"); // 1234 + * parseDecimal("1234", { minorUnits: 2 }); // 12.34 + * parseDecimal("-R$ 1,00"); // -1 + * ``` + */ +export const parseDecimal = (value: string, options?: ParseDecimalOptions): number => { + if (typeof value !== "string" || value === "") return 0; + + const minorUnits = options?.minorUnits ?? 0; + const maxFractionDigits = options?.maxFractionDigits ?? 2; + + const firstDigitIndex = value.search(/\d/); + const prefix = firstDigitIndex === -1 ? value : value.slice(0, firstDigitIndex); + const sign = prefix.includes("-") ? -1 : 1; + + const cleaned = value.replace(/[^\d.,]/g, ""); + const separatorIndex = Math.max(cleaned.lastIndexOf(","), cleaned.lastIndexOf(".")); + const fraction = separatorIndex === -1 ? "" : cleaned.slice(separatorIndex + 1); + const isDecimal = fraction.length >= 1 && fraction.length <= maxFractionDigits; + + if (separatorIndex === -1) { + const digits = cleaned || "0"; + + return sign * (Number.parseInt(digits, 10) / 10 ** minorUnits) || 0; + } + + const integerPart = (isDecimal ? cleaned.slice(0, separatorIndex) : cleaned).replace(/\D/g, ""); + + return sign * Number.parseFloat(`${integerPart || "0"}.${isDecimal ? fraction : "0"}`) || 0; +}; diff --git a/src/format-currency/format-currency.test.ts b/src/format-currency/format-currency.test.ts index 06881c52..fdd77ca4 100644 --- a/src/format-currency/format-currency.test.ts +++ b/src/format-currency/format-currency.test.ts @@ -71,4 +71,47 @@ describe("formatCurrency", () => { expect(formatCurrency(100000.001, { precision: 3 })).toBe("100.000,001"); expect(formatCurrency(1000000.001, { precision: 3 })).toBe("1.000.000,001"); }); + + it("should read the separators of string inputs", () => { + expect(formatCurrency("1234.56")).toBe("1.234,56"); + expect(formatCurrency("1234,56")).toBe("1.234,56"); + expect(formatCurrency("1.234,56")).toBe("1.234,56"); + expect(formatCurrency("R$ 1.234,56")).toBe("1.234,56"); + expect(formatCurrency("1234.56", { precision: 3 })).toBe("1.234,560"); + expect(formatCurrency("1.234,5")).toBe("1.234,50"); + expect(formatCurrency("R$ 1.234")).toBe("1.234,00"); + }); + + it("should preserve the sign of string inputs", () => { + expect(formatCurrency("-10.5")).toBe("-10,50"); + expect(formatCurrency("-1.234,56")).toBe("-1.234,56"); + expect(formatCurrency("-R$ 1,00", { symbol: true })).toBe("-R$ 1,00"); + }); + + it("should read plain digit strings as whole units", () => { + expect(formatCurrency("")).toBe("0,00"); + expect(formatCurrency("0")).toBe("0,00"); + expect(formatCurrency("123456")).toBe("123.456,00"); + expect(formatCurrency("-1234")).toBe("-1.234,00"); + }); + + it("should clamp the precision to the range accepted by Intl", () => { + expect(formatCurrency(1.5, { precision: -1 })).toBe("2"); + expect(formatCurrency(1.5, { precision: 21 })).toBe(`1,5${"0".repeat(19)}`); + expect(formatCurrency(1.5, { precision: Number.NaN })).toBe("1,50"); + }); + + it("should return an empty string for non finite numbers", () => { + expect(formatCurrency(Number.NaN)).toBe(""); + expect(formatCurrency(Number.POSITIVE_INFINITY)).toBe(""); + expect(formatCurrency(Number.NEGATIVE_INFINITY)).toBe(""); + expect(formatCurrency(Number.NaN, { symbol: true })).toBe(""); + // @ts-expect-error + expect(formatCurrency(undefined)).toBe(""); + }); + + it("should replace the non-breaking space", () => { + expect(formatCurrency(1234.56, { symbol: true })).toBe("R$ 1.234,56"); + expect(formatCurrency(1234.56, { symbol: true })).not.toContain("\u00a0"); + }); }); diff --git a/src/format-currency/format-currency.ts b/src/format-currency/format-currency.ts index 69d84206..bb5046f9 100644 --- a/src/format-currency/format-currency.ts +++ b/src/format-currency/format-currency.ts @@ -1,37 +1,80 @@ -import { sanitizeToDigits } from "../_internals/sanitize-to-digits/sanitize-to-digits"; +import { DEFAULT_PRECISION, clampPrecision } from "../_internals/clamp-precision/clamp-precision"; +import { parseDecimal } from "../_internals/parse-decimal/parse-decimal"; export type FormatCurrencyOptions = { + /** Whether to prefix the result with the "R$" currency symbol (default: `false`). */ symbol?: boolean; + /** Number of decimal places to show. Defaults to 2, clamped to 0-20. */ precision?: number; }; +let formatters: Map | undefined; + +const getFormatter = (symbol: boolean, precision: number): Intl.NumberFormat => { + formatters ??= new Map(); + + const key = `${symbol}|${precision}`; + const cached = formatters.get(key); + + if (cached) return cached; + + const formatter = new Intl.NumberFormat("pt-BR", { + style: symbol ? "currency" : "decimal", + currency: "BRL", + currencyDisplay: symbol ? "symbol" : undefined, + maximumFractionDigits: precision, + minimumFractionDigits: precision, + }); + + formatters.set(key, formatter); + + return formatter; +}; + +const toNumber = (value: unknown, precision: number): number => + typeof value === "string" + ? parseDecimal(value, { maxFractionDigits: Math.max(DEFAULT_PRECISION, precision) }) + : Number(value); + /** * Formats a given value as a currency string in Brazilian Real (BRL). * + * String inputs are read by the same rule as `parseCurrency`, except that a value written + * without any separator stays in whole units: the last `,` or `.` followed by 1 to 2 digits + * (or up to `precision` digits, when that is larger) is the decimal separator, every other + * `,` or `.` is a thousands separator, and a `-` written before the first digit is preserved. + * So `"1.234,56"` formats as `"1.234,56"`, `"-10.5"` as `"-10,50"` and `"1234"` as + * `"1.234,00"`. + * + * A value that is not a finite number, such as `NaN`, `Infinity` or `-Infinity`, formats as + * an empty string. + * + * The precision is clamped to the `0..100` range accepted by `Intl.NumberFormat`. + * * @param {string|number} value - The value to be formatted. Can be a string or a number. - * @param {Object} options - Optional formatting options. + * @param {FormatCurrencyOptions} [options] - Optional formatting options. * @param {boolean} options.symbol - If true, includes the currency symbol in the formatted string. - * @param {number} options.precision - The number of decimal places to include in the formatted string. Defaults to 2. - * @returns {string} The formatted currency string. + * @param {number} options.precision - The number of decimal places to include in the formatted string. Defaults to 2, clamped to 0-20. + * @returns {string} The formatted currency string, or an empty string when the value is not finite. * * @example * ```typescript * formatCurrency(1234.56); // "1.234,56" * formatCurrency(1234.56, { symbol: true }); // "R$ 1.234,56" - * formatCurrency("1234.56", { precision: 3 }); // "1.234,560" + * formatCurrency(-10.5); // "-10,50" + * formatCurrency("1.234,56"); // "1.234,56" + * formatCurrency("1234"); // "1.234,00" + * formatCurrency(Number.NaN); // "" * ``` */ export const formatCurrency = (value: string | number, options?: FormatCurrencyOptions): string => { - const enhancedValue = - typeof value === "string" ? Number.parseFloat(sanitizeToDigits(value) || "0") : value; + const precision = clampPrecision(options?.precision); - return new Intl.NumberFormat("pt-BR", { - style: options?.symbol ? "currency" : "decimal", - currency: "BRL", - currencyDisplay: options?.symbol ? "symbol" : undefined, - maximumFractionDigits: options?.precision ?? 2, - minimumFractionDigits: options?.precision ?? 2, - }) + const enhancedValue = toNumber(value, precision); + + if (!Number.isFinite(enhancedValue)) return ""; + + return getFormatter(Boolean(options?.symbol), precision) .format(enhancedValue) - .replace(" ", " "); + .replace("\u00a0", " "); }; diff --git a/src/parse-currency/parse-currency.test.ts b/src/parse-currency/parse-currency.test.ts index 9887a057..4540e126 100644 --- a/src/parse-currency/parse-currency.test.ts +++ b/src/parse-currency/parse-currency.test.ts @@ -1,4 +1,5 @@ import { describe, expect, test } from "../_internals/test/runtime"; +import { formatCurrency } from "../format-currency/format-currency"; import { parseCurrency } from "./parse-currency"; describe("parseCurrency", () => { @@ -54,4 +55,70 @@ describe("parseCurrency", () => { expect(parseCurrency("R$ 1.000.000,01")).toBe(1000000.01); }); }); + + describe("should preserve the sign", () => { + test("when the value is negative", () => { + expect(parseCurrency("-R$ 1,00")).toBe(-1); + expect(parseCurrency("-1.234,56")).toBe(-1234.56); + expect(parseCurrency("R$ -0,50")).toBe(-0.5); + }); + + test("when the value is negative zero", () => { + expect(Object.is(parseCurrency("-0,00"), 0)).toBe(true); + }); + }); + + describe("should read the separators", () => { + test("when the fraction is shorter than the precision", () => { + expect(parseCurrency("R$ 1.234,5")).toBe(1234.5); + expect(parseCurrency("R$ 1.234.5")).toBe(1234.5); + }); + + test("when the separator is a dot", () => { + expect(parseCurrency("1234.56")).toBe(1234.56); + expect(parseCurrency("12.34")).toBe(12.34); + }); + + test("when there is only a thousands separator", () => { + expect(parseCurrency("R$ 1.234")).toBe(1234); + expect(parseCurrency("R$ 1.000.000")).toBe(1000000); + expect(parseCurrency("1,5")).toBe(1.5); + expect(parseCurrency("1.059")).toBe(1059); + }); + }); + + describe("should keep the cents convention for plain digit strings", () => { + test("when there is no separator at all", () => { + expect(parseCurrency("1234")).toBe(12.34); + expect(parseCurrency("R$ 50")).toBe(0.5); + expect(parseCurrency("1234", { precision: 0 })).toBe(1234); + }); + }); + + describe("should honor the precision option", () => { + test("when precision is 3", () => { + expect(parseCurrency("1,001", { precision: 3 })).toBe(1.001); + expect(parseCurrency("R$ 1.000,001", { precision: 3 })).toBe(1000.001); + }); + + test("when the precision is out of range", () => { + expect(parseCurrency("R$ 1,50", { precision: -1 })).toBe(1.5); + expect(parseCurrency("R$ 150", { precision: -1 })).toBe(150); + expect(parseCurrency("R$ 150", { precision: 21 })).toBe(150 / 10 ** 20); + }); + }); + + describe("should round-trip with formatCurrency", () => { + test("when using the default precision", () => { + expect(parseCurrency(formatCurrency(1234.56))).toBe(1234.56); + expect(parseCurrency(formatCurrency(-1234.56, { symbol: true }))).toBe(-1234.56); + }); + + test("when using a custom precision", () => { + expect(parseCurrency(formatCurrency(1.001, { precision: 3 }), { precision: 3 })).toBe(1.001); + expect(parseCurrency(formatCurrency(1000000.001, { precision: 3 }), { precision: 3 })).toBe( + 1000000.001, + ); + }); + }); }); diff --git a/src/parse-currency/parse-currency.ts b/src/parse-currency/parse-currency.ts index 7ccb338c..d5c0ac8c 100644 --- a/src/parse-currency/parse-currency.ts +++ b/src/parse-currency/parse-currency.ts @@ -1,10 +1,24 @@ -import { sanitizeToDigits } from "../_internals/sanitize-to-digits/sanitize-to-digits"; +import { DEFAULT_PRECISION, clampPrecision } from "../_internals/clamp-precision/clamp-precision"; +import { parseDecimal } from "../_internals/parse-decimal/parse-decimal"; + +export type ParseCurrencyOptions = { + /** Number of decimal places used as the minor unit scale. Fractions accept up to two digits, or `precision` digits when it is greater. Defaults to 2, clamped to 0-20. */ + precision?: number; +}; /** * Parses a string representing Brazilian currency format into a number. - * Removes all non-digit characters and converts the result to a decimal number. + * + * The last `,` or `.` followed by 1 to 2 digits (or up to `precision` digits, when that is + * larger) is the decimal separator; every other `,` or `.` is a thousands separator, so + * `"R$ 1.234,56"` parses to 1234.56 and `"R$ 1.234"` to 1234. A value written without any + * separator keeps the cents convention and is divided by `10 ** precision`, so `"1234"` + * parses to 12.34. A `-` written before the first digit is preserved, so `"-R$ 1,00"` parses + * to -1. * * @param {string} value - The string value to be parsed (e.g., "R$ 1.234,56" or "1234,56") + * @param {ParseCurrencyOptions} [options] - Optional parsing options. + * @param {number} options.precision - The number of decimal places used as the minor unit scale. Fractions accept up to two digits, or `precision` digits when it is greater. Defaults to 2, clamped to 0-20. * @returns {number} The parsed number value (e.g., 1234.56) * * @example @@ -12,13 +26,18 @@ import { sanitizeToDigits } from "../_internals/sanitize-to-digits/sanitize-to-d * parseCurrency("R$ 1.234,56"); // returns 1234.56 * parseCurrency("1234,56"); // returns 1234.56 * parseCurrency("R$ 0,50"); // returns 0.50 + * parseCurrency("R$ 1.234"); // returns 1234 + * parseCurrency("1234"); // returns 12.34 + * parseCurrency("-R$ 1,00"); // returns -1 + * parseCurrency("R$ 1,001", { precision: 3 }); // returns 1.001 * parseCurrency(""); // returns 0 * ``` */ -export const parseCurrency = (value: string): number => { - if (!value || typeof value !== "string") return 0; - - const digits = sanitizeToDigits(value) || "0"; +export const parseCurrency = (value: string, options?: ParseCurrencyOptions): number => { + const precision = clampPrecision(options?.precision); - return Number.parseInt(digits, 10) / 100; + return parseDecimal(value, { + minorUnits: precision, + maxFractionDigits: Math.max(DEFAULT_PRECISION, precision), + }); }; From 228d12766b37af31050a482923c79ae973be6d96 Mon Sep 17 00:00:00 2001 From: Hyan Mandian <5044101+hyanmandian@users.noreply.github.com> Date: Fri, 11 Sep 2026 17:32:07 -0300 Subject: [PATCH 02/13] fix(ie): accept SP produtor rural and Tocantins 11-digit registrations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Goiás prefixes are 10/11/15 (were 10/11/12/20); Pará accepts 15 and 75-79; Mato Grosso do Sul accepts 28/50. Support São Paulo produtor rural (P0MMMSSSSD000) and Tocantins 11-digit codes; the UF is now case-insensitive. --- src/is-valid-ie/constants.ts | 27 ++ src/is-valid-ie/is-valid-ie.test.ts | 651 +++++++++++++++++----------- src/is-valid-ie/is-valid-ie.ts | 368 +++++----------- 3 files changed, 548 insertions(+), 498 deletions(-) create mode 100644 src/is-valid-ie/constants.ts diff --git a/src/is-valid-ie/constants.ts b/src/is-valid-ie/constants.ts new file mode 100644 index 00000000..281ff7a2 --- /dev/null +++ b/src/is-valid-ie/constants.ts @@ -0,0 +1,27 @@ +/** + * Per-state IE (Inscrição Estadual) validation tables. + * + * @see Official: http://www.sintegra.gov.br/insc_est.html + */ + +export const BA_MOD_10_DIGITS = [0, 1, 2, 3, 4, 5, 8]; + +export const GO_PREFIXES = ["10", "11", "15"]; + +export const GO_DUAL_DIGIT_IE = 11_094_402; + +export const GO_SPECIAL_RANGE_START = 10_103_105; + +export const GO_SPECIAL_RANGE_END = 10_119_997; + +export const MA_PREFIXES = ["12"]; + +export const MS_PREFIXES = ["28", "50"]; + +export const PA_PREFIXES = ["15", "75", "76", "77", "78", "79"]; + +export const SP_FIRST_WEIGHTS = [1, 3, 4, 5, 6, 7, 8, 10]; + +export const SP_SECOND_WEIGHTS = [3, 2, 10, 9, 8, 7, 6, 5, 4, 3, 2]; + +export const TO_TYPES = ["01", "02", "03", "99"]; diff --git a/src/is-valid-ie/is-valid-ie.test.ts b/src/is-valid-ie/is-valid-ie.test.ts index 97270a60..f310afa9 100644 --- a/src/is-valid-ie/is-valid-ie.test.ts +++ b/src/is-valid-ie/is-valid-ie.test.ts @@ -2,45 +2,53 @@ import { describe, expect, test } from "../_internals/test/runtime"; import { isValidIe } from "./is-valid-ie"; describe("isValidIe", () => { - describe("should return true for AC", () => { - test("when IE for AC is correct", () => { + describe("AC", () => { + test("should return true for a valid IE, including one with formatting characters", () => { expect(isValidIe("AC", "0108368143106")).toBe(true); expect(isValidIe("AC", "01.349.541/474-57")).toBe(true); }); - }); - describe("should return false for AC", () => { - test("when IE for AC is incorrect", () => { - // incorrect second digit + + test("should return false when the second verifier digit is incorrect", () => { expect(isValidIe("AC", "0187634580933")).toBe(false); - // incorrect first digit. + }); + + test("should return false when the first verifier digit is incorrect", () => { expect(isValidIe("AC", "0187634580924")).toBe(false); - // it does not starts with 01 + }); + + test("should return false when the IE does not start with 01", () => { expect(isValidIe("AC", "0018763458000")).toBe(false); - // length bigger then 13 + }); + + test("should return false when the length is bigger than 13", () => { expect(isValidIe("AC", "01018763458064")).toBe(false); }); }); - describe("should return true for AL", () => { - test("when IE for AL is correct", () => { + describe("AL", () => { + test("should return true for a valid IE", () => { expect(isValidIe("AL", "248659758")).toBe(true); - // digit 10 converted to 0 + }); + + test("should return true when the check digit 10 is converted to 0", () => { expect(isValidIe("AL", "247424170")).toBe(true); }); - }); - describe("should return false for AL", () => { - test("when IE for AL is incorrect", () => { - // incorrect verified digit + + test("should return false when the verifier digit is incorrect", () => { expect(isValidIe("AL", "248659759")).toBe(false); - // it does not starts with 24 + }); + + test("should return false when the IE does not start with 24", () => { expect(isValidIe("AL", "258659750")).toBe(false); - // lenght more then 9 + }); + + test("should return false when the length is more than 9", () => { expect(isValidIe("AL", "2486597584")).toBe(false); }); }); - describe("should return true for AP", () => { - test("when IE for AP is correct", () => { + describe("AP", () => { + test("should return true for valid IEs", () => { expect(isValidIe("AP", "036029572")).toBe(true); expect(isValidIe("AP", "030123459")).toBe(true); expect(isValidIe("AP", "030000080")).toBe(true); @@ -49,445 +57,602 @@ describe("isValidIe", () => { expect(isValidIe("AP", "030170020")).toBe(true); expect(isValidIe("AP", "030170071")).toBe(true); }); - }); - describe("should return false for AP", () => { - test("when IE for AP is incorrect", () => { - // verifier digit false + + test("should return false when the verifier digit is incorrect", () => { expect(isValidIe("AP", "036029573")).toBe(false); - // more then 9 digits + }); + + test("should return false when the length is more than 9 digits", () => { expect(isValidIe("AP", "0306029570")).toBe(false); - // does not start with 03 + }); + + test("should return false when the IE does not start with 03", () => { expect(isValidIe("AP", "003060292")).toBe(false); }); }); - describe("should return true for AM", () => { - test("when IE for AM is correct", () => { + describe("AM", () => { + test("should return true for valid IEs, including one with formatting characters", () => { expect(isValidIe("AM", "48.063.523-4")).toBe(true); expect(isValidIe("AM", "036029572")).toBe(true); expect(isValidIe("AM", "000000019")).toBe(true); expect(isValidIe("AM", "046893830")).toBe(true); }); - }); - describe("should return false for AM", () => { - test("when IE for AM is incorrect", () => { - // verifier digit false + + test("should return false when the verifier digit is incorrect", () => { expect(isValidIe("AM", "036029573")).toBe(false); - // more then 9 digits + }); + + test("should return false when the length is more than 9 digits", () => { expect(isValidIe("AM", "0036029572")).toBe(false); }); }); - describe("should return true for BA", () => { - test("when IE for BA is correct", () => { - // 8 digits - // mod 10 + describe("BA", () => { + test("should return true for an 8-digit IE using the mod 10 rule", () => { expect(isValidIe("BA", "12345663")).toBe(true); - // mod 11 + }); + + test("should return true for an 8-digit IE using the mod 11 rule", () => { expect(isValidIe("BA", "74219145")).toBe(true); - // 9 digits - // mod 10 + }); + + test("should return true for a 9-digit IE using the mod 10 rule", () => { expect(isValidIe("BA", "038343081")).toBe(true); expect(isValidIe("BA", "100000306")).toBe(true); - // mod 11 + }); + + test("should return true for a 9-digit IE using the mod 11 rule", () => { expect(isValidIe("BA", "778514741")).toBe(true); - // 9 digits starting with 0 + }); + + test("should return true for a 9-digit IE starting with 0", () => { expect(isValidIe("BA", "078771760")).toBe(true); expect(isValidIe("BA", "039474751")).toBe(true); expect(isValidIe("BA", "090529323")).toBe(true); - // 8 digits starting with 0 + }); + + test("should return true for an 8-digit IE starting with 0", () => { expect(isValidIe("BA", "04772253")).toBe(true); }); - }); - describe("should return false for BA", () => { - test("when IE for BA is incorrect", () => { - // mod 10 + + test("should return false for an 8-digit IE with an incorrect mod 10 digit", () => { expect(isValidIe("BA", "12345636")).toBe(false); - // mod 11 + }); + + test("should return false for an 8-digit IE with an incorrect mod 11 digit", () => { expect(isValidIe("BA", "74219154")).toBe(false); + }); - // 9 digits - // mod 10 + test("should return false for a 9-digit IE with an incorrect mod 10 digit", () => { expect(isValidIe("BA", "038343001")).toBe(false); - // mod 11 + }); + + test("should return false for a 9-digit IE with an incorrect mod 11 digit", () => { expect(isValidIe("BA", "778514731")).toBe(false); - // more than 9 digits + }); + + test("should return false when the length is more than 9 digits", () => { expect(isValidIe("BA", "0012345636")).toBe(false); }); }); - describe("should return true for CE", () => { - test("when IE for CE is correct", () => { + describe("CE", () => { + test("should return true for a valid IE", () => { expect(isValidIe("CE", "853511942")).toBe(true); }); - }); - describe("should return false for CE", () => { - test("when IE for CE is incorrect", () => { + + test("should return false when the digit is incorrect", () => { expect(isValidIe("CE", "853511943")).toBe(false); - // more than 9 digits + }); + + test("should return false when the length is more than 9 digits", () => { expect(isValidIe("CE", "0853511942")).toBe(false); }); }); - describe("should return true for DF", () => { - test("when IE for DF is correct", () => { + describe("DF", () => { + test("should return true for a valid IE", () => { expect(isValidIe("DF", "0754002000176")).toBe(true); - // tenth digit converted to 0 + }); + + test("should return true when the tenth digit is converted to 0", () => { expect(isValidIe("DF", "0754002000508")).toBe(true); }); - }); - describe("should return false for DF", () => { - test("when IE for DF is incorrect", () => { - // does not start with 07 + + test("should return false when the IE does not start with 07", () => { expect(isValidIe("DF", "0108368143017")).toBe(false); - // does not have 13 digits + }); + + test("should return false when the length is not 13 digits", () => { expect(isValidIe("DF", "07008368143094")).toBe(false); - // digit incorrect + }); + + test("should return false when the digit is incorrect", () => { expect(isValidIe("DF", "0754002000175")).toBe(false); }); }); - describe("should return true for ES", () => { - test("when IE for ES is correct", () => { + describe("ES", () => { + test("should return true for a valid IE", () => { expect(isValidIe("ES", "639191444")).toBe(true); }); - }); - describe("should return false for ES", () => { - test("when IE for ES is incorrect", () => { + + test("should return false when the digit is incorrect", () => { expect(isValidIe("ES", "639191445")).toBe(false); - // more than 9 digits + }); + + test("should return false when the length is more than 9 digits", () => { expect(isValidIe("ES", "0639191444")).toBe(false); }); }); - describe("should return true for GO", () => { - test("when IE for GO is correct", () => { - // base rule + describe("GO", () => { + test("should return true for a valid IE using the base rule", () => { expect(isValidIe("GO", "109161793")).toBe(true); - // verified digit is 1, internal - expect(isValidIe("GO", "101031131")).toBe(true); - // verified digit is 1, external + }); + + test("should return true for an IE with prefix 15", () => { + expect(isValidIe("GO", "159876540")).toBe(true); + }); + + test("should return true when the remainder is 1 and the inscricao falls inside the special range 10103105..10119997", () => { + expect(isValidIe("GO", "101031051")).toBe(true); + }); + + test("should return true when the remainder is 0", () => { expect(isValidIe("GO", "101030940")).toBe(true); }); - }); - describe("should return false for GO", () => { - test("when IE for GO is incorrect", () => { - // verified digit incorrect + + test("should return true for IE 11094402, which accepts both digit 0 and digit 1", () => { + expect(isValidIe("GO", "110944020")).toBe(true); + expect(isValidIe("GO", "110944021")).toBe(true); + }); + + test("should return false when the verified digit is incorrect", () => { expect(isValidIe("GO", "109161794")).toBe(false); - // does not start with 10,11 or 15 + }); + + test("should return false when the IE does not start with 10, 11 or 15", () => { expect(isValidIe("GO", "121031131")).toBe(false); - // length different from 9 + }); + + test("should return false for prefixes 20 to 29, which the current SEFAZ-GO rule does not accept", () => { + expect(isValidIe("GO", "209876549")).toBe(false); + }); + + test("should return false when the length is different from 9", () => { expect(isValidIe("GO", "0101030940")).toBe(false); }); + + test("should return false when the remainder is 0, since the special range rule must not apply (10103113 has remainder 0, so the digit is 0, not 1)", () => { + expect(isValidIe("GO", "101031131")).toBe(false); + expect(isValidIe("GO", "101031130")).toBe(true); + }); + + test("should return false when the remainder is 1 but the inscricao (10000007) falls outside the special range 10103105..10119997, so the digit is 0", () => { + expect(isValidIe("GO", "100000071")).toBe(false); + expect(isValidIe("GO", "100000070")).toBe(true); + }); }); - describe("should return true for MA", () => { - test("when IE for MA is correct", () => { - // base rule + describe("MA", () => { + test("should return true for a valid IE using the base rule", () => { expect(isValidIe("MA", "120000008")).toBe(true); - // digit 11 converted to zero + }); + + test("should return true when digit 11 is converted to zero", () => { expect(isValidIe("MA", "120000040")).toBe(true); - // digit 10 converted to 1 + }); + + test("should return true when digit 10 is converted to 1", () => { expect(isValidIe("MA", "120000130")).toBe(true); }); - }); - describe("should return false for MA", () => { - test("when IE for MA is incorrect", () => { - // verified digit incorrect + + test("should return false when the verified digit is incorrect", () => { expect(isValidIe("MA", "120000007")).toBe(false); - // does not start with 12 + }); + + test("should return false when the IE does not start with 12", () => { expect(isValidIe("MA", "109161793")).toBe(false); - // length different from 9 + }); + + test("should return false when the length is different from 9", () => { expect(isValidIe("MA", "0120000008")).toBe(false); }); }); - describe("should return true for MG", () => { - test("when IE for MG is correct", () => { - // base rule + describe("MG", () => { + test("should return true for a valid IE using the base rule", () => { expect(isValidIe("MG", "4333908330177")).toBe(true); - // digit 10 converted to 0 + }); + + test("should return true when the first check digit 10 is converted to 0", () => { expect(isValidIe("MG", "4333908330410")).toBe(true); expect(isValidIe("MG", "7489439278602")).toBe(true); - // digit 11 converted to 0 + }); + + test("should return true when the second check digit 11 is converted to 0", () => { expect(isValidIe("MG", "4333908332560")).toBe(true); }); - }); - describe("should return false for MG", () => { - test("when IE for MG is incorrect", () => { - // first verified digit incorrect + + test("should return false when the first verified digit is incorrect", () => { expect(isValidIe("MG", "4333908330167")).toBe(false); - // length different from 13 + }); + + test("should return false when the length is different from 13", () => { expect(isValidIe("MG", "04333908330177")).toBe(false); - // second verified digit incorrect + }); + + test("should return false when the second verified digit is incorrect", () => { expect(isValidIe("MG", "4333908330176")).toBe(false); }); }); - describe("should return true for MT", () => { - test("when IE for MT is correct", () => { - // base rule + describe("MT", () => { + test("should return true for a valid IE using the base rule", () => { expect(isValidIe("MT", "60474120469")).toBe(true); }); - }); - describe("should return false for MT", () => { - test("when IE for MT is incorrect", () => { - // verified digit incorrect + + test("should return false when the verified digit is incorrect", () => { expect(isValidIe("MT", "12345678901")).toBe(false); - // length different from 11 + }); + + test("should return false when the length is different from 11", () => { expect(isValidIe("MT", "1234567890112")).toBe(false); }); }); - describe("should return true for MS", () => { - test("when IE for MS is correct", () => { - // base rule + describe("MS", () => { + test("should return true for a valid IE using the base rule", () => { expect(isValidIe("MS", "280000006")).toBe(true); - // digit 10 converted to 0 + }); + + test("should return true when digit 10 is converted to 0", () => { expect(isValidIe("MS", "280000090")).toBe(true); - // digit 11 converted to 0 + }); + + test("should return true when digit 11 is converted to 0", () => { expect(isValidIe("MS", "280000030")).toBe(true); }); - }); - describe("should return false for MS", () => { - test("when IE for MS is incorrect", () => { - // verified digit incorrect + + test("should return true for an IE with prefix 50", () => { + expect(isValidIe("MS", "500000000")).toBe(true); + }); + + test("should return false when the verified digit is incorrect", () => { expect(isValidIe("MS", "280000031")).toBe(false); - // length different from 9 + }); + + test("should return false when the length is different from 9", () => { expect(isValidIe("MS", "0280000006")).toBe(false); - // does not start with 28 + }); + + test("should return false when the IE does not start with 28", () => { expect(isValidIe("MS", "853511942")).toBe(false); }); }); - describe("should return true for PA", () => { - test("when IE for PA is correct", () => { - // base rule + describe("PA", () => { + test("should return true for a valid IE using the base rule", () => { expect(isValidIe("PA", "150000006")).toBe(true); - // digit 10 converted to 0 + }); + + test("should return true when digit 10 is converted to 0", () => { expect(isValidIe("PA", "150000260")).toBe(true); - // digit 11 converted to 0 + }); + + test("should return true when digit 11 is converted to 0", () => { expect(isValidIe("PA", "150000030")).toBe(true); }); - }); - describe("should return false for PA", () => { - test("when IE for PA is incorrect", () => { - // does not start with 15 + + test("should return true for IEs with prefixes 75 to 79", () => { + expect(isValidIe("PA", "750000023")).toBe(true); + expect(isValidIe("PA", "760000000")).toBe(true); + expect(isValidIe("PA", "770000002")).toBe(true); + expect(isValidIe("PA", "780000005")).toBe(true); + expect(isValidIe("PA", "790000008")).toBe(true); + }); + + test("should return false when the IE does not start with 15, 75, 76, 77, 78 or 79", () => { expect(isValidIe("PA", "120000008")).toBe(false); - // length different from 9 + }); + + test("should return false when the length is different from 9", () => { expect(isValidIe("PA", "0150000006")).toBe(false); - // digit incorrect + }); + + test("should return false when the digit is incorrect", () => { expect(isValidIe("PA", "150000007")).toBe(false); }); }); - describe("should return true for PB", () => { - test("when IE for PB is correct", () => { - // base rule + describe("PB", () => { + test("should return true for a valid IE using the base rule", () => { expect(isValidIe("PB", "853511942")).toBe(true); - // digit 10 converted to 0 + }); + + test("should return true when digit 10 is converted to 0", () => { expect(isValidIe("PB", "853512230")).toBe(true); - // digit 11 converted to 0 + }); + + test("should return true when digit 11 is converted to 0", () => { expect(isValidIe("PB", "853511950")).toBe(true); }); - }); - describe("should return false for PB", () => { - test("when IE for PB is incorrect", () => { - // length different from 9 + + test("should return false when the length is different from 9", () => { expect(isValidIe("PB", "0853511942")).toBe(false); - // digit incorrect + }); + + test("should return false when the digit is incorrect", () => { expect(isValidIe("PB", "853511943")).toBe(false); }); }); - describe("should return true for PE", () => { - test("when IE for PE is correct", () => { - // base rule + describe("PE", () => { + test("should return true for a valid IE using the base rule", () => { expect(isValidIe("PE", "288625706")).toBe(true); }); - }); - describe("should return false for PE", () => { - test("when IE for PE is incorrect", () => { - // length different from 9 digits + + test("should return false when the length is different from 9 digits", () => { expect(isValidIe("PE", "0925870110")).toBe(false); - // digit incorrect + }); + + test("should return false when the digit is incorrect", () => { expect(isValidIe("PE", "925870101")).toBe(false); }); }); - describe("should return true for PI", () => { - test("when IE for PI is correct", () => { - // base rule + describe("PI", () => { + test("should return true for a valid IE using the base rule", () => { expect(isValidIe("PI", "052364534")).toBe(true); }); }); - describe("should return true for PR", () => { - test("when IE for PR is correct", () => { - // base rule + describe("PR", () => { + test("should return true for a valid IE using the base rule", () => { expect(isValidIe("PR", "4447953604")).toBe(true); }); - }); - describe("should return false for PR", () => { - test("when IE for PR is incorrect", () => { - // length different from 10 digits + + test("should return true when the first check digit is below 10 and needs no clamping", () => { + expect(isValidIe("PR", "0000000191")).toBe(true); + }); + + test("should return true when the second check digit is the exceptional 10, clamped to 0", () => { + expect(isValidIe("PR", "0000000000")).toBe(true); + }); + + test("should return false when the length is different from 10 digits", () => { expect(isValidIe("PR", "04447953604")).toBe(false); - // digit incorrect + }); + + test("should return false when the digit is incorrect", () => { expect(isValidIe("PR", "4447953640")).toBe(false); }); }); - describe("should return true for RJ", () => { - test("when IE for RJ is correct", () => { - // base rule + describe("RJ", () => { + test("should return true for a valid IE using the base rule", () => { expect(isValidIe("RJ", "62545372")).toBe(true); - // digit 10 converted to 0 + }); + + test("should return true when digit 10 is converted to 0", () => { expect(isValidIe("RJ", "62545470")).toBe(true); - // digit 11 converted to 0 + }); + + test("should return true when digit 11 is converted to 0", () => { expect(isValidIe("RJ", "62545380")).toBe(true); }); - }); - describe("should return false for RJ", () => { - test("when IE for RJ is incorrect", () => { - // first verified digit incorrect + + test("should return false when the first verified digit is incorrect", () => { expect(isValidIe("RJ", "20441620")).toBe(false); - // length different from 8 + }); + + test("should return false when the length is different from 8", () => { expect(isValidIe("RJ", "020441623")).toBe(false); }); }); - describe("should return true for RN", () => { - test("when IE for RN is correct", () => { - // base rule + describe("RN", () => { + test("should return true for a valid IE using the base rule", () => { expect(isValidIe("RN", "2007693232")).toBe(true); - // digit 10 converted to 0 + }); + + test("should return true when digit 10 is converted to 0", () => { expect(isValidIe("RN", "2003569880")).toBe(true); - // old IE + }); + + test("should return true for an old-format IE", () => { expect(isValidIe("RN", "203569881")).toBe(true); }); - }); - describe("should return false for RN", () => { - test("when IE for RN is incorrect", () => { - // first verified digit incorrect + + test("should return false when the first verified digit is incorrect", () => { expect(isValidIe("RN", "2007693231")).toBe(false); - // does not start with 20 + }); + + test("should return false when the IE does not start with 20", () => { expect(isValidIe("RN", "0203569881")).toBe(false); - // length different from 9 or 10 + }); + + test("should return false when the length is different from 9 or 10", () => { expect(isValidIe("RN", "20356988104")).toBe(false); }); }); - describe("should return true for RO", () => { - test("when IE for RO is correct", () => { - // base rule + describe("RO", () => { + test("should return true for a valid IE using the base rule", () => { expect(isValidIe("RO", "01078042249629")).toBe(true); - // digit 10 converted to 0 + }); + + test("should return true when digit 10 is converted to 0", () => { expect(isValidIe("RO", "01078042249670")).toBe(true); - // digit 11 converted to 0 + }); + + test("should return true when digit 11 is converted to 0", () => { expect(isValidIe("RO", "01078042249751")).toBe(true); }); - }); - describe("should return false for RO", () => { - test("when IE for RO is incorrect", () => { - // first verified digit incorrect + + test("should return false when the first verified digit is incorrect", () => { expect(isValidIe("RO", "01078042249756")).toBe(false); - // length different from 14 + }); + + test("should return false when the length is different from 14", () => { expect(isValidIe("RO", "001078042249627")).toBe(false); }); }); - describe("should return true for RR", () => { - test("when IE for RR is correct", () => { - // base rule + describe("RR", () => { + test("should return true for a valid IE using the base rule", () => { expect(isValidIe("RR", "240061536")).toBe(true); }); - }); - describe("should return false for RR", () => { - test("when IE for RR is incorrect", () => { - // first verified digit incorrect + + test("should return false when the first verified digit is incorrect", () => { expect(isValidIe("RR", "240061537")).toBe(false); - // length different from 9 + }); + + test("should return false when the length is different from 9", () => { expect(isValidIe("RR", "2400615366")).toBe(false); - // does not start with 24 + }); + + test("should return false when the IE does not start with 24", () => { expect(isValidIe("RR", "024006150")).toBe(false); }); }); - describe("should return true for RS", () => { - test("when IE for RS is correct", () => { - // base rule + describe("RS", () => { + test("should return true for a valid IE using the base rule", () => { expect(isValidIe("RS", "0305169149")).toBe(true); - // digit 10 converted to 0 + }); + + test("should return true when digit 10 is converted to 0", () => { expect(isValidIe("RS", "1202762660")).toBe(true); - // digit 11 converted to 0 + }); + + test("should return true when digit 11 is converted to 0", () => { expect(isValidIe("RS", "1202762120")).toBe(true); }); - }); - describe("should return false for RS", () => { - test("when IE for RS is incorrect", () => { - // first verified digit incorrect + + test("should return false when the first verified digit is incorrect", () => { expect(isValidIe("RS", "2007693232")).toBe(false); - // length different from 10 + }); + + test("should return false when the length is different from 10", () => { expect(isValidIe("RS", "02007693230")).toBe(false); }); }); - describe("should return true for SC", () => { - test("when IE for SC is correct", () => { - // base rule + describe("SC", () => { + test("should return true for a valid IE using the base rule", () => { expect(isValidIe("SC", "330430572")).toBe(true); }); }); - describe("should return true for SE", () => { - test("when IE for SE is correct", () => { - // base rule + describe("SE", () => { + test("should return true for a valid IE using the base rule", () => { expect(isValidIe("SE", "017682606")).toBe(true); }); }); - describe("should return true for SP", () => { - test("when IE for SP is correct", () => { - // base rule + describe("SP", () => { + test("should return true for a valid IE using the base rule", () => { expect(isValidIe("SP", "110042490114")).toBe(true); }); - }); - describe("should return false for SP", () => { - test("when IE for SP is incorrect", () => { - // length bigger than 12 + + test("should return true for a produtor rural IE (P0MMMSSSSD000), including with formatting characters and lowercase prefix", () => { + expect(isValidIe("SP", "P011004243002")).toBe(true); + expect(isValidIe("SP", "P-01100424.3/002")).toBe(true); + expect(isValidIe("SP", "p011004243002")).toBe(true); + }); + + test("should return false when the length is bigger than 12", () => { expect(isValidIe("SP", "1110042494114")).toBe(false); - // second verified digit incorrect + }); + + test("should return false when the second verified digit is incorrect", () => { expect(isValidIe("SP", "110042490113")).toBe(false); - // first verified digit incorrect + }); + + test("should return false when the first verified digit is incorrect", () => { expect(isValidIe("SP", "110042498113")).toBe(false); }); + + test("should return false for a produtor rural IE with an incorrect verified digit", () => { + expect(isValidIe("SP", "P011004244002")).toBe(false); + }); + + test("should return false for a produtor rural IE with a length different from 13", () => { + expect(isValidIe("SP", "P01100424300")).toBe(false); + }); + + test("should return false when a letter appears in a position that must be a digit", () => { + expect(isValidIe("SP", "11004249011A")).toBe(false); + }); }); - describe("should return true for TO", () => { - test("when IE for TO is correct", () => { - // OLD base rule + describe("TO", () => { + test("should return true for a valid IE using the old base rule", () => { expect(isValidIe("TO", "01027737427")).toBe(true); + }); - // NEW base rule + test("should return true for a valid IE using the new base rule", () => { expect(isValidIe("TO", "294467696")).toBe(true); + }); - // Digit zero + test("should return true when the digit is zero", () => { expect(isValidIe("TO", "294150870")).toBe(true); }); - }); - describe("should return false for TO", () => { - test("when IE for TO is incorrect", () => { - // Old rule category invalid + + test("should return false for an old-rule IE with an invalid category", () => { expect(isValidIe("TO", "01047737427")).toBe(false); + }); - // more than 11 digits + test("should return false for an 11-digit IE with an invalid type, since it must not fall back to the 9-digit rule", () => { + expect(isValidIe("TO", "29000000947")).toBe(false); + }); + + test("should return false when the length is more than 11 digits", () => { expect(isValidIe("TO", "099999916599")).toBe(false); + }); - // verified digit incorrect + test("should return false when the verified digit is incorrect", () => { expect(isValidIe("TO", "99999916598")).toBe(false); + }); - // new rule verified digit incorrect + test("should return false for a new-rule IE with an incorrect verified digit", () => { expect(isValidIe("TO", "294467690")).toBe(false); }); }); + + describe("state code lookup", () => { + test("should not resolve properties from the prototype chain", () => { + // @ts-expect-error + expect(isValidIe("constructor", "110042490114")).toBe(false); + // @ts-expect-error + expect(isValidIe("toString", "110042490114")).toBe(false); + // @ts-expect-error + expect(isValidIe("__proto__", "110042490114")).toBe(false); + // @ts-expect-error + expect(isValidIe("valueOf", "110042490114")).toBe(false); + }); + + test("should accept lowercase state codes", () => { + // @ts-expect-error + expect(isValidIe("sp", "110042490114")).toBe(true); + // @ts-expect-error + expect(isValidIe("go", "109161793")).toBe(true); + }); + + test("should return false for missing arguments", () => { + // @ts-expect-error + expect(isValidIe(null, "110042490114")).toBe(false); + // @ts-expect-error + expect(isValidIe(1, "110042490114")).toBe(false); + // @ts-expect-error + expect(isValidIe("SP", null)).toBe(false); + }); + + test("should return false when the sanitized IE is empty", () => { + expect(isValidIe("RJ", "----")).toBe(false); + }); + }); }); diff --git a/src/is-valid-ie/is-valid-ie.ts b/src/is-valid-ie/is-valid-ie.ts index 4a495a61..c465d9aa 100644 --- a/src/is-valid-ie/is-valid-ie.ts +++ b/src/is-valid-ie/is-valid-ie.ts @@ -1,5 +1,19 @@ import type { StateCode } from "../_internals/constants/states"; +import { sanitizeToAlphanumeric } from "../_internals/sanitize-to-alphanumeric/sanitize-to-alphanumeric"; import { sanitizeToDigits } from "../_internals/sanitize-to-digits/sanitize-to-digits"; +import { + BA_MOD_10_DIGITS, + GO_DUAL_DIGIT_IE, + GO_PREFIXES, + GO_SPECIAL_RANGE_END, + GO_SPECIAL_RANGE_START, + MA_PREFIXES, + MS_PREFIXES, + PA_PREFIXES, + SP_FIRST_WEIGHTS, + SP_SECOND_WEIGHTS, + TO_TYPES, +} from "./constants"; type IeValidator = (ie: string) => boolean; @@ -8,20 +22,20 @@ const checkLength = (ie: string, length: number | number[]): boolean => { return length.includes(ie.length); }; -const startsWith = (ie: string, prefix: string): boolean => - ie.substring(0, prefix.length) === prefix; +const startsWithAny = (ie: string, prefixes: readonly string[]): boolean => + prefixes.some((prefix) => ie.substring(0, prefix.length) === prefix); + +const startsWith = (ie: string, prefix: string): boolean => startsWithAny(ie, [prefix]); type CalcDigitDecreasingParams = { body: string; startWeight: number; - minWeight: number; mod?: number; }; const calcDigitDecreasing = ({ body, startWeight, - minWeight, mod = 11, }: CalcDigitDecreasingParams): number => { let weight = startWeight; @@ -31,9 +45,6 @@ const calcDigitDecreasing = ({ const digit = body.charCodeAt(i) - 48; sum += digit * weight; weight--; - if (weight < minWeight) { - weight = minWeight === 1 ? 9 : 11; - } } const rest = sum % mod; @@ -41,16 +52,25 @@ const calcDigitDecreasing = ({ return dig >= 10 ? 0 : dig; }; -const validateAC: IeValidator = (ie: string) => { - if (!checkLength(ie, 13)) return false; - if (!startsWith(ie, "01")) return false; +const validateMod11Ie = (ie: string, prefixes?: readonly string[]): boolean => { + if (!checkLength(ie, 9)) return false; + if (prefixes && !startsWithAny(ie, prefixes)) return false; - const body = ie.substring(0, 11); + const body = ie.substring(0, 8); + const dig = calcDigitDecreasing({ + body, + startWeight: body.length + 1, + }); + + return Number.parseInt(ie.charAt(8), 10) === dig; +}; + +const calcDFDigit = (body: string): number => { let weight = body.length - 7; let sum = 0; for (let i = 0; i < body.length; i++) { - const digit = ie.charCodeAt(i) - 48; + const digit = body.charCodeAt(i) - 48; sum += digit * weight; weight--; if (weight === 1) { @@ -60,29 +80,24 @@ const validateAC: IeValidator = (ie: string) => { const mod = 11; const rest = sum % mod; - let firstDig = mod - rest; - if (firstDig >= 10) { - firstDig = 0; + let dig = mod - rest; + if (dig >= 10) { + dig = 0; } - const bodyWithFirst = body + firstDig; - weight = bodyWithFirst.length - 7; - sum = 0; + return dig; +}; - for (let i = 0; i < bodyWithFirst.length; i++) { - const digit = i < body.length ? ie.charCodeAt(i) - 48 : firstDig; - sum += digit * weight; - weight--; - if (weight === 1) { - weight = 9; - } - } +const SP_RURAL_PATTERN = /^P\d{12}$/; +const SP_COMPANY_PATTERN = /^\d{12}$/; - const rest2 = sum % mod; - let secondDig = mod - rest2; - if (secondDig >= 10) { - secondDig = 0; - } +const validateAC: IeValidator = (ie: string) => { + if (!checkLength(ie, 13)) return false; + if (!startsWith(ie, "01")) return false; + + const body = ie.substring(0, 11); + const firstDig = calcDFDigit(body); + const secondDig = calcDFDigit(body + firstDig); return ( Number.parseInt(ie.charAt(11), 10) === firstDig && @@ -152,26 +167,14 @@ const validateAP: IeValidator = (ie: string) => { return dig === Number.parseInt(ie.charAt(position), 10); }; -const validateAM: IeValidator = (ie: string) => { - if (!checkLength(ie, 9)) return false; - - const body = ie.substring(0, 8); - const dig = calcDigitDecreasing({ - body, - startWeight: body.length + 1, - minWeight: 1, - }); - - return Number.parseInt(ie.charAt(8), 10) === dig; -}; +const validateAM: IeValidator = (ie) => validateMod11Ie(ie); const validateBA: IeValidator = (ie: string) => { if (!checkLength(ie, [8, 9])) return false; const pos = ie.length === 9 ? 1 : 0; const charAt = Number.parseInt(ie.substring(pos, pos + 1), 10); - const arr = [0, 1, 2, 3, 4, 5, 8]; - const mod = arr.includes(charAt) ? 10 : 11; + const mod = BA_MOD_10_DIGITS.includes(charAt) ? 10 : 11; const body = ie.substring(0, ie.length - 2); let weight = body.length + 1; @@ -211,18 +214,7 @@ const validateBA: IeValidator = (ie: string) => { ); }; -const validateCE: IeValidator = (ie: string) => { - if (!checkLength(ie, 9)) return false; - - const body = ie.substring(0, 8); - const dig = calcDigitDecreasing({ - body, - startWeight: body.length + 1, - minWeight: 1, - }); - - return Number.parseInt(ie.charAt(8), 10) === dig; -}; +const validateCE: IeValidator = (ie) => validateMod11Ie(ie); const validateDF: IeValidator = (ie: string) => { if (!checkLength(ie, 13)) return false; @@ -240,51 +232,20 @@ const validateDF: IeValidator = (ie: string) => { ); }; -const calcDFDigit = (body: string): number => { - let weight = body.length - 7; - let sum = 0; - - for (let i = 0; i < body.length; i++) { - const digit = Number.parseInt(body.charAt(i), 10); - sum += digit * weight; - weight--; - if (weight === 1) { - weight = 9; - } - } - - const mod = 11; - const rest = sum % mod; - let dig = mod - rest; - if (dig >= 10) { - dig = 0; - } - - return dig; -}; - -const validateES: IeValidator = (ie: string) => { - if (!checkLength(ie, 9)) return false; - - const body = ie.substring(0, 8); - const dig = calcDigitDecreasing({ - body, - startWeight: body.length + 1, - minWeight: 1, - }); - - return Number.parseInt(ie.charAt(8), 10) === dig; -}; +const validateES: IeValidator = (ie) => validateMod11Ie(ie); const validateGO: IeValidator = (ie: string) => { if (!checkLength(ie, 9)) return false; - - const beginWith = ["10", "11", "12", "20"]; - const begin = ie.substring(0, 2); - if (!beginWith.includes(begin)) return false; + if (!startsWithAny(ie, GO_PREFIXES)) return false; const body = ie.substring(0, 8); const bodyInt = Number.parseInt(body, 10); + const checkDigit = Number.parseInt(ie.charAt(8), 10); + + if (bodyInt === GO_DUAL_DIGIT_IE) { + return checkDigit === 0 || checkDigit === 1; + } + let weight = 9; let sum = 0; @@ -295,32 +256,20 @@ const validateGO: IeValidator = (ie: string) => { } const rest = sum % 11; - let dig = 11 - rest; + let dig: number; - if (dig >= 10) { - if (dig === 11 && bodyInt >= 10_103_105 && bodyInt <= 10_119_997) { - dig = 1; - } else { - dig = 0; - } + if (rest === 0) { + dig = 0; + } else if (rest === 1) { + dig = bodyInt >= GO_SPECIAL_RANGE_START && bodyInt <= GO_SPECIAL_RANGE_END ? 1 : 0; + } else { + dig = 11 - rest; } - return Number.parseInt(ie.charAt(8), 10) === dig; + return checkDigit === dig; }; -const validateMA: IeValidator = (ie: string) => { - if (!checkLength(ie, 9)) return false; - if (!startsWith(ie, "12")) return false; - - const body = ie.substring(0, 8); - const dig = calcDigitDecreasing({ - body, - startWeight: body.length + 1, - minWeight: 1, - }); - - return Number.parseInt(ie.charAt(8), 10) === dig; -}; +const validateMA: IeValidator = (ie) => validateMod11Ie(ie, MA_PREFIXES); const validateMG: IeValidator = (ie: string) => { if (!checkLength(ie, 13)) return false; @@ -394,46 +343,11 @@ const validateMT: IeValidator = (ie: string) => { return Number.parseInt(ie.charAt(10), 10) === dig; }; -const validateMS: IeValidator = (ie: string) => { - if (!checkLength(ie, 9)) return false; - if (!startsWith(ie, "28")) return false; +const validateMS: IeValidator = (ie) => validateMod11Ie(ie, MS_PREFIXES); - const body = ie.substring(0, 8); - const dig = calcDigitDecreasing({ - body, - startWeight: body.length + 1, - minWeight: 1, - }); +const validatePA: IeValidator = (ie) => validateMod11Ie(ie, PA_PREFIXES); - return Number.parseInt(ie.charAt(8), 10) === dig; -}; - -const validatePA: IeValidator = (ie: string) => { - if (!checkLength(ie, 9)) return false; - if (!startsWith(ie, "15")) return false; - - const body = ie.substring(0, 8); - const dig = calcDigitDecreasing({ - body, - startWeight: body.length + 1, - minWeight: 1, - }); - - return Number.parseInt(ie.charAt(8), 10) === dig; -}; - -const validatePB: IeValidator = (ie: string) => { - if (!checkLength(ie, 9)) return false; - - const body = ie.substring(0, 8); - const dig = calcDigitDecreasing({ - body, - startWeight: body.length + 1, - minWeight: 1, - }); - - return Number.parseInt(ie.charAt(8), 10) === dig; -}; +const validatePB: IeValidator = (ie) => validateMod11Ie(ie); const validatePE: IeValidator = (ie: string) => { if (!checkLength(ie, 9)) return false; @@ -477,18 +391,7 @@ const validatePE: IeValidator = (ie: string) => { ); }; -const validatePI: IeValidator = (ie: string) => { - if (!checkLength(ie, 9)) return false; - - const body = ie.substring(0, 8); - const dig = calcDigitDecreasing({ - body, - startWeight: body.length + 1, - minWeight: 1, - }); - - return Number.parseInt(ie.charAt(8), 10) === dig; -}; +const validatePI: IeValidator = (ie) => validateMod11Ie(ie); const validatePR: IeValidator = (ie: string) => { if (!checkLength(ie, 10)) return false; @@ -517,7 +420,7 @@ const validatePR: IeValidator = (ie: string) => { sum = 0; for (let i = 0; i < bodyWithFirst.length; i++) { - const digit = Number.parseInt(bodyWithFirst.charAt(i), 10); + const digit = bodyWithFirst.charCodeAt(i) - 48; sum += digit * weight; weight--; if (weight === 1) { @@ -545,7 +448,7 @@ const validateRJ: IeValidator = (ie: string) => { let sum = 0; for (let i = 0; i < body.length; i++) { - const digit = Number.parseInt(ie.charAt(i), 10); + const digit = ie.charCodeAt(i) - 48; sum += digit * weight; weight--; if (weight === 1) { @@ -597,7 +500,7 @@ const validateRO: IeValidator = (ie: string) => { let sum = 0; for (let i = 0; i < body.length; i++) { - const digit = Number.parseInt(ie.charAt(i), 10); + const digit = ie.charCodeAt(i) - 48; sum += digit * weight; weight--; if (weight === 1) { @@ -658,64 +561,32 @@ const validateRS: IeValidator = (ie: string) => { return Number.parseInt(ie.charAt(9), 10) === dig; }; -const validateSC: IeValidator = (ie: string) => { - if (!checkLength(ie, 9)) return false; - - const body = ie.substring(0, 8); - const dig = calcDigitDecreasing({ - body, - startWeight: body.length + 1, - minWeight: 1, - }); - - return Number.parseInt(ie.charAt(8), 10) === dig; -}; - -const validateSE: IeValidator = (ie: string) => { - if (!checkLength(ie, 9)) return false; +const validateSC: IeValidator = (ie) => validateMod11Ie(ie); - const body = ie.substring(0, 8); - const dig = calcDigitDecreasing({ - body, - startWeight: body.length + 1, - minWeight: 1, - }); - - return Number.parseInt(ie.charAt(8), 10) === dig; -}; - -const validateSP: IeValidator = (ie: string) => { - if (!checkLength(ie, 12)) return false; +const validateSE: IeValidator = (ie) => validateMod11Ie(ie); - const body = ie.substring(0, 8); - const weightFirst = [1, 3, 4, 5, 6, 7, 8, 10]; +const calcSPDigit = (body: string, weights: readonly number[]): number => { let sum = 0; for (let i = 0; i < body.length; i++) { - const digit = ie.charCodeAt(i) - 48; - sum += digit * weightFirst[i]; + sum += (body.charCodeAt(i) - 48) * weights[i]; } - const dig = sum % 11; - const digitStr = String(dig); - const firstDig = Number.parseInt(digitStr.charAt(digitStr.length - 1), 10); + return (sum % 11) % 10; +}; - const bodyForSecond = ie.substring(0, 11); - let weight = 3; - let sum2 = 0; +const validateSP: IeValidator = (ie: string) => { + if (SP_RURAL_PATTERN.test(ie)) { + const body = ie.substring(1, 9); + const dig = calcSPDigit(body, SP_FIRST_WEIGHTS); - for (let i = 0; i < bodyForSecond.length; i++) { - const digit = ie.charCodeAt(i) - 48; - sum2 += digit * weight; - weight--; - if (weight === 1) { - weight = 10; - } + return Number.parseInt(ie.charAt(9), 10) === dig; } - const dig2 = sum2 % 11; - const digitStr2 = String(dig2); - const secondDig = Number.parseInt(digitStr2.charAt(digitStr2.length - 1), 10); + if (!SP_COMPANY_PATTERN.test(ie)) return false; + + const firstDig = calcSPDigit(ie.substring(0, 8), SP_FIRST_WEIGHTS); + const secondDig = calcSPDigit(ie.substring(0, 11), SP_SECOND_WEIGHTS); return ( Number.parseInt(ie.charAt(8), 10) === firstDig && @@ -726,50 +597,28 @@ const validateSP: IeValidator = (ie: string) => { const validateTO: IeValidator = (ie: string) => { if (!checkLength(ie, [9, 11])) return false; - if (ie.length === 11) { - const begin = ie.substring(2, 4); - const beginWith = ["01", "02", "03", "99"]; - if (beginWith.includes(begin)) { - const body = ie.substring(0, 2) + ie.substring(4, 10); - let weight = 9; - let sum = 0; - - for (let i = 0; i < body.length; i++) { - const digit = Number.parseInt(body.charAt(i), 10); - sum += digit * weight; - weight--; - } - - const rest = sum % 11; - let dig = 11 - rest; - if (rest < 2) { - dig = 0; - } - - return Number.parseInt(ie.charAt(10), 10) === dig; - } - } + const isLegacy = ie.length === 11; - const body = ie.substring(0, 8); + if (isLegacy && !TO_TYPES.includes(ie.substring(2, 4))) return false; + + const body = isLegacy ? ie.substring(0, 2) + ie.substring(4, 10) : ie.substring(0, 8); + const position = isLegacy ? 10 : 8; let weight = 9; let sum = 0; for (let i = 0; i < body.length; i++) { - const digit = ie.charCodeAt(i) - 48; + const digit = body.charCodeAt(i) - 48; sum += digit * weight; weight--; } const rest = sum % 11; - let dig = 11 - rest; - if (rest < 2) { - dig = 0; - } + const dig = rest < 2 ? 0 : 11 - rest; - return Number.parseInt(ie.charAt(8), 10) === dig; + return Number.parseInt(ie.charAt(position), 10) === dig; }; -const IE_VALIDATORS: Record = { +const IE_VALIDATORS: Record = { AC: validateAC, AL: validateAL, AP: validateAP, @@ -797,10 +646,10 @@ const IE_VALIDATORS: Record = { SE: validateSE, SP: validateSP, TO: validateTO, -}; +} satisfies Record; /** - * Validates a Brazilian state registration number (Inscrição Estadual). + * Validates a Brazilian state tax registration number (IE). * * @param {StateCode} stateCode - The state abbreviation (e.g., 'SP', 'RJ', 'MG') * @param {string} ie - The state registration number to validate @@ -809,17 +658,26 @@ const IE_VALIDATORS: Record = { * @example * ```typescript * isValidIe('SP', '110042490114'); // true + * isValidIe('SP', 'P011004243002'); // true * isValidIe('RJ', '12345'); // false * ``` + * + * @see Official: http://www.sintegra.gov.br/insc_est.html */ export const isValidIe = (stateCode: StateCode, ie: string): boolean => { - if (!stateCode || !ie || typeof ie !== "string") return false; + if (!stateCode || typeof stateCode !== "string") return false; + if (typeof ie !== "string" || ie === "") return false; - const digits = sanitizeToDigits(ie); - if (!digits) return false; + const normalizedStateCode = stateCode.toUpperCase(); - const validator = IE_VALIDATORS[stateCode]; + const validator = Object.hasOwn(IE_VALIDATORS, normalizedStateCode) + ? IE_VALIDATORS[normalizedStateCode] + : undefined; if (!validator) return false; - return validator(digits); + const sanitize = normalizedStateCode === "SP" ? sanitizeToAlphanumeric : sanitizeToDigits; + const value = sanitize(ie); + if (!value) return false; + + return validator(value); }; From 979ed6e5129e8ae6f3a004cd3e89ee20074a8edc Mon Sep 17 00:00:00 2001 From: Hyan Mandian <5044101+hyanmandian@users.noreply.github.com> Date: Fri, 11 Sep 2026 17:32:07 -0300 Subject: [PATCH 03/13] fix(cep): reject non-digit characters and drop the dead widenet provider isValidCep only counted digits, so letters mixed with 8 digits passed; it now matches a plain digit regex after stripping the usual separators and accepts number input. Remove the widenet provider from getAddressInfoByCep'"'"'s default list (dead endpoint, HTTP 502); it stayed usable when requested explicitly. getCepInfoByAddress lets the underlying fetch failure propagate instead of wrapping it. --- src/format-cep/constants.ts | 1 - src/format-cep/format-cep.test.ts | 4 +- src/format-cep/format-cep.ts | 23 +++- src/generate-cep/generate-cep.ts | 15 +++ .../get-address-info-by-cep.test.ts | 117 ++++++++++++++++-- .../get-address-info-by-cep.ts | 51 ++++---- .../get-cep-info-by-address.test.ts | 60 ++++++++- .../get-cep-info-by-address.ts | 65 ++++++---- src/is-valid-cep/constants.ts | 1 - src/is-valid-cep/is-valid-cep.test.ts | 29 ++++- src/is-valid-cep/is-valid-cep.ts | 23 ++-- 11 files changed, 311 insertions(+), 78 deletions(-) delete mode 100644 src/format-cep/constants.ts delete mode 100644 src/is-valid-cep/constants.ts diff --git a/src/format-cep/constants.ts b/src/format-cep/constants.ts deleted file mode 100644 index 3122517e..00000000 --- a/src/format-cep/constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const LENGTH = 8; diff --git a/src/format-cep/format-cep.test.ts b/src/format-cep/format-cep.test.ts index 5a191da0..df7764bf 100644 --- a/src/format-cep/format-cep.test.ts +++ b/src/format-cep/format-cep.test.ts @@ -1,5 +1,5 @@ +import { CEP_LENGTH } from "../_internals/constants/cep"; import { describe, expect, it } from "../_internals/test/runtime"; -import { LENGTH } from "./constants"; import { formatCep } from "./format-cep"; describe("formatCep", () => { @@ -15,7 +15,7 @@ describe("formatCep", () => { expect(formatCep("01001000")).toBe("01001-000"); }); - it(`should NOT add digits after the CEP length (${LENGTH})`, () => { + it(`should NOT add digits after the CEP length (${CEP_LENGTH})`, () => { expect(formatCep("01001000000000")).toBe("01001-000"); }); diff --git a/src/format-cep/format-cep.ts b/src/format-cep/format-cep.ts index d92a898a..17e37fdb 100644 --- a/src/format-cep/format-cep.ts +++ b/src/format-cep/format-cep.ts @@ -1,4 +1,5 @@ import { type FormatParams, format } from "../_internals/format/format"; +import { isNullish } from "../_internals/is-nullish/is-nullish"; import { sanitizeToDigits } from "../_internals/sanitize-to-digits/sanitize-to-digits"; export type FormatCepOptions = Pick; @@ -7,13 +8,23 @@ export type FormatCepOptions = Pick; * Formats a given value as a Brazilian postal code (CEP). * * @param {string|number} value - The value to be formatted, either as a string or a number. - * @param {Object} options - Optional formatting options. + * @param {FormatCepOptions} [options] - Optional formatting options. * @param {boolean} options.pad - Whether to pad the value with leading zeros. * @returns {string} The formatted CEP string in the pattern "00000-000". + * + * @example + * ```typescript + * formatCep("01310930"); // "01310-930" + * formatCep("1310930", { pad: true }); // "01310-930" + * ``` + * + * @see Official: https://www.correios.com.br/enviar/precisa-de-ajuda/tudo-sobre-cep */ export const formatCep = (value: string | number, options?: FormatCepOptions): string => - format({ - pad: options?.pad, - value: sanitizeToDigits(value), - pattern: "00000-000", - }); + isNullish(value) + ? "" + : format({ + pad: options?.pad, + value: sanitizeToDigits(value), + pattern: "00000-000", + }); diff --git a/src/generate-cep/generate-cep.ts b/src/generate-cep/generate-cep.ts index c2f5c9c8..07ccf3d1 100644 --- a/src/generate-cep/generate-cep.ts +++ b/src/generate-cep/generate-cep.ts @@ -1,3 +1,18 @@ import { generateRandomNumber } from "../_internals/generate-random-number/generate-random-number"; +/** + * Generates a random Brazilian CEP (postal code). + * + * Uses `Math.random()` internally, so it is not cryptographically secure, do not use for security purposes. + * A CEP has no check digit, so every 8 digit string is structurally valid. + * + * @returns {string} A random 8 digit CEP without formatting. + * + * @example + * ```typescript + * generateCep(); // "01310930" + * ``` + * + * @see Official: https://www.correios.com.br/enviar/precisa-de-ajuda/tudo-sobre-cep + */ export const generateCep = (): string => generateRandomNumber(8); diff --git a/src/get-address-info-by-cep/get-address-info-by-cep.test.ts b/src/get-address-info-by-cep/get-address-info-by-cep.test.ts index b2cdd4c2..981724f6 100644 --- a/src/get-address-info-by-cep/get-address-info-by-cep.test.ts +++ b/src/get-address-info-by-cep/get-address-info-by-cep.test.ts @@ -101,9 +101,7 @@ function shouldRunLiveCepTests() { if (typeof Deno !== "undefined") { return Deno.env.get("RUN_LIVE_CEP_TESTS") === "1"; } - } catch { - // ignore - } + } catch {} try { const maybeProcess = ( @@ -117,9 +115,7 @@ function shouldRunLiveCepTests() { if (maybeProcess?.env?.RUN_LIVE_CEP_TESTS === "1") { return true; } - } catch { - // ignore - } + } catch {} return false; } @@ -132,7 +128,7 @@ describe("getAddressInfoByCep", () => { const originalFetch = globalThis.fetch; beforeEach(() => { - globalThis.fetch = fetchMock as unknown as typeof fetch; + globalThis.fetch = fetchMock as typeof fetch; fetchMock.mockClear(); setupFetchMock(fetchMock); }); @@ -186,13 +182,36 @@ describe("getAddressInfoByCep", () => { }); describe("provider selection", () => { - it("should use all providers by default", async () => { + it("should use the default providers (viacep, brasilapi) and skip the deprecated widenet provider", async () => { const result = await getAddressInfoByCep(VALID_CEP); expect(result).toBeDefined(); expect(result.cep).toBe(VALID_CEP); expect(result.state).toBe("SP"); expect(result.city).toBe("São Paulo"); + + const requestedUrls = fetchMock.mock.calls.map(([input]: [string | URL | Request]) => + typeof input === "string" ? input : input instanceof URL ? input.href : input.url, + ); + expect(requestedUrls.some((url: string) => url.includes("widenet"))).toBe(false); + }); + + it("should still allow widenet to be used when explicitly requested", async () => { + const result = await getAddressInfoByCep(VALID_CEP, { + providers: ["widenet"], + }); + + expect(result).toBeDefined(); + expect(result.cep).toBe(VALID_CEP); + }); + + it("should throw GetAddressInfoByCepValidationError for a providers array made only of inherited Object property names", async () => { + await expect( + getAddressInfoByCep(VALID_CEP, { + // @ts-expect-error + providers: ["constructor", "toString"], + }), + ).rejects.toThrow(GetAddressInfoByCepValidationError); }); it("should use only specified providers", async () => { @@ -310,6 +329,16 @@ describe("getAddressInfoByCep", () => { ); }); + it("should throw GetAddressInfoByCepServiceError when Widenet's HTTP request itself fails", async () => { + setupFetchMock(fetchMock, { + widenet: createJsonResponse({}, 503), + }); + + await expect(getAddressInfoByCep(VALID_CEP, { providers: ["widenet"] })).rejects.toThrow( + GetAddressInfoByCepServiceError, + ); + }); + it("should throw GetAddressInfoByCepNotFoundError when BrasilAPI returns errors", async () => { setupFetchMock(fetchMock, { brasilapi: createJsonResponse({ @@ -394,6 +423,78 @@ describe("getAddressInfoByCep", () => { GetAddressInfoByCepNotFoundError, ); }); + + it("should prioritize GetAddressInfoByCepNotFoundError when mixed across only 2 providers", async () => { + setupFetchMock(fetchMock, { + brasilapi: new Error("Network error"), + viacep: createJsonResponse({ erro: true }), + }); + + await expect( + getAddressInfoByCep("00000000", { providers: ["viacep", "brasilapi"] }), + ).rejects.toThrow(GetAddressInfoByCepNotFoundError); + }); + + it("should throw GetAddressInfoByCepServiceError when 2 providers both fail with network errors", async () => { + setupFetchMock(fetchMock, { + brasilapi: new Error("Network error"), + viacep: new Error("Connection failed"), + }); + + await expect( + getAddressInfoByCep(VALID_CEP, { providers: ["viacep", "brasilapi"] }), + ).rejects.toThrow(GetAddressInfoByCepServiceError); + }); + }); + + describe("missing optional fields default to empty string", () => { + it("should default bairro/localidade/logradouro to empty string for ViaCEP", async () => { + setupFetchMock(fetchMock, { + viacep: createJsonResponse({ cep: "01310-100", uf: "SP" }), + }); + + const result = await getAddressInfoByCep(VALID_CEP, { providers: ["viacep"] }); + + expect(result.neighborhood).toBe(""); + expect(result.city).toBe(""); + expect(result.street).toBe(""); + }); + + it("should default uf to empty string for ViaCEP", async () => { + setupFetchMock(fetchMock, { + viacep: createJsonResponse({ cep: "01310-100" }), + }); + + const result = await getAddressInfoByCep(VALID_CEP, { providers: ["viacep"] }); + + expect(result.state).toBe(""); + }); + + it("should default district/city/address to empty string for Widenet", async () => { + setupFetchMock(fetchMock, { + widenet: createJsonResponse({ code: "01310-100", ok: true, status: 200 }), + }); + + const result = await getAddressInfoByCep(VALID_CEP, { providers: ["widenet"] }); + + expect(result.neighborhood).toBe(""); + expect(result.city).toBe(""); + expect(result.street).toBe(""); + expect(result.state).toBe(""); + }); + + it("should default neighborhood/city/street to empty string for BrasilAPI", async () => { + setupFetchMock(fetchMock, { + brasilapi: createJsonResponse({ cep: VALID_CEP }), + }); + + const result = await getAddressInfoByCep(VALID_CEP, { providers: ["brasilapi"] }); + + expect(result.neighborhood).toBe(""); + expect(result.city).toBe(""); + expect(result.street).toBe(""); + expect(result.state).toBe(""); + }); }); describe("provider integration", () => { diff --git a/src/get-address-info-by-cep/get-address-info-by-cep.ts b/src/get-address-info-by-cep/get-address-info-by-cep.ts index 945e8433..b5c629fc 100644 --- a/src/get-address-info-by-cep/get-address-info-by-cep.ts +++ b/src/get-address-info-by-cep/get-address-info-by-cep.ts @@ -1,7 +1,4 @@ import { fetchWithRetry } from "../_internals/fetch-with-retry/fetch-with-retry"; -/** - * based on https://github.com/BrasilAPI/cep-promise - */ import { sanitizeToDigits } from "../_internals/sanitize-to-digits/sanitize-to-digits"; import { isValidCep } from "../is-valid-cep/is-valid-cep"; @@ -34,16 +31,22 @@ export class GetAddressInfoByCepServiceError extends GetAddressInfoByCepError { } export type AddressInfo = { + /** The 8 digit CEP, no mask. */ cep: string; + /** Two letter state code, e.g. "SP". */ state: string; + /** City name. */ city: string; + /** Neighborhood name, empty when the CEP covers a whole city. */ neighborhood: string; + /** Street name, empty when the CEP covers a whole city. */ street: string; }; export type CepProvider = "viacep" | "widenet" | "brasilapi"; export type GetAddressInfoByCepOptions = { + /** Which CEP services to race, in the order given (default: all of them). */ providers?: CepProvider[]; }; @@ -84,7 +87,7 @@ const fetchViaCep = async (cep: string): Promise => { throw new Error(`ViaCEP request failed with status ${response.status}`); } - const data = (await response.json()) as ViaCepResponse; + const data: ViaCepResponse = await response.json(); if (data.erro || !data.cep) { throw new GetAddressInfoByCepNotFoundError("CEP não encontrado"); @@ -108,7 +111,7 @@ const fetchWidenet = async (cep: string): Promise => { throw new Error(`Widenet request failed with status ${response.status}`); } - const data = (await response.json()) as WidenetResponse; + const data: WidenetResponse = await response.json(); if (data.status !== 200 || !data.ok || !data.code) { throw new GetAddressInfoByCepNotFoundError("CEP não encontrado"); @@ -130,7 +133,7 @@ const fetchBrasilApi = async (cep: string): Promise => { throw new Error(`BrasilAPI request failed with status ${response.status}`); } - const data = (await response.json()) as BrasilApiResponse; + const data: BrasilApiResponse = await response.json(); if (data.errors || !data.cep) { throw new GetAddressInfoByCepNotFoundError("CEP não encontrado"); @@ -157,7 +160,9 @@ const providerMap: Record Promise> = * * @param {string|number} cep - The CEP (Brazilian postal code) to search for. Can be a string or number. * @param {GetAddressInfoByCepOptions} options - Optional configuration for the function. - * @param {CepProvider[]} options.providers - List of providers to use. Defaults to all providers if not specified. + * @param {CepProvider[]} options.providers - List of providers to use. Defaults to `["viacep", "brasilapi"]` + * if not specified (the deprecated `"widenet"` provider is excluded from the default list, but can still + * be requested explicitly). * @returns {Promise} A promise that resolves to the address information. * @throws {GetAddressInfoByCepValidationError} If the CEP format is invalid. * @throws {GetAddressInfoByCepNotFoundError} If the CEP is not found in any of the services. @@ -176,6 +181,10 @@ const providerMap: Record Promise> = * // Using number input * const address = await getAddressInfoByCep(1310100); * ``` + * + * @see Official: https://www.correios.com.br/enviar/precisa-de-ajuda/tudo-sobre-cep + * @see Based on: https://viacep.com.br/ Default `"viacep"` provider. + * @see Based on: https://brasilapi.com.br/docs#tag/CEP Default `"brasilapi"` provider. */ export const getAddressInfoByCep = async ( cep: string | number, @@ -196,12 +205,12 @@ export const getAddressInfoByCep = async ( if (options.providers.length === 0) { throw new GetAddressInfoByCepValidationError("Nenhum provedor válido especificado"); } - providersToUse = options.providers.filter((p) => p in providerMap); + providersToUse = options.providers.filter((p) => Object.hasOwn(providerMap, p)); if (providersToUse.length === 0) { throw new GetAddressInfoByCepValidationError("Nenhum provedor válido especificado"); } } else { - providersToUse = ["viacep", "widenet", "brasilapi"] as CepProvider[]; + providersToUse = ["viacep", "brasilapi"] as CepProvider[]; } const providerPromises = providersToUse.map((provider) => @@ -217,30 +226,22 @@ export const getAddressInfoByCep = async ( const rejections = results.filter((result) => result.status === "rejected"); - const notFoundErrors = rejections.filter((rejection) => { - const error = rejection.reason?.error || rejection.reason; - return error instanceof GetAddressInfoByCepNotFoundError; - }); + const notFoundErrors = rejections.filter( + (rejection) => rejection.reason?.error instanceof GetAddressInfoByCepNotFoundError, + ); - const networkErrors = rejections.filter((rejection) => { - const error = rejection.reason?.error || rejection.reason; - return !(error instanceof GetAddressInfoByCepNotFoundError); - }); + const networkErrors = rejections.filter( + (rejection) => !(rejection.reason?.error instanceof GetAddressInfoByCepNotFoundError), + ); if (notFoundErrors.length === rejections.length) { throw new GetAddressInfoByCepNotFoundError("CEP não encontrado em nenhum serviço"); } if (networkErrors.length === rejections.length) { - throw new GetAddressInfoByCepServiceError( - "Todos os serviços estão fora de serviço ou indisponíveis", - ); - } - - if (notFoundErrors.length > 0) { - throw new GetAddressInfoByCepNotFoundError("CEP não encontrado em nenhum serviço"); + throw new GetAddressInfoByCepServiceError("Erro ao consultar os serviços de CEP"); } - throw new GetAddressInfoByCepServiceError("Erro ao consultar os serviços de CEP"); + throw new GetAddressInfoByCepNotFoundError("CEP não encontrado em nenhum serviço"); } }; diff --git a/src/get-cep-info-by-address/get-cep-info-by-address.test.ts b/src/get-cep-info-by-address/get-cep-info-by-address.test.ts index 01eabd0c..bc2e3c24 100644 --- a/src/get-cep-info-by-address/get-cep-info-by-address.test.ts +++ b/src/get-cep-info-by-address/get-cep-info-by-address.test.ts @@ -1,6 +1,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "../_internals/test/runtime"; import { GetCepInfoByAddressError, + GetCepInfoByAddressNotFoundError, GetCepInfoByAddressValidationError, getCepInfoByAddress, } from "./get-cep-info-by-address"; @@ -10,7 +11,7 @@ describe("getCepInfoByAddress", () => { const originalFetch = globalThis.fetch; beforeEach(() => { - globalThis.fetch = fetchMock as unknown as typeof fetch; + globalThis.fetch = fetchMock as typeof fetch; fetchMock.mockClear(); }); @@ -29,6 +30,59 @@ describe("getCepInfoByAddress", () => { ).rejects.toThrow(GetCepInfoByAddressValidationError); }); + it("should throw GetCepInfoByAddressValidationError for an empty city", async () => { + await expect( + getCepInfoByAddress({ + federalUnit: "SP", + city: "", + street: "Avenida Paulista", + }), + ).rejects.toThrow(GetCepInfoByAddressValidationError); + expect(fetchMock).not.toHaveBeenCalled(); + }); + + it("should throw GetCepInfoByAddressValidationError for an empty street", async () => { + await expect( + getCepInfoByAddress({ + federalUnit: "SP", + city: "São Paulo", + street: "", + }), + ).rejects.toThrow(GetCepInfoByAddressValidationError); + expect(fetchMock).not.toHaveBeenCalled(); + }); + + it("should throw GetCepInfoByAddressError when the response is not ok", async () => { + fetchMock.mockResolvedValueOnce({ + json: async () => ({}), + ok: false, + status: 500, + }); + + await expect( + getCepInfoByAddress({ + federalUnit: "SP", + city: "São Paulo", + street: "Avenida Paulista", + }), + ).rejects.toThrow(GetCepInfoByAddressError); + }); + + it("should throw GetCepInfoByAddressNotFoundError when ViaCEP returns an empty array", async () => { + fetchMock.mockResolvedValueOnce({ + json: async () => [], + ok: true, + }); + + await expect( + getCepInfoByAddress({ + federalUnit: "SP", + city: "Cidade Inexistente", + street: "Rua Inexistente", + }), + ).rejects.toThrow(GetCepInfoByAddressNotFoundError); + }); + it("should return addresses from ViaCEP", async () => { fetchMock.mockResolvedValueOnce({ ok: true, @@ -60,7 +114,7 @@ describe("getCepInfoByAddress", () => { ]); }); - it("should wrap transport failures with GetCepInfoByAddressError", async () => { + it("should retry and then propagate transport failures unwrapped", async () => { fetchMock.mockRejectedValueOnce( Object.assign(new TypeError("fetch failed"), { cause: { code: "UND_ERR_SOCKET" }, @@ -83,7 +137,7 @@ describe("getCepInfoByAddress", () => { city: "São Paulo", street: "Avenida Paulista", }), - ).rejects.toThrow(GetCepInfoByAddressError); + ).rejects.toThrow(TypeError); expect(fetchMock).toHaveBeenCalledTimes(3); }); }); diff --git a/src/get-cep-info-by-address/get-cep-info-by-address.ts b/src/get-cep-info-by-address/get-cep-info-by-address.ts index 51344ba7..6190bb6f 100644 --- a/src/get-cep-info-by-address/get-cep-info-by-address.ts +++ b/src/get-cep-info-by-address/get-cep-info-by-address.ts @@ -1,5 +1,6 @@ import { DATA as STATES, type StateCode } from "../_internals/constants/states"; import { fetchWithRetry } from "../_internals/fetch-with-retry/fetch-with-retry"; +import { removeAccents } from "../remove-accents/remove-accents"; export class GetCepInfoByAddressError extends Error { constructor(message: string) { @@ -23,35 +24,64 @@ export class GetCepInfoByAddressNotFoundError extends GetCepInfoByAddressError { } export type CepAddressInfo = { + /** The CEP, masked as "00000-000" the way ViaCEP returns it. */ cep: string; + /** Street name. */ logradouro: string; + /** Extra address information, e.g. a house number range. */ complemento: string; + /** Neighborhood name. */ bairro: string; + /** City name. */ localidade: string; + /** Two letter state code, e.g. "SP". */ uf: string; + /** The 7 digit IBGE municipality code. */ ibge?: string; + /** GIA code, used by the São Paulo state tax authority. */ gia?: string; + /** Area code (DDD) of the city. */ ddd?: string; + /** SIAFI code of the municipality. */ siafi?: string; }; export type GetCepInfoByAddressOptions = { + /** Two letter state code, e.g. "SP". */ federalUnit: string; + /** City name. Must not be empty; ViaCEP itself rejects values shorter than 3 characters. */ city: string; + /** Street name or part of it. Must not be empty; ViaCEP itself rejects values shorter than 3 characters. */ street: string; }; -const VALID_STATE_CODES = new Set(STATES.map((state) => state.code)); - const isStateCode = (value: string): value is StateCode => - VALID_STATE_CODES.has(value as StateCode); - -const normalizeAddressPart = (value: string): string => - value - .normalize("NFD") - .replace(/[\u0300-\u036f]/g, "") - .trim(); - + STATES.some((state) => state.code === value); + +const normalizeAddressPart = (value: string): string => removeAccents(value).trim(); + +/** + * Looks every CEP of a Brazilian street up on the ViaCEP API. + * + * @param {GetCepInfoByAddressOptions} params - The address to look up. + * @param {string} params.federalUnit - The two letter state code (e.g. "SP"). + * @param {string} params.city - The city name. + * @param {string} params.street - The street name, or part of it. + * @returns {Promise} Every address matching the query. + * @throws {GetCepInfoByAddressValidationError} When the UF, city or street is missing or invalid. + * @throws {GetCepInfoByAddressNotFoundError} When no address matches the query. + * @throws {GetCepInfoByAddressError} When ViaCEP answers with an HTTP error status. A request + * that cannot be performed at all rejects with the underlying `fetch` error instead. + * + * @example + * ```typescript + * await getCepInfoByAddress({ federalUnit: "SP", city: "São Paulo", street: "Avenida Paulista" }); + * // [{ cep: "01310-100", street: "Avenida Paulista", ... }] + * ``` + * + * @see Official: https://www.correios.com.br/enviar/precisa-de-ajuda/tudo-sobre-cep + * @see Based on: https://viacep.com.br/ + */ export const getCepInfoByAddress = async ({ federalUnit, city, @@ -67,22 +97,15 @@ export const getCepInfoByAddress = async ({ throw new GetCepInfoByAddressValidationError("City and street are required"); } - let response: Response; - try { - response = await fetchWithRetry( - `https://viacep.com.br/ws/${normalizedUf}/${encodeURIComponent(normalizeAddressPart(city))}/${encodeURIComponent(normalizeAddressPart(street))}/json/`, - ); - } catch (error) { - throw new GetCepInfoByAddressError( - error instanceof Error ? error.message : "ViaCEP request failed", - ); - } + const response = await fetchWithRetry( + `https://viacep.com.br/ws/${normalizedUf}/${encodeURIComponent(normalizeAddressPart(city))}/${encodeURIComponent(normalizeAddressPart(street))}/json/`, + ); if (!response.ok) { throw new GetCepInfoByAddressError(`ViaCEP request failed with status ${response.status}`); } - const data = (await response.json()) as CepAddressInfo[]; + const data: CepAddressInfo[] = await response.json(); if (!Array.isArray(data) || data.length === 0) { throw new GetCepInfoByAddressNotFoundError(`${normalizedUf} - ${city} - ${street}`); diff --git a/src/is-valid-cep/constants.ts b/src/is-valid-cep/constants.ts deleted file mode 100644 index 3122517e..00000000 --- a/src/is-valid-cep/constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const LENGTH = 8; diff --git a/src/is-valid-cep/is-valid-cep.test.ts b/src/is-valid-cep/is-valid-cep.test.ts index 5b94714f..a6fd84bd 100644 --- a/src/is-valid-cep/is-valid-cep.test.ts +++ b/src/is-valid-cep/is-valid-cep.test.ts @@ -1,5 +1,4 @@ import { describe, expect, test } from "../_internals/test/runtime"; -import { LENGTH } from "./constants"; import { isValidCep } from "./is-valid-cep"; describe("isValidCep", () => { @@ -18,13 +17,23 @@ describe("isValidCep", () => { expect(isValidCep(undefined)).toBe(false); }); - test(`when length is less than ${LENGTH}`, () => { + test("when it is an object", () => { + // @ts-expect-error + expect(isValidCep({})).toBe(false); + }); + + test("when length is less than 8", () => { expect(isValidCep("12345")).toBe(false); }); - test(`when length is greater than ${LENGTH}`, () => { + test("when length is greater than 8", () => { expect(isValidCep("123456789")).toBe(false); }); + + test("when it contains letters", () => { + expect(isValidCep("abc01310100")).toBe(false); + expect(isValidCep("0131010a")).toBe(false); + }); }); describe("should return true", () => { @@ -35,5 +44,19 @@ describe("isValidCep", () => { test("when is a CEP valid with mask", () => { expect(isValidCep("01310-100")).toBe(true); }); + + test("when is a CEP valid as a number", () => { + expect(isValidCep(20040020)).toBe(true); + }); + + test("when is a CEP valid with leading/trailing whitespace", () => { + expect(isValidCep(" 01310-100 ")).toBe(true); + }); + + test("when is a CEP valid with any punctuation the published version accepted", () => { + expect(isValidCep("92.500-000")).toBe(true); + expect(isValidCep("013 10 100")).toBe(true); + expect(isValidCep("01310.100")).toBe(true); + }); }); }); diff --git a/src/is-valid-cep/is-valid-cep.ts b/src/is-valid-cep/is-valid-cep.ts index b70eeefa..69a5d299 100644 --- a/src/is-valid-cep/is-valid-cep.ts +++ b/src/is-valid-cep/is-valid-cep.ts @@ -1,23 +1,30 @@ -import { sanitizeToDigits } from "../_internals/sanitize-to-digits/sanitize-to-digits"; -import { LENGTH } from "./constants"; +const SEPARATORS_REGEX = /[\s.-]/g; + +const CEP_REGEX = /^\d{8}$/; /** * Validates if a CEP (Brazilian postal code) is valid. * - * @param {string} cep - The CEP value to be validated. + * Spaces, dots and hyphens are ignored, so every punctuated form of a CEP is accepted, but + * any other character, a letter in particular, makes the value invalid. + * + * @param {string|number} cep - The CEP value to be validated. * @returns {boolean} True if the CEP is valid, false otherwise. * * @example * ```typescript * isValidCep("01310100"); // true * isValidCep("01310-100"); // true + * isValidCep("92.500-000"); // true + * isValidCep(20040020); // true + * isValidCep("abc01310100"); // false (invalid format) * isValidCep("12345"); // false (invalid length) * ``` + * + * @see Official: https://www.correios.com.br/enviar/precisa-de-ajuda/tudo-sobre-cep */ -export const isValidCep = (cep: string): boolean => { - if (!cep || typeof cep !== "string") return false; - - const digits = sanitizeToDigits(cep); +export const isValidCep = (cep: string | number): boolean => { + if (typeof cep !== "string" && typeof cep !== "number") return false; - return digits.length === LENGTH; + return CEP_REGEX.test(String(cep).replace(SEPARATORS_REGEX, "")); }; From 476e5a462f36d8823f47f4bb649082e8ca6a23c3 Mon Sep 17 00:00:00 2001 From: Hyan Mandian <5044101+hyanmandian@users.noreply.github.com> Date: Fri, 11 Sep 2026 17:32:07 -0300 Subject: [PATCH 04/13] fix(states): sort by pt-BR locale and return fresh, mutation-safe copies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit getStates() used code-unit order ("Pará" after "Paraná"); it now sorts with localeCompare in the "pt-BR" locale. getStates() returned the shared internal array, so mutating the result corrupted data for every subsequent call; it now returns fresh objects on every call. Add ibgeCode to each State entry (the 2-digit IBGE code of the Federative Unit). --- scripts/states.ts | 34 +++++++++++-- src/_internals/constants/states.ts | 78 +++++++++++++++++++----------- src/get-states/get-states.test.ts | 51 ++++++++++++++++++- src/get-states/get-states.ts | 18 +++++-- 4 files changed, 145 insertions(+), 36 deletions(-) diff --git a/scripts/states.ts b/scripts/states.ts index fa3fc730..9468e43e 100644 --- a/scripts/states.ts +++ b/scripts/states.ts @@ -19,6 +19,23 @@ type State = { }; }; +const isState = (value: unknown): value is State => + typeof value === "object" && + value !== null && + "id" in value && + typeof value.id === "number" && + "sigla" in value && + typeof value.sigla === "string" && + "nome" in value && + typeof value.nome === "string" && + "regiao" in value && + typeof value.regiao === "object" && + value.regiao !== null && + "sigla" in value.regiao && + typeof value.regiao.sigla === "string" && + "nome" in value.regiao && + typeof value.regiao.nome === "string"; + const main = async () => { const response = await fetchWithRetry( "https://servicodados.ibge.gov.br/api/v1/localidades/estados", @@ -28,21 +45,32 @@ const main = async () => { throw new Error(`IBGE states request failed with status ${response.status}`); } - const json = (await response.json()) as State[]; + const json: unknown = await response.json(); + + if (!Array.isArray(json) || json.length === 0 || !json.every(isState)) { + throw new Error( + "IBGE states payload is not an array of states with id, sigla, nome and regiao", + ); + } const states = json - .sort((cityA, cityB) => (cityA.nome > cityB.nome ? 1 : -1)) + .sort((stateA, stateB) => stateA.nome.localeCompare(stateB.nome, "pt-BR")) .map((state) => ({ code: state.sigla, name: state.nome, regionCode: state.regiao.sigla, regionName: state.regiao.nome, + ibgeCode: state.id, })); await writeFile( resolve(scriptsDir, "..", "./src/_internals/constants/states.ts"), `/** - * @type {Array<{code: string, name: string, regionCode: string, regionName: string}>} + * Brazilian states published by the IBGE, sorted by name with \`localeCompare\` in the "pt-BR" + * locale. \`ibgeCode\` is the 2-digit IBGE code of the Federative Unit ("cUF"), the same code + * found in the first field of every DF-e access key (chave de acesso). + * + * @see https://servicodados.ibge.gov.br/api/docs/localidades */ export const DATA = ${JSON.stringify(states)} as const diff --git a/src/_internals/constants/states.ts b/src/_internals/constants/states.ts index 13983fb8..88a455fb 100644 --- a/src/_internals/constants/states.ts +++ b/src/_internals/constants/states.ts @@ -1,34 +1,56 @@ /** - * @type {Array<{code: string, name: string, regionCode: string, regionName: string}>} + * Brazilian states published by the IBGE, sorted by name with `localeCompare` in the "pt-BR" + * locale. `ibgeCode` is the 2-digit IBGE code of the Federative Unit ("cUF"), the same code + * found in the first field of every DF-e access key (chave de acesso). + * + * @see https://servicodados.ibge.gov.br/api/docs/localidades */ export const DATA = [ - { code: "AC", name: "Acre", regionCode: "N", regionName: "Norte" }, - { code: "AL", name: "Alagoas", regionCode: "NE", regionName: "Nordeste" }, - { code: "AP", name: "Amapá", regionCode: "N", regionName: "Norte" }, - { code: "AM", name: "Amazonas", regionCode: "N", regionName: "Norte" }, - { code: "BA", name: "Bahia", regionCode: "NE", regionName: "Nordeste" }, - { code: "CE", name: "Ceará", regionCode: "NE", regionName: "Nordeste" }, - { code: "DF", name: "Distrito Federal", regionCode: "CO", regionName: "Centro-Oeste" }, - { code: "ES", name: "Espírito Santo", regionCode: "SE", regionName: "Sudeste" }, - { code: "GO", name: "Goiás", regionCode: "CO", regionName: "Centro-Oeste" }, - { code: "MA", name: "Maranhão", regionCode: "NE", regionName: "Nordeste" }, - { code: "MT", name: "Mato Grosso", regionCode: "CO", regionName: "Centro-Oeste" }, - { code: "MS", name: "Mato Grosso do Sul", regionCode: "CO", regionName: "Centro-Oeste" }, - { code: "MG", name: "Minas Gerais", regionCode: "SE", regionName: "Sudeste" }, - { code: "PR", name: "Paraná", regionCode: "S", regionName: "Sul" }, - { code: "PB", name: "Paraíba", regionCode: "NE", regionName: "Nordeste" }, - { code: "PA", name: "Pará", regionCode: "N", regionName: "Norte" }, - { code: "PE", name: "Pernambuco", regionCode: "NE", regionName: "Nordeste" }, - { code: "PI", name: "Piauí", regionCode: "NE", regionName: "Nordeste" }, - { code: "RN", name: "Rio Grande do Norte", regionCode: "NE", regionName: "Nordeste" }, - { code: "RS", name: "Rio Grande do Sul", regionCode: "S", regionName: "Sul" }, - { code: "RJ", name: "Rio de Janeiro", regionCode: "SE", regionName: "Sudeste" }, - { code: "RO", name: "Rondônia", regionCode: "N", regionName: "Norte" }, - { code: "RR", name: "Roraima", regionCode: "N", regionName: "Norte" }, - { code: "SC", name: "Santa Catarina", regionCode: "S", regionName: "Sul" }, - { code: "SE", name: "Sergipe", regionCode: "NE", regionName: "Nordeste" }, - { code: "SP", name: "São Paulo", regionCode: "SE", regionName: "Sudeste" }, - { code: "TO", name: "Tocantins", regionCode: "N", regionName: "Norte" }, + { code: "AC", name: "Acre", regionCode: "N", regionName: "Norte", ibgeCode: 12 }, + { code: "AL", name: "Alagoas", regionCode: "NE", regionName: "Nordeste", ibgeCode: 27 }, + { code: "AP", name: "Amapá", regionCode: "N", regionName: "Norte", ibgeCode: 16 }, + { code: "AM", name: "Amazonas", regionCode: "N", regionName: "Norte", ibgeCode: 13 }, + { code: "BA", name: "Bahia", regionCode: "NE", regionName: "Nordeste", ibgeCode: 29 }, + { code: "CE", name: "Ceará", regionCode: "NE", regionName: "Nordeste", ibgeCode: 23 }, + { + code: "DF", + name: "Distrito Federal", + regionCode: "CO", + regionName: "Centro-Oeste", + ibgeCode: 53, + }, + { code: "ES", name: "Espírito Santo", regionCode: "SE", regionName: "Sudeste", ibgeCode: 32 }, + { code: "GO", name: "Goiás", regionCode: "CO", regionName: "Centro-Oeste", ibgeCode: 52 }, + { code: "MA", name: "Maranhão", regionCode: "NE", regionName: "Nordeste", ibgeCode: 21 }, + { code: "MT", name: "Mato Grosso", regionCode: "CO", regionName: "Centro-Oeste", ibgeCode: 51 }, + { + code: "MS", + name: "Mato Grosso do Sul", + regionCode: "CO", + regionName: "Centro-Oeste", + ibgeCode: 50, + }, + { code: "MG", name: "Minas Gerais", regionCode: "SE", regionName: "Sudeste", ibgeCode: 31 }, + { code: "PA", name: "Pará", regionCode: "N", regionName: "Norte", ibgeCode: 15 }, + { code: "PB", name: "Paraíba", regionCode: "NE", regionName: "Nordeste", ibgeCode: 25 }, + { code: "PR", name: "Paraná", regionCode: "S", regionName: "Sul", ibgeCode: 41 }, + { code: "PE", name: "Pernambuco", regionCode: "NE", regionName: "Nordeste", ibgeCode: 26 }, + { code: "PI", name: "Piauí", regionCode: "NE", regionName: "Nordeste", ibgeCode: 22 }, + { code: "RJ", name: "Rio de Janeiro", regionCode: "SE", regionName: "Sudeste", ibgeCode: 33 }, + { + code: "RN", + name: "Rio Grande do Norte", + regionCode: "NE", + regionName: "Nordeste", + ibgeCode: 24, + }, + { code: "RS", name: "Rio Grande do Sul", regionCode: "S", regionName: "Sul", ibgeCode: 43 }, + { code: "RO", name: "Rondônia", regionCode: "N", regionName: "Norte", ibgeCode: 11 }, + { code: "RR", name: "Roraima", regionCode: "N", regionName: "Norte", ibgeCode: 14 }, + { code: "SC", name: "Santa Catarina", regionCode: "S", regionName: "Sul", ibgeCode: 42 }, + { code: "SP", name: "São Paulo", regionCode: "SE", regionName: "Sudeste", ibgeCode: 35 }, + { code: "SE", name: "Sergipe", regionCode: "NE", regionName: "Nordeste", ibgeCode: 28 }, + { code: "TO", name: "Tocantins", regionCode: "N", regionName: "Norte", ibgeCode: 17 }, ] as const; export type State = (typeof DATA)[number]; diff --git a/src/get-states/get-states.test.ts b/src/get-states/get-states.test.ts index 3d5a05fe..2ef7291a 100644 --- a/src/get-states/get-states.test.ts +++ b/src/get-states/get-states.test.ts @@ -2,12 +2,59 @@ import { DATA } from "../_internals/constants/states"; import { describe, expect, it } from "../_internals/test/runtime"; import { getStates } from "./get-states"; +const NUMBER_OF_BRAZILIAN_STATES = 27; + describe("getStates", () => { it(`should return an array with ${DATA.length} states`, () => { expect(getStates().length).toBe(DATA.length); }); - it("should return an array sort by name", () => { - expect(getStates()).toEqual(DATA); + it(`should return exactly ${NUMBER_OF_BRAZILIAN_STATES} states`, () => { + expect(getStates().length).toBe(NUMBER_OF_BRAZILIAN_STATES); + }); + + it("should return an array sorted by name with the pt-BR comparator", () => { + const names = getStates().map((state) => state.name); + const sortedNames = [...names].sort((a, b) => a.localeCompare(b, "pt-BR")); + + expect(names).toEqual(sortedNames); + }); + + it("should sort Pará, Paraíba and Paraná the way a Brazilian reader expects", () => { + const names = getStates().map((state) => state.name); + + const paraIndex = names.indexOf("Pará"); + const paraibaIndex = names.indexOf("Paraíba"); + const paranaIndex = names.indexOf("Paraná"); + + expect(paraIndex).toBeLessThan(paraibaIndex); + expect(paraibaIndex).toBeLessThan(paranaIndex); + }); + + it("should sort Rio de Janeiro, Rio Grande do Norte and Rio Grande do Sul the way a Brazilian reader expects", () => { + const names = getStates().map((state) => state.name); + + const rioDeJaneiroIndex = names.indexOf("Rio de Janeiro"); + const rioGrandeDoNorteIndex = names.indexOf("Rio Grande do Norte"); + const rioGrandeDoSulIndex = names.indexOf("Rio Grande do Sul"); + + expect(rioDeJaneiroIndex).toBeLessThan(rioGrandeDoNorteIndex); + expect(rioGrandeDoNorteIndex).toBeLessThan(rioGrandeDoSulIndex); + }); + + it("should sort São Paulo before Sergipe", () => { + const names = getStates().map((state) => state.name); + + expect(names.indexOf("São Paulo")).toBeLessThan(names.indexOf("Sergipe")); + }); + + it("should return unique deep copies so mutating the result does not leak between calls", () => { + const first = getStates(); + + Object.assign(first[0], { name: "X" }); + + const second = getStates(); + expect(second[0].name).not.toBe("X"); + expect(second).toEqual(DATA.map((state) => ({ ...state }))); }); }); diff --git a/src/get-states/get-states.ts b/src/get-states/get-states.ts index e2dbf838..7f85c11b 100644 --- a/src/get-states/get-states.ts +++ b/src/get-states/get-states.ts @@ -4,9 +4,21 @@ import { DATA, type State } from "../_internals/constants/states"; * Retrieves a list of all Brazilian states with their codes and names. * * Returns an array of state objects containing the two-letter state code - * and the full state name. The list is automatically sorted alphabetically - * by state name. + * and the full state name. The list is sorted by state name with `localeCompare` + * in the "pt-BR" locale, so accented names land where a Brazilian reader expects + * them: Pará, Paraíba, Paraná and Rio de Janeiro, Rio Grande do Norte, Rio Grande do Sul. + * + * Each call returns a fresh array of fresh objects, so mutating the result + * (e.g. `getStates()[0].name = "X"`) never affects the underlying data or + * subsequent calls. * * @returns {State[]} An array of all Brazilian states sorted by name + * + * @example + * ```typescript + * getStates()[0]; // { code: "AC", name: "Acre", regionCode: "N", regionName: "Norte", ibgeCode: 12 } + * ``` + * + * @see Official: https://servicodados.ibge.gov.br/api/docs/localidades */ -export const getStates = (): State[] => [...DATA]; +export const getStates = (): State[] => DATA.map((state) => ({ ...state })); From 27cc5dda671bd25c62ed19a3424403f8193ebdd5 Mon Sep 17 00:00:00 2001 From: Hyan Mandian <5044101+hyanmandian@users.noreply.github.com> Date: Fri, 11 Sep 2026 17:32:07 -0300 Subject: [PATCH 05/13] fix(cities): return fresh copies and cache the combined list getCities() returned the shared internal arrays, the same mutation hazard as getStates(); it now returns fresh, mutation-safe arrays (Object.hasOwn replaces the `in` check) and the all-cities list is computed once and cached. scripts/cities.ts: fall back to the IBGE regiao-imediata field when microrregiao is absent from the API response. --- scripts/cities.ts | 112 +- src/_internals/constants/cities.ts | 11180 +++++++++++++-------------- src/get-cities/get-cities.test.ts | 60 +- src/get-cities/get-cities.ts | 29 +- 4 files changed, 5736 insertions(+), 5645 deletions(-) diff --git a/scripts/cities.ts b/scripts/cities.ts index 39d21a70..a2384dff 100644 --- a/scripts/cities.ts +++ b/scripts/cities.ts @@ -4,10 +4,13 @@ import { writeFile } from "node:fs/promises"; import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; +import { DATA as STATES } from "../src/_internals/constants/states.ts"; import { fetchWithRetry } from "../src/_internals/fetch-with-retry/fetch-with-retry.ts"; const scriptsDir = dirname(fileURLToPath(import.meta.url)); +const STATE_CODES = STATES.map((state) => state.code); + type City = { id: number; nome: string; @@ -28,6 +31,19 @@ type City = { }; }; }; + } | null; + "regiao-imediata": { + id: number; + nome: string; + "regiao-intermediaria": { + id: number; + nome: string; + UF: { + id: number; + sigla: string; + nome: string; + }; + }; }; }; @@ -40,57 +56,71 @@ const main = async () => { throw new Error(`IBGE municipalities request failed with status ${response.status}`); } - const json = (await response.json()) as City[]; + const json: City[] = await response.json(); - const cities = Object.fromEntries( - Object.entries( - json.reduce( - (acc, city) => { - const stateInitials = city?.microrregiao?.mesorregiao?.UF?.sigla; + const byState = json.reduce( + (acc, city) => { + const stateInitials = + city?.microrregiao?.mesorregiao?.UF?.sigla ?? + city?.["regiao-imediata"]?.["regiao-intermediaria"]?.UF?.sigla; - if (!stateInitials) return acc; + if (!stateInitials) return acc; - if (!acc[stateInitials]) { - acc[stateInitials] = []; - } + if (!acc[stateInitials]) { + acc[stateInitials] = []; + } - acc[stateInitials].push(city.nome); + acc[stateInitials].push([city.nome, String(city.id)]); - return acc; - }, - {} as Record, - ), - ) - .sort(([a], [b]) => a.localeCompare(b)) - .map(([state, cityNames]) => [state, cityNames.sort((a, b) => a.localeCompare(b))]), + return acc; + }, + {} as Record, ); + const sortedEntries = Object.entries(byState) + .sort(([a], [b]) => a.localeCompare(b)) + .map( + ([state, tuples]) => [state, tuples.sort(([a], [b]) => a.localeCompare(b, "pt-BR"))] as const, + ); + + const body = sortedEntries + .map(([state, tuples]) => { + const rows = tuples.map(([name, code]) => `\t\t[${JSON.stringify(name)}, "${code}"],`); + + return `\t${state}: [\n${rows.join("\n")}\n\t],`; + }) + .join("\n"); + + const missingStates = STATE_CODES.filter((code) => !byState[code]); + + if (missingStates.length > 0) { + throw new Error(`IBGE response is missing municipalities for: ${missingStates.join(", ")}`); + } + await writeFile( resolve(scriptsDir, "..", "./src/_internals/constants/cities.ts"), - `/** - * A collection of Brazilian cities categorized by their respective states. - * - * @constant - * @type {Object} - * @property {string[]} GO - Cities in the state of Goiás. - * @property {string[]} MG - Cities in the state of Minas Gerais. - * @property {string[]} PA - Cities in the state of Pará. - * @property {string[]} CE - Cities in the state of Ceará. - * @property {string[]} BA - Cities in the state of Bahia. - * @property {string[]} PR - Cities in the state of Paraná. - * @property {string[]} SC - Cities in the state of Santa Catarina. - * @property {string[]} PE - Cities in the state of Pernambuco. - * @property {string[]} TO - Cities in the state of Tocantins. - * @property {string[]} RN - Cities in the state of Rio Grande do Norte. - * @property {string[]} PI - Cities in the state of Piauí. - * @property {string[]} RS - Cities in the state of Rio Grande do Sul. - * @property {string[]} MT - Cities in the state of Mato Grosso. - * @property {string[]} AC - Cities in the state of Acre. - * @property {string[]} SP - Cities in the state of São Paulo. - * @property {string[]} ES - Cities in the state of Espírito Santo. - * @property {string[]} MA - Cities in the state of Maranhão. + `import type { StateCode } from "./states"; + +/** + * Brazilian municipalities by state, published by the IBGE. \`DATA\` holds, for each state, a + * \`[name, ibgeCode]\` tuple per municipality, sorted by name with \`localeCompare\` in the + * "pt-BR" locale. Generated by \`scripts/cities.ts\`. + * + * @see https://servicodados.ibge.gov.br/api/docs/localidades */ -export const DATA = ${JSON.stringify(cities)} as const`, +export type Municipality = { + /** The 7-digit IBGE municipality code. */ + code: string; + /** The municipality name. */ + name: string; + /** The two-letter code of the state the municipality belongs to. */ + stateCode: StateCode; +}; + +export const DATA: Record = { +${body} +}; +`, ); }; diff --git a/src/_internals/constants/cities.ts b/src/_internals/constants/cities.ts index 26cc8338..100e7509 100644 --- a/src/_internals/constants/cities.ts +++ b/src/_internals/constants/cities.ts @@ -1,5647 +1,5643 @@ +import type { StateCode } from "./states"; + /** - * A collection of Brazilian cities categorized by their respective states. + * Brazilian municipalities by state, published by the IBGE. `DATA` holds, for each state, a + * `[name, ibgeCode]` tuple per municipality, sorted by name with `localeCompare` in the + * "pt-BR" locale. Generated by `scripts/cities.ts`. * - * @constant - * @type {Object} - * @property {string[]} GO - Cities in the state of Goiás. - * @property {string[]} MG - Cities in the state of Minas Gerais. - * @property {string[]} PA - Cities in the state of Pará. - * @property {string[]} CE - Cities in the state of Ceará. - * @property {string[]} BA - Cities in the state of Bahia. - * @property {string[]} PR - Cities in the state of Paraná. - * @property {string[]} SC - Cities in the state of Santa Catarina. - * @property {string[]} PE - Cities in the state of Pernambuco. - * @property {string[]} TO - Cities in the state of Tocantins. - * @property {string[]} RN - Cities in the state of Rio Grande do Norte. - * @property {string[]} PI - Cities in the state of Piauí. - * @property {string[]} RS - Cities in the state of Rio Grande do Sul. - * @property {string[]} MT - Cities in the state of Mato Grosso. - * @property {string[]} AC - Cities in the state of Acre. - * @property {string[]} SP - Cities in the state of São Paulo. - * @property {string[]} ES - Cities in the state of Espírito Santo. - * @property {string[]} MA - Cities in the state of Maranhão. + * @see https://servicodados.ibge.gov.br/api/docs/localidades */ -export const DATA = { +export type Municipality = { + /** The 7-digit IBGE municipality code. */ + code: string; + /** The municipality name. */ + name: string; + /** The two-letter code of the state the municipality belongs to. */ + stateCode: StateCode; +}; + +export const DATA: Record = { AC: [ - "Acrelândia", - "Assis Brasil", - "Brasiléia", - "Bujari", - "Capixaba", - "Cruzeiro do Sul", - "Epitaciolândia", - "Feijó", - "Jordão", - "Mâncio Lima", - "Manoel Urbano", - "Marechal Thaumaturgo", - "Plácido de Castro", - "Porto Acre", - "Porto Walter", - "Rio Branco", - "Rodrigues Alves", - "Santa Rosa do Purus", - "Sena Madureira", - "Senador Guiomard", - "Tarauacá", - "Xapuri", + ["Acrelândia", "1200013"], + ["Assis Brasil", "1200054"], + ["Brasiléia", "1200104"], + ["Bujari", "1200138"], + ["Capixaba", "1200179"], + ["Cruzeiro do Sul", "1200203"], + ["Epitaciolândia", "1200252"], + ["Feijó", "1200302"], + ["Jordão", "1200328"], + ["Mâncio Lima", "1200336"], + ["Manoel Urbano", "1200344"], + ["Marechal Thaumaturgo", "1200351"], + ["Plácido de Castro", "1200385"], + ["Porto Acre", "1200807"], + ["Porto Walter", "1200393"], + ["Rio Branco", "1200401"], + ["Rodrigues Alves", "1200427"], + ["Santa Rosa do Purus", "1200435"], + ["Sena Madureira", "1200500"], + ["Senador Guiomard", "1200450"], + ["Tarauacá", "1200609"], + ["Xapuri", "1200708"], ], AL: [ - "Água Branca", - "Anadia", - "Arapiraca", - "Atalaia", - "Barra de Santo Antônio", - "Barra de São Miguel", - "Batalha", - "Belém", - "Belo Monte", - "Boca da Mata", - "Branquinha", - "Cacimbinhas", - "Cajueiro", - "Campestre", - "Campo Alegre", - "Campo Grande", - "Canapi", - "Capela", - "Carneiros", - "Chã Preta", - "Coité do Nóia", - "Colônia Leopoldina", - "Coqueiro Seco", - "Coruripe", - "Craíbas", - "Delmiro Gouveia", - "Dois Riachos", - "Estrela de Alagoas", - "Feira Grande", - "Feliz Deserto", - "Flexeiras", - "Girau do Ponciano", - "Ibateguara", - "Igaci", - "Igreja Nova", - "Inhapi", - "Jacaré dos Homens", - "Jacuípe", - "Japaratinga", - "Jaramataia", - "Jequiá da Praia", - "Joaquim Gomes", - "Jundiá", - "Junqueiro", - "Lagoa da Canoa", - "Limoeiro de Anadia", - "Maceió", - "Major Isidoro", - "Mar Vermelho", - "Maragogi", - "Maravilha", - "Marechal Deodoro", - "Maribondo", - "Mata Grande", - "Matriz de Camaragibe", - "Messias", - "Minador do Negrão", - "Monteirópolis", - "Murici", - "Novo Lino", - "Olho d'Água das Flores", - "Olho d'Água do Casado", - "Olho d'Água Grande", - "Olivença", - "Ouro Branco", - "Palestina", - "Palmeira dos Índios", - "Pão de Açúcar", - "Pariconha", - "Paripueira", - "Passo de Camaragibe", - "Paulo Jacinto", - "Penedo", - "Piaçabuçu", - "Pilar", - "Pindoba", - "Piranhas", - "Poço das Trincheiras", - "Porto Calvo", - "Porto de Pedras", - "Porto Real do Colégio", - "Quebrangulo", - "Rio Largo", - "Roteiro", - "Santa Luzia do Norte", - "Santana do Ipanema", - "Santana do Mundaú", - "São Brás", - "São José da Laje", - "São José da Tapera", - "São Luís do Quitunde", - "São Miguel dos Campos", - "São Miguel dos Milagres", - "São Sebastião", - "Satuba", - "Senador Rui Palmeira", - "Tanque d'Arca", - "Taquarana", - "Teotônio Vilela", - "Traipu", - "União dos Palmares", - "Viçosa", + ["Água Branca", "2700102"], + ["Anadia", "2700201"], + ["Arapiraca", "2700300"], + ["Atalaia", "2700409"], + ["Barra de Santo Antônio", "2700508"], + ["Barra de São Miguel", "2700607"], + ["Batalha", "2700706"], + ["Belém", "2700805"], + ["Belo Monte", "2700904"], + ["Boca da Mata", "2701001"], + ["Branquinha", "2701100"], + ["Cacimbinhas", "2701209"], + ["Cajueiro", "2701308"], + ["Campestre", "2701357"], + ["Campo Alegre", "2701407"], + ["Campo Grande", "2701506"], + ["Canapi", "2701605"], + ["Capela", "2701704"], + ["Carneiros", "2701803"], + ["Chã Preta", "2701902"], + ["Coité do Nóia", "2702009"], + ["Colônia Leopoldina", "2702108"], + ["Coqueiro Seco", "2702207"], + ["Coruripe", "2702306"], + ["Craíbas", "2702355"], + ["Delmiro Gouveia", "2702405"], + ["Dois Riachos", "2702504"], + ["Estrela de Alagoas", "2702553"], + ["Feira Grande", "2702603"], + ["Feliz Deserto", "2702702"], + ["Flexeiras", "2702801"], + ["Girau do Ponciano", "2702900"], + ["Ibateguara", "2703007"], + ["Igaci", "2703106"], + ["Igreja Nova", "2703205"], + ["Inhapi", "2703304"], + ["Jacaré dos Homens", "2703403"], + ["Jacuípe", "2703502"], + ["Japaratinga", "2703601"], + ["Jaramataia", "2703700"], + ["Jequiá da Praia", "2703759"], + ["Joaquim Gomes", "2703809"], + ["Jundiá", "2703908"], + ["Junqueiro", "2704005"], + ["Lagoa da Canoa", "2704104"], + ["Limoeiro de Anadia", "2704203"], + ["Maceió", "2704302"], + ["Major Isidoro", "2704401"], + ["Mar Vermelho", "2704906"], + ["Maragogi", "2704500"], + ["Maravilha", "2704609"], + ["Marechal Deodoro", "2704708"], + ["Maribondo", "2704807"], + ["Mata Grande", "2705002"], + ["Matriz de Camaragibe", "2705101"], + ["Messias", "2705200"], + ["Minador do Negrão", "2705309"], + ["Monteirópolis", "2705408"], + ["Murici", "2705507"], + ["Novo Lino", "2705606"], + ["Olho d'Água das Flores", "2705705"], + ["Olho d'Água do Casado", "2705804"], + ["Olho d'Água Grande", "2705903"], + ["Olivença", "2706000"], + ["Ouro Branco", "2706109"], + ["Palestina", "2706208"], + ["Palmeira dos Índios", "2706307"], + ["Pão de Açúcar", "2706406"], + ["Pariconha", "2706422"], + ["Paripueira", "2706448"], + ["Passo de Camaragibe", "2706505"], + ["Paulo Jacinto", "2706604"], + ["Penedo", "2706703"], + ["Piaçabuçu", "2706802"], + ["Pilar", "2706901"], + ["Pindoba", "2707008"], + ["Piranhas", "2707107"], + ["Poço das Trincheiras", "2707206"], + ["Porto Calvo", "2707305"], + ["Porto de Pedras", "2707404"], + ["Porto Real do Colégio", "2707503"], + ["Quebrangulo", "2707602"], + ["Rio Largo", "2707701"], + ["Roteiro", "2707800"], + ["Santa Luzia do Norte", "2707909"], + ["Santana do Ipanema", "2708006"], + ["Santana do Mundaú", "2708105"], + ["São Brás", "2708204"], + ["São José da Laje", "2708303"], + ["São José da Tapera", "2708402"], + ["São Luís do Quitunde", "2708501"], + ["São Miguel dos Campos", "2708600"], + ["São Miguel dos Milagres", "2708709"], + ["São Sebastião", "2708808"], + ["Satuba", "2708907"], + ["Senador Rui Palmeira", "2708956"], + ["Tanque d'Arca", "2709004"], + ["Taquarana", "2709103"], + ["Teotônio Vilela", "2709152"], + ["Traipu", "2709202"], + ["União dos Palmares", "2709301"], + ["Viçosa", "2709400"], ], AM: [ - "Alvarães", - "Amaturá", - "Anamã", - "Anori", - "Apuí", - "Atalaia do Norte", - "Autazes", - "Barcelos", - "Barreirinha", - "Benjamin Constant", - "Beruri", - "Boa Vista do Ramos", - "Boca do Acre", - "Borba", - "Caapiranga", - "Canutama", - "Carauari", - "Careiro", - "Careiro da Várzea", - "Coari", - "Codajás", - "Eirunepé", - "Envira", - "Fonte Boa", - "Guajará", - "Humaitá", - "Ipixuna", - "Iranduba", - "Itacoatiara", - "Itamarati", - "Itapiranga", - "Japurá", - "Juruá", - "Jutaí", - "Lábrea", - "Manacapuru", - "Manaquiri", - "Manaus", - "Manicoré", - "Maraã", - "Maués", - "Nhamundá", - "Nova Olinda do Norte", - "Novo Airão", - "Novo Aripuanã", - "Parintins", - "Pauini", - "Presidente Figueiredo", - "Rio Preto da Eva", - "Santa Isabel do Rio Negro", - "Santo Antônio do Içá", - "São Gabriel da Cachoeira", - "São Paulo de Olivença", - "São Sebastião do Uatumã", - "Silves", - "Tabatinga", - "Tapauá", - "Tefé", - "Tonantins", - "Uarini", - "Urucará", - "Urucurituba", + ["Alvarães", "1300029"], + ["Amaturá", "1300060"], + ["Anamã", "1300086"], + ["Anori", "1300102"], + ["Apuí", "1300144"], + ["Atalaia do Norte", "1300201"], + ["Autazes", "1300300"], + ["Barcelos", "1300409"], + ["Barreirinha", "1300508"], + ["Benjamin Constant", "1300607"], + ["Beruri", "1300631"], + ["Boa Vista do Ramos", "1300680"], + ["Boca do Acre", "1300706"], + ["Borba", "1300805"], + ["Caapiranga", "1300839"], + ["Canutama", "1300904"], + ["Carauari", "1301001"], + ["Careiro", "1301100"], + ["Careiro da Várzea", "1301159"], + ["Coari", "1301209"], + ["Codajás", "1301308"], + ["Eirunepé", "1301407"], + ["Envira", "1301506"], + ["Fonte Boa", "1301605"], + ["Guajará", "1301654"], + ["Humaitá", "1301704"], + ["Ipixuna", "1301803"], + ["Iranduba", "1301852"], + ["Itacoatiara", "1301902"], + ["Itamarati", "1301951"], + ["Itapiranga", "1302009"], + ["Japurá", "1302108"], + ["Juruá", "1302207"], + ["Jutaí", "1302306"], + ["Lábrea", "1302405"], + ["Manacapuru", "1302504"], + ["Manaquiri", "1302553"], + ["Manaus", "1302603"], + ["Manicoré", "1302702"], + ["Maraã", "1302801"], + ["Maués", "1302900"], + ["Nhamundá", "1303007"], + ["Nova Olinda do Norte", "1303106"], + ["Novo Airão", "1303205"], + ["Novo Aripuanã", "1303304"], + ["Parintins", "1303403"], + ["Pauini", "1303502"], + ["Presidente Figueiredo", "1303536"], + ["Rio Preto da Eva", "1303569"], + ["Santa Isabel do Rio Negro", "1303601"], + ["Santo Antônio do Içá", "1303700"], + ["São Gabriel da Cachoeira", "1303809"], + ["São Paulo de Olivença", "1303908"], + ["São Sebastião do Uatumã", "1303957"], + ["Silves", "1304005"], + ["Tabatinga", "1304062"], + ["Tapauá", "1304104"], + ["Tefé", "1304203"], + ["Tonantins", "1304237"], + ["Uarini", "1304260"], + ["Urucará", "1304302"], + ["Urucurituba", "1304401"], ], AP: [ - "Amapá", - "Calçoene", - "Cutias", - "Ferreira Gomes", - "Itaubal", - "Laranjal do Jari", - "Macapá", - "Mazagão", - "Oiapoque", - "Pedra Branca do Amapari", - "Porto Grande", - "Pracuúba", - "Santana", - "Serra do Navio", - "Tartarugalzinho", - "Vitória do Jari", + ["Amapá", "1600105"], + ["Calçoene", "1600204"], + ["Cutias", "1600212"], + ["Ferreira Gomes", "1600238"], + ["Itaubal", "1600253"], + ["Laranjal do Jari", "1600279"], + ["Macapá", "1600303"], + ["Mazagão", "1600402"], + ["Oiapoque", "1600501"], + ["Pedra Branca do Amapari", "1600154"], + ["Porto Grande", "1600535"], + ["Pracuúba", "1600550"], + ["Santana", "1600600"], + ["Serra do Navio", "1600055"], + ["Tartarugalzinho", "1600709"], + ["Vitória do Jari", "1600808"], ], BA: [ - "Abaíra", - "Abaré", - "Acajutiba", - "Adustina", - "Água Fria", - "Aiquara", - "Alagoinhas", - "Alcobaça", - "Almadina", - "Amargosa", - "Amélia Rodrigues", - "América Dourada", - "Anagé", - "Andaraí", - "Andorinha", - "Angical", - "Anguera", - "Antas", - "Antônio Cardoso", - "Antônio Gonçalves", - "Aporá", - "Apuarema", - "Araçás", - "Aracatu", - "Araci", - "Aramari", - "Arataca", - "Aratuípe", - "Aurelino Leal", - "Baianópolis", - "Baixa Grande", - "Banzaê", - "Barra", - "Barra da Estiva", - "Barra do Choça", - "Barra do Mendes", - "Barra do Rocha", - "Barreiras", - "Barro Alto", - "Barro Preto", - "Barrocas", - "Belmonte", - "Belo Campo", - "Biritinga", - "Boa Nova", - "Boa Vista do Tupim", - "Bom Jesus da Lapa", - "Bom Jesus da Serra", - "Boninal", - "Bonito", - "Boquira", - "Botuporã", - "Brejões", - "Brejolândia", - "Brotas de Macaúbas", - "Brumado", - "Buerarema", - "Buritirama", - "Caatiba", - "Cabaceiras do Paraguaçu", - "Cachoeira", - "Caculé", - "Caém", - "Caetanos", - "Caetité", - "Cafarnaum", - "Cairu", - "Caldeirão Grande", - "Camacan", - "Camaçari", - "Camamu", - "Campo Alegre de Lourdes", - "Campo Formoso", - "Canápolis", - "Canarana", - "Canavieiras", - "Candeal", - "Candeias", - "Candiba", - "Cândido Sales", - "Cansanção", - "Canudos", - "Capela do Alto Alegre", - "Capim Grosso", - "Caraíbas", - "Caravelas", - "Cardeal da Silva", - "Carinhanha", - "Casa Nova", - "Castro Alves", - "Catolândia", - "Catu", - "Caturama", - "Central", - "Chorrochó", - "Cícero Dantas", - "Cipó", - "Coaraci", - "Cocos", - "Conceição da Feira", - "Conceição do Almeida", - "Conceição do Coité", - "Conceição do Jacuípe", - "Conde", - "Condeúba", - "Contendas do Sincorá", - "Coração de Maria", - "Cordeiros", - "Coribe", - "Coronel João Sá", - "Correntina", - "Cotegipe", - "Cravolândia", - "Crisópolis", - "Cristópolis", - "Cruz das Almas", - "Curaçá", - "Dário Meira", - "Dias d'Ávila", - "Dom Basílio", - "Dom Macedo Costa", - "Elísio Medrado", - "Encruzilhada", - "Entre Rios", - "Érico Cardoso", - "Esplanada", - "Euclides da Cunha", - "Eunápolis", - "Fátima", - "Feira da Mata", - "Feira de Santana", - "Filadélfia", - "Firmino Alves", - "Floresta Azul", - "Formosa do Rio Preto", - "Gandu", - "Gavião", - "Gentio do Ouro", - "Glória", - "Gongogi", - "Governador Mangabeira", - "Guajeru", - "Guanambi", - "Guaratinga", - "Heliópolis", - "Iaçu", - "Ibiassucê", - "Ibicaraí", - "Ibicoara", - "Ibicuí", - "Ibipeba", - "Ibipitanga", - "Ibiquera", - "Ibirapitanga", - "Ibirapuã", - "Ibirataia", - "Ibitiara", - "Ibititá", - "Ibotirama", - "Ichu", - "Igaporã", - "Igrapiúna", - "Iguaí", - "Ilhéus", - "Inhambupe", - "Ipecaetá", - "Ipiaú", - "Ipirá", - "Ipupiara", - "Irajuba", - "Iramaia", - "Iraquara", - "Irará", - "Irecê", - "Itabela", - "Itaberaba", - "Itabuna", - "Itacaré", - "Itaeté", - "Itagi", - "Itagibá", - "Itagimirim", - "Itaguaçu da Bahia", - "Itaju do Colônia", - "Itajuípe", - "Itamaraju", - "Itamari", - "Itambé", - "Itanagra", - "Itanhém", - "Itaparica", - "Itapé", - "Itapebi", - "Itapetinga", - "Itapicuru", - "Itapitanga", - "Itaquara", - "Itarantim", - "Itatim", - "Itiruçu", - "Itiúba", - "Itororó", - "Ituaçu", - "Ituberá", - "Iuiu", - "Jaborandi", - "Jacaraci", - "Jacobina", - "Jaguaquara", - "Jaguarari", - "Jaguaripe", - "Jandaíra", - "Jequié", - "Jeremoabo", - "Jiquiriçá", - "Jitaúna", - "João Dourado", - "Juazeiro", - "Jucuruçu", - "Jussara", - "Jussari", - "Jussiape", - "Lafaiete Coutinho", - "Lagoa Real", - "Laje", - "Lajedão", - "Lajedinho", - "Lajedo do Tabocal", - "Lamarão", - "Lapão", - "Lauro de Freitas", - "Lençóis", - "Licínio de Almeida", - "Livramento de Nossa Senhora", - "Luís Eduardo Magalhães", - "Macajuba", - "Macarani", - "Macaúbas", - "Macururé", - "Madre de Deus", - "Maetinga", - "Maiquinique", - "Mairi", - "Malhada", - "Malhada de Pedras", - "Manoel Vitorino", - "Mansidão", - "Maracás", - "Maragogipe", - "Maraú", - "Marcionílio Souza", - "Mascote", - "Mata de São João", - "Matina", - "Medeiros Neto", - "Miguel Calmon", - "Milagres", - "Mirangaba", - "Mirante", - "Monte Santo", - "Morpará", - "Morro do Chapéu", - "Mortugaba", - "Mucugê", - "Mucuri", - "Mulungu do Morro", - "Mundo Novo", - "Muniz Ferreira", - "Muquém do São Francisco", - "Muritiba", - "Mutuípe", - "Nazaré", - "Nilo Peçanha", - "Nordestina", - "Nova Canaã", - "Nova Fátima", - "Nova Ibiá", - "Nova Itarana", - "Nova Redenção", - "Nova Soure", - "Nova Viçosa", - "Novo Horizonte", - "Novo Triunfo", - "Olindina", - "Oliveira dos Brejinhos", - "Ouriçangas", - "Ourolândia", - "Palmas de Monte Alto", - "Palmeiras", - "Paramirim", - "Paratinga", - "Paripiranga", - "Pau Brasil", - "Paulo Afonso", - "Pé de Serra", - "Pedrão", - "Pedro Alexandre", - "Piatã", - "Pilão Arcado", - "Pindaí", - "Pindobaçu", - "Pintadas", - "Piraí do Norte", - "Piripá", - "Piritiba", - "Planaltino", - "Planalto", - "Poções", - "Pojuca", - "Ponto Novo", - "Porto Seguro", - "Potiraguá", - "Prado", - "Presidente Dutra", - "Presidente Jânio Quadros", - "Presidente Tancredo Neves", - "Queimadas", - "Quijingue", - "Quixabeira", - "Rafael Jambeiro", - "Remanso", - "Retirolândia", - "Riachão das Neves", - "Riachão do Jacuípe", - "Riacho de Santana", - "Ribeira do Amparo", - "Ribeira do Pombal", - "Ribeirão do Largo", - "Rio de Contas", - "Rio do Antônio", - "Rio do Pires", - "Rio Real", - "Rodelas", - "Ruy Barbosa", - "Salinas da Margarida", - "Salvador", - "Santa Bárbara", - "Santa Brígida", - "Santa Cruz Cabrália", - "Santa Cruz da Vitória", - "Santa Inês", - "Santa Luzia", - "Santa Maria da Vitória", - "Santa Rita de Cássia", - "Santa Terezinha", - "Santaluz", - "Santana", - "Santanópolis", - "Santo Amaro", - "Santo Antônio de Jesus", - "Santo Estêvão", - "São Desidério", - "São Domingos", - "São Felipe", - "São Félix", - "São Félix do Coribe", - "São Francisco do Conde", - "São Gabriel", - "São Gonçalo dos Campos", - "São José da Vitória", - "São José do Jacuípe", - "São Miguel das Matas", - "São Sebastião do Passé", - "Sapeaçu", - "Sátiro Dias", - "Saubara", - "Saúde", - "Seabra", - "Sebastião Laranjeiras", - "Senhor do Bonfim", - "Sento Sé", - "Serra do Ramalho", - "Serra Dourada", - "Serra Preta", - "Serrinha", - "Serrolândia", - "Simões Filho", - "Sítio do Mato", - "Sítio do Quinto", - "Sobradinho", - "Souto Soares", - "Tabocas do Brejo Velho", - "Tanhaçu", - "Tanque Novo", - "Tanquinho", - "Taperoá", - "Tapiramutá", - "Teixeira de Freitas", - "Teodoro Sampaio", - "Teofilândia", - "Teolândia", - "Terra Nova", - "Tremedal", - "Tucano", - "Uauá", - "Ubaíra", - "Ubaitaba", - "Ubatã", - "Uibaí", - "Umburanas", - "Una", - "Urandi", - "Uruçuca", - "Utinga", - "Valença", - "Valente", - "Várzea da Roça", - "Várzea do Poço", - "Várzea Nova", - "Varzedo", - "Vera Cruz", - "Vereda", - "Vitória da Conquista", - "Wagner", - "Wanderley", - "Wenceslau Guimarães", - "Xique-Xique", + ["Abaíra", "2900108"], + ["Abaré", "2900207"], + ["Acajutiba", "2900306"], + ["Adustina", "2900355"], + ["Água Fria", "2900405"], + ["Aiquara", "2900603"], + ["Alagoinhas", "2900702"], + ["Alcobaça", "2900801"], + ["Almadina", "2900900"], + ["Amargosa", "2901007"], + ["Amélia Rodrigues", "2901106"], + ["América Dourada", "2901155"], + ["Anagé", "2901205"], + ["Andaraí", "2901304"], + ["Andorinha", "2901353"], + ["Angical", "2901403"], + ["Anguera", "2901502"], + ["Antas", "2901601"], + ["Antônio Cardoso", "2901700"], + ["Antônio Gonçalves", "2901809"], + ["Aporá", "2901908"], + ["Apuarema", "2901957"], + ["Araçás", "2902054"], + ["Aracatu", "2902005"], + ["Araci", "2902104"], + ["Aramari", "2902203"], + ["Arataca", "2902252"], + ["Aratuípe", "2902302"], + ["Aurelino Leal", "2902401"], + ["Baianópolis", "2902500"], + ["Baixa Grande", "2902609"], + ["Banzaê", "2902658"], + ["Barra", "2902708"], + ["Barra da Estiva", "2902807"], + ["Barra do Choça", "2902906"], + ["Barra do Mendes", "2903003"], + ["Barra do Rocha", "2903102"], + ["Barreiras", "2903201"], + ["Barro Alto", "2903235"], + ["Barro Preto", "2903300"], + ["Barrocas", "2903276"], + ["Belmonte", "2903409"], + ["Belo Campo", "2903508"], + ["Biritinga", "2903607"], + ["Boa Nova", "2903706"], + ["Boa Vista do Tupim", "2903805"], + ["Bom Jesus da Lapa", "2903904"], + ["Bom Jesus da Serra", "2903953"], + ["Boninal", "2904001"], + ["Bonito", "2904050"], + ["Boquira", "2904100"], + ["Botuporã", "2904209"], + ["Brejões", "2904308"], + ["Brejolândia", "2904407"], + ["Brotas de Macaúbas", "2904506"], + ["Brumado", "2904605"], + ["Buerarema", "2904704"], + ["Buritirama", "2904753"], + ["Caatiba", "2904803"], + ["Cabaceiras do Paraguaçu", "2904852"], + ["Cachoeira", "2904902"], + ["Caculé", "2905008"], + ["Caém", "2905107"], + ["Caetanos", "2905156"], + ["Caetité", "2905206"], + ["Cafarnaum", "2905305"], + ["Cairu", "2905404"], + ["Caldeirão Grande", "2905503"], + ["Camacan", "2905602"], + ["Camaçari", "2905701"], + ["Camamu", "2905800"], + ["Campo Alegre de Lourdes", "2905909"], + ["Campo Formoso", "2906006"], + ["Canápolis", "2906105"], + ["Canarana", "2906204"], + ["Canavieiras", "2906303"], + ["Candeal", "2906402"], + ["Candeias", "2906501"], + ["Candiba", "2906600"], + ["Cândido Sales", "2906709"], + ["Cansanção", "2906808"], + ["Canudos", "2906824"], + ["Capela do Alto Alegre", "2906857"], + ["Capim Grosso", "2906873"], + ["Caraíbas", "2906899"], + ["Caravelas", "2906907"], + ["Cardeal da Silva", "2907004"], + ["Carinhanha", "2907103"], + ["Casa Nova", "2907202"], + ["Castro Alves", "2907301"], + ["Catolândia", "2907400"], + ["Catu", "2907509"], + ["Caturama", "2907558"], + ["Central", "2907608"], + ["Chorrochó", "2907707"], + ["Cícero Dantas", "2907806"], + ["Cipó", "2907905"], + ["Coaraci", "2908002"], + ["Cocos", "2908101"], + ["Conceição da Feira", "2908200"], + ["Conceição do Almeida", "2908309"], + ["Conceição do Coité", "2908408"], + ["Conceição do Jacuípe", "2908507"], + ["Conde", "2908606"], + ["Condeúba", "2908705"], + ["Contendas do Sincorá", "2908804"], + ["Coração de Maria", "2908903"], + ["Cordeiros", "2909000"], + ["Coribe", "2909109"], + ["Coronel João Sá", "2909208"], + ["Correntina", "2909307"], + ["Cotegipe", "2909406"], + ["Cravolândia", "2909505"], + ["Crisópolis", "2909604"], + ["Cristópolis", "2909703"], + ["Cruz das Almas", "2909802"], + ["Curaçá", "2909901"], + ["Dário Meira", "2910008"], + ["Dias d'Ávila", "2910057"], + ["Dom Basílio", "2910107"], + ["Dom Macedo Costa", "2910206"], + ["Elísio Medrado", "2910305"], + ["Encruzilhada", "2910404"], + ["Entre Rios", "2910503"], + ["Érico Cardoso", "2900504"], + ["Esplanada", "2910602"], + ["Euclides da Cunha", "2910701"], + ["Eunápolis", "2910727"], + ["Fátima", "2910750"], + ["Feira da Mata", "2910776"], + ["Feira de Santana", "2910800"], + ["Filadélfia", "2910859"], + ["Firmino Alves", "2910909"], + ["Floresta Azul", "2911006"], + ["Formosa do Rio Preto", "2911105"], + ["Gandu", "2911204"], + ["Gavião", "2911253"], + ["Gentio do Ouro", "2911303"], + ["Glória", "2911402"], + ["Gongogi", "2911501"], + ["Governador Mangabeira", "2911600"], + ["Guajeru", "2911659"], + ["Guanambi", "2911709"], + ["Guaratinga", "2911808"], + ["Heliópolis", "2911857"], + ["Iaçu", "2911907"], + ["Ibiassucê", "2912004"], + ["Ibicaraí", "2912103"], + ["Ibicoara", "2912202"], + ["Ibicuí", "2912301"], + ["Ibipeba", "2912400"], + ["Ibipitanga", "2912509"], + ["Ibiquera", "2912608"], + ["Ibirapitanga", "2912707"], + ["Ibirapuã", "2912806"], + ["Ibirataia", "2912905"], + ["Ibitiara", "2913002"], + ["Ibititá", "2913101"], + ["Ibotirama", "2913200"], + ["Ichu", "2913309"], + ["Igaporã", "2913408"], + ["Igrapiúna", "2913457"], + ["Iguaí", "2913507"], + ["Ilhéus", "2913606"], + ["Inhambupe", "2913705"], + ["Ipecaetá", "2913804"], + ["Ipiaú", "2913903"], + ["Ipirá", "2914000"], + ["Ipupiara", "2914109"], + ["Irajuba", "2914208"], + ["Iramaia", "2914307"], + ["Iraquara", "2914406"], + ["Irará", "2914505"], + ["Irecê", "2914604"], + ["Itabela", "2914653"], + ["Itaberaba", "2914703"], + ["Itabuna", "2914802"], + ["Itacaré", "2914901"], + ["Itaeté", "2915007"], + ["Itagi", "2915106"], + ["Itagibá", "2915205"], + ["Itagimirim", "2915304"], + ["Itaguaçu da Bahia", "2915353"], + ["Itaju do Colônia", "2915403"], + ["Itajuípe", "2915502"], + ["Itamaraju", "2915601"], + ["Itamari", "2915700"], + ["Itambé", "2915809"], + ["Itanagra", "2915908"], + ["Itanhém", "2916005"], + ["Itaparica", "2916104"], + ["Itapé", "2916203"], + ["Itapebi", "2916302"], + ["Itapetinga", "2916401"], + ["Itapicuru", "2916500"], + ["Itapitanga", "2916609"], + ["Itaquara", "2916708"], + ["Itarantim", "2916807"], + ["Itatim", "2916856"], + ["Itiruçu", "2916906"], + ["Itiúba", "2917003"], + ["Itororó", "2917102"], + ["Ituaçu", "2917201"], + ["Ituberá", "2917300"], + ["Iuiu", "2917334"], + ["Jaborandi", "2917359"], + ["Jacaraci", "2917409"], + ["Jacobina", "2917508"], + ["Jaguaquara", "2917607"], + ["Jaguarari", "2917706"], + ["Jaguaripe", "2917805"], + ["Jandaíra", "2917904"], + ["Jequié", "2918001"], + ["Jeremoabo", "2918100"], + ["Jiquiriçá", "2918209"], + ["Jitaúna", "2918308"], + ["João Dourado", "2918357"], + ["Juazeiro", "2918407"], + ["Jucuruçu", "2918456"], + ["Jussara", "2918506"], + ["Jussari", "2918555"], + ["Jussiape", "2918605"], + ["Lafaiete Coutinho", "2918704"], + ["Lagoa Real", "2918753"], + ["Laje", "2918803"], + ["Lajedão", "2918902"], + ["Lajedinho", "2919009"], + ["Lajedo do Tabocal", "2919058"], + ["Lamarão", "2919108"], + ["Lapão", "2919157"], + ["Lauro de Freitas", "2919207"], + ["Lençóis", "2919306"], + ["Licínio de Almeida", "2919405"], + ["Livramento de Nossa Senhora", "2919504"], + ["Luís Eduardo Magalhães", "2919553"], + ["Macajuba", "2919603"], + ["Macarani", "2919702"], + ["Macaúbas", "2919801"], + ["Macururé", "2919900"], + ["Madre de Deus", "2919926"], + ["Maetinga", "2919959"], + ["Maiquinique", "2920007"], + ["Mairi", "2920106"], + ["Malhada", "2920205"], + ["Malhada de Pedras", "2920304"], + ["Manoel Vitorino", "2920403"], + ["Mansidão", "2920452"], + ["Maracás", "2920502"], + ["Maragogipe", "2920601"], + ["Maraú", "2920700"], + ["Marcionílio Souza", "2920809"], + ["Mascote", "2920908"], + ["Mata de São João", "2921005"], + ["Matina", "2921054"], + ["Medeiros Neto", "2921104"], + ["Miguel Calmon", "2921203"], + ["Milagres", "2921302"], + ["Mirangaba", "2921401"], + ["Mirante", "2921450"], + ["Monte Santo", "2921500"], + ["Morpará", "2921609"], + ["Morro do Chapéu", "2921708"], + ["Mortugaba", "2921807"], + ["Mucugê", "2921906"], + ["Mucuri", "2922003"], + ["Mulungu do Morro", "2922052"], + ["Mundo Novo", "2922102"], + ["Muniz Ferreira", "2922201"], + ["Muquém do São Francisco", "2922250"], + ["Muritiba", "2922300"], + ["Mutuípe", "2922409"], + ["Nazaré", "2922508"], + ["Nilo Peçanha", "2922607"], + ["Nordestina", "2922656"], + ["Nova Canaã", "2922706"], + ["Nova Fátima", "2922730"], + ["Nova Ibiá", "2922755"], + ["Nova Itarana", "2922805"], + ["Nova Redenção", "2922854"], + ["Nova Soure", "2922904"], + ["Nova Viçosa", "2923001"], + ["Novo Horizonte", "2923035"], + ["Novo Triunfo", "2923050"], + ["Olindina", "2923100"], + ["Oliveira dos Brejinhos", "2923209"], + ["Ouriçangas", "2923308"], + ["Ourolândia", "2923357"], + ["Palmas de Monte Alto", "2923407"], + ["Palmeiras", "2923506"], + ["Paramirim", "2923605"], + ["Paratinga", "2923704"], + ["Paripiranga", "2923803"], + ["Pau Brasil", "2923902"], + ["Paulo Afonso", "2924009"], + ["Pé de Serra", "2924058"], + ["Pedrão", "2924108"], + ["Pedro Alexandre", "2924207"], + ["Piatã", "2924306"], + ["Pilão Arcado", "2924405"], + ["Pindaí", "2924504"], + ["Pindobaçu", "2924603"], + ["Pintadas", "2924652"], + ["Piraí do Norte", "2924678"], + ["Piripá", "2924702"], + ["Piritiba", "2924801"], + ["Planaltino", "2924900"], + ["Planalto", "2925006"], + ["Poções", "2925105"], + ["Pojuca", "2925204"], + ["Ponto Novo", "2925253"], + ["Porto Seguro", "2925303"], + ["Potiraguá", "2925402"], + ["Prado", "2925501"], + ["Presidente Dutra", "2925600"], + ["Presidente Jânio Quadros", "2925709"], + ["Presidente Tancredo Neves", "2925758"], + ["Queimadas", "2925808"], + ["Quijingue", "2925907"], + ["Quixabeira", "2925931"], + ["Rafael Jambeiro", "2925956"], + ["Remanso", "2926004"], + ["Retirolândia", "2926103"], + ["Riachão das Neves", "2926202"], + ["Riachão do Jacuípe", "2926301"], + ["Riacho de Santana", "2926400"], + ["Ribeira do Amparo", "2926509"], + ["Ribeira do Pombal", "2926608"], + ["Ribeirão do Largo", "2926657"], + ["Rio de Contas", "2926707"], + ["Rio do Antônio", "2926806"], + ["Rio do Pires", "2926905"], + ["Rio Real", "2927002"], + ["Rodelas", "2927101"], + ["Ruy Barbosa", "2927200"], + ["Salinas da Margarida", "2927309"], + ["Salvador", "2927408"], + ["Santa Bárbara", "2927507"], + ["Santa Brígida", "2927606"], + ["Santa Cruz Cabrália", "2927705"], + ["Santa Cruz da Vitória", "2927804"], + ["Santa Inês", "2927903"], + ["Santa Luzia", "2928059"], + ["Santa Maria da Vitória", "2928109"], + ["Santa Rita de Cássia", "2928406"], + ["Santa Terezinha", "2928505"], + ["Santaluz", "2928000"], + ["Santana", "2928208"], + ["Santanópolis", "2928307"], + ["Santo Amaro", "2928604"], + ["Santo Antônio de Jesus", "2928703"], + ["Santo Estêvão", "2928802"], + ["São Desidério", "2928901"], + ["São Domingos", "2928950"], + ["São Felipe", "2929107"], + ["São Félix", "2929008"], + ["São Félix do Coribe", "2929057"], + ["São Francisco do Conde", "2929206"], + ["São Gabriel", "2929255"], + ["São Gonçalo dos Campos", "2929305"], + ["São José da Vitória", "2929354"], + ["São José do Jacuípe", "2929370"], + ["São Miguel das Matas", "2929404"], + ["São Sebastião do Passé", "2929503"], + ["Sapeaçu", "2929602"], + ["Sátiro Dias", "2929701"], + ["Saubara", "2929750"], + ["Saúde", "2929800"], + ["Seabra", "2929909"], + ["Sebastião Laranjeiras", "2930006"], + ["Senhor do Bonfim", "2930105"], + ["Sento Sé", "2930204"], + ["Serra do Ramalho", "2930154"], + ["Serra Dourada", "2930303"], + ["Serra Preta", "2930402"], + ["Serrinha", "2930501"], + ["Serrolândia", "2930600"], + ["Simões Filho", "2930709"], + ["Sítio do Mato", "2930758"], + ["Sítio do Quinto", "2930766"], + ["Sobradinho", "2930774"], + ["Souto Soares", "2930808"], + ["Tabocas do Brejo Velho", "2930907"], + ["Tanhaçu", "2931004"], + ["Tanque Novo", "2931053"], + ["Tanquinho", "2931103"], + ["Taperoá", "2931202"], + ["Tapiramutá", "2931301"], + ["Teixeira de Freitas", "2931350"], + ["Teodoro Sampaio", "2931400"], + ["Teofilândia", "2931509"], + ["Teolândia", "2931608"], + ["Terra Nova", "2931707"], + ["Tremedal", "2931806"], + ["Tucano", "2931905"], + ["Uauá", "2932002"], + ["Ubaíra", "2932101"], + ["Ubaitaba", "2932200"], + ["Ubatã", "2932309"], + ["Uibaí", "2932408"], + ["Umburanas", "2932457"], + ["Una", "2932507"], + ["Urandi", "2932606"], + ["Uruçuca", "2932705"], + ["Utinga", "2932804"], + ["Valença", "2932903"], + ["Valente", "2933000"], + ["Várzea da Roça", "2933059"], + ["Várzea do Poço", "2933109"], + ["Várzea Nova", "2933158"], + ["Varzedo", "2933174"], + ["Vera Cruz", "2933208"], + ["Vereda", "2933257"], + ["Vitória da Conquista", "2933307"], + ["Wagner", "2933406"], + ["Wanderley", "2933455"], + ["Wenceslau Guimarães", "2933505"], + ["Xique-Xique", "2933604"], ], CE: [ - "Abaiara", - "Acarape", - "Acaraú", - "Acopiara", - "Aiuaba", - "Alcântaras", - "Altaneira", - "Alto Santo", - "Amontada", - "Antonina do Norte", - "Apuiarés", - "Aquiraz", - "Aracati", - "Aracoiaba", - "Ararendá", - "Araripe", - "Aratuba", - "Arneiroz", - "Assaré", - "Aurora", - "Baixio", - "Banabuiú", - "Barbalha", - "Barreira", - "Barro", - "Barroquinha", - "Baturité", - "Beberibe", - "Bela Cruz", - "Boa Viagem", - "Brejo Santo", - "Camocim", - "Campos Sales", - "Canindé", - "Capistrano", - "Caridade", - "Cariré", - "Caririaçu", - "Cariús", - "Carnaubal", - "Cascavel", - "Catarina", - "Catunda", - "Caucaia", - "Cedro", - "Chaval", - "Choró", - "Chorozinho", - "Coreaú", - "Crateús", - "Crato", - "Croatá", - "Cruz", - "Deputado Irapuan Pinheiro", - "Ereré", - "Eusébio", - "Farias Brito", - "Forquilha", - "Fortaleza", - "Fortim", - "Frecheirinha", - "General Sampaio", - "Graça", - "Granja", - "Granjeiro", - "Groaíras", - "Guaiúba", - "Guaraciaba do Norte", - "Guaramiranga", - "Hidrolândia", - "Horizonte", - "Ibaretama", - "Ibiapina", - "Ibicuitinga", - "Icapuí", - "Icó", - "Iguatu", - "Independência", - "Ipaporanga", - "Ipaumirim", - "Ipu", - "Ipueiras", - "Iracema", - "Irauçuba", - "Itaiçaba", - "Itaitinga", - "Itapajé", - "Itapipoca", - "Itapiúna", - "Itarema", - "Itatira", - "Jaguaretama", - "Jaguaribara", - "Jaguaribe", - "Jaguaruana", - "Jardim", - "Jati", - "Jijoca de Jericoacoara", - "Juazeiro do Norte", - "Jucás", - "Lavras da Mangabeira", - "Limoeiro do Norte", - "Madalena", - "Maracanaú", - "Maranguape", - "Marco", - "Martinópole", - "Massapê", - "Mauriti", - "Meruoca", - "Milagres", - "Milhã", - "Miraíma", - "Missão Velha", - "Mombaça", - "Monsenhor Tabosa", - "Morada Nova", - "Moraújo", - "Morrinhos", - "Mucambo", - "Mulungu", - "Nova Olinda", - "Nova Russas", - "Novo Oriente", - "Ocara", - "Orós", - "Pacajus", - "Pacatuba", - "Pacoti", - "Pacujá", - "Palhano", - "Palmácia", - "Paracuru", - "Paraipaba", - "Parambu", - "Paramoti", - "Pedra Branca", - "Penaforte", - "Pentecoste", - "Pereiro", - "Pindoretama", - "Piquet Carneiro", - "Pires Ferreira", - "Poranga", - "Porteiras", - "Potengi", - "Potiretama", - "Quiterianópolis", - "Quixadá", - "Quixelô", - "Quixeramobim", - "Quixeré", - "Redenção", - "Reriutaba", - "Russas", - "Saboeiro", - "Salitre", - "Santa Quitéria", - "Santana do Acaraú", - "Santana do Cariri", - "São Benedito", - "São Gonçalo do Amarante", - "São João do Jaguaribe", - "São Luís do Curu", - "Senador Pompeu", - "Senador Sá", - "Sobral", - "Solonópole", - "Tabuleiro do Norte", - "Tamboril", - "Tarrafas", - "Tauá", - "Tejuçuoca", - "Tianguá", - "Trairi", - "Tururu", - "Ubajara", - "Umari", - "Umirim", - "Uruburetama", - "Uruoca", - "Varjota", - "Várzea Alegre", - "Viçosa do Ceará", + ["Abaiara", "2300101"], + ["Acarape", "2300150"], + ["Acaraú", "2300200"], + ["Acopiara", "2300309"], + ["Aiuaba", "2300408"], + ["Alcântaras", "2300507"], + ["Altaneira", "2300606"], + ["Alto Santo", "2300705"], + ["Amontada", "2300754"], + ["Antonina do Norte", "2300804"], + ["Apuiarés", "2300903"], + ["Aquiraz", "2301000"], + ["Aracati", "2301109"], + ["Aracoiaba", "2301208"], + ["Ararendá", "2301257"], + ["Araripe", "2301307"], + ["Aratuba", "2301406"], + ["Arneiroz", "2301505"], + ["Assaré", "2301604"], + ["Aurora", "2301703"], + ["Baixio", "2301802"], + ["Banabuiú", "2301851"], + ["Barbalha", "2301901"], + ["Barreira", "2301950"], + ["Barro", "2302008"], + ["Barroquinha", "2302057"], + ["Baturité", "2302107"], + ["Beberibe", "2302206"], + ["Bela Cruz", "2302305"], + ["Boa Viagem", "2302404"], + ["Brejo Santo", "2302503"], + ["Camocim", "2302602"], + ["Campos Sales", "2302701"], + ["Canindé", "2302800"], + ["Capistrano", "2302909"], + ["Caridade", "2303006"], + ["Cariré", "2303105"], + ["Caririaçu", "2303204"], + ["Cariús", "2303303"], + ["Carnaubal", "2303402"], + ["Cascavel", "2303501"], + ["Catarina", "2303600"], + ["Catunda", "2303659"], + ["Caucaia", "2303709"], + ["Cedro", "2303808"], + ["Chaval", "2303907"], + ["Choró", "2303931"], + ["Chorozinho", "2303956"], + ["Coreaú", "2304004"], + ["Crateús", "2304103"], + ["Crato", "2304202"], + ["Croatá", "2304236"], + ["Cruz", "2304251"], + ["Deputado Irapuan Pinheiro", "2304269"], + ["Ereré", "2304277"], + ["Eusébio", "2304285"], + ["Farias Brito", "2304301"], + ["Forquilha", "2304350"], + ["Fortaleza", "2304400"], + ["Fortim", "2304459"], + ["Frecheirinha", "2304509"], + ["General Sampaio", "2304608"], + ["Graça", "2304657"], + ["Granja", "2304707"], + ["Granjeiro", "2304806"], + ["Groaíras", "2304905"], + ["Guaiúba", "2304954"], + ["Guaraciaba do Norte", "2305001"], + ["Guaramiranga", "2305100"], + ["Hidrolândia", "2305209"], + ["Horizonte", "2305233"], + ["Ibaretama", "2305266"], + ["Ibiapina", "2305308"], + ["Ibicuitinga", "2305332"], + ["Icapuí", "2305357"], + ["Icó", "2305407"], + ["Iguatu", "2305506"], + ["Independência", "2305605"], + ["Ipaporanga", "2305654"], + ["Ipaumirim", "2305704"], + ["Ipu", "2305803"], + ["Ipueiras", "2305902"], + ["Iracema", "2306009"], + ["Irauçuba", "2306108"], + ["Itaiçaba", "2306207"], + ["Itaitinga", "2306256"], + ["Itapajé", "2306306"], + ["Itapipoca", "2306405"], + ["Itapiúna", "2306504"], + ["Itarema", "2306553"], + ["Itatira", "2306603"], + ["Jaguaretama", "2306702"], + ["Jaguaribara", "2306801"], + ["Jaguaribe", "2306900"], + ["Jaguaruana", "2307007"], + ["Jardim", "2307106"], + ["Jati", "2307205"], + ["Jijoca de Jericoacoara", "2307254"], + ["Juazeiro do Norte", "2307304"], + ["Jucás", "2307403"], + ["Lavras da Mangabeira", "2307502"], + ["Limoeiro do Norte", "2307601"], + ["Madalena", "2307635"], + ["Maracanaú", "2307650"], + ["Maranguape", "2307700"], + ["Marco", "2307809"], + ["Martinópole", "2307908"], + ["Massapê", "2308005"], + ["Mauriti", "2308104"], + ["Meruoca", "2308203"], + ["Milagres", "2308302"], + ["Milhã", "2308351"], + ["Miraíma", "2308377"], + ["Missão Velha", "2308401"], + ["Mombaça", "2308500"], + ["Monsenhor Tabosa", "2308609"], + ["Morada Nova", "2308708"], + ["Moraújo", "2308807"], + ["Morrinhos", "2308906"], + ["Mucambo", "2309003"], + ["Mulungu", "2309102"], + ["Nova Olinda", "2309201"], + ["Nova Russas", "2309300"], + ["Novo Oriente", "2309409"], + ["Ocara", "2309458"], + ["Orós", "2309508"], + ["Pacajus", "2309607"], + ["Pacatuba", "2309706"], + ["Pacoti", "2309805"], + ["Pacujá", "2309904"], + ["Palhano", "2310001"], + ["Palmácia", "2310100"], + ["Paracuru", "2310209"], + ["Paraipaba", "2310258"], + ["Parambu", "2310308"], + ["Paramoti", "2310407"], + ["Pedra Branca", "2310506"], + ["Penaforte", "2310605"], + ["Pentecoste", "2310704"], + ["Pereiro", "2310803"], + ["Pindoretama", "2310852"], + ["Piquet Carneiro", "2310902"], + ["Pires Ferreira", "2310951"], + ["Poranga", "2311009"], + ["Porteiras", "2311108"], + ["Potengi", "2311207"], + ["Potiretama", "2311231"], + ["Quiterianópolis", "2311264"], + ["Quixadá", "2311306"], + ["Quixelô", "2311355"], + ["Quixeramobim", "2311405"], + ["Quixeré", "2311504"], + ["Redenção", "2311603"], + ["Reriutaba", "2311702"], + ["Russas", "2311801"], + ["Saboeiro", "2311900"], + ["Salitre", "2311959"], + ["Santa Quitéria", "2312205"], + ["Santana do Acaraú", "2312007"], + ["Santana do Cariri", "2312106"], + ["São Benedito", "2312304"], + ["São Gonçalo do Amarante", "2312403"], + ["São João do Jaguaribe", "2312502"], + ["São Luís do Curu", "2312601"], + ["Senador Pompeu", "2312700"], + ["Senador Sá", "2312809"], + ["Sobral", "2312908"], + ["Solonópole", "2313005"], + ["Tabuleiro do Norte", "2313104"], + ["Tamboril", "2313203"], + ["Tarrafas", "2313252"], + ["Tauá", "2313302"], + ["Tejuçuoca", "2313351"], + ["Tianguá", "2313401"], + ["Trairi", "2313500"], + ["Tururu", "2313559"], + ["Ubajara", "2313609"], + ["Umari", "2313708"], + ["Umirim", "2313757"], + ["Uruburetama", "2313807"], + ["Uruoca", "2313906"], + ["Varjota", "2313955"], + ["Várzea Alegre", "2314003"], + ["Viçosa do Ceará", "2314102"], ], - DF: ["Brasília"], + DF: [["Brasília", "5300108"]], ES: [ - "Afonso Cláudio", - "Água Doce do Norte", - "Águia Branca", - "Alegre", - "Alfredo Chaves", - "Alto Rio Novo", - "Anchieta", - "Apiacá", - "Aracruz", - "Atílio Vivácqua", - "Baixo Guandu", - "Barra de São Francisco", - "Boa Esperança", - "Bom Jesus do Norte", - "Brejetuba", - "Cachoeiro de Itapemirim", - "Cariacica", - "Castelo", - "Colatina", - "Conceição da Barra", - "Conceição do Castelo", - "Divino de São Lourenço", - "Domingos Martins", - "Dores do Rio Preto", - "Ecoporanga", - "Fundão", - "Governador Lindenberg", - "Guaçuí", - "Guarapari", - "Ibatiba", - "Ibiraçu", - "Ibitirama", - "Iconha", - "Irupi", - "Itaguaçu", - "Itapemirim", - "Itarana", - "Iúna", - "Jaguaré", - "Jerônimo Monteiro", - "João Neiva", - "Laranja da Terra", - "Linhares", - "Mantenópolis", - "Marataízes", - "Marechal Floriano", - "Marilândia", - "Mimoso do Sul", - "Montanha", - "Mucurici", - "Muniz Freire", - "Muqui", - "Nova Venécia", - "Pancas", - "Pedro Canário", - "Pinheiros", - "Piúma", - "Ponto Belo", - "Presidente Kennedy", - "Rio Bananal", - "Rio Novo do Sul", - "Santa Leopoldina", - "Santa Maria de Jetibá", - "Santa Teresa", - "São Domingos do Norte", - "São Gabriel da Palha", - "São José do Calçado", - "São Mateus", - "São Roque do Canaã", - "Serra", - "Sooretama", - "Vargem Alta", - "Venda Nova do Imigrante", - "Viana", - "Vila Pavão", - "Vila Valério", - "Vila Velha", - "Vitória", + ["Afonso Cláudio", "3200102"], + ["Água Doce do Norte", "3200169"], + ["Águia Branca", "3200136"], + ["Alegre", "3200201"], + ["Alfredo Chaves", "3200300"], + ["Alto Rio Novo", "3200359"], + ["Anchieta", "3200409"], + ["Apiacá", "3200508"], + ["Aracruz", "3200607"], + ["Atílio Vivácqua", "3200706"], + ["Baixo Guandu", "3200805"], + ["Barra de São Francisco", "3200904"], + ["Boa Esperança", "3201001"], + ["Bom Jesus do Norte", "3201100"], + ["Brejetuba", "3201159"], + ["Cachoeiro de Itapemirim", "3201209"], + ["Cariacica", "3201308"], + ["Castelo", "3201407"], + ["Colatina", "3201506"], + ["Conceição da Barra", "3201605"], + ["Conceição do Castelo", "3201704"], + ["Divino de São Lourenço", "3201803"], + ["Domingos Martins", "3201902"], + ["Dores do Rio Preto", "3202009"], + ["Ecoporanga", "3202108"], + ["Fundão", "3202207"], + ["Governador Lindenberg", "3202256"], + ["Guaçuí", "3202306"], + ["Guarapari", "3202405"], + ["Ibatiba", "3202454"], + ["Ibiraçu", "3202504"], + ["Ibitirama", "3202553"], + ["Iconha", "3202603"], + ["Irupi", "3202652"], + ["Itaguaçu", "3202702"], + ["Itapemirim", "3202801"], + ["Itarana", "3202900"], + ["Iúna", "3203007"], + ["Jaguaré", "3203056"], + ["Jerônimo Monteiro", "3203106"], + ["João Neiva", "3203130"], + ["Laranja da Terra", "3203163"], + ["Linhares", "3203205"], + ["Mantenópolis", "3203304"], + ["Marataízes", "3203320"], + ["Marechal Floriano", "3203346"], + ["Marilândia", "3203353"], + ["Mimoso do Sul", "3203403"], + ["Montanha", "3203502"], + ["Mucurici", "3203601"], + ["Muniz Freire", "3203700"], + ["Muqui", "3203809"], + ["Nova Venécia", "3203908"], + ["Pancas", "3204005"], + ["Pedro Canário", "3204054"], + ["Pinheiros", "3204104"], + ["Piúma", "3204203"], + ["Ponto Belo", "3204252"], + ["Presidente Kennedy", "3204302"], + ["Rio Bananal", "3204351"], + ["Rio Novo do Sul", "3204401"], + ["Santa Leopoldina", "3204500"], + ["Santa Maria de Jetibá", "3204559"], + ["Santa Teresa", "3204609"], + ["São Domingos do Norte", "3204658"], + ["São Gabriel da Palha", "3204708"], + ["São José do Calçado", "3204807"], + ["São Mateus", "3204906"], + ["São Roque do Canaã", "3204955"], + ["Serra", "3205002"], + ["Sooretama", "3205010"], + ["Vargem Alta", "3205036"], + ["Venda Nova do Imigrante", "3205069"], + ["Viana", "3205101"], + ["Vila Pavão", "3205150"], + ["Vila Valério", "3205176"], + ["Vila Velha", "3205200"], + ["Vitória", "3205309"], ], GO: [ - "Abadia de Goiás", - "Abadiânia", - "Acreúna", - "Adelândia", - "Água Fria de Goiás", - "Água Limpa", - "Águas Lindas de Goiás", - "Alexânia", - "Aloândia", - "Alto Horizonte", - "Alto Paraíso de Goiás", - "Alvorada do Norte", - "Amaralina", - "Americano do Brasil", - "Amorinópolis", - "Anápolis", - "Anhanguera", - "Anicuns", - "Aparecida de Goiânia", - "Aparecida do Rio Doce", - "Aporé", - "Araçu", - "Aragarças", - "Aragoiânia", - "Araguapaz", - "Arenópolis", - "Aruanã", - "Aurilândia", - "Avelinópolis", - "Baliza", - "Barro Alto", - "Bela Vista de Goiás", - "Bom Jardim de Goiás", - "Bom Jesus de Goiás", - "Bonfinópolis", - "Bonópolis", - "Brazabrantes", - "Britânia", - "Buriti Alegre", - "Buriti de Goiás", - "Buritinópolis", - "Cabeceiras", - "Cachoeira Alta", - "Cachoeira de Goiás", - "Cachoeira Dourada", - "Caçu", - "Caiapônia", - "Caldas Novas", - "Caldazinha", - "Campestre de Goiás", - "Campinaçu", - "Campinorte", - "Campo Alegre de Goiás", - "Campo Limpo de Goiás", - "Campos Belos", - "Campos Verdes", - "Carmo do Rio Verde", - "Castelândia", - "Catalão", - "Caturaí", - "Cavalcante", - "Ceres", - "Cezarina", - "Chapadão do Céu", - "Cidade Ocidental", - "Cocalzinho de Goiás", - "Colinas do Sul", - "Córrego do Ouro", - "Corumbá de Goiás", - "Corumbaíba", - "Cristalina", - "Cristianópolis", - "Crixás", - "Cromínia", - "Cumari", - "Damianópolis", - "Damolândia", - "Davinópolis", - "Diorama", - "Divinópolis de Goiás", - "Doverlândia", - "Edealina", - "Edéia", - "Estrela do Norte", - "Faina", - "Fazenda Nova", - "Firminópolis", - "Flores de Goiás", - "Formosa", - "Formoso", - "Gameleira de Goiás", - "Goianápolis", - "Goiandira", - "Goianésia", - "Goiânia", - "Goianira", - "Goiás", - "Goiatuba", - "Gouvelândia", - "Guapó", - "Guaraíta", - "Guarani de Goiás", - "Guarinos", - "Heitoraí", - "Hidrolândia", - "Hidrolina", - "Iaciara", - "Inaciolândia", - "Indiara", - "Inhumas", - "Ipameri", - "Ipiranga de Goiás", - "Iporá", - "Israelândia", - "Itaberaí", - "Itaguari", - "Itaguaru", - "Itajá", - "Itapaci", - "Itapirapuã", - "Itapuranga", - "Itarumã", - "Itauçu", - "Itumbiara", - "Ivolândia", - "Jandaia", - "Jaraguá", - "Jataí", - "Jaupaci", - "Jesúpolis", - "Joviânia", - "Jussara", - "Lagoa Santa", - "Leopoldo de Bulhões", - "Luziânia", - "Mairipotaba", - "Mambaí", - "Mara Rosa", - "Marzagão", - "Matrinchã", - "Maurilândia", - "Mimoso de Goiás", - "Minaçu", - "Mineiros", - "Moiporá", - "Monte Alegre de Goiás", - "Montes Claros de Goiás", - "Montividiu", - "Montividiu do Norte", - "Morrinhos", - "Morro Agudo de Goiás", - "Mossâmedes", - "Mozarlândia", - "Mundo Novo", - "Mutunópolis", - "Nazário", - "Nerópolis", - "Niquelândia", - "Nova América", - "Nova Aurora", - "Nova Crixás", - "Nova Glória", - "Nova Iguaçu de Goiás", - "Nova Roma", - "Nova Veneza", - "Novo Brasil", - "Novo Gama", - "Novo Planalto", - "Orizona", - "Ouro Verde de Goiás", - "Ouvidor", - "Padre Bernardo", - "Palestina de Goiás", - "Palmeiras de Goiás", - "Palmelo", - "Palminópolis", - "Panamá", - "Paranaiguara", - "Paraúna", - "Perolândia", - "Petrolina de Goiás", - "Pilar de Goiás", - "Piracanjuba", - "Piranhas", - "Pirenópolis", - "Pires do Rio", - "Planaltina", - "Pontalina", - "Porangatu", - "Porteirão", - "Portelândia", - "Posse", - "Professor Jamil", - "Quirinópolis", - "Rialma", - "Rianápolis", - "Rio Quente", - "Rio Verde", - "Rubiataba", - "Sanclerlândia", - "Santa Bárbara de Goiás", - "Santa Cruz de Goiás", - "Santa Fé de Goiás", - "Santa Helena de Goiás", - "Santa Isabel", - "Santa Rita do Araguaia", - "Santa Rita do Novo Destino", - "Santa Rosa de Goiás", - "Santa Tereza de Goiás", - "Santa Terezinha de Goiás", - "Santo Antônio da Barra", - "Santo Antônio de Goiás", - "Santo Antônio do Descoberto", - "São Domingos", - "São Francisco de Goiás", - "São João d'Aliança", - "São João da Paraúna", - "São Luís de Montes Belos", - "São Luiz do Norte", - "São Miguel do Araguaia", - "São Miguel do Passa Quatro", - "São Patrício", - "São Simão", - "Senador Canedo", - "Serranópolis", - "Silvânia", - "Simolândia", - "Sítio d'Abadia", - "Taquaral de Goiás", - "Teresina de Goiás", - "Terezópolis de Goiás", - "Três Ranchos", - "Trindade", - "Trombas", - "Turvânia", - "Turvelândia", - "Uirapuru", - "Uruaçu", - "Uruana", - "Urutaí", - "Valparaíso de Goiás", - "Varjão", - "Vianópolis", - "Vicentinópolis", - "Vila Boa", - "Vila Propício", + ["Abadia de Goiás", "5200050"], + ["Abadiânia", "5200100"], + ["Acreúna", "5200134"], + ["Adelândia", "5200159"], + ["Água Fria de Goiás", "5200175"], + ["Água Limpa", "5200209"], + ["Águas Lindas de Goiás", "5200258"], + ["Alexânia", "5200308"], + ["Aloândia", "5200506"], + ["Alto Horizonte", "5200555"], + ["Alto Paraíso de Goiás", "5200605"], + ["Alvorada do Norte", "5200803"], + ["Amaralina", "5200829"], + ["Americano do Brasil", "5200852"], + ["Amorinópolis", "5200902"], + ["Anápolis", "5201108"], + ["Anhanguera", "5201207"], + ["Anicuns", "5201306"], + ["Aparecida de Goiânia", "5201405"], + ["Aparecida do Rio Doce", "5201454"], + ["Aporé", "5201504"], + ["Araçu", "5201603"], + ["Aragarças", "5201702"], + ["Aragoiânia", "5201801"], + ["Araguapaz", "5202155"], + ["Arenópolis", "5202353"], + ["Aruanã", "5202502"], + ["Aurilândia", "5202601"], + ["Avelinópolis", "5202809"], + ["Baliza", "5203104"], + ["Barro Alto", "5203203"], + ["Bela Vista de Goiás", "5203302"], + ["Bom Jardim de Goiás", "5203401"], + ["Bom Jesus de Goiás", "5203500"], + ["Bonfinópolis", "5203559"], + ["Bonópolis", "5203575"], + ["Brazabrantes", "5203609"], + ["Britânia", "5203807"], + ["Buriti Alegre", "5203906"], + ["Buriti de Goiás", "5203939"], + ["Buritinópolis", "5203962"], + ["Cabeceiras", "5204003"], + ["Cachoeira Alta", "5204102"], + ["Cachoeira de Goiás", "5204201"], + ["Cachoeira Dourada", "5204250"], + ["Caçu", "5204300"], + ["Caiapônia", "5204409"], + ["Caldas Novas", "5204508"], + ["Caldazinha", "5204557"], + ["Campestre de Goiás", "5204607"], + ["Campinaçu", "5204656"], + ["Campinorte", "5204706"], + ["Campo Alegre de Goiás", "5204805"], + ["Campo Limpo de Goiás", "5204854"], + ["Campos Belos", "5204904"], + ["Campos Verdes", "5204953"], + ["Carmo do Rio Verde", "5205000"], + ["Castelândia", "5205059"], + ["Catalão", "5205109"], + ["Caturaí", "5205208"], + ["Cavalcante", "5205307"], + ["Ceres", "5205406"], + ["Cezarina", "5205455"], + ["Chapadão do Céu", "5205471"], + ["Cidade Ocidental", "5205497"], + ["Cocalzinho de Goiás", "5205513"], + ["Colinas do Sul", "5205521"], + ["Córrego do Ouro", "5205703"], + ["Corumbá de Goiás", "5205802"], + ["Corumbaíba", "5205901"], + ["Cristalina", "5206206"], + ["Cristianópolis", "5206305"], + ["Crixás", "5206404"], + ["Cromínia", "5206503"], + ["Cumari", "5206602"], + ["Damianópolis", "5206701"], + ["Damolândia", "5206800"], + ["Davinópolis", "5206909"], + ["Diorama", "5207105"], + ["Divinópolis de Goiás", "5208301"], + ["Doverlândia", "5207253"], + ["Edealina", "5207352"], + ["Edéia", "5207402"], + ["Estrela do Norte", "5207501"], + ["Faina", "5207535"], + ["Fazenda Nova", "5207600"], + ["Firminópolis", "5207808"], + ["Flores de Goiás", "5207907"], + ["Formosa", "5208004"], + ["Formoso", "5208103"], + ["Gameleira de Goiás", "5208152"], + ["Goianápolis", "5208400"], + ["Goiandira", "5208509"], + ["Goianésia", "5208608"], + ["Goiânia", "5208707"], + ["Goianira", "5208806"], + ["Goiás", "5208905"], + ["Goiatuba", "5209101"], + ["Gouvelândia", "5209150"], + ["Guapó", "5209200"], + ["Guaraíta", "5209291"], + ["Guarani de Goiás", "5209408"], + ["Guarinos", "5209457"], + ["Heitoraí", "5209606"], + ["Hidrolândia", "5209705"], + ["Hidrolina", "5209804"], + ["Iaciara", "5209903"], + ["Inaciolândia", "5209937"], + ["Indiara", "5209952"], + ["Inhumas", "5210000"], + ["Ipameri", "5210109"], + ["Ipiranga de Goiás", "5210158"], + ["Iporá", "5210208"], + ["Israelândia", "5210307"], + ["Itaberaí", "5210406"], + ["Itaguari", "5210562"], + ["Itaguaru", "5210604"], + ["Itajá", "5210802"], + ["Itapaci", "5210901"], + ["Itapirapuã", "5211008"], + ["Itapuranga", "5211206"], + ["Itarumã", "5211305"], + ["Itauçu", "5211404"], + ["Itumbiara", "5211503"], + ["Ivolândia", "5211602"], + ["Jandaia", "5211701"], + ["Jaraguá", "5211800"], + ["Jataí", "5211909"], + ["Jaupaci", "5212006"], + ["Jesúpolis", "5212055"], + ["Joviânia", "5212105"], + ["Jussara", "5212204"], + ["Lagoa Santa", "5212253"], + ["Leopoldo de Bulhões", "5212303"], + ["Luziânia", "5212501"], + ["Mairipotaba", "5212600"], + ["Mambaí", "5212709"], + ["Mara Rosa", "5212808"], + ["Marzagão", "5212907"], + ["Matrinchã", "5212956"], + ["Maurilândia", "5213004"], + ["Mimoso de Goiás", "5213053"], + ["Minaçu", "5213087"], + ["Mineiros", "5213103"], + ["Moiporá", "5213400"], + ["Monte Alegre de Goiás", "5213509"], + ["Montes Claros de Goiás", "5213707"], + ["Montividiu", "5213756"], + ["Montividiu do Norte", "5213772"], + ["Morrinhos", "5213806"], + ["Morro Agudo de Goiás", "5213855"], + ["Mossâmedes", "5213905"], + ["Mozarlândia", "5214002"], + ["Mundo Novo", "5214051"], + ["Mutunópolis", "5214101"], + ["Nazário", "5214408"], + ["Nerópolis", "5214507"], + ["Niquelândia", "5214606"], + ["Nova América", "5214705"], + ["Nova Aurora", "5214804"], + ["Nova Crixás", "5214838"], + ["Nova Glória", "5214861"], + ["Nova Iguaçu de Goiás", "5214879"], + ["Nova Roma", "5214903"], + ["Nova Veneza", "5215009"], + ["Novo Brasil", "5215207"], + ["Novo Gama", "5215231"], + ["Novo Planalto", "5215256"], + ["Orizona", "5215306"], + ["Ouro Verde de Goiás", "5215405"], + ["Ouvidor", "5215504"], + ["Padre Bernardo", "5215603"], + ["Palestina de Goiás", "5215652"], + ["Palmeiras de Goiás", "5215702"], + ["Palmelo", "5215801"], + ["Palminópolis", "5215900"], + ["Panamá", "5216007"], + ["Paranaiguara", "5216304"], + ["Paraúna", "5216403"], + ["Perolândia", "5216452"], + ["Petrolina de Goiás", "5216809"], + ["Pilar de Goiás", "5216908"], + ["Piracanjuba", "5217104"], + ["Piranhas", "5217203"], + ["Pirenópolis", "5217302"], + ["Pires do Rio", "5217401"], + ["Planaltina", "5217609"], + ["Pontalina", "5217708"], + ["Porangatu", "5218003"], + ["Porteirão", "5218052"], + ["Portelândia", "5218102"], + ["Posse", "5218300"], + ["Professor Jamil", "5218391"], + ["Quirinópolis", "5218508"], + ["Rialma", "5218607"], + ["Rianápolis", "5218706"], + ["Rio Quente", "5218789"], + ["Rio Verde", "5218805"], + ["Rubiataba", "5218904"], + ["Sanclerlândia", "5219001"], + ["Santa Bárbara de Goiás", "5219100"], + ["Santa Cruz de Goiás", "5219209"], + ["Santa Fé de Goiás", "5219258"], + ["Santa Helena de Goiás", "5219308"], + ["Santa Isabel", "5219357"], + ["Santa Rita do Araguaia", "5219407"], + ["Santa Rita do Novo Destino", "5219456"], + ["Santa Rosa de Goiás", "5219506"], + ["Santa Tereza de Goiás", "5219605"], + ["Santa Terezinha de Goiás", "5219704"], + ["Santo Antônio da Barra", "5219712"], + ["Santo Antônio de Goiás", "5219738"], + ["Santo Antônio do Descoberto", "5219753"], + ["São Domingos", "5219803"], + ["São Francisco de Goiás", "5219902"], + ["São João d'Aliança", "5220009"], + ["São João da Paraúna", "5220058"], + ["São Luís de Montes Belos", "5220108"], + ["São Luiz do Norte", "5220157"], + ["São Miguel do Araguaia", "5220207"], + ["São Miguel do Passa Quatro", "5220264"], + ["São Patrício", "5220280"], + ["São Simão", "5220405"], + ["Senador Canedo", "5220454"], + ["Serranópolis", "5220504"], + ["Silvânia", "5220603"], + ["Simolândia", "5220686"], + ["Sítio d'Abadia", "5220702"], + ["Taquaral de Goiás", "5221007"], + ["Teresina de Goiás", "5221080"], + ["Terezópolis de Goiás", "5221197"], + ["Três Ranchos", "5221304"], + ["Trindade", "5221403"], + ["Trombas", "5221452"], + ["Turvânia", "5221502"], + ["Turvelândia", "5221551"], + ["Uirapuru", "5221577"], + ["Uruaçu", "5221601"], + ["Uruana", "5221700"], + ["Urutaí", "5221809"], + ["Valparaíso de Goiás", "5221858"], + ["Varjão", "5221908"], + ["Vianópolis", "5222005"], + ["Vicentinópolis", "5222054"], + ["Vila Boa", "5222203"], + ["Vila Propício", "5222302"], ], MA: [ - "Açailândia", - "Afonso Cunha", - "Água Doce do Maranhão", - "Alcântara", - "Aldeias Altas", - "Altamira do Maranhão", - "Alto Alegre do Maranhão", - "Alto Alegre do Pindaré", - "Alto Parnaíba", - "Amapá do Maranhão", - "Amarante do Maranhão", - "Anajatuba", - "Anapurus", - "Apicum-Açu", - "Araguanã", - "Araioses", - "Arame", - "Arari", - "Axixá", - "Bacabal", - "Bacabeira", - "Bacuri", - "Bacurituba", - "Balsas", - "Barão de Grajaú", - "Barra do Corda", - "Barreirinhas", - "Bela Vista do Maranhão", - "Belágua", - "Benedito Leite", - "Bequimão", - "Bernardo do Mearim", - "Boa Vista do Gurupi", - "Bom Jardim", - "Bom Jesus das Selvas", - "Bom Lugar", - "Brejo", - "Brejo de Areia", - "Buriti", - "Buriti Bravo", - "Buriticupu", - "Buritirana", - "Cachoeira Grande", - "Cajapió", - "Cajari", - "Campestre do Maranhão", - "Cândido Mendes", - "Cantanhede", - "Capinzal do Norte", - "Carolina", - "Carutapera", - "Caxias", - "Cedral", - "Central do Maranhão", - "Centro do Guilherme", - "Centro Novo do Maranhão", - "Chapadinha", - "Cidelândia", - "Codó", - "Coelho Neto", - "Colinas", - "Conceição do Lago-Açu", - "Coroatá", - "Cururupu", - "Davinópolis", - "Dom Pedro", - "Duque Bacelar", - "Esperantinópolis", - "Estreito", - "Feira Nova do Maranhão", - "Fernando Falcão", - "Formosa da Serra Negra", - "Fortaleza dos Nogueiras", - "Fortuna", - "Godofredo Viana", - "Gonçalves Dias", - "Governador Archer", - "Governador Edison Lobão", - "Governador Eugênio Barros", - "Governador Luiz Rocha", - "Governador Newton Bello", - "Governador Nunes Freire", - "Graça Aranha", - "Grajaú", - "Guimarães", - "Humberto de Campos", - "Icatu", - "Igarapé do Meio", - "Igarapé Grande", - "Imperatriz", - "Itaipava do Grajaú", - "Itapecuru Mirim", - "Itinga do Maranhão", - "Jatobá", - "Jenipapo dos Vieiras", - "João Lisboa", - "Joselândia", - "Junco do Maranhão", - "Lago da Pedra", - "Lago do Junco", - "Lago dos Rodrigues", - "Lago Verde", - "Lagoa do Mato", - "Lagoa Grande do Maranhão", - "Lajeado Novo", - "Lima Campos", - "Loreto", - "Luís Domingues", - "Magalhães de Almeida", - "Maracaçumé", - "Marajá do Sena", - "Maranhãozinho", - "Mata Roma", - "Matinha", - "Matões", - "Matões do Norte", - "Milagres do Maranhão", - "Mirador", - "Miranda do Norte", - "Mirinzal", - "Monção", - "Montes Altos", - "Morros", - "Nina Rodrigues", - "Nova Colinas", - "Nova Iorque", - "Nova Olinda do Maranhão", - "Olho d'Água das Cunhãs", - "Olinda Nova do Maranhão", - "Paço do Lumiar", - "Palmeirândia", - "Paraibano", - "Parnarama", - "Passagem Franca", - "Pastos Bons", - "Paulino Neves", - "Paulo Ramos", - "Pedreiras", - "Pedro do Rosário", - "Penalva", - "Peri Mirim", - "Peritoró", - "Pindaré-Mirim", - "Pinheiro", - "Pio XII", - "Pirapemas", - "Poção de Pedras", - "Porto Franco", - "Porto Rico do Maranhão", - "Presidente Dutra", - "Presidente Juscelino", - "Presidente Médici", - "Presidente Sarney", - "Presidente Vargas", - "Primeira Cruz", - "Raposa", - "Riachão", - "Ribamar Fiquene", - "Rosário", - "Sambaíba", - "Santa Filomena do Maranhão", - "Santa Helena", - "Santa Inês", - "Santa Luzia", - "Santa Luzia do Paruá", - "Santa Quitéria do Maranhão", - "Santa Rita", - "Santana do Maranhão", - "Santo Amaro do Maranhão", - "Santo Antônio dos Lopes", - "São Benedito do Rio Preto", - "São Bento", - "São Bernardo", - "São Domingos do Azeitão", - "São Domingos do Maranhão", - "São Félix de Balsas", - "São Francisco do Brejão", - "São Francisco do Maranhão", - "São João Batista", - "São João do Carú", - "São João do Paraíso", - "São João do Soter", - "São João dos Patos", - "São José de Ribamar", - "São José dos Basílios", - "São Luís", - "São Luís Gonzaga do Maranhão", - "São Mateus do Maranhão", - "São Pedro da Água Branca", - "São Pedro dos Crentes", - "São Raimundo das Mangabeiras", - "São Raimundo do Doca Bezerra", - "São Roberto", - "São Vicente Ferrer", - "Satubinha", - "Senador Alexandre Costa", - "Senador La Rocque", - "Serrano do Maranhão", - "Sítio Novo", - "Sucupira do Norte", - "Sucupira do Riachão", - "Tasso Fragoso", - "Timbiras", - "Timon", - "Trizidela do Vale", - "Tufilândia", - "Tuntum", - "Turiaçu", - "Turilândia", - "Tutóia", - "Urbano Santos", - "Vargem Grande", - "Viana", - "Vila Nova dos Martírios", - "Vitória do Mearim", - "Vitorino Freire", - "Zé Doca", + ["Açailândia", "2100055"], + ["Afonso Cunha", "2100105"], + ["Água Doce do Maranhão", "2100154"], + ["Alcântara", "2100204"], + ["Aldeias Altas", "2100303"], + ["Altamira do Maranhão", "2100402"], + ["Alto Alegre do Maranhão", "2100436"], + ["Alto Alegre do Pindaré", "2100477"], + ["Alto Parnaíba", "2100501"], + ["Amapá do Maranhão", "2100550"], + ["Amarante do Maranhão", "2100600"], + ["Anajatuba", "2100709"], + ["Anapurus", "2100808"], + ["Apicum-Açu", "2100832"], + ["Araguanã", "2100873"], + ["Araioses", "2100907"], + ["Arame", "2100956"], + ["Arari", "2101004"], + ["Axixá", "2101103"], + ["Bacabal", "2101202"], + ["Bacabeira", "2101251"], + ["Bacuri", "2101301"], + ["Bacurituba", "2101350"], + ["Balsas", "2101400"], + ["Barão de Grajaú", "2101509"], + ["Barra do Corda", "2101608"], + ["Barreirinhas", "2101707"], + ["Bela Vista do Maranhão", "2101772"], + ["Belágua", "2101731"], + ["Benedito Leite", "2101806"], + ["Bequimão", "2101905"], + ["Bernardo do Mearim", "2101939"], + ["Boa Vista do Gurupi", "2101970"], + ["Bom Jardim", "2102002"], + ["Bom Jesus das Selvas", "2102036"], + ["Bom Lugar", "2102077"], + ["Brejo", "2102101"], + ["Brejo de Areia", "2102150"], + ["Buriti", "2102200"], + ["Buriti Bravo", "2102309"], + ["Buriticupu", "2102325"], + ["Buritirana", "2102358"], + ["Cachoeira Grande", "2102374"], + ["Cajapió", "2102408"], + ["Cajari", "2102507"], + ["Campestre do Maranhão", "2102556"], + ["Cândido Mendes", "2102606"], + ["Cantanhede", "2102705"], + ["Capinzal do Norte", "2102754"], + ["Carolina", "2102804"], + ["Carutapera", "2102903"], + ["Caxias", "2103000"], + ["Cedral", "2103109"], + ["Central do Maranhão", "2103125"], + ["Centro do Guilherme", "2103158"], + ["Centro Novo do Maranhão", "2103174"], + ["Chapadinha", "2103208"], + ["Cidelândia", "2103257"], + ["Codó", "2103307"], + ["Coelho Neto", "2103406"], + ["Colinas", "2103505"], + ["Conceição do Lago-Açu", "2103554"], + ["Coroatá", "2103604"], + ["Cururupu", "2103703"], + ["Davinópolis", "2103752"], + ["Dom Pedro", "2103802"], + ["Duque Bacelar", "2103901"], + ["Esperantinópolis", "2104008"], + ["Estreito", "2104057"], + ["Feira Nova do Maranhão", "2104073"], + ["Fernando Falcão", "2104081"], + ["Formosa da Serra Negra", "2104099"], + ["Fortaleza dos Nogueiras", "2104107"], + ["Fortuna", "2104206"], + ["Godofredo Viana", "2104305"], + ["Gonçalves Dias", "2104404"], + ["Governador Archer", "2104503"], + ["Governador Edison Lobão", "2104552"], + ["Governador Eugênio Barros", "2104602"], + ["Governador Luiz Rocha", "2104628"], + ["Governador Newton Bello", "2104651"], + ["Governador Nunes Freire", "2104677"], + ["Graça Aranha", "2104701"], + ["Grajaú", "2104800"], + ["Guimarães", "2104909"], + ["Humberto de Campos", "2105005"], + ["Icatu", "2105104"], + ["Igarapé do Meio", "2105153"], + ["Igarapé Grande", "2105203"], + ["Imperatriz", "2105302"], + ["Itaipava do Grajaú", "2105351"], + ["Itapecuru Mirim", "2105401"], + ["Itinga do Maranhão", "2105427"], + ["Jatobá", "2105450"], + ["Jenipapo dos Vieiras", "2105476"], + ["João Lisboa", "2105500"], + ["Joselândia", "2105609"], + ["Junco do Maranhão", "2105658"], + ["Lago da Pedra", "2105708"], + ["Lago do Junco", "2105807"], + ["Lago dos Rodrigues", "2105948"], + ["Lago Verde", "2105906"], + ["Lagoa do Mato", "2105922"], + ["Lagoa Grande do Maranhão", "2105963"], + ["Lajeado Novo", "2105989"], + ["Lima Campos", "2106003"], + ["Loreto", "2106102"], + ["Luís Domingues", "2106201"], + ["Magalhães de Almeida", "2106300"], + ["Maracaçumé", "2106326"], + ["Marajá do Sena", "2106359"], + ["Maranhãozinho", "2106375"], + ["Mata Roma", "2106409"], + ["Matinha", "2106508"], + ["Matões", "2106607"], + ["Matões do Norte", "2106631"], + ["Milagres do Maranhão", "2106672"], + ["Mirador", "2106706"], + ["Miranda do Norte", "2106755"], + ["Mirinzal", "2106805"], + ["Monção", "2106904"], + ["Montes Altos", "2107001"], + ["Morros", "2107100"], + ["Nina Rodrigues", "2107209"], + ["Nova Colinas", "2107258"], + ["Nova Iorque", "2107308"], + ["Nova Olinda do Maranhão", "2107357"], + ["Olho d'Água das Cunhãs", "2107407"], + ["Olinda Nova do Maranhão", "2107456"], + ["Paço do Lumiar", "2107506"], + ["Palmeirândia", "2107605"], + ["Paraibano", "2107704"], + ["Parnarama", "2107803"], + ["Passagem Franca", "2107902"], + ["Pastos Bons", "2108009"], + ["Paulino Neves", "2108058"], + ["Paulo Ramos", "2108108"], + ["Pedreiras", "2108207"], + ["Pedro do Rosário", "2108256"], + ["Penalva", "2108306"], + ["Peri Mirim", "2108405"], + ["Peritoró", "2108454"], + ["Pindaré-Mirim", "2108504"], + ["Pinheiro", "2108603"], + ["Pio XII", "2108702"], + ["Pirapemas", "2108801"], + ["Poção de Pedras", "2108900"], + ["Porto Franco", "2109007"], + ["Porto Rico do Maranhão", "2109056"], + ["Presidente Dutra", "2109106"], + ["Presidente Juscelino", "2109205"], + ["Presidente Médici", "2109239"], + ["Presidente Sarney", "2109270"], + ["Presidente Vargas", "2109304"], + ["Primeira Cruz", "2109403"], + ["Raposa", "2109452"], + ["Riachão", "2109502"], + ["Ribamar Fiquene", "2109551"], + ["Rosário", "2109601"], + ["Sambaíba", "2109700"], + ["Santa Filomena do Maranhão", "2109759"], + ["Santa Helena", "2109809"], + ["Santa Inês", "2109908"], + ["Santa Luzia", "2110005"], + ["Santa Luzia do Paruá", "2110039"], + ["Santa Quitéria do Maranhão", "2110104"], + ["Santa Rita", "2110203"], + ["Santana do Maranhão", "2110237"], + ["Santo Amaro do Maranhão", "2110278"], + ["Santo Antônio dos Lopes", "2110302"], + ["São Benedito do Rio Preto", "2110401"], + ["São Bento", "2110500"], + ["São Bernardo", "2110609"], + ["São Domingos do Azeitão", "2110658"], + ["São Domingos do Maranhão", "2110708"], + ["São Félix de Balsas", "2110807"], + ["São Francisco do Brejão", "2110856"], + ["São Francisco do Maranhão", "2110906"], + ["São João Batista", "2111003"], + ["São João do Carú", "2111029"], + ["São João do Paraíso", "2111052"], + ["São João do Soter", "2111078"], + ["São João dos Patos", "2111102"], + ["São José de Ribamar", "2111201"], + ["São José dos Basílios", "2111250"], + ["São Luís", "2111300"], + ["São Luís Gonzaga do Maranhão", "2111409"], + ["São Mateus do Maranhão", "2111508"], + ["São Pedro da Água Branca", "2111532"], + ["São Pedro dos Crentes", "2111573"], + ["São Raimundo das Mangabeiras", "2111607"], + ["São Raimundo do Doca Bezerra", "2111631"], + ["São Roberto", "2111672"], + ["São Vicente Ferrer", "2111706"], + ["Satubinha", "2111722"], + ["Senador Alexandre Costa", "2111748"], + ["Senador La Rocque", "2111763"], + ["Serrano do Maranhão", "2111789"], + ["Sítio Novo", "2111805"], + ["Sucupira do Norte", "2111904"], + ["Sucupira do Riachão", "2111953"], + ["Tasso Fragoso", "2112001"], + ["Timbiras", "2112100"], + ["Timon", "2112209"], + ["Trizidela do Vale", "2112233"], + ["Tufilândia", "2112274"], + ["Tuntum", "2112308"], + ["Turiaçu", "2112407"], + ["Turilândia", "2112456"], + ["Tutóia", "2112506"], + ["Urbano Santos", "2112605"], + ["Vargem Grande", "2112704"], + ["Viana", "2112803"], + ["Vila Nova dos Martírios", "2112852"], + ["Vitória do Mearim", "2112902"], + ["Vitorino Freire", "2113009"], + ["Zé Doca", "2114007"], ], MG: [ - "Abadia dos Dourados", - "Abaeté", - "Abre Campo", - "Acaiaca", - "Açucena", - "Água Boa", - "Água Comprida", - "Aguanil", - "Águas Formosas", - "Águas Vermelhas", - "Aimorés", - "Aiuruoca", - "Alagoa", - "Albertina", - "Além Paraíba", - "Alfenas", - "Alfredo Vasconcelos", - "Almenara", - "Alpercata", - "Alpinópolis", - "Alterosa", - "Alto Caparaó", - "Alto Jequitibá", - "Alto Rio Doce", - "Alvarenga", - "Alvinópolis", - "Alvorada de Minas", - "Amparo do Serra", - "Andradas", - "Andrelândia", - "Angelândia", - "Antônio Carlos", - "Antônio Dias", - "Antônio Prado de Minas", - "Araçaí", - "Aracitaba", - "Araçuaí", - "Araguari", - "Arantina", - "Araponga", - "Araporã", - "Arapuá", - "Araújos", - "Araxá", - "Arceburgo", - "Arcos", - "Areado", - "Argirita", - "Aricanduva", - "Arinos", - "Astolfo Dutra", - "Ataléia", - "Augusto de Lima", - "Baependi", - "Baldim", - "Bambuí", - "Bandeira", - "Bandeira do Sul", - "Barão de Cocais", - "Barão do Monte Alto", - "Barbacena", - "Barra Longa", - "Barroso", - "Bela Vista de Minas", - "Belmiro Braga", - "Belo Horizonte", - "Belo Oriente", - "Belo Vale", - "Berilo", - "Berizal", - "Bertópolis", - "Betim", - "Bias Fortes", - "Bicas", - "Biquinhas", - "Boa Esperança", - "Bocaina de Minas", - "Bocaiúva", - "Bom Despacho", - "Bom Jardim de Minas", - "Bom Jesus da Penha", - "Bom Jesus do Amparo", - "Bom Jesus do Galho", - "Bom Repouso", - "Bom Sucesso", - "Bonfim", - "Bonfinópolis de Minas", - "Bonito de Minas", - "Borda da Mata", - "Botelhos", - "Botumirim", - "Brás Pires", - "Brasilândia de Minas", - "Brasília de Minas", - "Braúnas", - "Brazópolis", - "Brumadinho", - "Bueno Brandão", - "Buenópolis", - "Bugre", - "Buritis", - "Buritizeiro", - "Cabeceira Grande", - "Cabo Verde", - "Cachoeira da Prata", - "Cachoeira de Minas", - "Cachoeira de Pajeú", - "Cachoeira Dourada", - "Caetanópolis", - "Caeté", - "Caiana", - "Cajuri", - "Caldas", - "Camacho", - "Camanducaia", - "Cambuí", - "Cambuquira", - "Campanário", - "Campanha", - "Campestre", - "Campina Verde", - "Campo Azul", - "Campo Belo", - "Campo do Meio", - "Campo Florido", - "Campos Altos", - "Campos Gerais", - "Cana Verde", - "Canaã", - "Canápolis", - "Candeias", - "Cantagalo", - "Caparaó", - "Capela Nova", - "Capelinha", - "Capetinga", - "Capim Branco", - "Capinópolis", - "Capitão Andrade", - "Capitão Enéas", - "Capitólio", - "Caputira", - "Caraí", - "Caranaíba", - "Carandaí", - "Carangola", - "Caratinga", - "Carbonita", - "Careaçu", - "Carlos Chagas", - "Carmésia", - "Carmo da Cachoeira", - "Carmo da Mata", - "Carmo de Minas", - "Carmo do Cajuru", - "Carmo do Paranaíba", - "Carmo do Rio Claro", - "Carmópolis de Minas", - "Carneirinho", - "Carrancas", - "Carvalhópolis", - "Carvalhos", - "Casa Grande", - "Cascalho Rico", - "Cássia", - "Cataguases", - "Catas Altas", - "Catas Altas da Noruega", - "Catuji", - "Catuti", - "Caxambu", - "Cedro do Abaeté", - "Central de Minas", - "Centralina", - "Chácara", - "Chalé", - "Chapada do Norte", - "Chapada Gaúcha", - "Chiador", - "Cipotânea", - "Claraval", - "Claro dos Poções", - "Cláudio", - "Coimbra", - "Coluna", - "Comendador Gomes", - "Comercinho", - "Conceição da Aparecida", - "Conceição da Barra de Minas", - "Conceição das Alagoas", - "Conceição das Pedras", - "Conceição de Ipanema", - "Conceição do Mato Dentro", - "Conceição do Pará", - "Conceição do Rio Verde", - "Conceição dos Ouros", - "Cônego Marinho", - "Confins", - "Congonhal", - "Congonhas", - "Congonhas do Norte", - "Conquista", - "Conselheiro Lafaiete", - "Conselheiro Pena", - "Consolação", - "Contagem", - "Coqueiral", - "Coração de Jesus", - "Cordisburgo", - "Cordislândia", - "Corinto", - "Coroaci", - "Coromandel", - "Coronel Fabriciano", - "Coronel Murta", - "Coronel Pacheco", - "Coronel Xavier Chaves", - "Córrego Danta", - "Córrego do Bom Jesus", - "Córrego Fundo", - "Córrego Novo", - "Couto de Magalhães de Minas", - "Crisólita", - "Cristais", - "Cristália", - "Cristiano Otoni", - "Cristina", - "Crucilândia", - "Cruzeiro da Fortaleza", - "Cruzília", - "Cuparaque", - "Curral de Dentro", - "Curvelo", - "Datas", - "Delfim Moreira", - "Delfinópolis", - "Delta", - "Descoberto", - "Desterro de Entre Rios", - "Desterro do Melo", - "Diamantina", - "Diogo de Vasconcelos", - "Dionísio", - "Divinésia", - "Divino", - "Divino das Laranjeiras", - "Divinolândia de Minas", - "Divinópolis", - "Divisa Alegre", - "Divisa Nova", - "Divisópolis", - "Dom Bosco", - "Dom Cavati", - "Dom Joaquim", - "Dom Silvério", - "Dom Viçoso", - "Dona Euzébia", - "Dores de Campos", - "Dores de Guanhães", - "Dores do Indaiá", - "Dores do Turvo", - "Doresópolis", - "Douradoquara", - "Durandé", - "Elói Mendes", - "Engenheiro Caldas", - "Engenheiro Navarro", - "Entre Folhas", - "Entre Rios de Minas", - "Ervália", - "Esmeraldas", - "Espera Feliz", - "Espinosa", - "Espírito Santo do Dourado", - "Estiva", - "Estrela Dalva", - "Estrela do Indaiá", - "Estrela do Sul", - "Eugenópolis", - "Ewbank da Câmara", - "Extrema", - "Fama", - "Faria Lemos", - "Felício dos Santos", - "Felisburgo", - "Felixlândia", - "Fernandes Tourinho", - "Ferros", - "Fervedouro", - "Florestal", - "Formiga", - "Formoso", - "Fortaleza de Minas", - "Fortuna de Minas", - "Francisco Badaró", - "Francisco Dumont", - "Francisco Sá", - "Franciscópolis", - "Frei Gaspar", - "Frei Inocêncio", - "Frei Lagonegro", - "Fronteira", - "Fronteira dos Vales", - "Fruta de Leite", - "Frutal", - "Funilândia", - "Galiléia", - "Gameleiras", - "Glaucilândia", - "Goiabeira", - "Goianá", - "Gonçalves", - "Gonzaga", - "Gouveia", - "Governador Valadares", - "Grão Mogol", - "Grupiara", - "Guanhães", - "Guapé", - "Guaraciaba", - "Guaraciama", - "Guaranésia", - "Guarani", - "Guarará", - "Guarda-Mor", - "Guaxupé", - "Guidoval", - "Guimarânia", - "Guiricema", - "Gurinhatã", - "Heliodora", - "Iapu", - "Ibertioga", - "Ibiá", - "Ibiaí", - "Ibiracatu", - "Ibiraci", - "Ibirité", - "Ibitiúra de Minas", - "Ibituruna", - "Icaraí de Minas", - "Igarapé", - "Igaratinga", - "Iguatama", - "Ijaci", - "Ilicínea", - "Imbé de Minas", - "Inconfidentes", - "Indaiabira", - "Indianópolis", - "Ingaí", - "Inhapim", - "Inhaúma", - "Inimutaba", - "Ipaba", - "Ipanema", - "Ipatinga", - "Ipiaçu", - "Ipuiúna", - "Iraí de Minas", - "Itabira", - "Itabirinha", - "Itabirito", - "Itacambira", - "Itacarambi", - "Itaguara", - "Itaipé", - "Itajubá", - "Itamarandiba", - "Itamarati de Minas", - "Itambacuri", - "Itambé do Mato Dentro", - "Itamogi", - "Itamonte", - "Itanhandu", - "Itanhomi", - "Itaobim", - "Itapagipe", - "Itapecerica", - "Itapeva", - "Itatiaiuçu", - "Itaú de Minas", - "Itaúna", - "Itaverava", - "Itinga", - "Itueta", - "Ituiutaba", - "Itumirim", - "Iturama", - "Itutinga", - "Jaboticatubas", - "Jacinto", - "Jacuí", - "Jacutinga", - "Jaguaraçu", - "Jaíba", - "Jampruca", - "Janaúba", - "Januária", - "Japaraíba", - "Japonvar", - "Jeceaba", - "Jenipapo de Minas", - "Jequeri", - "Jequitaí", - "Jequitibá", - "Jequitinhonha", - "Jesuânia", - "Joaíma", - "Joanésia", - "João Monlevade", - "João Pinheiro", - "Joaquim Felício", - "Jordânia", - "José Gonçalves de Minas", - "José Raydan", - "Josenópolis", - "Juatuba", - "Juiz de Fora", - "Juramento", - "Juruaia", - "Juvenília", - "Ladainha", - "Lagamar", - "Lagoa da Prata", - "Lagoa dos Patos", - "Lagoa Dourada", - "Lagoa Formosa", - "Lagoa Grande", - "Lagoa Santa", - "Lajinha", - "Lambari", - "Lamim", - "Laranjal", - "Lassance", - "Lavras", - "Leandro Ferreira", - "Leme do Prado", - "Leopoldina", - "Liberdade", - "Lima Duarte", - "Limeira do Oeste", - "Lontra", - "Luisburgo", - "Luislândia", - "Luminárias", - "Luz", - "Machacalis", - "Machado", - "Madre de Deus de Minas", - "Malacacheta", - "Mamonas", - "Manga", - "Manhuaçu", - "Manhumirim", - "Mantena", - "Mar de Espanha", - "Maravilhas", - "Maria da Fé", - "Mariana", - "Marilac", - "Mário Campos", - "Maripá de Minas", - "Marliéria", - "Marmelópolis", - "Martinho Campos", - "Martins Soares", - "Mata Verde", - "Materlândia", - "Mateus Leme", - "Mathias Lobato", - "Matias Barbosa", - "Matias Cardoso", - "Matipó", - "Mato Verde", - "Matozinhos", - "Matutina", - "Medeiros", - "Medina", - "Mendes Pimentel", - "Mercês", - "Mesquita", - "Minas Novas", - "Minduri", - "Mirabela", - "Miradouro", - "Miraí", - "Miravânia", - "Moeda", - "Moema", - "Monjolos", - "Monsenhor Paulo", - "Montalvânia", - "Monte Alegre de Minas", - "Monte Azul", - "Monte Belo", - "Monte Carmelo", - "Monte Formoso", - "Monte Santo de Minas", - "Monte Sião", - "Montes Claros", - "Montezuma", - "Morada Nova de Minas", - "Morro da Garça", - "Morro do Pilar", - "Munhoz", - "Muriaé", - "Mutum", - "Muzambinho", - "Nacip Raydan", - "Nanuque", - "Naque", - "Natalândia", - "Natércia", - "Nazareno", - "Nepomuceno", - "Ninheira", - "Nova Belém", - "Nova Era", - "Nova Lima", - "Nova Módica", - "Nova Ponte", - "Nova Porteirinha", - "Nova Resende", - "Nova Serrana", - "Nova União", - "Novo Cruzeiro", - "Novo Oriente de Minas", - "Novorizonte", - "Olaria", - "Olhos-d'Água", - "Olímpio Noronha", - "Oliveira", - "Oliveira Fortes", - "Onça de Pitangui", - "Oratórios", - "Orizânia", - "Ouro Branco", - "Ouro Fino", - "Ouro Preto", - "Ouro Verde de Minas", - "Padre Carvalho", - "Padre Paraíso", - "Pai Pedro", - "Paineiras", - "Pains", - "Paiva", - "Palma", - "Palmópolis", - "Papagaios", - "Pará de Minas", - "Paracatu", - "Paraguaçu", - "Paraisópolis", - "Paraopeba", - "Passa Quatro", - "Passa Tempo", - "Passa Vinte", - "Passabém", - "Passos", - "Patis", - "Patos de Minas", - "Patrocínio", - "Patrocínio do Muriaé", - "Paula Cândido", - "Paulistas", - "Pavão", - "Peçanha", - "Pedra Azul", - "Pedra Bonita", - "Pedra do Anta", - "Pedra do Indaiá", - "Pedra Dourada", - "Pedralva", - "Pedras de Maria da Cruz", - "Pedrinópolis", - "Pedro Leopoldo", - "Pedro Teixeira", - "Pequeri", - "Pequi", - "Perdigão", - "Perdizes", - "Perdões", - "Periquito", - "Pescador", - "Piau", - "Piedade de Caratinga", - "Piedade de Ponte Nova", - "Piedade do Rio Grande", - "Piedade dos Gerais", - "Pimenta", - "Pingo-d'Água", - "Pintópolis", - "Piracema", - "Pirajuba", - "Piranga", - "Piranguçu", - "Piranguinho", - "Pirapetinga", - "Pirapora", - "Piraúba", - "Pitangui", - "Piumhi", - "Planura", - "Poço Fundo", - "Poços de Caldas", - "Pocrane", - "Pompéu", - "Ponte Nova", - "Ponto Chique", - "Ponto dos Volantes", - "Porteirinha", - "Porto Firme", - "Poté", - "Pouso Alegre", - "Pouso Alto", - "Prados", - "Prata", - "Pratápolis", - "Pratinha", - "Presidente Bernardes", - "Presidente Juscelino", - "Presidente Kubitschek", - "Presidente Olegário", - "Prudente de Morais", - "Quartel Geral", - "Queluzito", - "Raposos", - "Raul Soares", - "Recreio", - "Reduto", - "Resende Costa", - "Resplendor", - "Ressaquinha", - "Riachinho", - "Riacho dos Machados", - "Ribeirão das Neves", - "Ribeirão Vermelho", - "Rio Acima", - "Rio Casca", - "Rio do Prado", - "Rio Doce", - "Rio Espera", - "Rio Manso", - "Rio Novo", - "Rio Paranaíba", - "Rio Pardo de Minas", - "Rio Piracicaba", - "Rio Pomba", - "Rio Preto", - "Rio Vermelho", - "Ritápolis", - "Rochedo de Minas", - "Rodeiro", - "Romaria", - "Rosário da Limeira", - "Rubelita", - "Rubim", - "Sabará", - "Sabinópolis", - "Sacramento", - "Salinas", - "Salto da Divisa", - "Santa Bárbara", - "Santa Bárbara do Leste", - "Santa Bárbara do Monte Verde", - "Santa Bárbara do Tugúrio", - "Santa Cruz de Minas", - "Santa Cruz de Salinas", - "Santa Cruz do Escalvado", - "Santa Efigênia de Minas", - "Santa Fé de Minas", - "Santa Helena de Minas", - "Santa Juliana", - "Santa Luzia", - "Santa Margarida", - "Santa Maria de Itabira", - "Santa Maria do Salto", - "Santa Maria do Suaçuí", - "Santa Rita de Caldas", - "Santa Rita de Ibitipoca", - "Santa Rita de Jacutinga", - "Santa Rita de Minas", - "Santa Rita do Itueto", - "Santa Rita do Sapucaí", - "Santa Rosa da Serra", - "Santa Vitória", - "Santana da Vargem", - "Santana de Cataguases", - "Santana de Pirapama", - "Santana do Deserto", - "Santana do Garambéu", - "Santana do Jacaré", - "Santana do Manhuaçu", - "Santana do Paraíso", - "Santana do Riacho", - "Santana dos Montes", - "Santo Antônio do Amparo", - "Santo Antônio do Aventureiro", - "Santo Antônio do Grama", - "Santo Antônio do Itambé", - "Santo Antônio do Jacinto", - "Santo Antônio do Monte", - "Santo Antônio do Retiro", - "Santo Antônio do Rio Abaixo", - "Santo Hipólito", - "Santos Dumont", - "São Bento Abade", - "São Brás do Suaçuí", - "São Domingos das Dores", - "São Domingos do Prata", - "São Félix de Minas", - "São Francisco", - "São Francisco de Paula", - "São Francisco de Sales", - "São Francisco do Glória", - "São Geraldo", - "São Geraldo da Piedade", - "São Geraldo do Baixio", - "São Gonçalo do Abaeté", - "São Gonçalo do Pará", - "São Gonçalo do Rio Abaixo", - "São Gonçalo do Rio Preto", - "São Gonçalo do Sapucaí", - "São Gotardo", - "São João Batista do Glória", - "São João da Lagoa", - "São João da Mata", - "São João da Ponte", - "São João das Missões", - "São João del Rei", - "São João do Manhuaçu", - "São João do Manteninha", - "São João do Oriente", - "São João do Pacuí", - "São João do Paraíso", - "São João Evangelista", - "São João Nepomuceno", - "São Joaquim de Bicas", - "São José da Barra", - "São José da Lapa", - "São José da Safira", - "São José da Varginha", - "São José do Alegre", - "São José do Divino", - "São José do Goiabal", - "São José do Jacuri", - "São José do Mantimento", - "São Lourenço", - "São Miguel do Anta", - "São Pedro da União", - "São Pedro do Suaçuí", - "São Pedro dos Ferros", - "São Romão", - "São Roque de Minas", - "São Sebastião da Bela Vista", - "São Sebastião da Vargem Alegre", - "São Sebastião do Anta", - "São Sebastião do Maranhão", - "São Sebastião do Oeste", - "São Sebastião do Paraíso", - "São Sebastião do Rio Preto", - "São Sebastião do Rio Verde", - "São Tiago", - "São Tomás de Aquino", - "São Tomé das Letras", - "São Vicente de Minas", - "Sapucaí-Mirim", - "Sardoá", - "Sarzedo", - "Sem-Peixe", - "Senador Amaral", - "Senador Cortes", - "Senador Firmino", - "Senador José Bento", - "Senador Modestino Gonçalves", - "Senhora de Oliveira", - "Senhora do Porto", - "Senhora dos Remédios", - "Sericita", - "Seritinga", - "Serra Azul de Minas", - "Serra da Saudade", - "Serra do Salitre", - "Serra dos Aimorés", - "Serrania", - "Serranópolis de Minas", - "Serranos", - "Serro", - "Sete Lagoas", - "Setubinha", - "Silveirânia", - "Silvianópolis", - "Simão Pereira", - "Simonésia", - "Sobrália", - "Soledade de Minas", - "Tabuleiro", - "Taiobeiras", - "Taparuba", - "Tapira", - "Tapiraí", - "Taquaraçu de Minas", - "Tarumirim", - "Teixeiras", - "Teófilo Otoni", - "Timóteo", - "Tiradentes", - "Tiros", - "Tocantins", - "Tocos do Moji", - "Toledo", - "Tombos", - "Três Corações", - "Três Marias", - "Três Pontas", - "Tumiritinga", - "Tupaciguara", - "Turmalina", - "Turvolândia", - "Ubá", - "Ubaí", - "Ubaporanga", - "Uberaba", - "Uberlândia", - "Umburatiba", - "Unaí", - "União de Minas", - "Uruana de Minas", - "Urucânia", - "Urucuia", - "Vargem Alegre", - "Vargem Bonita", - "Vargem Grande do Rio Pardo", - "Varginha", - "Varjão de Minas", - "Várzea da Palma", - "Varzelândia", - "Vazante", - "Verdelândia", - "Veredinha", - "Veríssimo", - "Vermelho Novo", - "Vespasiano", - "Viçosa", - "Vieiras", - "Virgem da Lapa", - "Virgínia", - "Virginópolis", - "Virgolândia", - "Visconde do Rio Branco", - "Volta Grande", - "Wenceslau Braz", + ["Abadia dos Dourados", "3100104"], + ["Abaeté", "3100203"], + ["Abre Campo", "3100302"], + ["Acaiaca", "3100401"], + ["Açucena", "3100500"], + ["Água Boa", "3100609"], + ["Água Comprida", "3100708"], + ["Aguanil", "3100807"], + ["Águas Formosas", "3100906"], + ["Águas Vermelhas", "3101003"], + ["Aimorés", "3101102"], + ["Aiuruoca", "3101201"], + ["Alagoa", "3101300"], + ["Albertina", "3101409"], + ["Além Paraíba", "3101508"], + ["Alfenas", "3101607"], + ["Alfredo Vasconcelos", "3101631"], + ["Almenara", "3101706"], + ["Alpercata", "3101805"], + ["Alpinópolis", "3101904"], + ["Alterosa", "3102001"], + ["Alto Caparaó", "3102050"], + ["Alto Jequitibá", "3153509"], + ["Alto Rio Doce", "3102100"], + ["Alvarenga", "3102209"], + ["Alvinópolis", "3102308"], + ["Alvorada de Minas", "3102407"], + ["Amparo do Serra", "3102506"], + ["Andradas", "3102605"], + ["Andrelândia", "3102803"], + ["Angelândia", "3102852"], + ["Antônio Carlos", "3102902"], + ["Antônio Dias", "3103009"], + ["Antônio Prado de Minas", "3103108"], + ["Araçaí", "3103207"], + ["Aracitaba", "3103306"], + ["Araçuaí", "3103405"], + ["Araguari", "3103504"], + ["Arantina", "3103603"], + ["Araponga", "3103702"], + ["Araporã", "3103751"], + ["Arapuá", "3103801"], + ["Araújos", "3103900"], + ["Araxá", "3104007"], + ["Arceburgo", "3104106"], + ["Arcos", "3104205"], + ["Areado", "3104304"], + ["Argirita", "3104403"], + ["Aricanduva", "3104452"], + ["Arinos", "3104502"], + ["Astolfo Dutra", "3104601"], + ["Ataléia", "3104700"], + ["Augusto de Lima", "3104809"], + ["Baependi", "3104908"], + ["Baldim", "3105004"], + ["Bambuí", "3105103"], + ["Bandeira", "3105202"], + ["Bandeira do Sul", "3105301"], + ["Barão de Cocais", "3105400"], + ["Barão do Monte Alto", "3105509"], + ["Barbacena", "3105608"], + ["Barra Longa", "3105707"], + ["Barroso", "3105905"], + ["Bela Vista de Minas", "3106002"], + ["Belmiro Braga", "3106101"], + ["Belo Horizonte", "3106200"], + ["Belo Oriente", "3106309"], + ["Belo Vale", "3106408"], + ["Berilo", "3106507"], + ["Berizal", "3106655"], + ["Bertópolis", "3106606"], + ["Betim", "3106705"], + ["Bias Fortes", "3106804"], + ["Bicas", "3106903"], + ["Biquinhas", "3107000"], + ["Boa Esperança", "3107109"], + ["Bocaina de Minas", "3107208"], + ["Bocaiúva", "3107307"], + ["Bom Despacho", "3107406"], + ["Bom Jardim de Minas", "3107505"], + ["Bom Jesus da Penha", "3107604"], + ["Bom Jesus do Amparo", "3107703"], + ["Bom Jesus do Galho", "3107802"], + ["Bom Repouso", "3107901"], + ["Bom Sucesso", "3108008"], + ["Bonfim", "3108107"], + ["Bonfinópolis de Minas", "3108206"], + ["Bonito de Minas", "3108255"], + ["Borda da Mata", "3108305"], + ["Botelhos", "3108404"], + ["Botumirim", "3108503"], + ["Brás Pires", "3108701"], + ["Brasilândia de Minas", "3108552"], + ["Brasília de Minas", "3108602"], + ["Braúnas", "3108800"], + ["Brazópolis", "3108909"], + ["Brumadinho", "3109006"], + ["Bueno Brandão", "3109105"], + ["Buenópolis", "3109204"], + ["Bugre", "3109253"], + ["Buritis", "3109303"], + ["Buritizeiro", "3109402"], + ["Cabeceira Grande", "3109451"], + ["Cabo Verde", "3109501"], + ["Cachoeira da Prata", "3109600"], + ["Cachoeira de Minas", "3109709"], + ["Cachoeira de Pajeú", "3102704"], + ["Cachoeira Dourada", "3109808"], + ["Caetanópolis", "3109907"], + ["Caeté", "3110004"], + ["Caiana", "3110103"], + ["Cajuri", "3110202"], + ["Caldas", "3110301"], + ["Camacho", "3110400"], + ["Camanducaia", "3110509"], + ["Cambuí", "3110608"], + ["Cambuquira", "3110707"], + ["Campanário", "3110806"], + ["Campanha", "3110905"], + ["Campestre", "3111002"], + ["Campina Verde", "3111101"], + ["Campo Azul", "3111150"], + ["Campo Belo", "3111200"], + ["Campo do Meio", "3111309"], + ["Campo Florido", "3111408"], + ["Campos Altos", "3111507"], + ["Campos Gerais", "3111606"], + ["Cana Verde", "3111903"], + ["Canaã", "3111705"], + ["Canápolis", "3111804"], + ["Candeias", "3112000"], + ["Cantagalo", "3112059"], + ["Caparaó", "3112109"], + ["Capela Nova", "3112208"], + ["Capelinha", "3112307"], + ["Capetinga", "3112406"], + ["Capim Branco", "3112505"], + ["Capinópolis", "3112604"], + ["Capitão Andrade", "3112653"], + ["Capitão Enéas", "3112703"], + ["Capitólio", "3112802"], + ["Caputira", "3112901"], + ["Caraí", "3113008"], + ["Caranaíba", "3113107"], + ["Carandaí", "3113206"], + ["Carangola", "3113305"], + ["Caratinga", "3113404"], + ["Carbonita", "3113503"], + ["Careaçu", "3113602"], + ["Carlos Chagas", "3113701"], + ["Carmésia", "3113800"], + ["Carmo da Cachoeira", "3113909"], + ["Carmo da Mata", "3114006"], + ["Carmo de Minas", "3114105"], + ["Carmo do Cajuru", "3114204"], + ["Carmo do Paranaíba", "3114303"], + ["Carmo do Rio Claro", "3114402"], + ["Carmópolis de Minas", "3114501"], + ["Carneirinho", "3114550"], + ["Carrancas", "3114600"], + ["Carvalhópolis", "3114709"], + ["Carvalhos", "3114808"], + ["Casa Grande", "3114907"], + ["Cascalho Rico", "3115003"], + ["Cássia", "3115102"], + ["Cataguases", "3115300"], + ["Catas Altas", "3115359"], + ["Catas Altas da Noruega", "3115409"], + ["Catuji", "3115458"], + ["Catuti", "3115474"], + ["Caxambu", "3115508"], + ["Cedro do Abaeté", "3115607"], + ["Central de Minas", "3115706"], + ["Centralina", "3115805"], + ["Chácara", "3115904"], + ["Chalé", "3116001"], + ["Chapada do Norte", "3116100"], + ["Chapada Gaúcha", "3116159"], + ["Chiador", "3116209"], + ["Cipotânea", "3116308"], + ["Claraval", "3116407"], + ["Claro dos Poções", "3116506"], + ["Cláudio", "3116605"], + ["Coimbra", "3116704"], + ["Coluna", "3116803"], + ["Comendador Gomes", "3116902"], + ["Comercinho", "3117009"], + ["Conceição da Aparecida", "3117108"], + ["Conceição da Barra de Minas", "3115201"], + ["Conceição das Alagoas", "3117306"], + ["Conceição das Pedras", "3117207"], + ["Conceição de Ipanema", "3117405"], + ["Conceição do Mato Dentro", "3117504"], + ["Conceição do Pará", "3117603"], + ["Conceição do Rio Verde", "3117702"], + ["Conceição dos Ouros", "3117801"], + ["Cônego Marinho", "3117836"], + ["Confins", "3117876"], + ["Congonhal", "3117900"], + ["Congonhas", "3118007"], + ["Congonhas do Norte", "3118106"], + ["Conquista", "3118205"], + ["Conselheiro Lafaiete", "3118304"], + ["Conselheiro Pena", "3118403"], + ["Consolação", "3118502"], + ["Contagem", "3118601"], + ["Coqueiral", "3118700"], + ["Coração de Jesus", "3118809"], + ["Cordisburgo", "3118908"], + ["Cordislândia", "3119005"], + ["Corinto", "3119104"], + ["Coroaci", "3119203"], + ["Coromandel", "3119302"], + ["Coronel Fabriciano", "3119401"], + ["Coronel Murta", "3119500"], + ["Coronel Pacheco", "3119609"], + ["Coronel Xavier Chaves", "3119708"], + ["Córrego Danta", "3119807"], + ["Córrego do Bom Jesus", "3119906"], + ["Córrego Fundo", "3119955"], + ["Córrego Novo", "3120003"], + ["Couto de Magalhães de Minas", "3120102"], + ["Crisólita", "3120151"], + ["Cristais", "3120201"], + ["Cristália", "3120300"], + ["Cristiano Otoni", "3120409"], + ["Cristina", "3120508"], + ["Crucilândia", "3120607"], + ["Cruzeiro da Fortaleza", "3120706"], + ["Cruzília", "3120805"], + ["Cuparaque", "3120839"], + ["Curral de Dentro", "3120870"], + ["Curvelo", "3120904"], + ["Datas", "3121001"], + ["Delfim Moreira", "3121100"], + ["Delfinópolis", "3121209"], + ["Delta", "3121258"], + ["Descoberto", "3121308"], + ["Desterro de Entre Rios", "3121407"], + ["Desterro do Melo", "3121506"], + ["Diamantina", "3121605"], + ["Diogo de Vasconcelos", "3121704"], + ["Dionísio", "3121803"], + ["Divinésia", "3121902"], + ["Divino", "3122009"], + ["Divino das Laranjeiras", "3122108"], + ["Divinolândia de Minas", "3122207"], + ["Divinópolis", "3122306"], + ["Divisa Alegre", "3122355"], + ["Divisa Nova", "3122405"], + ["Divisópolis", "3122454"], + ["Dom Bosco", "3122470"], + ["Dom Cavati", "3122504"], + ["Dom Joaquim", "3122603"], + ["Dom Silvério", "3122702"], + ["Dom Viçoso", "3122801"], + ["Dona Euzébia", "3122900"], + ["Dores de Campos", "3123007"], + ["Dores de Guanhães", "3123106"], + ["Dores do Indaiá", "3123205"], + ["Dores do Turvo", "3123304"], + ["Doresópolis", "3123403"], + ["Douradoquara", "3123502"], + ["Durandé", "3123528"], + ["Elói Mendes", "3123601"], + ["Engenheiro Caldas", "3123700"], + ["Engenheiro Navarro", "3123809"], + ["Entre Folhas", "3123858"], + ["Entre Rios de Minas", "3123908"], + ["Ervália", "3124005"], + ["Esmeraldas", "3124104"], + ["Espera Feliz", "3124203"], + ["Espinosa", "3124302"], + ["Espírito Santo do Dourado", "3124401"], + ["Estiva", "3124500"], + ["Estrela Dalva", "3124609"], + ["Estrela do Indaiá", "3124708"], + ["Estrela do Sul", "3124807"], + ["Eugenópolis", "3124906"], + ["Ewbank da Câmara", "3125002"], + ["Extrema", "3125101"], + ["Fama", "3125200"], + ["Faria Lemos", "3125309"], + ["Felício dos Santos", "3125408"], + ["Felisburgo", "3125606"], + ["Felixlândia", "3125705"], + ["Fernandes Tourinho", "3125804"], + ["Ferros", "3125903"], + ["Fervedouro", "3125952"], + ["Florestal", "3126000"], + ["Formiga", "3126109"], + ["Formoso", "3126208"], + ["Fortaleza de Minas", "3126307"], + ["Fortuna de Minas", "3126406"], + ["Francisco Badaró", "3126505"], + ["Francisco Dumont", "3126604"], + ["Francisco Sá", "3126703"], + ["Franciscópolis", "3126752"], + ["Frei Gaspar", "3126802"], + ["Frei Inocêncio", "3126901"], + ["Frei Lagonegro", "3126950"], + ["Fronteira", "3127008"], + ["Fronteira dos Vales", "3127057"], + ["Fruta de Leite", "3127073"], + ["Frutal", "3127107"], + ["Funilândia", "3127206"], + ["Galiléia", "3127305"], + ["Gameleiras", "3127339"], + ["Glaucilândia", "3127354"], + ["Goiabeira", "3127370"], + ["Goianá", "3127388"], + ["Gonçalves", "3127404"], + ["Gonzaga", "3127503"], + ["Gouveia", "3127602"], + ["Governador Valadares", "3127701"], + ["Grão Mogol", "3127800"], + ["Grupiara", "3127909"], + ["Guanhães", "3128006"], + ["Guapé", "3128105"], + ["Guaraciaba", "3128204"], + ["Guaraciama", "3128253"], + ["Guaranésia", "3128303"], + ["Guarani", "3128402"], + ["Guarará", "3128501"], + ["Guarda-Mor", "3128600"], + ["Guaxupé", "3128709"], + ["Guidoval", "3128808"], + ["Guimarânia", "3128907"], + ["Guiricema", "3129004"], + ["Gurinhatã", "3129103"], + ["Heliodora", "3129202"], + ["Iapu", "3129301"], + ["Ibertioga", "3129400"], + ["Ibiá", "3129509"], + ["Ibiaí", "3129608"], + ["Ibiracatu", "3129657"], + ["Ibiraci", "3129707"], + ["Ibirité", "3129806"], + ["Ibitiúra de Minas", "3129905"], + ["Ibituruna", "3130002"], + ["Icaraí de Minas", "3130051"], + ["Igarapé", "3130101"], + ["Igaratinga", "3130200"], + ["Iguatama", "3130309"], + ["Ijaci", "3130408"], + ["Ilicínea", "3130507"], + ["Imbé de Minas", "3130556"], + ["Inconfidentes", "3130606"], + ["Indaiabira", "3130655"], + ["Indianópolis", "3130705"], + ["Ingaí", "3130804"], + ["Inhapim", "3130903"], + ["Inhaúma", "3131000"], + ["Inimutaba", "3131109"], + ["Ipaba", "3131158"], + ["Ipanema", "3131208"], + ["Ipatinga", "3131307"], + ["Ipiaçu", "3131406"], + ["Ipuiúna", "3131505"], + ["Iraí de Minas", "3131604"], + ["Itabira", "3131703"], + ["Itabirinha", "3131802"], + ["Itabirito", "3131901"], + ["Itacambira", "3132008"], + ["Itacarambi", "3132107"], + ["Itaguara", "3132206"], + ["Itaipé", "3132305"], + ["Itajubá", "3132404"], + ["Itamarandiba", "3132503"], + ["Itamarati de Minas", "3132602"], + ["Itambacuri", "3132701"], + ["Itambé do Mato Dentro", "3132800"], + ["Itamogi", "3132909"], + ["Itamonte", "3133006"], + ["Itanhandu", "3133105"], + ["Itanhomi", "3133204"], + ["Itaobim", "3133303"], + ["Itapagipe", "3133402"], + ["Itapecerica", "3133501"], + ["Itapeva", "3133600"], + ["Itatiaiuçu", "3133709"], + ["Itaú de Minas", "3133758"], + ["Itaúna", "3133808"], + ["Itaverava", "3133907"], + ["Itinga", "3134004"], + ["Itueta", "3134103"], + ["Ituiutaba", "3134202"], + ["Itumirim", "3134301"], + ["Iturama", "3134400"], + ["Itutinga", "3134509"], + ["Jaboticatubas", "3134608"], + ["Jacinto", "3134707"], + ["Jacuí", "3134806"], + ["Jacutinga", "3134905"], + ["Jaguaraçu", "3135001"], + ["Jaíba", "3135050"], + ["Jampruca", "3135076"], + ["Janaúba", "3135100"], + ["Januária", "3135209"], + ["Japaraíba", "3135308"], + ["Japonvar", "3135357"], + ["Jeceaba", "3135407"], + ["Jenipapo de Minas", "3135456"], + ["Jequeri", "3135506"], + ["Jequitaí", "3135605"], + ["Jequitibá", "3135704"], + ["Jequitinhonha", "3135803"], + ["Jesuânia", "3135902"], + ["Joaíma", "3136009"], + ["Joanésia", "3136108"], + ["João Monlevade", "3136207"], + ["João Pinheiro", "3136306"], + ["Joaquim Felício", "3136405"], + ["Jordânia", "3136504"], + ["José Gonçalves de Minas", "3136520"], + ["José Raydan", "3136553"], + ["Josenópolis", "3136579"], + ["Juatuba", "3136652"], + ["Juiz de Fora", "3136702"], + ["Juramento", "3136801"], + ["Juruaia", "3136900"], + ["Juvenília", "3136959"], + ["Ladainha", "3137007"], + ["Lagamar", "3137106"], + ["Lagoa da Prata", "3137205"], + ["Lagoa dos Patos", "3137304"], + ["Lagoa Dourada", "3137403"], + ["Lagoa Formosa", "3137502"], + ["Lagoa Grande", "3137536"], + ["Lagoa Santa", "3137601"], + ["Lajinha", "3137700"], + ["Lambari", "3137809"], + ["Lamim", "3137908"], + ["Laranjal", "3138005"], + ["Lassance", "3138104"], + ["Lavras", "3138203"], + ["Leandro Ferreira", "3138302"], + ["Leme do Prado", "3138351"], + ["Leopoldina", "3138401"], + ["Liberdade", "3138500"], + ["Lima Duarte", "3138609"], + ["Limeira do Oeste", "3138625"], + ["Lontra", "3138658"], + ["Luisburgo", "3138674"], + ["Luislândia", "3138682"], + ["Luminárias", "3138708"], + ["Luz", "3138807"], + ["Machacalis", "3138906"], + ["Machado", "3139003"], + ["Madre de Deus de Minas", "3139102"], + ["Malacacheta", "3139201"], + ["Mamonas", "3139250"], + ["Manga", "3139300"], + ["Manhuaçu", "3139409"], + ["Manhumirim", "3139508"], + ["Mantena", "3139607"], + ["Mar de Espanha", "3139805"], + ["Maravilhas", "3139706"], + ["Maria da Fé", "3139904"], + ["Mariana", "3140001"], + ["Marilac", "3140100"], + ["Mário Campos", "3140159"], + ["Maripá de Minas", "3140209"], + ["Marliéria", "3140308"], + ["Marmelópolis", "3140407"], + ["Martinho Campos", "3140506"], + ["Martins Soares", "3140530"], + ["Mata Verde", "3140555"], + ["Materlândia", "3140605"], + ["Mateus Leme", "3140704"], + ["Mathias Lobato", "3171501"], + ["Matias Barbosa", "3140803"], + ["Matias Cardoso", "3140852"], + ["Matipó", "3140902"], + ["Mato Verde", "3141009"], + ["Matozinhos", "3141108"], + ["Matutina", "3141207"], + ["Medeiros", "3141306"], + ["Medina", "3141405"], + ["Mendes Pimentel", "3141504"], + ["Mercês", "3141603"], + ["Mesquita", "3141702"], + ["Minas Novas", "3141801"], + ["Minduri", "3141900"], + ["Mirabela", "3142007"], + ["Miradouro", "3142106"], + ["Miraí", "3142205"], + ["Miravânia", "3142254"], + ["Moeda", "3142304"], + ["Moema", "3142403"], + ["Monjolos", "3142502"], + ["Monsenhor Paulo", "3142601"], + ["Montalvânia", "3142700"], + ["Monte Alegre de Minas", "3142809"], + ["Monte Azul", "3142908"], + ["Monte Belo", "3143005"], + ["Monte Carmelo", "3143104"], + ["Monte Formoso", "3143153"], + ["Monte Santo de Minas", "3143203"], + ["Monte Sião", "3143401"], + ["Montes Claros", "3143302"], + ["Montezuma", "3143450"], + ["Morada Nova de Minas", "3143500"], + ["Morro da Garça", "3143609"], + ["Morro do Pilar", "3143708"], + ["Munhoz", "3143807"], + ["Muriaé", "3143906"], + ["Mutum", "3144003"], + ["Muzambinho", "3144102"], + ["Nacip Raydan", "3144201"], + ["Nanuque", "3144300"], + ["Naque", "3144359"], + ["Natalândia", "3144375"], + ["Natércia", "3144409"], + ["Nazareno", "3144508"], + ["Nepomuceno", "3144607"], + ["Ninheira", "3144656"], + ["Nova Belém", "3144672"], + ["Nova Era", "3144706"], + ["Nova Lima", "3144805"], + ["Nova Módica", "3144904"], + ["Nova Ponte", "3145000"], + ["Nova Porteirinha", "3145059"], + ["Nova Resende", "3145109"], + ["Nova Serrana", "3145208"], + ["Nova União", "3136603"], + ["Novo Cruzeiro", "3145307"], + ["Novo Oriente de Minas", "3145356"], + ["Novorizonte", "3145372"], + ["Olaria", "3145406"], + ["Olhos-d'Água", "3145455"], + ["Olímpio Noronha", "3145505"], + ["Oliveira", "3145604"], + ["Oliveira Fortes", "3145703"], + ["Onça de Pitangui", "3145802"], + ["Oratórios", "3145851"], + ["Orizânia", "3145877"], + ["Ouro Branco", "3145901"], + ["Ouro Fino", "3146008"], + ["Ouro Preto", "3146107"], + ["Ouro Verde de Minas", "3146206"], + ["Padre Carvalho", "3146255"], + ["Padre Paraíso", "3146305"], + ["Pai Pedro", "3146552"], + ["Paineiras", "3146404"], + ["Pains", "3146503"], + ["Paiva", "3146602"], + ["Palma", "3146701"], + ["Palmópolis", "3146750"], + ["Papagaios", "3146909"], + ["Pará de Minas", "3147105"], + ["Paracatu", "3147006"], + ["Paraguaçu", "3147204"], + ["Paraisópolis", "3147303"], + ["Paraopeba", "3147402"], + ["Passa Quatro", "3147600"], + ["Passa Tempo", "3147709"], + ["Passa Vinte", "3147808"], + ["Passabém", "3147501"], + ["Passos", "3147907"], + ["Patis", "3147956"], + ["Patos de Minas", "3148004"], + ["Patrocínio", "3148103"], + ["Patrocínio do Muriaé", "3148202"], + ["Paula Cândido", "3148301"], + ["Paulistas", "3148400"], + ["Pavão", "3148509"], + ["Peçanha", "3148608"], + ["Pedra Azul", "3148707"], + ["Pedra Bonita", "3148756"], + ["Pedra do Anta", "3148806"], + ["Pedra do Indaiá", "3148905"], + ["Pedra Dourada", "3149002"], + ["Pedralva", "3149101"], + ["Pedras de Maria da Cruz", "3149150"], + ["Pedrinópolis", "3149200"], + ["Pedro Leopoldo", "3149309"], + ["Pedro Teixeira", "3149408"], + ["Pequeri", "3149507"], + ["Pequi", "3149606"], + ["Perdigão", "3149705"], + ["Perdizes", "3149804"], + ["Perdões", "3149903"], + ["Periquito", "3149952"], + ["Pescador", "3150000"], + ["Piau", "3150109"], + ["Piedade de Caratinga", "3150158"], + ["Piedade de Ponte Nova", "3150208"], + ["Piedade do Rio Grande", "3150307"], + ["Piedade dos Gerais", "3150406"], + ["Pimenta", "3150505"], + ["Pingo-d'Água", "3150539"], + ["Pintópolis", "3150570"], + ["Piracema", "3150604"], + ["Pirajuba", "3150703"], + ["Piranga", "3150802"], + ["Piranguçu", "3150901"], + ["Piranguinho", "3151008"], + ["Pirapetinga", "3151107"], + ["Pirapora", "3151206"], + ["Piraúba", "3151305"], + ["Pitangui", "3151404"], + ["Piumhi", "3151503"], + ["Planura", "3151602"], + ["Poço Fundo", "3151701"], + ["Poços de Caldas", "3151800"], + ["Pocrane", "3151909"], + ["Pompéu", "3152006"], + ["Ponte Nova", "3152105"], + ["Ponto Chique", "3152131"], + ["Ponto dos Volantes", "3152170"], + ["Porteirinha", "3152204"], + ["Porto Firme", "3152303"], + ["Poté", "3152402"], + ["Pouso Alegre", "3152501"], + ["Pouso Alto", "3152600"], + ["Prados", "3152709"], + ["Prata", "3152808"], + ["Pratápolis", "3152907"], + ["Pratinha", "3153004"], + ["Presidente Bernardes", "3153103"], + ["Presidente Juscelino", "3153202"], + ["Presidente Kubitschek", "3153301"], + ["Presidente Olegário", "3153400"], + ["Prudente de Morais", "3153608"], + ["Quartel Geral", "3153707"], + ["Queluzito", "3153806"], + ["Raposos", "3153905"], + ["Raul Soares", "3154002"], + ["Recreio", "3154101"], + ["Reduto", "3154150"], + ["Resende Costa", "3154200"], + ["Resplendor", "3154309"], + ["Ressaquinha", "3154408"], + ["Riachinho", "3154457"], + ["Riacho dos Machados", "3154507"], + ["Ribeirão das Neves", "3154606"], + ["Ribeirão Vermelho", "3154705"], + ["Rio Acima", "3154804"], + ["Rio Casca", "3154903"], + ["Rio do Prado", "3155108"], + ["Rio Doce", "3155009"], + ["Rio Espera", "3155207"], + ["Rio Manso", "3155306"], + ["Rio Novo", "3155405"], + ["Rio Paranaíba", "3155504"], + ["Rio Pardo de Minas", "3155603"], + ["Rio Piracicaba", "3155702"], + ["Rio Pomba", "3155801"], + ["Rio Preto", "3155900"], + ["Rio Vermelho", "3156007"], + ["Ritápolis", "3156106"], + ["Rochedo de Minas", "3156205"], + ["Rodeiro", "3156304"], + ["Romaria", "3156403"], + ["Rosário da Limeira", "3156452"], + ["Rubelita", "3156502"], + ["Rubim", "3156601"], + ["Sabará", "3156700"], + ["Sabinópolis", "3156809"], + ["Sacramento", "3156908"], + ["Salinas", "3157005"], + ["Salto da Divisa", "3157104"], + ["Santa Bárbara", "3157203"], + ["Santa Bárbara do Leste", "3157252"], + ["Santa Bárbara do Monte Verde", "3157278"], + ["Santa Bárbara do Tugúrio", "3157302"], + ["Santa Cruz de Minas", "3157336"], + ["Santa Cruz de Salinas", "3157377"], + ["Santa Cruz do Escalvado", "3157401"], + ["Santa Efigênia de Minas", "3157500"], + ["Santa Fé de Minas", "3157609"], + ["Santa Helena de Minas", "3157658"], + ["Santa Juliana", "3157708"], + ["Santa Luzia", "3157807"], + ["Santa Margarida", "3157906"], + ["Santa Maria de Itabira", "3158003"], + ["Santa Maria do Salto", "3158102"], + ["Santa Maria do Suaçuí", "3158201"], + ["Santa Rita de Caldas", "3159209"], + ["Santa Rita de Ibitipoca", "3159407"], + ["Santa Rita de Jacutinga", "3159308"], + ["Santa Rita de Minas", "3159357"], + ["Santa Rita do Itueto", "3159506"], + ["Santa Rita do Sapucaí", "3159605"], + ["Santa Rosa da Serra", "3159704"], + ["Santa Vitória", "3159803"], + ["Santana da Vargem", "3158300"], + ["Santana de Cataguases", "3158409"], + ["Santana de Pirapama", "3158508"], + ["Santana do Deserto", "3158607"], + ["Santana do Garambéu", "3158706"], + ["Santana do Jacaré", "3158805"], + ["Santana do Manhuaçu", "3158904"], + ["Santana do Paraíso", "3158953"], + ["Santana do Riacho", "3159001"], + ["Santana dos Montes", "3159100"], + ["Santo Antônio do Amparo", "3159902"], + ["Santo Antônio do Aventureiro", "3160009"], + ["Santo Antônio do Grama", "3160108"], + ["Santo Antônio do Itambé", "3160207"], + ["Santo Antônio do Jacinto", "3160306"], + ["Santo Antônio do Monte", "3160405"], + ["Santo Antônio do Retiro", "3160454"], + ["Santo Antônio do Rio Abaixo", "3160504"], + ["Santo Hipólito", "3160603"], + ["Santos Dumont", "3160702"], + ["São Bento Abade", "3160801"], + ["São Brás do Suaçuí", "3160900"], + ["São Domingos das Dores", "3160959"], + ["São Domingos do Prata", "3161007"], + ["São Félix de Minas", "3161056"], + ["São Francisco", "3161106"], + ["São Francisco de Paula", "3161205"], + ["São Francisco de Sales", "3161304"], + ["São Francisco do Glória", "3161403"], + ["São Geraldo", "3161502"], + ["São Geraldo da Piedade", "3161601"], + ["São Geraldo do Baixio", "3161650"], + ["São Gonçalo do Abaeté", "3161700"], + ["São Gonçalo do Pará", "3161809"], + ["São Gonçalo do Rio Abaixo", "3161908"], + ["São Gonçalo do Rio Preto", "3125507"], + ["São Gonçalo do Sapucaí", "3162005"], + ["São Gotardo", "3162104"], + ["São João Batista do Glória", "3162203"], + ["São João da Lagoa", "3162252"], + ["São João da Mata", "3162302"], + ["São João da Ponte", "3162401"], + ["São João das Missões", "3162450"], + ["São João del Rei", "3162500"], + ["São João do Manhuaçu", "3162559"], + ["São João do Manteninha", "3162575"], + ["São João do Oriente", "3162609"], + ["São João do Pacuí", "3162658"], + ["São João do Paraíso", "3162708"], + ["São João Evangelista", "3162807"], + ["São João Nepomuceno", "3162906"], + ["São Joaquim de Bicas", "3162922"], + ["São José da Barra", "3162948"], + ["São José da Lapa", "3162955"], + ["São José da Safira", "3163003"], + ["São José da Varginha", "3163102"], + ["São José do Alegre", "3163201"], + ["São José do Divino", "3163300"], + ["São José do Goiabal", "3163409"], + ["São José do Jacuri", "3163508"], + ["São José do Mantimento", "3163607"], + ["São Lourenço", "3163706"], + ["São Miguel do Anta", "3163805"], + ["São Pedro da União", "3163904"], + ["São Pedro do Suaçuí", "3164100"], + ["São Pedro dos Ferros", "3164001"], + ["São Romão", "3164209"], + ["São Roque de Minas", "3164308"], + ["São Sebastião da Bela Vista", "3164407"], + ["São Sebastião da Vargem Alegre", "3164431"], + ["São Sebastião do Anta", "3164472"], + ["São Sebastião do Maranhão", "3164506"], + ["São Sebastião do Oeste", "3164605"], + ["São Sebastião do Paraíso", "3164704"], + ["São Sebastião do Rio Preto", "3164803"], + ["São Sebastião do Rio Verde", "3164902"], + ["São Tiago", "3165008"], + ["São Tomás de Aquino", "3165107"], + ["São Tomé das Letras", "3165206"], + ["São Vicente de Minas", "3165305"], + ["Sapucaí-Mirim", "3165404"], + ["Sardoá", "3165503"], + ["Sarzedo", "3165537"], + ["Sem-Peixe", "3165560"], + ["Senador Amaral", "3165578"], + ["Senador Cortes", "3165602"], + ["Senador Firmino", "3165701"], + ["Senador José Bento", "3165800"], + ["Senador Modestino Gonçalves", "3165909"], + ["Senhora de Oliveira", "3166006"], + ["Senhora do Porto", "3166105"], + ["Senhora dos Remédios", "3166204"], + ["Sericita", "3166303"], + ["Seritinga", "3166402"], + ["Serra Azul de Minas", "3166501"], + ["Serra da Saudade", "3166600"], + ["Serra do Salitre", "3166808"], + ["Serra dos Aimorés", "3166709"], + ["Serrania", "3166907"], + ["Serranópolis de Minas", "3166956"], + ["Serranos", "3167004"], + ["Serro", "3167103"], + ["Sete Lagoas", "3167202"], + ["Setubinha", "3165552"], + ["Silveirânia", "3167301"], + ["Silvianópolis", "3167400"], + ["Simão Pereira", "3167509"], + ["Simonésia", "3167608"], + ["Sobrália", "3167707"], + ["Soledade de Minas", "3167806"], + ["Tabuleiro", "3167905"], + ["Taiobeiras", "3168002"], + ["Taparuba", "3168051"], + ["Tapira", "3168101"], + ["Tapiraí", "3168200"], + ["Taquaraçu de Minas", "3168309"], + ["Tarumirim", "3168408"], + ["Teixeiras", "3168507"], + ["Teófilo Otoni", "3168606"], + ["Timóteo", "3168705"], + ["Tiradentes", "3168804"], + ["Tiros", "3168903"], + ["Tocantins", "3169000"], + ["Tocos do Moji", "3169059"], + ["Toledo", "3169109"], + ["Tombos", "3169208"], + ["Três Corações", "3169307"], + ["Três Marias", "3169356"], + ["Três Pontas", "3169406"], + ["Tumiritinga", "3169505"], + ["Tupaciguara", "3169604"], + ["Turmalina", "3169703"], + ["Turvolândia", "3169802"], + ["Ubá", "3169901"], + ["Ubaí", "3170008"], + ["Ubaporanga", "3170057"], + ["Uberaba", "3170107"], + ["Uberlândia", "3170206"], + ["Umburatiba", "3170305"], + ["Unaí", "3170404"], + ["União de Minas", "3170438"], + ["Uruana de Minas", "3170479"], + ["Urucânia", "3170503"], + ["Urucuia", "3170529"], + ["Vargem Alegre", "3170578"], + ["Vargem Bonita", "3170602"], + ["Vargem Grande do Rio Pardo", "3170651"], + ["Varginha", "3170701"], + ["Varjão de Minas", "3170750"], + ["Várzea da Palma", "3170800"], + ["Varzelândia", "3170909"], + ["Vazante", "3171006"], + ["Verdelândia", "3171030"], + ["Veredinha", "3171071"], + ["Veríssimo", "3171105"], + ["Vermelho Novo", "3171154"], + ["Vespasiano", "3171204"], + ["Viçosa", "3171303"], + ["Vieiras", "3171402"], + ["Virgem da Lapa", "3171600"], + ["Virgínia", "3171709"], + ["Virginópolis", "3171808"], + ["Virgolândia", "3171907"], + ["Visconde do Rio Branco", "3172004"], + ["Volta Grande", "3172103"], + ["Wenceslau Braz", "3172202"], ], MS: [ - "Água Clara", - "Alcinópolis", - "Amambai", - "Anastácio", - "Anaurilândia", - "Angélica", - "Antônio João", - "Aparecida do Taboado", - "Aquidauana", - "Aral Moreira", - "Bandeirantes", - "Bataguassu", - "Batayporã", - "Bela Vista", - "Bodoquena", - "Bonito", - "Brasilândia", - "Caarapó", - "Camapuã", - "Campo Grande", - "Caracol", - "Cassilândia", - "Chapadão do Sul", - "Corguinho", - "Coronel Sapucaia", - "Corumbá", - "Costa Rica", - "Coxim", - "Deodápolis", - "Dois Irmãos do Buriti", - "Douradina", - "Dourados", - "Eldorado", - "Fátima do Sul", - "Figueirão", - "Glória de Dourados", - "Guia Lopes da Laguna", - "Iguatemi", - "Inocência", - "Itaporã", - "Itaquiraí", - "Ivinhema", - "Japorã", - "Jaraguari", - "Jardim", - "Jateí", - "Juti", - "Ladário", - "Laguna Carapã", - "Maracaju", - "Miranda", - "Mundo Novo", - "Naviraí", - "Nioaque", - "Nova Alvorada do Sul", - "Nova Andradina", - "Novo Horizonte do Sul", - "Paraíso das Águas", - "Paranaíba", - "Paranhos", - "Pedro Gomes", - "Ponta Porã", - "Porto Murtinho", - "Ribas do Rio Pardo", - "Rio Brilhante", - "Rio Negro", - "Rio Verde de Mato Grosso", - "Rochedo", - "Santa Rita do Pardo", - "São Gabriel do Oeste", - "Selvíria", - "Sete Quedas", - "Sidrolândia", - "Sonora", - "Tacuru", - "Taquarussu", - "Terenos", - "Três Lagoas", - "Vicentina", + ["Água Clara", "5000203"], + ["Alcinópolis", "5000252"], + ["Amambai", "5000609"], + ["Anastácio", "5000708"], + ["Anaurilândia", "5000807"], + ["Angélica", "5000856"], + ["Antônio João", "5000906"], + ["Aparecida do Taboado", "5001003"], + ["Aquidauana", "5001102"], + ["Aral Moreira", "5001243"], + ["Bandeirantes", "5001508"], + ["Bataguassu", "5001904"], + ["Batayporã", "5002001"], + ["Bela Vista", "5002100"], + ["Bodoquena", "5002159"], + ["Bonito", "5002209"], + ["Brasilândia", "5002308"], + ["Caarapó", "5002407"], + ["Camapuã", "5002605"], + ["Campo Grande", "5002704"], + ["Caracol", "5002803"], + ["Cassilândia", "5002902"], + ["Chapadão do Sul", "5002951"], + ["Corguinho", "5003108"], + ["Coronel Sapucaia", "5003157"], + ["Corumbá", "5003207"], + ["Costa Rica", "5003256"], + ["Coxim", "5003306"], + ["Deodápolis", "5003454"], + ["Dois Irmãos do Buriti", "5003488"], + ["Douradina", "5003504"], + ["Dourados", "5003702"], + ["Eldorado", "5003751"], + ["Fátima do Sul", "5003801"], + ["Figueirão", "5003900"], + ["Glória de Dourados", "5004007"], + ["Guia Lopes da Laguna", "5004106"], + ["Iguatemi", "5004304"], + ["Inocência", "5004403"], + ["Itaporã", "5004502"], + ["Itaquiraí", "5004601"], + ["Ivinhema", "5004700"], + ["Japorã", "5004809"], + ["Jaraguari", "5004908"], + ["Jardim", "5005004"], + ["Jateí", "5005103"], + ["Juti", "5005152"], + ["Ladário", "5005202"], + ["Laguna Carapã", "5005251"], + ["Maracaju", "5005400"], + ["Miranda", "5005608"], + ["Mundo Novo", "5005681"], + ["Naviraí", "5005707"], + ["Nioaque", "5005806"], + ["Nova Alvorada do Sul", "5006002"], + ["Nova Andradina", "5006200"], + ["Novo Horizonte do Sul", "5006259"], + ["Paraíso das Águas", "5006275"], + ["Paranaíba", "5006309"], + ["Paranhos", "5006358"], + ["Pedro Gomes", "5006408"], + ["Ponta Porã", "5006606"], + ["Porto Murtinho", "5006903"], + ["Ribas do Rio Pardo", "5007109"], + ["Rio Brilhante", "5007208"], + ["Rio Negro", "5007307"], + ["Rio Verde de Mato Grosso", "5007406"], + ["Rochedo", "5007505"], + ["Santa Rita do Pardo", "5007554"], + ["São Gabriel do Oeste", "5007695"], + ["Selvíria", "5007802"], + ["Sete Quedas", "5007703"], + ["Sidrolândia", "5007901"], + ["Sonora", "5007935"], + ["Tacuru", "5007950"], + ["Taquarussu", "5007976"], + ["Terenos", "5008008"], + ["Três Lagoas", "5008305"], + ["Vicentina", "5008404"], ], MT: [ - "Acorizal", - "Água Boa", - "Alta Floresta", - "Alto Araguaia", - "Alto Boa Vista", - "Alto Garças", - "Alto Paraguai", - "Alto Taquari", - "Apiacás", - "Araguaiana", - "Araguainha", - "Araputanga", - "Arenápolis", - "Aripuanã", - "Barão de Melgaço", - "Barra do Bugres", - "Barra do Garças", - "Bom Jesus do Araguaia", - "Brasnorte", - "Cáceres", - "Campinápolis", - "Campo Novo do Parecis", - "Campo Verde", - "Campos de Júlio", - "Canabrava do Norte", - "Canarana", - "Carlinda", - "Castanheira", - "Chapada dos Guimarães", - "Cláudia", - "Cocalinho", - "Colíder", - "Colniza", - "Comodoro", - "Confresa", - "Conquista D'Oeste", - "Cotriguaçu", - "Cuiabá", - "Curvelândia", - "Denise", - "Diamantino", - "Dom Aquino", - "Feliz Natal", - "Figueirópolis D'Oeste", - "Gaúcha do Norte", - "General Carneiro", - "Glória D'Oeste", - "Guarantã do Norte", - "Guiratinga", - "Indiavaí", - "Ipiranga do Norte", - "Itanhangá", - "Itaúba", - "Itiquira", - "Jaciara", - "Jangada", - "Jauru", - "Juara", - "Juína", - "Juruena", - "Juscimeira", - "Lambari D'Oeste", - "Lucas do Rio Verde", - "Luciara", - "Marcelândia", - "Matupá", - "Mirassol d'Oeste", - "Nobres", - "Nortelândia", - "Nossa Senhora do Livramento", - "Nova Bandeirantes", - "Nova Brasilândia", - "Nova Canaã do Norte", - "Nova Guarita", - "Nova Lacerda", - "Nova Marilândia", - "Nova Maringá", - "Nova Monte Verde", - "Nova Mutum", - "Nova Nazaré", - "Nova Olímpia", - "Nova Santa Helena", - "Nova Ubiratã", - "Nova Xavantina", - "Novo Horizonte do Norte", - "Novo Mundo", - "Novo Santo Antônio", - "Novo São Joaquim", - "Paranaíta", - "Paranatinga", - "Pedra Preta", - "Peixoto de Azevedo", - "Planalto da Serra", - "Poconé", - "Pontal do Araguaia", - "Ponte Branca", - "Pontes e Lacerda", - "Porto Alegre do Norte", - "Porto dos Gaúchos", - "Porto Esperidião", - "Porto Estrela", - "Poxoréu", - "Primavera do Leste", - "Querência", - "Reserva do Cabaçal", - "Ribeirão Cascalheira", - "Ribeirãozinho", - "Rio Branco", - "Rondolândia", - "Rondonópolis", - "Rosário Oeste", - "Salto do Céu", - "Santa Carmem", - "Santa Cruz do Xingu", - "Santa Rita do Trivelato", - "Santa Terezinha", - "Santo Afonso", - "Santo Antônio de Leverger", - "Santo Antônio do Leste", - "São Félix do Araguaia", - "São José do Povo", - "São José do Rio Claro", - "São José do Xingu", - "São José dos Quatro Marcos", - "São Pedro da Cipa", - "Sapezal", - "Serra Nova Dourada", - "Sinop", - "Sorriso", - "Tabaporã", - "Tangará da Serra", - "Tapurah", - "Terra Nova do Norte", - "Tesouro", - "Torixoréu", - "União do Sul", - "Vale de São Domingos", - "Várzea Grande", - "Vera", - "Vila Bela da Santíssima Trindade", - "Vila Rica", + ["Acorizal", "5100102"], + ["Água Boa", "5100201"], + ["Alta Floresta", "5100250"], + ["Alto Araguaia", "5100300"], + ["Alto Boa Vista", "5100359"], + ["Alto Garças", "5100409"], + ["Alto Paraguai", "5100508"], + ["Alto Taquari", "5100607"], + ["Apiacás", "5100805"], + ["Araguaiana", "5101001"], + ["Araguainha", "5101209"], + ["Araputanga", "5101258"], + ["Arenápolis", "5101308"], + ["Aripuanã", "5101407"], + ["Barão de Melgaço", "5101605"], + ["Barra do Bugres", "5101704"], + ["Barra do Garças", "5101803"], + ["Boa Esperança do Norte", "5101837"], + ["Bom Jesus do Araguaia", "5101852"], + ["Brasnorte", "5101902"], + ["Cáceres", "5102504"], + ["Campinápolis", "5102603"], + ["Campo Novo do Parecis", "5102637"], + ["Campo Verde", "5102678"], + ["Campos de Júlio", "5102686"], + ["Canabrava do Norte", "5102694"], + ["Canarana", "5102702"], + ["Carlinda", "5102793"], + ["Castanheira", "5102850"], + ["Chapada dos Guimarães", "5103007"], + ["Cláudia", "5103056"], + ["Cocalinho", "5103106"], + ["Colíder", "5103205"], + ["Colniza", "5103254"], + ["Comodoro", "5103304"], + ["Confresa", "5103353"], + ["Conquista D'Oeste", "5103361"], + ["Cotriguaçu", "5103379"], + ["Cuiabá", "5103403"], + ["Curvelândia", "5103437"], + ["Denise", "5103452"], + ["Diamantino", "5103502"], + ["Dom Aquino", "5103601"], + ["Feliz Natal", "5103700"], + ["Figueirópolis D'Oeste", "5103809"], + ["Gaúcha do Norte", "5103858"], + ["General Carneiro", "5103908"], + ["Glória D'Oeste", "5103957"], + ["Guarantã do Norte", "5104104"], + ["Guiratinga", "5104203"], + ["Indiavaí", "5104500"], + ["Ipiranga do Norte", "5104526"], + ["Itanhangá", "5104542"], + ["Itaúba", "5104559"], + ["Itiquira", "5104609"], + ["Jaciara", "5104807"], + ["Jangada", "5104906"], + ["Jauru", "5105002"], + ["Juara", "5105101"], + ["Juína", "5105150"], + ["Juruena", "5105176"], + ["Juscimeira", "5105200"], + ["Lambari D'Oeste", "5105234"], + ["Lucas do Rio Verde", "5105259"], + ["Luciara", "5105309"], + ["Marcelândia", "5105580"], + ["Matupá", "5105606"], + ["Mirassol d'Oeste", "5105622"], + ["Nobres", "5105903"], + ["Nortelândia", "5106000"], + ["Nossa Senhora do Livramento", "5106109"], + ["Nova Bandeirantes", "5106158"], + ["Nova Brasilândia", "5106208"], + ["Nova Canaã do Norte", "5106216"], + ["Nova Guarita", "5108808"], + ["Nova Lacerda", "5106182"], + ["Nova Marilândia", "5108857"], + ["Nova Maringá", "5108907"], + ["Nova Monte Verde", "5108956"], + ["Nova Mutum", "5106224"], + ["Nova Nazaré", "5106174"], + ["Nova Olímpia", "5106232"], + ["Nova Santa Helena", "5106190"], + ["Nova Ubiratã", "5106240"], + ["Nova Xavantina", "5106257"], + ["Novo Horizonte do Norte", "5106273"], + ["Novo Mundo", "5106265"], + ["Novo Santo Antônio", "5106315"], + ["Novo São Joaquim", "5106281"], + ["Paranaíta", "5106299"], + ["Paranatinga", "5106307"], + ["Pedra Preta", "5106372"], + ["Peixoto de Azevedo", "5106422"], + ["Planalto da Serra", "5106455"], + ["Poconé", "5106505"], + ["Pontal do Araguaia", "5106653"], + ["Ponte Branca", "5106703"], + ["Pontes e Lacerda", "5106752"], + ["Porto Alegre do Norte", "5106778"], + ["Porto dos Gaúchos", "5106802"], + ["Porto Esperidião", "5106828"], + ["Porto Estrela", "5106851"], + ["Poxoréu", "5107008"], + ["Primavera do Leste", "5107040"], + ["Querência", "5107065"], + ["Reserva do Cabaçal", "5107156"], + ["Ribeirão Cascalheira", "5107180"], + ["Ribeirãozinho", "5107198"], + ["Rio Branco", "5107206"], + ["Rondolândia", "5107578"], + ["Rondonópolis", "5107602"], + ["Rosário Oeste", "5107701"], + ["Salto do Céu", "5107750"], + ["Santa Carmem", "5107248"], + ["Santa Cruz do Xingu", "5107743"], + ["Santa Rita do Trivelato", "5107768"], + ["Santa Terezinha", "5107776"], + ["Santo Afonso", "5107263"], + ["Santo Antônio de Leverger", "5107800"], + ["Santo Antônio do Leste", "5107792"], + ["São Félix do Araguaia", "5107859"], + ["São José do Povo", "5107297"], + ["São José do Rio Claro", "5107305"], + ["São José do Xingu", "5107354"], + ["São José dos Quatro Marcos", "5107107"], + ["São Pedro da Cipa", "5107404"], + ["Sapezal", "5107875"], + ["Serra Nova Dourada", "5107883"], + ["Sinop", "5107909"], + ["Sorriso", "5107925"], + ["Tabaporã", "5107941"], + ["Tangará da Serra", "5107958"], + ["Tapurah", "5108006"], + ["Terra Nova do Norte", "5108055"], + ["Tesouro", "5108105"], + ["Torixoréu", "5108204"], + ["União do Sul", "5108303"], + ["Vale de São Domingos", "5108352"], + ["Várzea Grande", "5108402"], + ["Vera", "5108501"], + ["Vila Bela da Santíssima Trindade", "5105507"], + ["Vila Rica", "5108600"], ], PA: [ - "Abaetetuba", - "Abel Figueiredo", - "Acará", - "Afuá", - "Água Azul do Norte", - "Alenquer", - "Almeirim", - "Altamira", - "Anajás", - "Ananindeua", - "Anapu", - "Augusto Corrêa", - "Aurora do Pará", - "Aveiro", - "Bagre", - "Baião", - "Bannach", - "Barcarena", - "Belém", - "Belterra", - "Benevides", - "Bom Jesus do Tocantins", - "Bonito", - "Bragança", - "Brasil Novo", - "Brejo Grande do Araguaia", - "Breu Branco", - "Breves", - "Bujaru", - "Cachoeira do Arari", - "Cachoeira do Piriá", - "Cametá", - "Canaã dos Carajás", - "Capanema", - "Capitão Poço", - "Castanhal", - "Chaves", - "Colares", - "Conceição do Araguaia", - "Concórdia do Pará", - "Cumaru do Norte", - "Curionópolis", - "Curralinho", - "Curuá", - "Curuçá", - "Dom Eliseu", - "Eldorado do Carajás", - "Faro", - "Floresta do Araguaia", - "Garrafão do Norte", - "Goianésia do Pará", - "Gurupá", - "Igarapé-Açu", - "Igarapé-Miri", - "Inhangapi", - "Ipixuna do Pará", - "Irituia", - "Itaituba", - "Itupiranga", - "Jacareacanga", - "Jacundá", - "Juruti", - "Limoeiro do Ajuru", - "Mãe do Rio", - "Magalhães Barata", - "Marabá", - "Maracanã", - "Marapanim", - "Marituba", - "Medicilândia", - "Melgaço", - "Mocajuba", - "Moju", - "Mojuí dos Campos", - "Monte Alegre", - "Muaná", - "Nova Esperança do Piriá", - "Nova Ipixuna", - "Nova Timboteua", - "Novo Progresso", - "Novo Repartimento", - "Óbidos", - "Oeiras do Pará", - "Oriximiná", - "Ourém", - "Ourilândia do Norte", - "Pacajá", - "Palestina do Pará", - "Paragominas", - "Parauapebas", - "Pau D'Arco", - "Peixe-Boi", - "Piçarra", - "Placas", - "Ponta de Pedras", - "Portel", - "Porto de Moz", - "Prainha", - "Primavera", - "Quatipuru", - "Redenção", - "Rio Maria", - "Rondon do Pará", - "Rurópolis", - "Salinópolis", - "Salvaterra", - "Santa Bárbara do Pará", - "Santa Cruz do Arari", - "Santa Izabel do Pará", - "Santa Luzia do Pará", - "Santa Maria das Barreiras", - "Santa Maria do Pará", - "Santana do Araguaia", - "Santarém", - "Santarém Novo", - "Santo Antônio do Tauá", - "São Caetano de Odivelas", - "São Domingos do Araguaia", - "São Domingos do Capim", - "São Félix do Xingu", - "São Francisco do Pará", - "São Geraldo do Araguaia", - "São João da Ponta", - "São João de Pirabas", - "São João do Araguaia", - "São Miguel do Guamá", - "São Sebastião da Boa Vista", - "Sapucaia", - "Senador José Porfírio", - "Soure", - "Tailândia", - "Terra Alta", - "Terra Santa", - "Tomé-Açu", - "Tracuateua", - "Trairão", - "Tucumã", - "Tucuruí", - "Ulianópolis", - "Uruará", - "Vigia", - "Viseu", - "Vitória do Xingu", - "Xinguara", + ["Abaetetuba", "1500107"], + ["Abel Figueiredo", "1500131"], + ["Acará", "1500206"], + ["Afuá", "1500305"], + ["Água Azul do Norte", "1500347"], + ["Alenquer", "1500404"], + ["Almeirim", "1500503"], + ["Altamira", "1500602"], + ["Anajás", "1500701"], + ["Ananindeua", "1500800"], + ["Anapu", "1500859"], + ["Augusto Corrêa", "1500909"], + ["Aurora do Pará", "1500958"], + ["Aveiro", "1501006"], + ["Bagre", "1501105"], + ["Baião", "1501204"], + ["Bannach", "1501253"], + ["Barcarena", "1501303"], + ["Belém", "1501402"], + ["Belterra", "1501451"], + ["Benevides", "1501501"], + ["Bom Jesus do Tocantins", "1501576"], + ["Bonito", "1501600"], + ["Bragança", "1501709"], + ["Brasil Novo", "1501725"], + ["Brejo Grande do Araguaia", "1501758"], + ["Breu Branco", "1501782"], + ["Breves", "1501808"], + ["Bujaru", "1501907"], + ["Cachoeira do Arari", "1502004"], + ["Cachoeira do Piriá", "1501956"], + ["Cametá", "1502103"], + ["Canaã dos Carajás", "1502152"], + ["Capanema", "1502202"], + ["Capitão Poço", "1502301"], + ["Castanhal", "1502400"], + ["Chaves", "1502509"], + ["Colares", "1502608"], + ["Conceição do Araguaia", "1502707"], + ["Concórdia do Pará", "1502756"], + ["Cumaru do Norte", "1502764"], + ["Curionópolis", "1502772"], + ["Curralinho", "1502806"], + ["Curuá", "1502855"], + ["Curuçá", "1502905"], + ["Dom Eliseu", "1502939"], + ["Eldorado do Carajás", "1502954"], + ["Faro", "1503002"], + ["Floresta do Araguaia", "1503044"], + ["Garrafão do Norte", "1503077"], + ["Goianésia do Pará", "1503093"], + ["Gurupá", "1503101"], + ["Igarapé-Açu", "1503200"], + ["Igarapé-Miri", "1503309"], + ["Inhangapi", "1503408"], + ["Ipixuna do Pará", "1503457"], + ["Irituia", "1503507"], + ["Itaituba", "1503606"], + ["Itupiranga", "1503705"], + ["Jacareacanga", "1503754"], + ["Jacundá", "1503804"], + ["Juruti", "1503903"], + ["Limoeiro do Ajuru", "1504000"], + ["Mãe do Rio", "1504059"], + ["Magalhães Barata", "1504109"], + ["Marabá", "1504208"], + ["Maracanã", "1504307"], + ["Marapanim", "1504406"], + ["Marituba", "1504422"], + ["Medicilândia", "1504455"], + ["Melgaço", "1504505"], + ["Mocajuba", "1504604"], + ["Moju", "1504703"], + ["Mojuí dos Campos", "1504752"], + ["Monte Alegre", "1504802"], + ["Muaná", "1504901"], + ["Nova Esperança do Piriá", "1504950"], + ["Nova Ipixuna", "1504976"], + ["Nova Timboteua", "1505007"], + ["Novo Progresso", "1505031"], + ["Novo Repartimento", "1505064"], + ["Óbidos", "1505106"], + ["Oeiras do Pará", "1505205"], + ["Oriximiná", "1505304"], + ["Ourém", "1505403"], + ["Ourilândia do Norte", "1505437"], + ["Pacajá", "1505486"], + ["Palestina do Pará", "1505494"], + ["Paragominas", "1505502"], + ["Parauapebas", "1505536"], + ["Pau D'Arco", "1505551"], + ["Peixe-Boi", "1505601"], + ["Piçarra", "1505635"], + ["Placas", "1505650"], + ["Ponta de Pedras", "1505700"], + ["Portel", "1505809"], + ["Porto de Moz", "1505908"], + ["Prainha", "1506005"], + ["Primavera", "1506104"], + ["Quatipuru", "1506112"], + ["Redenção", "1506138"], + ["Rio Maria", "1506161"], + ["Rondon do Pará", "1506187"], + ["Rurópolis", "1506195"], + ["Salinópolis", "1506203"], + ["Salvaterra", "1506302"], + ["Santa Bárbara do Pará", "1506351"], + ["Santa Cruz do Arari", "1506401"], + ["Santa Izabel do Pará", "1506500"], + ["Santa Luzia do Pará", "1506559"], + ["Santa Maria das Barreiras", "1506583"], + ["Santa Maria do Pará", "1506609"], + ["Santana do Araguaia", "1506708"], + ["Santarém", "1506807"], + ["Santarém Novo", "1506906"], + ["Santo Antônio do Tauá", "1507003"], + ["São Caetano de Odivelas", "1507102"], + ["São Domingos do Araguaia", "1507151"], + ["São Domingos do Capim", "1507201"], + ["São Félix do Xingu", "1507300"], + ["São Francisco do Pará", "1507409"], + ["São Geraldo do Araguaia", "1507458"], + ["São João da Ponta", "1507466"], + ["São João de Pirabas", "1507474"], + ["São João do Araguaia", "1507508"], + ["São Miguel do Guamá", "1507607"], + ["São Sebastião da Boa Vista", "1507706"], + ["Sapucaia", "1507755"], + ["Senador José Porfírio", "1507805"], + ["Soure", "1507904"], + ["Tailândia", "1507953"], + ["Terra Alta", "1507961"], + ["Terra Santa", "1507979"], + ["Tomé-Açu", "1508001"], + ["Tracuateua", "1508035"], + ["Trairão", "1508050"], + ["Tucumã", "1508084"], + ["Tucuruí", "1508100"], + ["Ulianópolis", "1508126"], + ["Uruará", "1508159"], + ["Vigia", "1508209"], + ["Viseu", "1508308"], + ["Vitória do Xingu", "1508357"], + ["Xinguara", "1508407"], ], PB: [ - "Água Branca", - "Aguiar", - "Alagoa Grande", - "Alagoa Nova", - "Alagoinha", - "Alcantil", - "Algodão de Jandaíra", - "Alhandra", - "Amparo", - "Aparecida", - "Araçagi", - "Arara", - "Araruna", - "Areia", - "Areia de Baraúnas", - "Areial", - "Aroeiras", - "Assunção", - "Baía da Traição", - "Bananeiras", - "Baraúna", - "Barra de Santa Rosa", - "Barra de Santana", - "Barra de São Miguel", - "Bayeux", - "Belém", - "Belém do Brejo do Cruz", - "Bernardino Batista", - "Boa Ventura", - "Boa Vista", - "Bom Jesus", - "Bom Sucesso", - "Bonito de Santa Fé", - "Boqueirão", - "Borborema", - "Brejo do Cruz", - "Brejo dos Santos", - "Caaporã", - "Cabaceiras", - "Cabedelo", - "Cachoeira dos Índios", - "Cacimba de Areia", - "Cacimba de Dentro", - "Cacimbas", - "Caiçara", - "Cajazeiras", - "Cajazeirinhas", - "Caldas Brandão", - "Camalaú", - "Campina Grande", - "Capim", - "Caraúbas", - "Carrapateira", - "Casserengue", - "Catingueira", - "Catolé do Rocha", - "Caturité", - "Conceição", - "Condado", - "Conde", - "Congo", - "Coremas", - "Coxixola", - "Cruz do Espírito Santo", - "Cubati", - "Cuité", - "Cuité de Mamanguape", - "Cuitegi", - "Curral de Cima", - "Curral Velho", - "Damião", - "Desterro", - "Diamante", - "Dona Inês", - "Duas Estradas", - "Emas", - "Esperança", - "Fagundes", - "Frei Martinho", - "Gado Bravo", - "Guarabira", - "Gurinhém", - "Gurjão", - "Ibiara", - "Igaracy", - "Imaculada", - "Ingá", - "Itabaiana", - "Itaporanga", - "Itapororoca", - "Itatuba", - "Jacaraú", - "Jericó", - "João Pessoa", - "Joca Claudino", - "Juarez Távora", - "Juazeirinho", - "Junco do Seridó", - "Juripiranga", - "Juru", - "Lagoa", - "Lagoa de Dentro", - "Lagoa Seca", - "Lastro", - "Livramento", - "Logradouro", - "Lucena", - "Mãe d'Água", - "Malta", - "Mamanguape", - "Manaíra", - "Marcação", - "Mari", - "Marizópolis", - "Massaranduba", - "Mataraca", - "Matinhas", - "Mato Grosso", - "Maturéia", - "Mogeiro", - "Montadas", - "Monte Horebe", - "Monteiro", - "Mulungu", - "Natuba", - "Nazarezinho", - "Nova Floresta", - "Nova Olinda", - "Nova Palmeira", - "Olho d'Água", - "Olivedos", - "Ouro Velho", - "Parari", - "Passagem", - "Patos", - "Paulista", - "Pedra Branca", - "Pedra Lavrada", - "Pedras de Fogo", - "Pedro Régis", - "Piancó", - "Picuí", - "Pilar", - "Pilões", - "Pilõezinhos", - "Pirpirituba", - "Pitimbu", - "Pocinhos", - "Poço Dantas", - "Poço de José de Moura", - "Pombal", - "Prata", - "Princesa Isabel", - "Puxinanã", - "Queimadas", - "Quixaba", - "Remígio", - "Riachão", - "Riachão do Bacamarte", - "Riachão do Poço", - "Riacho de Santo Antônio", - "Riacho dos Cavalos", - "Rio Tinto", - "Salgadinho", - "Salgado de São Félix", - "Santa Cecília", - "Santa Cruz", - "Santa Helena", - "Santa Inês", - "Santa Luzia", - "Santa Rita", - "Santa Teresinha", - "Santana de Mangueira", - "Santana dos Garrotes", - "Santo André", - "São Bentinho", - "São Bento", - "São Domingos", - "São Domingos do Cariri", - "São Francisco", - "São João do Cariri", - "São João do Rio do Peixe", - "São João do Tigre", - "São José da Lagoa Tapada", - "São José de Caiana", - "São José de Espinharas", - "São José de Piranhas", - "São José de Princesa", - "São José do Bonfim", - "São José do Brejo do Cruz", - "São José do Sabugi", - "São José dos Cordeiros", - "São José dos Ramos", - "São Mamede", - "São Miguel de Taipu", - "São Sebastião de Lagoa de Roça", - "São Sebastião do Umbuzeiro", - "São Vicente do Seridó", - "Sapé", - "Serra Branca", - "Serra da Raiz", - "Serra Grande", - "Serra Redonda", - "Serraria", - "Sertãozinho", - "Sobrado", - "Solânea", - "Soledade", - "Sossêgo", - "Sousa", - "Sumé", - "Tacima", - "Taperoá", - "Tavares", - "Teixeira", - "Tenório", - "Triunfo", - "Uiraúna", - "Umbuzeiro", - "Várzea", - "Vieirópolis", - "Vista Serrana", - "Zabelê", + ["Água Branca", "2500106"], + ["Aguiar", "2500205"], + ["Alagoa Grande", "2500304"], + ["Alagoa Nova", "2500403"], + ["Alagoinha", "2500502"], + ["Alcantil", "2500536"], + ["Algodão de Jandaíra", "2500577"], + ["Alhandra", "2500601"], + ["Amparo", "2500734"], + ["Aparecida", "2500775"], + ["Araçagi", "2500809"], + ["Arara", "2500908"], + ["Araruna", "2501005"], + ["Areia", "2501104"], + ["Areia de Baraúnas", "2501153"], + ["Areial", "2501203"], + ["Aroeiras", "2501302"], + ["Assunção", "2501351"], + ["Baía da Traição", "2501401"], + ["Bananeiras", "2501500"], + ["Baraúna", "2501534"], + ["Barra de Santa Rosa", "2501609"], + ["Barra de Santana", "2501575"], + ["Barra de São Miguel", "2501708"], + ["Bayeux", "2501807"], + ["Belém", "2501906"], + ["Belém do Brejo do Cruz", "2502003"], + ["Bernardino Batista", "2502052"], + ["Boa Ventura", "2502102"], + ["Boa Vista", "2502151"], + ["Bom Jesus", "2502201"], + ["Bom Sucesso", "2502300"], + ["Bonito de Santa Fé", "2502409"], + ["Boqueirão", "2502508"], + ["Borborema", "2502706"], + ["Brejo do Cruz", "2502805"], + ["Brejo dos Santos", "2502904"], + ["Caaporã", "2503001"], + ["Cabaceiras", "2503100"], + ["Cabedelo", "2503209"], + ["Cachoeira dos Índios", "2503308"], + ["Cacimba de Areia", "2503407"], + ["Cacimba de Dentro", "2503506"], + ["Cacimbas", "2503555"], + ["Caiçara", "2503605"], + ["Cajazeiras", "2503704"], + ["Cajazeirinhas", "2503753"], + ["Caldas Brandão", "2503803"], + ["Camalaú", "2503902"], + ["Campina Grande", "2504009"], + ["Capim", "2504033"], + ["Caraúbas", "2504074"], + ["Carrapateira", "2504108"], + ["Casserengue", "2504157"], + ["Catingueira", "2504207"], + ["Catolé do Rocha", "2504306"], + ["Caturité", "2504355"], + ["Conceição", "2504405"], + ["Condado", "2504504"], + ["Conde", "2504603"], + ["Congo", "2504702"], + ["Coremas", "2504801"], + ["Coxixola", "2504850"], + ["Cruz do Espírito Santo", "2504900"], + ["Cubati", "2505006"], + ["Cuité", "2505105"], + ["Cuité de Mamanguape", "2505238"], + ["Cuitegi", "2505204"], + ["Curral de Cima", "2505279"], + ["Curral Velho", "2505303"], + ["Damião", "2505352"], + ["Desterro", "2505402"], + ["Diamante", "2505600"], + ["Dona Inês", "2505709"], + ["Duas Estradas", "2505808"], + ["Emas", "2505907"], + ["Esperança", "2506004"], + ["Fagundes", "2506103"], + ["Frei Martinho", "2506202"], + ["Gado Bravo", "2506251"], + ["Guarabira", "2506301"], + ["Gurinhém", "2506400"], + ["Gurjão", "2506509"], + ["Ibiara", "2506608"], + ["Igaracy", "2502607"], + ["Imaculada", "2506707"], + ["Ingá", "2506806"], + ["Itabaiana", "2506905"], + ["Itaporanga", "2507002"], + ["Itapororoca", "2507101"], + ["Itatuba", "2507200"], + ["Jacaraú", "2507309"], + ["Jericó", "2507408"], + ["João Pessoa", "2507507"], + ["Joca Claudino", "2513653"], + ["Juarez Távora", "2507606"], + ["Juazeirinho", "2507705"], + ["Junco do Seridó", "2507804"], + ["Juripiranga", "2507903"], + ["Juru", "2508000"], + ["Lagoa", "2508109"], + ["Lagoa de Dentro", "2508208"], + ["Lagoa Seca", "2508307"], + ["Lastro", "2508406"], + ["Livramento", "2508505"], + ["Logradouro", "2508554"], + ["Lucena", "2508604"], + ["Mãe d'Água", "2508703"], + ["Malta", "2508802"], + ["Mamanguape", "2508901"], + ["Manaíra", "2509008"], + ["Marcação", "2509057"], + ["Mari", "2509107"], + ["Marizópolis", "2509156"], + ["Massaranduba", "2509206"], + ["Mataraca", "2509305"], + ["Matinhas", "2509339"], + ["Mato Grosso", "2509370"], + ["Maturéia", "2509396"], + ["Mogeiro", "2509404"], + ["Montadas", "2509503"], + ["Monte Horebe", "2509602"], + ["Monteiro", "2509701"], + ["Mulungu", "2509800"], + ["Natuba", "2509909"], + ["Nazarezinho", "2510006"], + ["Nova Floresta", "2510105"], + ["Nova Olinda", "2510204"], + ["Nova Palmeira", "2510303"], + ["Olho d'Água", "2510402"], + ["Olivedos", "2510501"], + ["Ouro Velho", "2510600"], + ["Parari", "2510659"], + ["Passagem", "2510709"], + ["Patos", "2510808"], + ["Paulista", "2510907"], + ["Pedra Branca", "2511004"], + ["Pedra Lavrada", "2511103"], + ["Pedras de Fogo", "2511202"], + ["Pedro Régis", "2512721"], + ["Piancó", "2511301"], + ["Picuí", "2511400"], + ["Pilar", "2511509"], + ["Pilões", "2511608"], + ["Pilõezinhos", "2511707"], + ["Pirpirituba", "2511806"], + ["Pitimbu", "2511905"], + ["Pocinhos", "2512002"], + ["Poço Dantas", "2512036"], + ["Poço de José de Moura", "2512077"], + ["Pombal", "2512101"], + ["Prata", "2512200"], + ["Princesa Isabel", "2512309"], + ["Puxinanã", "2512408"], + ["Queimadas", "2512507"], + ["Quixaba", "2512606"], + ["Remígio", "2512705"], + ["Riachão", "2512747"], + ["Riachão do Bacamarte", "2512754"], + ["Riachão do Poço", "2512762"], + ["Riacho de Santo Antônio", "2512788"], + ["Riacho dos Cavalos", "2512804"], + ["Rio Tinto", "2512903"], + ["Salgadinho", "2513000"], + ["Salgado de São Félix", "2513109"], + ["Santa Cecília", "2513158"], + ["Santa Cruz", "2513208"], + ["Santa Helena", "2513307"], + ["Santa Inês", "2513356"], + ["Santa Luzia", "2513406"], + ["Santa Rita", "2513703"], + ["Santa Teresinha", "2513802"], + ["Santana de Mangueira", "2513505"], + ["Santana dos Garrotes", "2513604"], + ["Santo André", "2513851"], + ["São Bentinho", "2513927"], + ["São Bento", "2513901"], + ["São Domingos", "2513968"], + ["São Domingos do Cariri", "2513943"], + ["São Francisco", "2513984"], + ["São João do Cariri", "2514008"], + ["São João do Rio do Peixe", "2500700"], + ["São João do Tigre", "2514107"], + ["São José da Lagoa Tapada", "2514206"], + ["São José de Caiana", "2514305"], + ["São José de Espinharas", "2514404"], + ["São José de Piranhas", "2514503"], + ["São José de Princesa", "2514552"], + ["São José do Bonfim", "2514602"], + ["São José do Brejo do Cruz", "2514651"], + ["São José do Sabugi", "2514701"], + ["São José dos Cordeiros", "2514800"], + ["São José dos Ramos", "2514453"], + ["São Mamede", "2514909"], + ["São Miguel de Taipu", "2515005"], + ["São Sebastião de Lagoa de Roça", "2515104"], + ["São Sebastião do Umbuzeiro", "2515203"], + ["São Vicente do Seridó", "2515401"], + ["Sapé", "2515302"], + ["Serra Branca", "2515500"], + ["Serra da Raiz", "2515609"], + ["Serra Grande", "2515708"], + ["Serra Redonda", "2515807"], + ["Serraria", "2515906"], + ["Sertãozinho", "2515930"], + ["Sobrado", "2515971"], + ["Solânea", "2516003"], + ["Soledade", "2516102"], + ["Sossêgo", "2516151"], + ["Sousa", "2516201"], + ["Sumé", "2516300"], + ["Tacima", "2516409"], + ["Taperoá", "2516508"], + ["Tavares", "2516607"], + ["Teixeira", "2516706"], + ["Tenório", "2516755"], + ["Triunfo", "2516805"], + ["Uiraúna", "2516904"], + ["Umbuzeiro", "2517001"], + ["Várzea", "2517100"], + ["Vieirópolis", "2517209"], + ["Vista Serrana", "2505501"], + ["Zabelê", "2517407"], ], PE: [ - "Abreu e Lima", - "Afogados da Ingazeira", - "Afrânio", - "Agrestina", - "Água Preta", - "Águas Belas", - "Alagoinha", - "Aliança", - "Altinho", - "Amaraji", - "Angelim", - "Araçoiaba", - "Araripina", - "Arcoverde", - "Barra de Guabiraba", - "Barreiros", - "Belém de Maria", - "Belém do São Francisco", - "Belo Jardim", - "Betânia", - "Bezerros", - "Bodocó", - "Bom Conselho", - "Bom Jardim", - "Bonito", - "Brejão", - "Brejinho", - "Brejo da Madre de Deus", - "Buenos Aires", - "Buíque", - "Cabo de Santo Agostinho", - "Cabrobó", - "Cachoeirinha", - "Caetés", - "Calçado", - "Calumbi", - "Camaragibe", - "Camocim de São Félix", - "Camutanga", - "Canhotinho", - "Capoeiras", - "Carnaíba", - "Carnaubeira da Penha", - "Carpina", - "Caruaru", - "Casinhas", - "Catende", - "Cedro", - "Chã de Alegria", - "Chã Grande", - "Condado", - "Correntes", - "Cortês", - "Cumaru", - "Cupira", - "Custódia", - "Dormentes", - "Escada", - "Exu", - "Feira Nova", - "Fernando de Noronha", - "Ferreiros", - "Flores", - "Floresta", - "Frei Miguelinho", - "Gameleira", - "Garanhuns", - "Glória do Goitá", - "Goiana", - "Granito", - "Gravatá", - "Iati", - "Ibimirim", - "Ibirajuba", - "Igarassu", - "Iguaracy", - "Ilha de Itamaracá", - "Inajá", - "Ingazeira", - "Ipojuca", - "Ipubi", - "Itacuruba", - "Itaíba", - "Itambé", - "Itapetim", - "Itapissuma", - "Itaquitinga", - "Jaboatão dos Guararapes", - "Jaqueira", - "Jataúba", - "Jatobá", - "João Alfredo", - "Joaquim Nabuco", - "Jucati", - "Jupi", - "Jurema", - "Lagoa de Itaenga", - "Lagoa do Carro", - "Lagoa do Ouro", - "Lagoa dos Gatos", - "Lagoa Grande", - "Lajedo", - "Limoeiro", - "Macaparana", - "Machados", - "Manari", - "Maraial", - "Mirandiba", - "Moreilândia", - "Moreno", - "Nazaré da Mata", - "Olinda", - "Orobó", - "Orocó", - "Ouricuri", - "Palmares", - "Palmeirina", - "Panelas", - "Paranatama", - "Parnamirim", - "Passira", - "Paudalho", - "Paulista", - "Pedra", - "Pesqueira", - "Petrolândia", - "Petrolina", - "Poção", - "Pombos", - "Primavera", - "Quipapá", - "Quixaba", - "Recife", - "Riacho das Almas", - "Ribeirão", - "Rio Formoso", - "Sairé", - "Salgadinho", - "Salgueiro", - "Saloá", - "Sanharó", - "Santa Cruz", - "Santa Cruz da Baixa Verde", - "Santa Cruz do Capibaribe", - "Santa Filomena", - "Santa Maria da Boa Vista", - "Santa Maria do Cambucá", - "Santa Terezinha", - "São Benedito do Sul", - "São Bento do Una", - "São Caitano", - "São João", - "São Joaquim do Monte", - "São José da Coroa Grande", - "São José do Belmonte", - "São José do Egito", - "São Lourenço da Mata", - "São Vicente Férrer", - "Serra Talhada", - "Serrita", - "Sertânia", - "Sirinhaém", - "Solidão", - "Surubim", - "Tabira", - "Tacaimbó", - "Tacaratu", - "Tamandaré", - "Taquaritinga do Norte", - "Terezinha", - "Terra Nova", - "Timbaúba", - "Toritama", - "Tracunhaém", - "Trindade", - "Triunfo", - "Tupanatinga", - "Tuparetama", - "Venturosa", - "Verdejante", - "Vertente do Lério", - "Vertentes", - "Vicência", - "Vitória de Santo Antão", - "Xexéu", + ["Abreu e Lima", "2600054"], + ["Afogados da Ingazeira", "2600104"], + ["Afrânio", "2600203"], + ["Agrestina", "2600302"], + ["Água Preta", "2600401"], + ["Águas Belas", "2600500"], + ["Alagoinha", "2600609"], + ["Aliança", "2600708"], + ["Altinho", "2600807"], + ["Amaraji", "2600906"], + ["Angelim", "2601003"], + ["Araçoiaba", "2601052"], + ["Araripina", "2601102"], + ["Arcoverde", "2601201"], + ["Barra de Guabiraba", "2601300"], + ["Barreiros", "2601409"], + ["Belém de Maria", "2601508"], + ["Belém do São Francisco", "2601607"], + ["Belo Jardim", "2601706"], + ["Betânia", "2601805"], + ["Bezerros", "2601904"], + ["Bodocó", "2602001"], + ["Bom Conselho", "2602100"], + ["Bom Jardim", "2602209"], + ["Bonito", "2602308"], + ["Brejão", "2602407"], + ["Brejinho", "2602506"], + ["Brejo da Madre de Deus", "2602605"], + ["Buenos Aires", "2602704"], + ["Buíque", "2602803"], + ["Cabo de Santo Agostinho", "2602902"], + ["Cabrobó", "2603009"], + ["Cachoeirinha", "2603108"], + ["Caetés", "2603207"], + ["Calçado", "2603306"], + ["Calumbi", "2603405"], + ["Camaragibe", "2603454"], + ["Camocim de São Félix", "2603504"], + ["Camutanga", "2603603"], + ["Canhotinho", "2603702"], + ["Capoeiras", "2603801"], + ["Carnaíba", "2603900"], + ["Carnaubeira da Penha", "2603926"], + ["Carpina", "2604007"], + ["Caruaru", "2604106"], + ["Casinhas", "2604155"], + ["Catende", "2604205"], + ["Cedro", "2604304"], + ["Chã de Alegria", "2604403"], + ["Chã Grande", "2604502"], + ["Condado", "2604601"], + ["Correntes", "2604700"], + ["Cortês", "2604809"], + ["Cumaru", "2604908"], + ["Cupira", "2605004"], + ["Custódia", "2605103"], + ["Dormentes", "2605152"], + ["Escada", "2605202"], + ["Exu", "2605301"], + ["Feira Nova", "2605400"], + ["Fernando de Noronha", "2605459"], + ["Ferreiros", "2605509"], + ["Flores", "2605608"], + ["Floresta", "2605707"], + ["Frei Miguelinho", "2605806"], + ["Gameleira", "2605905"], + ["Garanhuns", "2606002"], + ["Glória do Goitá", "2606101"], + ["Goiana", "2606200"], + ["Granito", "2606309"], + ["Gravatá", "2606408"], + ["Iati", "2606507"], + ["Ibimirim", "2606606"], + ["Ibirajuba", "2606705"], + ["Igarassu", "2606804"], + ["Iguaracy", "2606903"], + ["Ilha de Itamaracá", "2607604"], + ["Inajá", "2607000"], + ["Ingazeira", "2607109"], + ["Ipojuca", "2607208"], + ["Ipubi", "2607307"], + ["Itacuruba", "2607406"], + ["Itaíba", "2607505"], + ["Itambé", "2607653"], + ["Itapetim", "2607703"], + ["Itapissuma", "2607752"], + ["Itaquitinga", "2607802"], + ["Jaboatão dos Guararapes", "2607901"], + ["Jaqueira", "2607950"], + ["Jataúba", "2608008"], + ["Jatobá", "2608057"], + ["João Alfredo", "2608107"], + ["Joaquim Nabuco", "2608206"], + ["Jucati", "2608255"], + ["Jupi", "2608305"], + ["Jurema", "2608404"], + ["Lagoa de Itaenga", "2608503"], + ["Lagoa do Carro", "2608453"], + ["Lagoa do Ouro", "2608602"], + ["Lagoa dos Gatos", "2608701"], + ["Lagoa Grande", "2608750"], + ["Lajedo", "2608800"], + ["Limoeiro", "2608909"], + ["Macaparana", "2609006"], + ["Machados", "2609105"], + ["Manari", "2609154"], + ["Maraial", "2609204"], + ["Mirandiba", "2609303"], + ["Moreilândia", "2614303"], + ["Moreno", "2609402"], + ["Nazaré da Mata", "2609501"], + ["Olinda", "2609600"], + ["Orobó", "2609709"], + ["Orocó", "2609808"], + ["Ouricuri", "2609907"], + ["Palmares", "2610004"], + ["Palmeirina", "2610103"], + ["Panelas", "2610202"], + ["Paranatama", "2610301"], + ["Parnamirim", "2610400"], + ["Passira", "2610509"], + ["Paudalho", "2610608"], + ["Paulista", "2610707"], + ["Pedra", "2610806"], + ["Pesqueira", "2610905"], + ["Petrolândia", "2611002"], + ["Petrolina", "2611101"], + ["Poção", "2611200"], + ["Pombos", "2611309"], + ["Primavera", "2611408"], + ["Quipapá", "2611507"], + ["Quixaba", "2611533"], + ["Recife", "2611606"], + ["Riacho das Almas", "2611705"], + ["Ribeirão", "2611804"], + ["Rio Formoso", "2611903"], + ["Sairé", "2612000"], + ["Salgadinho", "2612109"], + ["Salgueiro", "2612208"], + ["Saloá", "2612307"], + ["Sanharó", "2612406"], + ["Santa Cruz", "2612455"], + ["Santa Cruz da Baixa Verde", "2612471"], + ["Santa Cruz do Capibaribe", "2612505"], + ["Santa Filomena", "2612554"], + ["Santa Maria da Boa Vista", "2612604"], + ["Santa Maria do Cambucá", "2612703"], + ["Santa Terezinha", "2612802"], + ["São Benedito do Sul", "2612901"], + ["São Bento do Una", "2613008"], + ["São Caitano", "2613107"], + ["São João", "2613206"], + ["São Joaquim do Monte", "2613305"], + ["São José da Coroa Grande", "2613404"], + ["São José do Belmonte", "2613503"], + ["São José do Egito", "2613602"], + ["São Lourenço da Mata", "2613701"], + ["São Vicente Férrer", "2613800"], + ["Serra Talhada", "2613909"], + ["Serrita", "2614006"], + ["Sertânia", "2614105"], + ["Sirinhaém", "2614204"], + ["Solidão", "2614402"], + ["Surubim", "2614501"], + ["Tabira", "2614600"], + ["Tacaimbó", "2614709"], + ["Tacaratu", "2614808"], + ["Tamandaré", "2614857"], + ["Taquaritinga do Norte", "2615003"], + ["Terezinha", "2615102"], + ["Terra Nova", "2615201"], + ["Timbaúba", "2615300"], + ["Toritama", "2615409"], + ["Tracunhaém", "2615508"], + ["Trindade", "2615607"], + ["Triunfo", "2615706"], + ["Tupanatinga", "2615805"], + ["Tuparetama", "2615904"], + ["Venturosa", "2616001"], + ["Verdejante", "2616100"], + ["Vertente do Lério", "2616183"], + ["Vertentes", "2616209"], + ["Vicência", "2616308"], + ["Vitória de Santo Antão", "2616407"], + ["Xexéu", "2616506"], ], PI: [ - "Acauã", - "Agricolândia", - "Água Branca", - "Alagoinha do Piauí", - "Alegrete do Piauí", - "Alto Longá", - "Altos", - "Alvorada do Gurguéia", - "Amarante", - "Angical do Piauí", - "Anísio de Abreu", - "Antônio Almeida", - "Aroazes", - "Aroeiras do Itaim", - "Arraial", - "Assunção do Piauí", - "Avelino Lopes", - "Baixa Grande do Ribeiro", - "Barra D'Alcântara", - "Barras", - "Barreiras do Piauí", - "Barro Duro", - "Batalha", - "Bela Vista do Piauí", - "Belém do Piauí", - "Beneditinos", - "Bertolínia", - "Betânia do Piauí", - "Boa Hora", - "Bocaina", - "Bom Jesus", - "Bom Princípio do Piauí", - "Bonfim do Piauí", - "Boqueirão do Piauí", - "Brasileira", - "Brejo do Piauí", - "Buriti dos Lopes", - "Buriti dos Montes", - "Cabeceiras do Piauí", - "Cajazeiras do Piauí", - "Cajueiro da Praia", - "Caldeirão Grande do Piauí", - "Campinas do Piauí", - "Campo Alegre do Fidalgo", - "Campo Grande do Piauí", - "Campo Largo do Piauí", - "Campo Maior", - "Canavieira", - "Canto do Buriti", - "Capitão de Campos", - "Capitão Gervásio Oliveira", - "Caracol", - "Caraúbas do Piauí", - "Caridade do Piauí", - "Castelo do Piauí", - "Caxingó", - "Cocal", - "Cocal de Telha", - "Cocal dos Alves", - "Coivaras", - "Colônia do Gurguéia", - "Colônia do Piauí", - "Conceição do Canindé", - "Coronel José Dias", - "Corrente", - "Cristalândia do Piauí", - "Cristino Castro", - "Curimatá", - "Currais", - "Curral Novo do Piauí", - "Curralinhos", - "Demerval Lobão", - "Dirceu Arcoverde", - "Dom Expedito Lopes", - "Dom Inocêncio", - "Domingos Mourão", - "Elesbão Veloso", - "Eliseu Martins", - "Esperantina", - "Fartura do Piauí", - "Flores do Piauí", - "Floresta do Piauí", - "Floriano", - "Francinópolis", - "Francisco Ayres", - "Francisco Macedo", - "Francisco Santos", - "Fronteiras", - "Geminiano", - "Gilbués", - "Guadalupe", - "Guaribas", - "Hugo Napoleão", - "Ilha Grande", - "Inhuma", - "Ipiranga do Piauí", - "Isaías Coelho", - "Itainópolis", - "Itaueira", - "Jacobina do Piauí", - "Jaicós", - "Jardim do Mulato", - "Jatobá do Piauí", - "Jerumenha", - "João Costa", - "Joaquim Pires", - "Joca Marques", - "José de Freitas", - "Juazeiro do Piauí", - "Júlio Borges", - "Jurema", - "Lagoa Alegre", - "Lagoa de São Francisco", - "Lagoa do Barro do Piauí", - "Lagoa do Piauí", - "Lagoa do Sítio", - "Lagoinha do Piauí", - "Landri Sales", - "Luís Correia", - "Luzilândia", - "Madeiro", - "Manoel Emídio", - "Marcolândia", - "Marcos Parente", - "Massapê do Piauí", - "Matias Olímpio", - "Miguel Alves", - "Miguel Leão", - "Milton Brandão", - "Monsenhor Gil", - "Monsenhor Hipólito", - "Monte Alegre do Piauí", - "Morro Cabeça no Tempo", - "Morro do Chapéu do Piauí", - "Murici dos Portelas", - "Nazaré do Piauí", - "Nazária", - "Nossa Senhora de Nazaré", - "Nossa Senhora dos Remédios", - "Nova Santa Rita", - "Novo Oriente do Piauí", - "Novo Santo Antônio", - "Oeiras", - "Olho D'Água do Piauí", - "Padre Marcos", - "Paes Landim", - "Pajeú do Piauí", - "Palmeira do Piauí", - "Palmeirais", - "Paquetá", - "Parnaguá", - "Parnaíba", - "Passagem Franca do Piauí", - "Patos do Piauí", - "Pau D'Arco do Piauí", - "Paulistana", - "Pavussu", - "Pedro II", - "Pedro Laurentino", - "Picos", - "Pimenteiras", - "Pio IX", - "Piracuruca", - "Piripiri", - "Porto", - "Porto Alegre do Piauí", - "Prata do Piauí", - "Queimada Nova", - "Redenção do Gurguéia", - "Regeneração", - "Riacho Frio", - "Ribeira do Piauí", - "Ribeiro Gonçalves", - "Rio Grande do Piauí", - "Santa Cruz do Piauí", - "Santa Cruz dos Milagres", - "Santa Filomena", - "Santa Luz", - "Santa Rosa do Piauí", - "Santana do Piauí", - "Santo Antônio de Lisboa", - "Santo Antônio dos Milagres", - "Santo Inácio do Piauí", - "São Braz do Piauí", - "São Félix do Piauí", - "São Francisco de Assis do Piauí", - "São Francisco do Piauí", - "São Gonçalo do Gurguéia", - "São Gonçalo do Piauí", - "São João da Canabrava", - "São João da Fronteira", - "São João da Serra", - "São João da Varjota", - "São João do Arraial", - "São João do Piauí", - "São José do Divino", - "São José do Peixe", - "São José do Piauí", - "São Julião", - "São Lourenço do Piauí", - "São Luis do Piauí", - "São Miguel da Baixa Grande", - "São Miguel do Fidalgo", - "São Miguel do Tapuio", - "São Pedro do Piauí", - "São Raimundo Nonato", - "Sebastião Barros", - "Sebastião Leal", - "Sigefredo Pacheco", - "Simões", - "Simplício Mendes", - "Socorro do Piauí", - "Sussuapara", - "Tamboril do Piauí", - "Tanque do Piauí", - "Teresina", - "União", - "Uruçuí", - "Valença do Piauí", - "Várzea Branca", - "Várzea Grande", - "Vera Mendes", - "Vila Nova do Piauí", - "Wall Ferraz", + ["Acauã", "2200053"], + ["Agricolândia", "2200103"], + ["Água Branca", "2200202"], + ["Alagoinha do Piauí", "2200251"], + ["Alegrete do Piauí", "2200277"], + ["Alto Longá", "2200301"], + ["Altos", "2200400"], + ["Alvorada do Gurguéia", "2200459"], + ["Amarante", "2200509"], + ["Angical do Piauí", "2200608"], + ["Anísio de Abreu", "2200707"], + ["Antônio Almeida", "2200806"], + ["Aroazes", "2200905"], + ["Aroeiras do Itaim", "2200954"], + ["Arraial", "2201002"], + ["Assunção do Piauí", "2201051"], + ["Avelino Lopes", "2201101"], + ["Baixa Grande do Ribeiro", "2201150"], + ["Barra D'Alcântara", "2201176"], + ["Barras", "2201200"], + ["Barreiras do Piauí", "2201309"], + ["Barro Duro", "2201408"], + ["Batalha", "2201507"], + ["Bela Vista do Piauí", "2201556"], + ["Belém do Piauí", "2201572"], + ["Beneditinos", "2201606"], + ["Bertolínia", "2201705"], + ["Betânia do Piauí", "2201739"], + ["Boa Hora", "2201770"], + ["Bocaina", "2201804"], + ["Bom Jesus", "2201903"], + ["Bom Princípio do Piauí", "2201919"], + ["Bonfim do Piauí", "2201929"], + ["Boqueirão do Piauí", "2201945"], + ["Brasileira", "2201960"], + ["Brejo do Piauí", "2201988"], + ["Buriti dos Lopes", "2202000"], + ["Buriti dos Montes", "2202026"], + ["Cabeceiras do Piauí", "2202059"], + ["Cajazeiras do Piauí", "2202075"], + ["Cajueiro da Praia", "2202083"], + ["Caldeirão Grande do Piauí", "2202091"], + ["Campinas do Piauí", "2202109"], + ["Campo Alegre do Fidalgo", "2202117"], + ["Campo Grande do Piauí", "2202133"], + ["Campo Largo do Piauí", "2202174"], + ["Campo Maior", "2202208"], + ["Canavieira", "2202251"], + ["Canto do Buriti", "2202307"], + ["Capitão de Campos", "2202406"], + ["Capitão Gervásio Oliveira", "2202455"], + ["Caracol", "2202505"], + ["Caraúbas do Piauí", "2202539"], + ["Caridade do Piauí", "2202554"], + ["Castelo do Piauí", "2202604"], + ["Caxingó", "2202653"], + ["Cocal", "2202703"], + ["Cocal de Telha", "2202711"], + ["Cocal dos Alves", "2202729"], + ["Coivaras", "2202737"], + ["Colônia do Gurguéia", "2202752"], + ["Colônia do Piauí", "2202778"], + ["Conceição do Canindé", "2202802"], + ["Coronel José Dias", "2202851"], + ["Corrente", "2202901"], + ["Cristalândia do Piauí", "2203008"], + ["Cristino Castro", "2203107"], + ["Curimatá", "2203206"], + ["Currais", "2203230"], + ["Curral Novo do Piauí", "2203271"], + ["Curralinhos", "2203255"], + ["Demerval Lobão", "2203305"], + ["Dirceu Arcoverde", "2203354"], + ["Dom Expedito Lopes", "2203404"], + ["Dom Inocêncio", "2203453"], + ["Domingos Mourão", "2203420"], + ["Elesbão Veloso", "2203503"], + ["Eliseu Martins", "2203602"], + ["Esperantina", "2203701"], + ["Fartura do Piauí", "2203750"], + ["Flores do Piauí", "2203800"], + ["Floresta do Piauí", "2203859"], + ["Floriano", "2203909"], + ["Francinópolis", "2204006"], + ["Francisco Ayres", "2204105"], + ["Francisco Macedo", "2204154"], + ["Francisco Santos", "2204204"], + ["Fronteiras", "2204303"], + ["Geminiano", "2204352"], + ["Gilbués", "2204402"], + ["Guadalupe", "2204501"], + ["Guaribas", "2204550"], + ["Hugo Napoleão", "2204600"], + ["Ilha Grande", "2204659"], + ["Inhuma", "2204709"], + ["Ipiranga do Piauí", "2204808"], + ["Isaías Coelho", "2204907"], + ["Itainópolis", "2205003"], + ["Itaueira", "2205102"], + ["Jacobina do Piauí", "2205151"], + ["Jaicós", "2205201"], + ["Jardim do Mulato", "2205250"], + ["Jatobá do Piauí", "2205276"], + ["Jerumenha", "2205300"], + ["João Costa", "2205359"], + ["Joaquim Pires", "2205409"], + ["Joca Marques", "2205458"], + ["José de Freitas", "2205508"], + ["Juazeiro do Piauí", "2205516"], + ["Júlio Borges", "2205524"], + ["Jurema", "2205532"], + ["Lagoa Alegre", "2205557"], + ["Lagoa de São Francisco", "2205573"], + ["Lagoa do Barro do Piauí", "2205565"], + ["Lagoa do Piauí", "2205581"], + ["Lagoa do Sítio", "2205599"], + ["Lagoinha do Piauí", "2205540"], + ["Landri Sales", "2205607"], + ["Luís Correia", "2205706"], + ["Luzilândia", "2205805"], + ["Madeiro", "2205854"], + ["Manoel Emídio", "2205904"], + ["Marcolândia", "2205953"], + ["Marcos Parente", "2206001"], + ["Massapê do Piauí", "2206050"], + ["Matias Olímpio", "2206100"], + ["Miguel Alves", "2206209"], + ["Miguel Leão", "2206308"], + ["Milton Brandão", "2206357"], + ["Monsenhor Gil", "2206407"], + ["Monsenhor Hipólito", "2206506"], + ["Monte Alegre do Piauí", "2206605"], + ["Morro Cabeça no Tempo", "2206654"], + ["Morro do Chapéu do Piauí", "2206670"], + ["Murici dos Portelas", "2206696"], + ["Nazaré do Piauí", "2206704"], + ["Nazária", "2206720"], + ["Nossa Senhora de Nazaré", "2206753"], + ["Nossa Senhora dos Remédios", "2206803"], + ["Nova Santa Rita", "2207959"], + ["Novo Oriente do Piauí", "2206902"], + ["Novo Santo Antônio", "2206951"], + ["Oeiras", "2207009"], + ["Olho D'Água do Piauí", "2207108"], + ["Padre Marcos", "2207207"], + ["Paes Landim", "2207306"], + ["Pajeú do Piauí", "2207355"], + ["Palmeira do Piauí", "2207405"], + ["Palmeirais", "2207504"], + ["Paquetá", "2207553"], + ["Parnaguá", "2207603"], + ["Parnaíba", "2207702"], + ["Passagem Franca do Piauí", "2207751"], + ["Patos do Piauí", "2207777"], + ["Pau D'Arco do Piauí", "2207793"], + ["Paulistana", "2207801"], + ["Pavussu", "2207850"], + ["Pedro II", "2207900"], + ["Pedro Laurentino", "2207934"], + ["Picos", "2208007"], + ["Pimenteiras", "2208106"], + ["Pio IX", "2208205"], + ["Piracuruca", "2208304"], + ["Piripiri", "2208403"], + ["Porto", "2208502"], + ["Porto Alegre do Piauí", "2208551"], + ["Prata do Piauí", "2208601"], + ["Queimada Nova", "2208650"], + ["Redenção do Gurguéia", "2208700"], + ["Regeneração", "2208809"], + ["Riacho Frio", "2208858"], + ["Ribeira do Piauí", "2208874"], + ["Ribeiro Gonçalves", "2208908"], + ["Rio Grande do Piauí", "2209005"], + ["Santa Cruz do Piauí", "2209104"], + ["Santa Cruz dos Milagres", "2209153"], + ["Santa Filomena", "2209203"], + ["Santa Luz", "2209302"], + ["Santa Rosa do Piauí", "2209377"], + ["Santana do Piauí", "2209351"], + ["Santo Antônio de Lisboa", "2209401"], + ["Santo Antônio dos Milagres", "2209450"], + ["Santo Inácio do Piauí", "2209500"], + ["São Braz do Piauí", "2209559"], + ["São Félix do Piauí", "2209609"], + ["São Francisco de Assis do Piauí", "2209658"], + ["São Francisco do Piauí", "2209708"], + ["São Gonçalo do Gurguéia", "2209757"], + ["São Gonçalo do Piauí", "2209807"], + ["São João da Canabrava", "2209856"], + ["São João da Fronteira", "2209872"], + ["São João da Serra", "2209906"], + ["São João da Varjota", "2209955"], + ["São João do Arraial", "2209971"], + ["São João do Piauí", "2210003"], + ["São José do Divino", "2210052"], + ["São José do Peixe", "2210102"], + ["São José do Piauí", "2210201"], + ["São Julião", "2210300"], + ["São Lourenço do Piauí", "2210359"], + ["São Luis do Piauí", "2210375"], + ["São Miguel da Baixa Grande", "2210383"], + ["São Miguel do Fidalgo", "2210391"], + ["São Miguel do Tapuio", "2210409"], + ["São Pedro do Piauí", "2210508"], + ["São Raimundo Nonato", "2210607"], + ["Sebastião Barros", "2210623"], + ["Sebastião Leal", "2210631"], + ["Sigefredo Pacheco", "2210656"], + ["Simões", "2210706"], + ["Simplício Mendes", "2210805"], + ["Socorro do Piauí", "2210904"], + ["Sussuapara", "2210938"], + ["Tamboril do Piauí", "2210953"], + ["Tanque do Piauí", "2210979"], + ["Teresina", "2211001"], + ["União", "2211100"], + ["Uruçuí", "2211209"], + ["Valença do Piauí", "2211308"], + ["Várzea Branca", "2211357"], + ["Várzea Grande", "2211407"], + ["Vera Mendes", "2211506"], + ["Vila Nova do Piauí", "2211605"], + ["Wall Ferraz", "2211704"], ], PR: [ - "Abatiá", - "Adrianópolis", - "Agudos do Sul", - "Almirante Tamandaré", - "Altamira do Paraná", - "Alto Paraíso", - "Alto Paraná", - "Alto Piquiri", - "Altônia", - "Alvorada do Sul", - "Amaporã", - "Ampére", - "Anahy", - "Andirá", - "Ângulo", - "Antonina", - "Antônio Olinto", - "Apucarana", - "Arapongas", - "Arapoti", - "Arapuã", - "Araruna", - "Araucária", - "Ariranha do Ivaí", - "Assaí", - "Assis Chateaubriand", - "Astorga", - "Atalaia", - "Balsa Nova", - "Bandeirantes", - "Barbosa Ferraz", - "Barra do Jacaré", - "Barracão", - "Bela Vista da Caroba", - "Bela Vista do Paraíso", - "Bituruna", - "Boa Esperança", - "Boa Esperança do Iguaçu", - "Boa Ventura de São Roque", - "Boa Vista da Aparecida", - "Bocaiúva do Sul", - "Bom Jesus do Sul", - "Bom Sucesso", - "Bom Sucesso do Sul", - "Borrazópolis", - "Braganey", - "Brasilândia do Sul", - "Cafeara", - "Cafelândia", - "Cafezal do Sul", - "Califórnia", - "Cambará", - "Cambé", - "Cambira", - "Campina da Lagoa", - "Campina do Simão", - "Campina Grande do Sul", - "Campo Bonito", - "Campo do Tenente", - "Campo Largo", - "Campo Magro", - "Campo Mourão", - "Cândido de Abreu", - "Candói", - "Cantagalo", - "Capanema", - "Capitão Leônidas Marques", - "Carambeí", - "Carlópolis", - "Cascavel", - "Castro", - "Catanduvas", - "Centenário do Sul", - "Cerro Azul", - "Céu Azul", - "Chopinzinho", - "Cianorte", - "Cidade Gaúcha", - "Clevelândia", - "Colombo", - "Colorado", - "Congonhinhas", - "Conselheiro Mairinck", - "Contenda", - "Corbélia", - "Cornélio Procópio", - "Coronel Domingos Soares", - "Coronel Vivida", - "Corumbataí do Sul", - "Cruz Machado", - "Cruzeiro do Iguaçu", - "Cruzeiro do Oeste", - "Cruzeiro do Sul", - "Cruzmaltina", - "Curitiba", - "Curiúva", - "Diamante D'Oeste", - "Diamante do Norte", - "Diamante do Sul", - "Dois Vizinhos", - "Douradina", - "Doutor Camargo", - "Doutor Ulysses", - "Enéas Marques", - "Engenheiro Beltrão", - "Entre Rios do Oeste", - "Esperança Nova", - "Espigão Alto do Iguaçu", - "Farol", - "Faxinal", - "Fazenda Rio Grande", - "Fênix", - "Fernandes Pinheiro", - "Figueira", - "Flor da Serra do Sul", - "Floraí", - "Floresta", - "Florestópolis", - "Flórida", - "Formosa do Oeste", - "Foz do Iguaçu", - "Foz do Jordão", - "Francisco Alves", - "Francisco Beltrão", - "General Carneiro", - "Godoy Moreira", - "Goioerê", - "Goioxim", - "Grandes Rios", - "Guaíra", - "Guairaçá", - "Guamiranga", - "Guapirama", - "Guaporema", - "Guaraci", - "Guaraniaçu", - "Guarapuava", - "Guaraqueçaba", - "Guaratuba", - "Honório Serpa", - "Ibaiti", - "Ibema", - "Ibiporã", - "Icaraíma", - "Iguaraçu", - "Iguatu", - "Imbaú", - "Imbituva", - "Inácio Martins", - "Inajá", - "Indianópolis", - "Ipiranga", - "Iporã", - "Iracema do Oeste", - "Irati", - "Iretama", - "Itaguajé", - "Itaipulândia", - "Itambaracá", - "Itambé", - "Itapejara d'Oeste", - "Itaperuçu", - "Itaúna do Sul", - "Ivaí", - "Ivaiporã", - "Ivaté", - "Ivatuba", - "Jaboti", - "Jacarezinho", - "Jaguapitã", - "Jaguariaíva", - "Jandaia do Sul", - "Janiópolis", - "Japira", - "Japurá", - "Jardim Alegre", - "Jardim Olinda", - "Jataizinho", - "Jesuítas", - "Joaquim Távora", - "Jundiaí do Sul", - "Juranda", - "Jussara", - "Kaloré", - "Lapa", - "Laranjal", - "Laranjeiras do Sul", - "Leópolis", - "Lidianópolis", - "Lindoeste", - "Loanda", - "Lobato", - "Londrina", - "Luiziana", - "Lunardelli", - "Lupionópolis", - "Mallet", - "Mamborê", - "Mandaguaçu", - "Mandaguari", - "Mandirituba", - "Manfrinópolis", - "Mangueirinha", - "Manoel Ribas", - "Marechal Cândido Rondon", - "Maria Helena", - "Marialva", - "Marilândia do Sul", - "Marilena", - "Mariluz", - "Maringá", - "Mariópolis", - "Maripá", - "Marmeleiro", - "Marquinho", - "Marumbi", - "Matelândia", - "Matinhos", - "Mato Rico", - "Mauá da Serra", - "Medianeira", - "Mercedes", - "Mirador", - "Miraselva", - "Missal", - "Moreira Sales", - "Morretes", - "Munhoz de Melo", - "Nossa Senhora das Graças", - "Nova Aliança do Ivaí", - "Nova América da Colina", - "Nova Aurora", - "Nova Cantu", - "Nova Esperança", - "Nova Esperança do Sudoeste", - "Nova Fátima", - "Nova Laranjeiras", - "Nova Londrina", - "Nova Olímpia", - "Nova Prata do Iguaçu", - "Nova Santa Bárbara", - "Nova Santa Rosa", - "Nova Tebas", - "Novo Itacolomi", - "Ortigueira", - "Ourizona", - "Ouro Verde do Oeste", - "Paiçandu", - "Palmas", - "Palmeira", - "Palmital", - "Palotina", - "Paraíso do Norte", - "Paranacity", - "Paranaguá", - "Paranapoema", - "Paranavaí", - "Pato Bragado", - "Pato Branco", - "Paula Freitas", - "Paulo Frontin", - "Peabiru", - "Perobal", - "Pérola", - "Pérola d'Oeste", - "Piên", - "Pinhais", - "Pinhal de São Bento", - "Pinhalão", - "Pinhão", - "Piraí do Sul", - "Piraquara", - "Pitanga", - "Pitangueiras", - "Planaltina do Paraná", - "Planalto", - "Ponta Grossa", - "Pontal do Paraná", - "Porecatu", - "Porto Amazonas", - "Porto Barreiro", - "Porto Rico", - "Porto Vitória", - "Prado Ferreira", - "Pranchita", - "Presidente Castelo Branco", - "Primeiro de Maio", - "Prudentópolis", - "Quarto Centenário", - "Quatiguá", - "Quatro Barras", - "Quatro Pontes", - "Quedas do Iguaçu", - "Querência do Norte", - "Quinta do Sol", - "Quitandinha", - "Ramilândia", - "Rancho Alegre", - "Rancho Alegre D'Oeste", - "Realeza", - "Rebouças", - "Renascença", - "Reserva", - "Reserva do Iguaçu", - "Ribeirão Claro", - "Ribeirão do Pinhal", - "Rio Azul", - "Rio Bom", - "Rio Bonito do Iguaçu", - "Rio Branco do Ivaí", - "Rio Branco do Sul", - "Rio Negro", - "Rolândia", - "Roncador", - "Rondon", - "Rosário do Ivaí", - "Sabáudia", - "Salgado Filho", - "Salto do Itararé", - "Salto do Lontra", - "Santa Amélia", - "Santa Cecília do Pavão", - "Santa Cruz de Monte Castelo", - "Santa Fé", - "Santa Helena", - "Santa Inês", - "Santa Isabel do Ivaí", - "Santa Izabel do Oeste", - "Santa Lúcia", - "Santa Maria do Oeste", - "Santa Mariana", - "Santa Mônica", - "Santa Tereza do Oeste", - "Santa Terezinha de Itaipu", - "Santana do Itararé", - "Santo Antônio da Platina", - "Santo Antônio do Caiuá", - "Santo Antônio do Paraíso", - "Santo Antônio do Sudoeste", - "Santo Inácio", - "São Carlos do Ivaí", - "São Jerônimo da Serra", - "São João", - "São João do Caiuá", - "São João do Ivaí", - "São João do Triunfo", - "São Jorge d'Oeste", - "São Jorge do Ivaí", - "São Jorge do Patrocínio", - "São José da Boa Vista", - "São José das Palmeiras", - "São José dos Pinhais", - "São Manoel do Paraná", - "São Mateus do Sul", - "São Miguel do Iguaçu", - "São Pedro do Iguaçu", - "São Pedro do Ivaí", - "São Pedro do Paraná", - "São Sebastião da Amoreira", - "São Tomé", - "Sapopema", - "Sarandi", - "Saudade do Iguaçu", - "Sengés", - "Serranópolis do Iguaçu", - "Sertaneja", - "Sertanópolis", - "Siqueira Campos", - "Sulina", - "Tamarana", - "Tamboara", - "Tapejara", - "Tapira", - "Teixeira Soares", - "Telêmaco Borba", - "Terra Boa", - "Terra Rica", - "Terra Roxa", - "Tibagi", - "Tijucas do Sul", - "Toledo", - "Tomazina", - "Três Barras do Paraná", - "Tunas do Paraná", - "Tuneiras do Oeste", - "Tupãssi", - "Turvo", - "Ubiratã", - "Umuarama", - "União da Vitória", - "Uniflor", - "Uraí", - "Ventania", - "Vera Cruz do Oeste", - "Verê", - "Virmond", - "Vitorino", - "Wenceslau Braz", - "Xambrê", + ["Abatiá", "4100103"], + ["Adrianópolis", "4100202"], + ["Agudos do Sul", "4100301"], + ["Almirante Tamandaré", "4100400"], + ["Altamira do Paraná", "4100459"], + ["Alto Paraíso", "4128625"], + ["Alto Paraná", "4100608"], + ["Alto Piquiri", "4100707"], + ["Altônia", "4100509"], + ["Alvorada do Sul", "4100806"], + ["Amaporã", "4100905"], + ["Ampére", "4101002"], + ["Anahy", "4101051"], + ["Andirá", "4101101"], + ["Ângulo", "4101150"], + ["Antonina", "4101200"], + ["Antônio Olinto", "4101309"], + ["Apucarana", "4101408"], + ["Arapongas", "4101507"], + ["Arapoti", "4101606"], + ["Arapuã", "4101655"], + ["Araruna", "4101705"], + ["Araucária", "4101804"], + ["Ariranha do Ivaí", "4101853"], + ["Assaí", "4101903"], + ["Assis Chateaubriand", "4102000"], + ["Astorga", "4102109"], + ["Atalaia", "4102208"], + ["Balsa Nova", "4102307"], + ["Bandeirantes", "4102406"], + ["Barbosa Ferraz", "4102505"], + ["Barra do Jacaré", "4102703"], + ["Barracão", "4102604"], + ["Bela Vista da Caroba", "4102752"], + ["Bela Vista do Paraíso", "4102802"], + ["Bituruna", "4102901"], + ["Boa Esperança", "4103008"], + ["Boa Esperança do Iguaçu", "4103024"], + ["Boa Ventura de São Roque", "4103040"], + ["Boa Vista da Aparecida", "4103057"], + ["Bocaiúva do Sul", "4103107"], + ["Bom Jesus do Sul", "4103156"], + ["Bom Sucesso", "4103206"], + ["Bom Sucesso do Sul", "4103222"], + ["Borrazópolis", "4103305"], + ["Braganey", "4103354"], + ["Brasilândia do Sul", "4103370"], + ["Cafeara", "4103404"], + ["Cafelândia", "4103453"], + ["Cafezal do Sul", "4103479"], + ["Califórnia", "4103503"], + ["Cambará", "4103602"], + ["Cambé", "4103701"], + ["Cambira", "4103800"], + ["Campina da Lagoa", "4103909"], + ["Campina do Simão", "4103958"], + ["Campina Grande do Sul", "4104006"], + ["Campo Bonito", "4104055"], + ["Campo do Tenente", "4104105"], + ["Campo Largo", "4104204"], + ["Campo Magro", "4104253"], + ["Campo Mourão", "4104303"], + ["Cândido de Abreu", "4104402"], + ["Candói", "4104428"], + ["Cantagalo", "4104451"], + ["Capanema", "4104501"], + ["Capitão Leônidas Marques", "4104600"], + ["Carambeí", "4104659"], + ["Carlópolis", "4104709"], + ["Cascavel", "4104808"], + ["Castro", "4104907"], + ["Catanduvas", "4105003"], + ["Centenário do Sul", "4105102"], + ["Cerro Azul", "4105201"], + ["Céu Azul", "4105300"], + ["Chopinzinho", "4105409"], + ["Cianorte", "4105508"], + ["Cidade Gaúcha", "4105607"], + ["Clevelândia", "4105706"], + ["Colombo", "4105805"], + ["Colorado", "4105904"], + ["Congonhinhas", "4106001"], + ["Conselheiro Mairinck", "4106100"], + ["Contenda", "4106209"], + ["Corbélia", "4106308"], + ["Cornélio Procópio", "4106407"], + ["Coronel Domingos Soares", "4106456"], + ["Coronel Vivida", "4106506"], + ["Corumbataí do Sul", "4106555"], + ["Cruz Machado", "4106803"], + ["Cruzeiro do Iguaçu", "4106571"], + ["Cruzeiro do Oeste", "4106605"], + ["Cruzeiro do Sul", "4106704"], + ["Cruzmaltina", "4106852"], + ["Curitiba", "4106902"], + ["Curiúva", "4107009"], + ["Diamante D'Oeste", "4107157"], + ["Diamante do Norte", "4107108"], + ["Diamante do Sul", "4107124"], + ["Dois Vizinhos", "4107207"], + ["Douradina", "4107256"], + ["Doutor Camargo", "4107306"], + ["Doutor Ulysses", "4128633"], + ["Enéas Marques", "4107405"], + ["Engenheiro Beltrão", "4107504"], + ["Entre Rios do Oeste", "4107538"], + ["Esperança Nova", "4107520"], + ["Espigão Alto do Iguaçu", "4107546"], + ["Farol", "4107553"], + ["Faxinal", "4107603"], + ["Fazenda Rio Grande", "4107652"], + ["Fênix", "4107702"], + ["Fernandes Pinheiro", "4107736"], + ["Figueira", "4107751"], + ["Flor da Serra do Sul", "4107850"], + ["Floraí", "4107801"], + ["Floresta", "4107900"], + ["Florestópolis", "4108007"], + ["Flórida", "4108106"], + ["Formosa do Oeste", "4108205"], + ["Foz do Iguaçu", "4108304"], + ["Foz do Jordão", "4108452"], + ["Francisco Alves", "4108320"], + ["Francisco Beltrão", "4108403"], + ["General Carneiro", "4108502"], + ["Godoy Moreira", "4108551"], + ["Goioerê", "4108601"], + ["Goioxim", "4108650"], + ["Grandes Rios", "4108700"], + ["Guaíra", "4108809"], + ["Guairaçá", "4108908"], + ["Guamiranga", "4108957"], + ["Guapirama", "4109005"], + ["Guaporema", "4109104"], + ["Guaraci", "4109203"], + ["Guaraniaçu", "4109302"], + ["Guarapuava", "4109401"], + ["Guaraqueçaba", "4109500"], + ["Guaratuba", "4109609"], + ["Honório Serpa", "4109658"], + ["Ibaiti", "4109708"], + ["Ibema", "4109757"], + ["Ibiporã", "4109807"], + ["Icaraíma", "4109906"], + ["Iguaraçu", "4110003"], + ["Iguatu", "4110052"], + ["Imbaú", "4110078"], + ["Imbituva", "4110102"], + ["Inácio Martins", "4110201"], + ["Inajá", "4110300"], + ["Indianópolis", "4110409"], + ["Ipiranga", "4110508"], + ["Iporã", "4110607"], + ["Iracema do Oeste", "4110656"], + ["Irati", "4110706"], + ["Iretama", "4110805"], + ["Itaguajé", "4110904"], + ["Itaipulândia", "4110953"], + ["Itambaracá", "4111001"], + ["Itambé", "4111100"], + ["Itapejara d'Oeste", "4111209"], + ["Itaperuçu", "4111258"], + ["Itaúna do Sul", "4111308"], + ["Ivaí", "4111407"], + ["Ivaiporã", "4111506"], + ["Ivaté", "4111555"], + ["Ivatuba", "4111605"], + ["Jaboti", "4111704"], + ["Jacarezinho", "4111803"], + ["Jaguapitã", "4111902"], + ["Jaguariaíva", "4112009"], + ["Jandaia do Sul", "4112108"], + ["Janiópolis", "4112207"], + ["Japira", "4112306"], + ["Japurá", "4112405"], + ["Jardim Alegre", "4112504"], + ["Jardim Olinda", "4112603"], + ["Jataizinho", "4112702"], + ["Jesuítas", "4112751"], + ["Joaquim Távora", "4112801"], + ["Jundiaí do Sul", "4112900"], + ["Juranda", "4112959"], + ["Jussara", "4113007"], + ["Kaloré", "4113106"], + ["Lapa", "4113205"], + ["Laranjal", "4113254"], + ["Laranjeiras do Sul", "4113304"], + ["Leópolis", "4113403"], + ["Lidianópolis", "4113429"], + ["Lindoeste", "4113452"], + ["Loanda", "4113502"], + ["Lobato", "4113601"], + ["Londrina", "4113700"], + ["Luiziana", "4113734"], + ["Lunardelli", "4113759"], + ["Lupionópolis", "4113809"], + ["Mallet", "4113908"], + ["Mamborê", "4114005"], + ["Mandaguaçu", "4114104"], + ["Mandaguari", "4114203"], + ["Mandirituba", "4114302"], + ["Manfrinópolis", "4114351"], + ["Mangueirinha", "4114401"], + ["Manoel Ribas", "4114500"], + ["Marechal Cândido Rondon", "4114609"], + ["Maria Helena", "4114708"], + ["Marialva", "4114807"], + ["Marilândia do Sul", "4114906"], + ["Marilena", "4115002"], + ["Mariluz", "4115101"], + ["Maringá", "4115200"], + ["Mariópolis", "4115309"], + ["Maripá", "4115358"], + ["Marmeleiro", "4115408"], + ["Marquinho", "4115457"], + ["Marumbi", "4115507"], + ["Matelândia", "4115606"], + ["Matinhos", "4115705"], + ["Mato Rico", "4115739"], + ["Mauá da Serra", "4115754"], + ["Medianeira", "4115804"], + ["Mercedes", "4115853"], + ["Mirador", "4115903"], + ["Miraselva", "4116000"], + ["Missal", "4116059"], + ["Moreira Sales", "4116109"], + ["Morretes", "4116208"], + ["Munhoz de Melo", "4116307"], + ["Nossa Senhora das Graças", "4116406"], + ["Nova Aliança do Ivaí", "4116505"], + ["Nova América da Colina", "4116604"], + ["Nova Aurora", "4116703"], + ["Nova Cantu", "4116802"], + ["Nova Esperança", "4116901"], + ["Nova Esperança do Sudoeste", "4116950"], + ["Nova Fátima", "4117008"], + ["Nova Laranjeiras", "4117057"], + ["Nova Londrina", "4117107"], + ["Nova Olímpia", "4117206"], + ["Nova Prata do Iguaçu", "4117255"], + ["Nova Santa Bárbara", "4117214"], + ["Nova Santa Rosa", "4117222"], + ["Nova Tebas", "4117271"], + ["Novo Itacolomi", "4117297"], + ["Ortigueira", "4117305"], + ["Ourizona", "4117404"], + ["Ouro Verde do Oeste", "4117453"], + ["Paiçandu", "4117503"], + ["Palmas", "4117602"], + ["Palmeira", "4117701"], + ["Palmital", "4117800"], + ["Palotina", "4117909"], + ["Paraíso do Norte", "4118006"], + ["Paranacity", "4118105"], + ["Paranaguá", "4118204"], + ["Paranapoema", "4118303"], + ["Paranavaí", "4118402"], + ["Pato Bragado", "4118451"], + ["Pato Branco", "4118501"], + ["Paula Freitas", "4118600"], + ["Paulo Frontin", "4118709"], + ["Peabiru", "4118808"], + ["Perobal", "4118857"], + ["Pérola", "4118907"], + ["Pérola d'Oeste", "4119004"], + ["Piên", "4119103"], + ["Pinhais", "4119152"], + ["Pinhal de São Bento", "4119251"], + ["Pinhalão", "4119202"], + ["Pinhão", "4119301"], + ["Piraí do Sul", "4119400"], + ["Piraquara", "4119509"], + ["Pitanga", "4119608"], + ["Pitangueiras", "4119657"], + ["Planaltina do Paraná", "4119707"], + ["Planalto", "4119806"], + ["Ponta Grossa", "4119905"], + ["Pontal do Paraná", "4119954"], + ["Porecatu", "4120002"], + ["Porto Amazonas", "4120101"], + ["Porto Barreiro", "4120150"], + ["Porto Rico", "4120200"], + ["Porto Vitória", "4120309"], + ["Prado Ferreira", "4120333"], + ["Pranchita", "4120358"], + ["Presidente Castelo Branco", "4120408"], + ["Primeiro de Maio", "4120507"], + ["Prudentópolis", "4120606"], + ["Quarto Centenário", "4120655"], + ["Quatiguá", "4120705"], + ["Quatro Barras", "4120804"], + ["Quatro Pontes", "4120853"], + ["Quedas do Iguaçu", "4120903"], + ["Querência do Norte", "4121000"], + ["Quinta do Sol", "4121109"], + ["Quitandinha", "4121208"], + ["Ramilândia", "4121257"], + ["Rancho Alegre", "4121307"], + ["Rancho Alegre D'Oeste", "4121356"], + ["Realeza", "4121406"], + ["Rebouças", "4121505"], + ["Renascença", "4121604"], + ["Reserva", "4121703"], + ["Reserva do Iguaçu", "4121752"], + ["Ribeirão Claro", "4121802"], + ["Ribeirão do Pinhal", "4121901"], + ["Rio Azul", "4122008"], + ["Rio Bom", "4122107"], + ["Rio Bonito do Iguaçu", "4122156"], + ["Rio Branco do Ivaí", "4122172"], + ["Rio Branco do Sul", "4122206"], + ["Rio Negro", "4122305"], + ["Rolândia", "4122404"], + ["Roncador", "4122503"], + ["Rondon", "4122602"], + ["Rosário do Ivaí", "4122651"], + ["Sabáudia", "4122701"], + ["Salgado Filho", "4122800"], + ["Salto do Itararé", "4122909"], + ["Salto do Lontra", "4123006"], + ["Santa Amélia", "4123105"], + ["Santa Cecília do Pavão", "4123204"], + ["Santa Cruz de Monte Castelo", "4123303"], + ["Santa Fé", "4123402"], + ["Santa Helena", "4123501"], + ["Santa Inês", "4123600"], + ["Santa Isabel do Ivaí", "4123709"], + ["Santa Izabel do Oeste", "4123808"], + ["Santa Lúcia", "4123824"], + ["Santa Maria do Oeste", "4123857"], + ["Santa Mariana", "4123907"], + ["Santa Mônica", "4123956"], + ["Santa Tereza do Oeste", "4124020"], + ["Santa Terezinha de Itaipu", "4124053"], + ["Santana do Itararé", "4124004"], + ["Santo Antônio da Platina", "4124103"], + ["Santo Antônio do Caiuá", "4124202"], + ["Santo Antônio do Paraíso", "4124301"], + ["Santo Antônio do Sudoeste", "4124400"], + ["Santo Inácio", "4124509"], + ["São Carlos do Ivaí", "4124608"], + ["São Jerônimo da Serra", "4124707"], + ["São João", "4124806"], + ["São João do Caiuá", "4124905"], + ["São João do Ivaí", "4125001"], + ["São João do Triunfo", "4125100"], + ["São Jorge d'Oeste", "4125209"], + ["São Jorge do Ivaí", "4125308"], + ["São Jorge do Patrocínio", "4125357"], + ["São José da Boa Vista", "4125407"], + ["São José das Palmeiras", "4125456"], + ["São José dos Pinhais", "4125506"], + ["São Manoel do Paraná", "4125555"], + ["São Mateus do Sul", "4125605"], + ["São Miguel do Iguaçu", "4125704"], + ["São Pedro do Iguaçu", "4125753"], + ["São Pedro do Ivaí", "4125803"], + ["São Pedro do Paraná", "4125902"], + ["São Sebastião da Amoreira", "4126009"], + ["São Tomé", "4126108"], + ["Sapopema", "4126207"], + ["Sarandi", "4126256"], + ["Saudade do Iguaçu", "4126272"], + ["Sengés", "4126306"], + ["Serranópolis do Iguaçu", "4126355"], + ["Sertaneja", "4126405"], + ["Sertanópolis", "4126504"], + ["Siqueira Campos", "4126603"], + ["Sulina", "4126652"], + ["Tamarana", "4126678"], + ["Tamboara", "4126702"], + ["Tapejara", "4126801"], + ["Tapira", "4126900"], + ["Teixeira Soares", "4127007"], + ["Telêmaco Borba", "4127106"], + ["Terra Boa", "4127205"], + ["Terra Rica", "4127304"], + ["Terra Roxa", "4127403"], + ["Tibagi", "4127502"], + ["Tijucas do Sul", "4127601"], + ["Toledo", "4127700"], + ["Tomazina", "4127809"], + ["Três Barras do Paraná", "4127858"], + ["Tunas do Paraná", "4127882"], + ["Tuneiras do Oeste", "4127908"], + ["Tupãssi", "4127957"], + ["Turvo", "4127965"], + ["Ubiratã", "4128005"], + ["Umuarama", "4128104"], + ["União da Vitória", "4128203"], + ["Uniflor", "4128302"], + ["Uraí", "4128401"], + ["Ventania", "4128534"], + ["Vera Cruz do Oeste", "4128559"], + ["Verê", "4128609"], + ["Virmond", "4128658"], + ["Vitorino", "4128708"], + ["Wenceslau Braz", "4128500"], + ["Xambrê", "4128807"], ], RJ: [ - "Angra dos Reis", - "Aperibé", - "Araruama", - "Areal", - "Armação dos Búzios", - "Arraial do Cabo", - "Barra do Piraí", - "Barra Mansa", - "Belford Roxo", - "Bom Jardim", - "Bom Jesus do Itabapoana", - "Cabo Frio", - "Cachoeiras de Macacu", - "Cambuci", - "Campos dos Goytacazes", - "Cantagalo", - "Carapebus", - "Cardoso Moreira", - "Carmo", - "Casimiro de Abreu", - "Comendador Levy Gasparian", - "Conceição de Macabu", - "Cordeiro", - "Duas Barras", - "Duque de Caxias", - "Engenheiro Paulo de Frontin", - "Guapimirim", - "Iguaba Grande", - "Itaboraí", - "Itaguaí", - "Italva", - "Itaocara", - "Itaperuna", - "Itatiaia", - "Japeri", - "Laje do Muriaé", - "Macaé", - "Macuco", - "Magé", - "Mangaratiba", - "Maricá", - "Mendes", - "Mesquita", - "Miguel Pereira", - "Miracema", - "Natividade", - "Nilópolis", - "Niterói", - "Nova Friburgo", - "Nova Iguaçu", - "Paracambi", - "Paraíba do Sul", - "Paraty", - "Paty do Alferes", - "Petrópolis", - "Pinheiral", - "Piraí", - "Porciúncula", - "Porto Real", - "Quatis", - "Queimados", - "Quissamã", - "Resende", - "Rio Bonito", - "Rio Claro", - "Rio das Flores", - "Rio das Ostras", - "Rio de Janeiro", - "Santa Maria Madalena", - "Santo Antônio de Pádua", - "São Fidélis", - "São Francisco de Itabapoana", - "São Gonçalo", - "São João da Barra", - "São João de Meriti", - "São José de Ubá", - "São José do Vale do Rio Preto", - "São Pedro da Aldeia", - "São Sebastião do Alto", - "Sapucaia", - "Saquarema", - "Seropédica", - "Silva Jardim", - "Sumidouro", - "Tanguá", - "Teresópolis", - "Trajano de Moraes", - "Três Rios", - "Valença", - "Varre-Sai", - "Vassouras", - "Volta Redonda", + ["Angra dos Reis", "3300100"], + ["Aperibé", "3300159"], + ["Araruama", "3300209"], + ["Areal", "3300225"], + ["Armação dos Búzios", "3300233"], + ["Arraial do Cabo", "3300258"], + ["Barra do Piraí", "3300308"], + ["Barra Mansa", "3300407"], + ["Belford Roxo", "3300456"], + ["Bom Jardim", "3300506"], + ["Bom Jesus do Itabapoana", "3300605"], + ["Cabo Frio", "3300704"], + ["Cachoeiras de Macacu", "3300803"], + ["Cambuci", "3300902"], + ["Campos dos Goytacazes", "3301009"], + ["Cantagalo", "3301108"], + ["Carapebus", "3300936"], + ["Cardoso Moreira", "3301157"], + ["Carmo", "3301207"], + ["Casimiro de Abreu", "3301306"], + ["Comendador Levy Gasparian", "3300951"], + ["Conceição de Macabu", "3301405"], + ["Cordeiro", "3301504"], + ["Duas Barras", "3301603"], + ["Duque de Caxias", "3301702"], + ["Engenheiro Paulo de Frontin", "3301801"], + ["Guapimirim", "3301850"], + ["Iguaba Grande", "3301876"], + ["Itaboraí", "3301900"], + ["Itaguaí", "3302007"], + ["Italva", "3302056"], + ["Itaocara", "3302106"], + ["Itaperuna", "3302205"], + ["Itatiaia", "3302254"], + ["Japeri", "3302270"], + ["Laje do Muriaé", "3302304"], + ["Macaé", "3302403"], + ["Macuco", "3302452"], + ["Magé", "3302502"], + ["Mangaratiba", "3302601"], + ["Maricá", "3302700"], + ["Mendes", "3302809"], + ["Mesquita", "3302858"], + ["Miguel Pereira", "3302908"], + ["Miracema", "3303005"], + ["Natividade", "3303104"], + ["Nilópolis", "3303203"], + ["Niterói", "3303302"], + ["Nova Friburgo", "3303401"], + ["Nova Iguaçu", "3303500"], + ["Paracambi", "3303609"], + ["Paraíba do Sul", "3303708"], + ["Paraty", "3303807"], + ["Paty do Alferes", "3303856"], + ["Petrópolis", "3303906"], + ["Pinheiral", "3303955"], + ["Piraí", "3304003"], + ["Porciúncula", "3304102"], + ["Porto Real", "3304110"], + ["Quatis", "3304128"], + ["Queimados", "3304144"], + ["Quissamã", "3304151"], + ["Resende", "3304201"], + ["Rio Bonito", "3304300"], + ["Rio Claro", "3304409"], + ["Rio das Flores", "3304508"], + ["Rio das Ostras", "3304524"], + ["Rio de Janeiro", "3304557"], + ["Santa Maria Madalena", "3304607"], + ["Santo Antônio de Pádua", "3304706"], + ["São Fidélis", "3304805"], + ["São Francisco de Itabapoana", "3304755"], + ["São Gonçalo", "3304904"], + ["São João da Barra", "3305000"], + ["São João de Meriti", "3305109"], + ["São José de Ubá", "3305133"], + ["São José do Vale do Rio Preto", "3305158"], + ["São Pedro da Aldeia", "3305208"], + ["São Sebastião do Alto", "3305307"], + ["Sapucaia", "3305406"], + ["Saquarema", "3305505"], + ["Seropédica", "3305554"], + ["Silva Jardim", "3305604"], + ["Sumidouro", "3305703"], + ["Tanguá", "3305752"], + ["Teresópolis", "3305802"], + ["Trajano de Moraes", "3305901"], + ["Três Rios", "3306008"], + ["Valença", "3306107"], + ["Varre-Sai", "3306156"], + ["Vassouras", "3306206"], + ["Volta Redonda", "3306305"], ], RN: [ - "Acari", - "Afonso Bezerra", - "Água Nova", - "Alexandria", - "Almino Afonso", - "Alto do Rodrigues", - "Angicos", - "Antônio Martins", - "Apodi", - "Areia Branca", - "Arez", - "Assú", - "Baía Formosa", - "Baraúna", - "Barcelona", - "Bento Fernandes", - "Bodó", - "Bom Jesus", - "Brejinho", - "Caiçara do Norte", - "Caiçara do Rio do Vento", - "Caicó", - "Campo Grande", - "Campo Redondo", - "Canguaretama", - "Caraúbas", - "Carnaúba dos Dantas", - "Carnaubais", - "Ceará-Mirim", - "Cerro Corá", - "Coronel Ezequiel", - "Coronel João Pessoa", - "Cruzeta", - "Currais Novos", - "Doutor Severiano", - "Encanto", - "Equador", - "Espírito Santo", - "Extremoz", - "Felipe Guerra", - "Fernando Pedroza", - "Florânia", - "Francisco Dantas", - "Frutuoso Gomes", - "Galinhos", - "Goianinha", - "Governador Dix-Sept Rosado", - "Grossos", - "Guamaré", - "Ielmo Marinho", - "Ipanguaçu", - "Ipueira", - "Itajá", - "Itaú", - "Jaçanã", - "Jandaíra", - "Janduís", - "Januário Cicco", - "Japi", - "Jardim de Angicos", - "Jardim de Piranhas", - "Jardim do Seridó", - "João Câmara", - "João Dias", - "José da Penha", - "Jucurutu", - "Jundiá", - "Lagoa d'Anta", - "Lagoa de Pedras", - "Lagoa de Velhos", - "Lagoa Nova", - "Lagoa Salgada", - "Lajes", - "Lajes Pintadas", - "Lucrécia", - "Luís Gomes", - "Macaíba", - "Macau", - "Major Sales", - "Marcelino Vieira", - "Martins", - "Maxaranguape", - "Messias Targino", - "Montanhas", - "Monte Alegre", - "Monte das Gameleiras", - "Mossoró", - "Natal", - "Nísia Floresta", - "Nova Cruz", - "Olho d'Água do Borges", - "Ouro Branco", - "Paraná", - "Paraú", - "Parazinho", - "Parelhas", - "Parnamirim", - "Passa e Fica", - "Passagem", - "Patu", - "Pau dos Ferros", - "Pedra Grande", - "Pedra Preta", - "Pedro Avelino", - "Pedro Velho", - "Pendências", - "Pilões", - "Poço Branco", - "Portalegre", - "Porto do Mangue", - "Pureza", - "Rafael Fernandes", - "Rafael Godeiro", - "Riacho da Cruz", - "Riacho de Santana", - "Riachuelo", - "Rio do Fogo", - "Rodolfo Fernandes", - "Ruy Barbosa", - "Santa Cruz", - "Santa Maria", - "Santana do Matos", - "Santana do Seridó", - "Santo Antônio", - "São Bento do Norte", - "São Bento do Trairí", - "São Fernando", - "São Francisco do Oeste", - "São Gonçalo do Amarante", - "São João do Sabugi", - "São José de Mipibu", - "São José do Campestre", - "São José do Seridó", - "São Miguel", - "São Miguel do Gostoso", - "São Paulo do Potengi", - "São Pedro", - "São Rafael", - "São Tomé", - "São Vicente", - "Senador Elói de Souza", - "Senador Georgino Avelino", - "Serra Caiada", - "Serra de São Bento", - "Serra do Mel", - "Serra Negra do Norte", - "Serrinha", - "Serrinha dos Pintos", - "Severiano Melo", - "Sítio Novo", - "Taboleiro Grande", - "Taipu", - "Tangará", - "Tenente Ananias", - "Tenente Laurentino Cruz", - "Tibau", - "Tibau do Sul", - "Timbaúba dos Batistas", - "Touros", - "Triunfo Potiguar", - "Umarizal", - "Upanema", - "Várzea", - "Venha-Ver", - "Vera Cruz", - "Viçosa", - "Vila Flor", + ["Acari", "2400109"], + ["Afonso Bezerra", "2400307"], + ["Água Nova", "2400406"], + ["Alexandria", "2400505"], + ["Almino Afonso", "2400604"], + ["Alto do Rodrigues", "2400703"], + ["Angicos", "2400802"], + ["Antônio Martins", "2400901"], + ["Apodi", "2401008"], + ["Areia Branca", "2401107"], + ["Arez", "2401206"], + ["Assú", "2400208"], + ["Baía Formosa", "2401404"], + ["Baraúna", "2401453"], + ["Barcelona", "2401503"], + ["Bento Fernandes", "2401602"], + ["Bodó", "2401651"], + ["Bom Jesus", "2401701"], + ["Brejinho", "2401800"], + ["Caiçara do Norte", "2401859"], + ["Caiçara do Rio do Vento", "2401909"], + ["Caicó", "2402006"], + ["Campo Grande", "2401305"], + ["Campo Redondo", "2402105"], + ["Canguaretama", "2402204"], + ["Caraúbas", "2402303"], + ["Carnaúba dos Dantas", "2402402"], + ["Carnaubais", "2402501"], + ["Ceará-Mirim", "2402600"], + ["Cerro Corá", "2402709"], + ["Coronel Ezequiel", "2402808"], + ["Coronel João Pessoa", "2402907"], + ["Cruzeta", "2403004"], + ["Currais Novos", "2403103"], + ["Doutor Severiano", "2403202"], + ["Encanto", "2403301"], + ["Equador", "2403400"], + ["Espírito Santo", "2403509"], + ["Extremoz", "2403608"], + ["Felipe Guerra", "2403707"], + ["Fernando Pedroza", "2403756"], + ["Florânia", "2403806"], + ["Francisco Dantas", "2403905"], + ["Frutuoso Gomes", "2404002"], + ["Galinhos", "2404101"], + ["Goianinha", "2404200"], + ["Governador Dix-Sept Rosado", "2404309"], + ["Grossos", "2404408"], + ["Guamaré", "2404507"], + ["Ielmo Marinho", "2404606"], + ["Ipanguaçu", "2404705"], + ["Ipueira", "2404804"], + ["Itajá", "2404853"], + ["Itaú", "2404903"], + ["Jaçanã", "2405009"], + ["Jandaíra", "2405108"], + ["Janduís", "2405207"], + ["Januário Cicco", "2405306"], + ["Japi", "2405405"], + ["Jardim de Angicos", "2405504"], + ["Jardim de Piranhas", "2405603"], + ["Jardim do Seridó", "2405702"], + ["João Câmara", "2405801"], + ["João Dias", "2405900"], + ["José da Penha", "2406007"], + ["Jucurutu", "2406106"], + ["Jundiá", "2406155"], + ["Lagoa d'Anta", "2406205"], + ["Lagoa de Pedras", "2406304"], + ["Lagoa de Velhos", "2406403"], + ["Lagoa Nova", "2406502"], + ["Lagoa Salgada", "2406601"], + ["Lajes", "2406700"], + ["Lajes Pintadas", "2406809"], + ["Lucrécia", "2406908"], + ["Luís Gomes", "2407005"], + ["Macaíba", "2407104"], + ["Macau", "2407203"], + ["Major Sales", "2407252"], + ["Marcelino Vieira", "2407302"], + ["Martins", "2407401"], + ["Maxaranguape", "2407500"], + ["Messias Targino", "2407609"], + ["Montanhas", "2407708"], + ["Monte Alegre", "2407807"], + ["Monte das Gameleiras", "2407906"], + ["Mossoró", "2408003"], + ["Natal", "2408102"], + ["Nísia Floresta", "2408201"], + ["Nova Cruz", "2408300"], + ["Olho d'Água do Borges", "2408409"], + ["Ouro Branco", "2408508"], + ["Paraná", "2408607"], + ["Paraú", "2408706"], + ["Parazinho", "2408805"], + ["Parelhas", "2408904"], + ["Parnamirim", "2403251"], + ["Passa e Fica", "2409100"], + ["Passagem", "2409209"], + ["Patu", "2409308"], + ["Pau dos Ferros", "2409407"], + ["Pedra Grande", "2409506"], + ["Pedra Preta", "2409605"], + ["Pedro Avelino", "2409704"], + ["Pedro Velho", "2409803"], + ["Pendências", "2409902"], + ["Pilões", "2410009"], + ["Poço Branco", "2410108"], + ["Portalegre", "2410207"], + ["Porto do Mangue", "2410256"], + ["Pureza", "2410405"], + ["Rafael Fernandes", "2410504"], + ["Rafael Godeiro", "2410603"], + ["Riacho da Cruz", "2410702"], + ["Riacho de Santana", "2410801"], + ["Riachuelo", "2410900"], + ["Rio do Fogo", "2408953"], + ["Rodolfo Fernandes", "2411007"], + ["Ruy Barbosa", "2411106"], + ["Santa Cruz", "2411205"], + ["Santa Maria", "2409332"], + ["Santana do Matos", "2411403"], + ["Santana do Seridó", "2411429"], + ["Santo Antônio", "2411502"], + ["São Bento do Norte", "2411601"], + ["São Bento do Trairí", "2411700"], + ["São Fernando", "2411809"], + ["São Francisco do Oeste", "2411908"], + ["São Gonçalo do Amarante", "2412005"], + ["São João do Sabugi", "2412104"], + ["São José de Mipibu", "2412203"], + ["São José do Campestre", "2412302"], + ["São José do Seridó", "2412401"], + ["São Miguel", "2412500"], + ["São Miguel do Gostoso", "2412559"], + ["São Paulo do Potengi", "2412609"], + ["São Pedro", "2412708"], + ["São Rafael", "2412807"], + ["São Tomé", "2412906"], + ["São Vicente", "2413003"], + ["Senador Elói de Souza", "2413102"], + ["Senador Georgino Avelino", "2413201"], + ["Serra Caiada", "2410306"], + ["Serra de São Bento", "2413300"], + ["Serra do Mel", "2413359"], + ["Serra Negra do Norte", "2413409"], + ["Serrinha", "2413508"], + ["Serrinha dos Pintos", "2413557"], + ["Severiano Melo", "2413607"], + ["Sítio Novo", "2413706"], + ["Taboleiro Grande", "2413805"], + ["Taipu", "2413904"], + ["Tangará", "2414001"], + ["Tenente Ananias", "2414100"], + ["Tenente Laurentino Cruz", "2414159"], + ["Tibau", "2411056"], + ["Tibau do Sul", "2414209"], + ["Timbaúba dos Batistas", "2414308"], + ["Touros", "2414407"], + ["Triunfo Potiguar", "2414456"], + ["Umarizal", "2414506"], + ["Upanema", "2414605"], + ["Várzea", "2414704"], + ["Venha-Ver", "2414753"], + ["Vera Cruz", "2414803"], + ["Viçosa", "2414902"], + ["Vila Flor", "2415008"], ], RO: [ - "Alta Floresta D'Oeste", - "Alto Alegre dos Parecis", - "Alto Paraíso", - "Alvorada D'Oeste", - "Ariquemes", - "Buritis", - "Cabixi", - "Cacaulândia", - "Cacoal", - "Campo Novo de Rondônia", - "Candeias do Jamari", - "Castanheiras", - "Cerejeiras", - "Chupinguaia", - "Colorado do Oeste", - "Corumbiara", - "Costa Marques", - "Cujubim", - "Espigão D'Oeste", - "Governador Jorge Teixeira", - "Guajará-Mirim", - "Itapuã do Oeste", - "Jaru", - "Ji-Paraná", - "Machadinho D'Oeste", - "Ministro Andreazza", - "Mirante da Serra", - "Monte Negro", - "Nova Brasilândia D'Oeste", - "Nova Mamoré", - "Nova União", - "Novo Horizonte do Oeste", - "Ouro Preto do Oeste", - "Parecis", - "Pimenta Bueno", - "Pimenteiras do Oeste", - "Porto Velho", - "Presidente Médici", - "Primavera de Rondônia", - "Rio Crespo", - "Rolim de Moura", - "Santa Luzia D'Oeste", - "São Felipe D'Oeste", - "São Francisco do Guaporé", - "São Miguel do Guaporé", - "Seringueiras", - "Teixeirópolis", - "Theobroma", - "Urupá", - "Vale do Anari", - "Vale do Paraíso", - "Vilhena", + ["Alta Floresta D'Oeste", "1100015"], + ["Alto Alegre dos Parecis", "1100379"], + ["Alto Paraíso", "1100403"], + ["Alvorada D'Oeste", "1100346"], + ["Ariquemes", "1100023"], + ["Buritis", "1100452"], + ["Cabixi", "1100031"], + ["Cacaulândia", "1100601"], + ["Cacoal", "1100049"], + ["Campo Novo de Rondônia", "1100700"], + ["Candeias do Jamari", "1100809"], + ["Castanheiras", "1100908"], + ["Cerejeiras", "1100056"], + ["Chupinguaia", "1100924"], + ["Colorado do Oeste", "1100064"], + ["Corumbiara", "1100072"], + ["Costa Marques", "1100080"], + ["Cujubim", "1100940"], + ["Espigão D'Oeste", "1100098"], + ["Governador Jorge Teixeira", "1101005"], + ["Guajará-Mirim", "1100106"], + ["Itapuã do Oeste", "1101104"], + ["Jaru", "1100114"], + ["Ji-Paraná", "1100122"], + ["Machadinho D'Oeste", "1100130"], + ["Ministro Andreazza", "1101203"], + ["Mirante da Serra", "1101302"], + ["Monte Negro", "1101401"], + ["Nova Brasilândia D'Oeste", "1100148"], + ["Nova Mamoré", "1100338"], + ["Nova União", "1101435"], + ["Novo Horizonte do Oeste", "1100502"], + ["Ouro Preto do Oeste", "1100155"], + ["Parecis", "1101450"], + ["Pimenta Bueno", "1100189"], + ["Pimenteiras do Oeste", "1101468"], + ["Porto Velho", "1100205"], + ["Presidente Médici", "1100254"], + ["Primavera de Rondônia", "1101476"], + ["Rio Crespo", "1100262"], + ["Rolim de Moura", "1100288"], + ["Santa Luzia D'Oeste", "1100296"], + ["São Felipe D'Oeste", "1101484"], + ["São Francisco do Guaporé", "1101492"], + ["São Miguel do Guaporé", "1100320"], + ["Seringueiras", "1101500"], + ["Teixeirópolis", "1101559"], + ["Theobroma", "1101609"], + ["Urupá", "1101708"], + ["Vale do Anari", "1101757"], + ["Vale do Paraíso", "1101807"], + ["Vilhena", "1100304"], ], RR: [ - "Alto Alegre", - "Amajari", - "Boa Vista", - "Bonfim", - "Cantá", - "Caracaraí", - "Caroebe", - "Iracema", - "Mucajaí", - "Normandia", - "Pacaraima", - "Rorainópolis", - "São João da Baliza", - "São Luiz do Anauá", - "Uiramutã", + ["Alto Alegre", "1400050"], + ["Amajari", "1400027"], + ["Boa Vista", "1400100"], + ["Bonfim", "1400159"], + ["Cantá", "1400175"], + ["Caracaraí", "1400209"], + ["Caroebe", "1400233"], + ["Iracema", "1400282"], + ["Mucajaí", "1400308"], + ["Normandia", "1400407"], + ["Pacaraima", "1400456"], + ["Rorainópolis", "1400472"], + ["São João da Baliza", "1400506"], + ["São Luiz do Anauá", "1400605"], + ["Uiramutã", "1400704"], ], RS: [ - "Aceguá", - "Água Santa", - "Agudo", - "Ajuricaba", - "Alecrim", - "Alegrete", - "Alegria", - "Almirante Tamandaré do Sul", - "Alpestre", - "Alto Alegre", - "Alto Feliz", - "Alvorada", - "Amaral Ferrador", - "Ametista do Sul", - "André da Rocha", - "Anta Gorda", - "Antônio Prado", - "Arambaré", - "Araricá", - "Aratiba", - "Arroio do Meio", - "Arroio do Padre", - "Arroio do Sal", - "Arroio do Tigre", - "Arroio dos Ratos", - "Arroio Grande", - "Arvorezinha", - "Augusto Pestana", - "Áurea", - "Bagé", - "Balneário Pinhal", - "Barão", - "Barão de Cotegipe", - "Barão do Triunfo", - "Barra do Guarita", - "Barra do Quaraí", - "Barra do Ribeiro", - "Barra do Rio Azul", - "Barra Funda", - "Barracão", - "Barros Cassal", - "Benjamin Constant do Sul", - "Bento Gonçalves", - "Boa Vista das Missões", - "Boa Vista do Buricá", - "Boa Vista do Cadeado", - "Boa Vista do Incra", - "Boa Vista do Sul", - "Bom Jesus", - "Bom Princípio", - "Bom Progresso", - "Bom Retiro do Sul", - "Boqueirão do Leão", - "Bossoroca", - "Bozano", - "Braga", - "Brochier", - "Butiá", - "Caçapava do Sul", - "Cacequi", - "Cachoeira do Sul", - "Cachoeirinha", - "Cacique Doble", - "Caibaté", - "Caiçara", - "Camaquã", - "Camargo", - "Cambará do Sul", - "Campestre da Serra", - "Campina das Missões", - "Campinas do Sul", - "Campo Bom", - "Campo Novo", - "Campos Borges", - "Candelária", - "Cândido Godói", - "Candiota", - "Canela", - "Canguçu", - "Canoas", - "Canudos do Vale", - "Capão Bonito do Sul", - "Capão da Canoa", - "Capão do Cipó", - "Capão do Leão", - "Capela de Santana", - "Capitão", - "Capivari do Sul", - "Caraá", - "Carazinho", - "Carlos Barbosa", - "Carlos Gomes", - "Casca", - "Caseiros", - "Catuípe", - "Caxias do Sul", - "Centenário", - "Cerrito", - "Cerro Branco", - "Cerro Grande", - "Cerro Grande do Sul", - "Cerro Largo", - "Chapada", - "Charqueadas", - "Charrua", - "Chiapetta", - "Chuí", - "Chuvisca", - "Cidreira", - "Ciríaco", - "Colinas", - "Colorado", - "Condor", - "Constantina", - "Coqueiro Baixo", - "Coqueiros do Sul", - "Coronel Barros", - "Coronel Bicaco", - "Coronel Pilar", - "Cotiporã", - "Coxilha", - "Crissiumal", - "Cristal", - "Cristal do Sul", - "Cruz Alta", - "Cruzaltense", - "Cruzeiro do Sul", - "David Canabarro", - "Derrubadas", - "Dezesseis de Novembro", - "Dilermando de Aguiar", - "Dois Irmãos", - "Dois Irmãos das Missões", - "Dois Lajeados", - "Dom Feliciano", - "Dom Pedrito", - "Dom Pedro de Alcântara", - "Dona Francisca", - "Doutor Maurício Cardoso", - "Doutor Ricardo", - "Eldorado do Sul", - "Encantado", - "Encruzilhada do Sul", - "Engenho Velho", - "Entre Rios do Sul", - "Entre-Ijuís", - "Erebango", - "Erechim", - "Ernestina", - "Erval Grande", - "Erval Seco", - "Esmeralda", - "Esperança do Sul", - "Espumoso", - "Estação", - "Estância Velha", - "Esteio", - "Estrela", - "Estrela Velha", - "Eugênio de Castro", - "Fagundes Varela", - "Farroupilha", - "Faxinal do Soturno", - "Faxinalzinho", - "Fazenda Vilanova", - "Feliz", - "Flores da Cunha", - "Floriano Peixoto", - "Fontoura Xavier", - "Formigueiro", - "Forquetinha", - "Fortaleza dos Valos", - "Frederico Westphalen", - "Garibaldi", - "Garruchos", - "Gaurama", - "General Câmara", - "Gentil", - "Getúlio Vargas", - "Giruá", - "Glorinha", - "Gramado", - "Gramado dos Loureiros", - "Gramado Xavier", - "Gravataí", - "Guabiju", - "Guaíba", - "Guaporé", - "Guarani das Missões", - "Harmonia", - "Herval", - "Herveiras", - "Horizontina", - "Hulha Negra", - "Humaitá", - "Ibarama", - "Ibiaçá", - "Ibiraiaras", - "Ibirapuitã", - "Ibirubá", - "Igrejinha", - "Ijuí", - "Ilópolis", - "Imbé", - "Imigrante", - "Independência", - "Inhacorá", - "Ipê", - "Ipiranga do Sul", - "Iraí", - "Itaara", - "Itacurubi", - "Itapuca", - "Itaqui", - "Itati", - "Itatiba do Sul", - "Ivorá", - "Ivoti", - "Jaboticaba", - "Jacuizinho", - "Jacutinga", - "Jaguarão", - "Jaguari", - "Jaquirana", - "Jari", - "Jóia", - "Júlio de Castilhos", - "Lagoa Bonita do Sul", - "Lagoa dos Três Cantos", - "Lagoa Vermelha", - "Lagoão", - "Lajeado", - "Lajeado do Bugre", - "Lavras do Sul", - "Liberato Salzano", - "Lindolfo Collor", - "Linha Nova", - "Maçambará", - "Machadinho", - "Mampituba", - "Manoel Viana", - "Maquiné", - "Maratá", - "Marau", - "Marcelino Ramos", - "Mariana Pimentel", - "Mariano Moro", - "Marques de Souza", - "Mata", - "Mato Castelhano", - "Mato Leitão", - "Mato Queimado", - "Maximiliano de Almeida", - "Minas do Leão", - "Miraguaí", - "Montauri", - "Monte Alegre dos Campos", - "Monte Belo do Sul", - "Montenegro", - "Mormaço", - "Morrinhos do Sul", - "Morro Redondo", - "Morro Reuter", - "Mostardas", - "Muçum", - "Muitos Capões", - "Muliterno", - "Não-Me-Toque", - "Nicolau Vergueiro", - "Nonoai", - "Nova Alvorada", - "Nova Araçá", - "Nova Bassano", - "Nova Boa Vista", - "Nova Bréscia", - "Nova Candelária", - "Nova Esperança do Sul", - "Nova Hartz", - "Nova Pádua", - "Nova Palma", - "Nova Petrópolis", - "Nova Prata", - "Nova Ramada", - "Nova Roma do Sul", - "Nova Santa Rita", - "Novo Barreiro", - "Novo Cabrais", - "Novo Hamburgo", - "Novo Machado", - "Novo Tiradentes", - "Novo Xingu", - "Osório", - "Paim Filho", - "Palmares do Sul", - "Palmeira das Missões", - "Palmitinho", - "Panambi", - "Pantano Grande", - "Paraí", - "Paraíso do Sul", - "Pareci Novo", - "Parobé", - "Passa Sete", - "Passo do Sobrado", - "Passo Fundo", - "Paulo Bento", - "Paverama", - "Pedras Altas", - "Pedro Osório", - "Pejuçara", - "Pelotas", - "Picada Café", - "Pinhal", - "Pinhal da Serra", - "Pinhal Grande", - "Pinheirinho do Vale", - "Pinheiro Machado", - "Pinto Bandeira", - "Pirapó", - "Piratini", - "Planalto", - "Poço das Antas", - "Pontão", - "Ponte Preta", - "Portão", - "Porto Alegre", - "Porto Lucena", - "Porto Mauá", - "Porto Vera Cruz", - "Porto Xavier", - "Pouso Novo", - "Presidente Lucena", - "Progresso", - "Protásio Alves", - "Putinga", - "Quaraí", - "Quatro Irmãos", - "Quevedos", - "Quinze de Novembro", - "Redentora", - "Relvado", - "Restinga Sêca", - "Rio dos Índios", - "Rio Grande", - "Rio Pardo", - "Riozinho", - "Roca Sales", - "Rodeio Bonito", - "Rolador", - "Rolante", - "Ronda Alta", - "Rondinha", - "Roque Gonzales", - "Rosário do Sul", - "Sagrada Família", - "Saldanha Marinho", - "Salto do Jacuí", - "Salvador das Missões", - "Salvador do Sul", - "Sananduva", - "Sant'Ana do Livramento", - "Santa Bárbara do Sul", - "Santa Cecília do Sul", - "Santa Clara do Sul", - "Santa Cruz do Sul", - "Santa Margarida do Sul", - "Santa Maria", - "Santa Maria do Herval", - "Santa Rosa", - "Santa Tereza", - "Santa Vitória do Palmar", - "Santana da Boa Vista", - "Santiago", - "Santo Ângelo", - "Santo Antônio da Patrulha", - "Santo Antônio das Missões", - "Santo Antônio do Palma", - "Santo Antônio do Planalto", - "Santo Augusto", - "Santo Cristo", - "Santo Expedito do Sul", - "São Borja", - "São Domingos do Sul", - "São Francisco de Assis", - "São Francisco de Paula", - "São Gabriel", - "São Jerônimo", - "São João da Urtiga", - "São João do Polêsine", - "São Jorge", - "São José das Missões", - "São José do Herval", - "São José do Hortêncio", - "São José do Inhacorá", - "São José do Norte", - "São José do Ouro", - "São José do Sul", - "São José dos Ausentes", - "São Leopoldo", - "São Lourenço do Sul", - "São Luiz Gonzaga", - "São Marcos", - "São Martinho", - "São Martinho da Serra", - "São Miguel das Missões", - "São Nicolau", - "São Paulo das Missões", - "São Pedro da Serra", - "São Pedro das Missões", - "São Pedro do Butiá", - "São Pedro do Sul", - "São Sebastião do Caí", - "São Sepé", - "São Valentim", - "São Valentim do Sul", - "São Valério do Sul", - "São Vendelino", - "São Vicente do Sul", - "Sapiranga", - "Sapucaia do Sul", - "Sarandi", - "Seberi", - "Sede Nova", - "Segredo", - "Selbach", - "Senador Salgado Filho", - "Sentinela do Sul", - "Serafina Corrêa", - "Sério", - "Sertão", - "Sertão Santana", - "Sete de Setembro", - "Severiano de Almeida", - "Silveira Martins", - "Sinimbu", - "Sobradinho", - "Soledade", - "Tabaí", - "Tapejara", - "Tapera", - "Tapes", - "Taquara", - "Taquari", - "Taquaruçu do Sul", - "Tavares", - "Tenente Portela", - "Terra de Areia", - "Teutônia", - "Tio Hugo", - "Tiradentes do Sul", - "Toropi", - "Torres", - "Tramandaí", - "Travesseiro", - "Três Arroios", - "Três Cachoeiras", - "Três Coroas", - "Três de Maio", - "Três Forquilhas", - "Três Palmeiras", - "Três Passos", - "Trindade do Sul", - "Triunfo", - "Tucunduva", - "Tunas", - "Tupanci do Sul", - "Tupanciretã", - "Tupandi", - "Tuparendi", - "Turuçu", - "Ubiretama", - "União da Serra", - "Unistalda", - "Uruguaiana", - "Vacaria", - "Vale do Sol", - "Vale Real", - "Vale Verde", - "Vanini", - "Venâncio Aires", - "Vera Cruz", - "Veranópolis", - "Vespasiano Corrêa", - "Viadutos", - "Viamão", - "Vicente Dutra", - "Victor Graeff", - "Vila Flores", - "Vila Lângaro", - "Vila Maria", - "Vila Nova do Sul", - "Vista Alegre", - "Vista Alegre do Prata", - "Vista Gaúcha", - "Vitória das Missões", - "Westfália", - "Xangri-lá", + ["Aceguá", "4300034"], + ["Água Santa", "4300059"], + ["Agudo", "4300109"], + ["Ajuricaba", "4300208"], + ["Alecrim", "4300307"], + ["Alegrete", "4300406"], + ["Alegria", "4300455"], + ["Almirante Tamandaré do Sul", "4300471"], + ["Alpestre", "4300505"], + ["Alto Alegre", "4300554"], + ["Alto Feliz", "4300570"], + ["Alvorada", "4300604"], + ["Amaral Ferrador", "4300638"], + ["Ametista do Sul", "4300646"], + ["André da Rocha", "4300661"], + ["Anta Gorda", "4300703"], + ["Antônio Prado", "4300802"], + ["Arambaré", "4300851"], + ["Araricá", "4300877"], + ["Aratiba", "4300901"], + ["Arroio do Meio", "4301008"], + ["Arroio do Padre", "4301073"], + ["Arroio do Sal", "4301057"], + ["Arroio do Tigre", "4301206"], + ["Arroio dos Ratos", "4301107"], + ["Arroio Grande", "4301305"], + ["Arvorezinha", "4301404"], + ["Augusto Pestana", "4301503"], + ["Áurea", "4301552"], + ["Bagé", "4301602"], + ["Balneário Pinhal", "4301636"], + ["Barão", "4301651"], + ["Barão de Cotegipe", "4301701"], + ["Barão do Triunfo", "4301750"], + ["Barra do Guarita", "4301859"], + ["Barra do Quaraí", "4301875"], + ["Barra do Ribeiro", "4301909"], + ["Barra do Rio Azul", "4301925"], + ["Barra Funda", "4301958"], + ["Barracão", "4301800"], + ["Barros Cassal", "4302006"], + ["Benjamin Constant do Sul", "4302055"], + ["Bento Gonçalves", "4302105"], + ["Boa Vista das Missões", "4302154"], + ["Boa Vista do Buricá", "4302204"], + ["Boa Vista do Cadeado", "4302220"], + ["Boa Vista do Incra", "4302238"], + ["Boa Vista do Sul", "4302253"], + ["Bom Jesus", "4302303"], + ["Bom Princípio", "4302352"], + ["Bom Progresso", "4302378"], + ["Bom Retiro do Sul", "4302402"], + ["Boqueirão do Leão", "4302451"], + ["Bossoroca", "4302501"], + ["Bozano", "4302584"], + ["Braga", "4302600"], + ["Brochier", "4302659"], + ["Butiá", "4302709"], + ["Caçapava do Sul", "4302808"], + ["Cacequi", "4302907"], + ["Cachoeira do Sul", "4303004"], + ["Cachoeirinha", "4303103"], + ["Cacique Doble", "4303202"], + ["Caibaté", "4303301"], + ["Caiçara", "4303400"], + ["Camaquã", "4303509"], + ["Camargo", "4303558"], + ["Cambará do Sul", "4303608"], + ["Campestre da Serra", "4303673"], + ["Campina das Missões", "4303707"], + ["Campinas do Sul", "4303806"], + ["Campo Bom", "4303905"], + ["Campo Novo", "4304002"], + ["Campos Borges", "4304101"], + ["Candelária", "4304200"], + ["Cândido Godói", "4304309"], + ["Candiota", "4304358"], + ["Canela", "4304408"], + ["Canguçu", "4304507"], + ["Canoas", "4304606"], + ["Canudos do Vale", "4304614"], + ["Capão Bonito do Sul", "4304622"], + ["Capão da Canoa", "4304630"], + ["Capão do Cipó", "4304655"], + ["Capão do Leão", "4304663"], + ["Capela de Santana", "4304689"], + ["Capitão", "4304697"], + ["Capivari do Sul", "4304671"], + ["Caraá", "4304713"], + ["Carazinho", "4304705"], + ["Carlos Barbosa", "4304804"], + ["Carlos Gomes", "4304853"], + ["Casca", "4304903"], + ["Caseiros", "4304952"], + ["Catuípe", "4305009"], + ["Caxias do Sul", "4305108"], + ["Centenário", "4305116"], + ["Cerrito", "4305124"], + ["Cerro Branco", "4305132"], + ["Cerro Grande", "4305157"], + ["Cerro Grande do Sul", "4305173"], + ["Cerro Largo", "4305207"], + ["Chapada", "4305306"], + ["Charqueadas", "4305355"], + ["Charrua", "4305371"], + ["Chiapetta", "4305405"], + ["Chuí", "4305439"], + ["Chuvisca", "4305447"], + ["Cidreira", "4305454"], + ["Ciríaco", "4305504"], + ["Colinas", "4305587"], + ["Colorado", "4305603"], + ["Condor", "4305702"], + ["Constantina", "4305801"], + ["Coqueiro Baixo", "4305835"], + ["Coqueiros do Sul", "4305850"], + ["Coronel Barros", "4305871"], + ["Coronel Bicaco", "4305900"], + ["Coronel Pilar", "4305934"], + ["Cotiporã", "4305959"], + ["Coxilha", "4305975"], + ["Crissiumal", "4306007"], + ["Cristal", "4306056"], + ["Cristal do Sul", "4306072"], + ["Cruz Alta", "4306106"], + ["Cruzaltense", "4306130"], + ["Cruzeiro do Sul", "4306205"], + ["David Canabarro", "4306304"], + ["Derrubadas", "4306320"], + ["Dezesseis de Novembro", "4306353"], + ["Dilermando de Aguiar", "4306379"], + ["Dois Irmãos", "4306403"], + ["Dois Irmãos das Missões", "4306429"], + ["Dois Lajeados", "4306452"], + ["Dom Feliciano", "4306502"], + ["Dom Pedrito", "4306601"], + ["Dom Pedro de Alcântara", "4306551"], + ["Dona Francisca", "4306700"], + ["Doutor Maurício Cardoso", "4306734"], + ["Doutor Ricardo", "4306759"], + ["Eldorado do Sul", "4306767"], + ["Encantado", "4306809"], + ["Encruzilhada do Sul", "4306908"], + ["Engenho Velho", "4306924"], + ["Entre Rios do Sul", "4306957"], + ["Entre-Ijuís", "4306932"], + ["Erebango", "4306973"], + ["Erechim", "4307005"], + ["Ernestina", "4307054"], + ["Erval Grande", "4307203"], + ["Erval Seco", "4307302"], + ["Esmeralda", "4307401"], + ["Esperança do Sul", "4307450"], + ["Espumoso", "4307500"], + ["Estação", "4307559"], + ["Estância Velha", "4307609"], + ["Esteio", "4307708"], + ["Estrela", "4307807"], + ["Estrela Velha", "4307815"], + ["Eugênio de Castro", "4307831"], + ["Fagundes Varela", "4307864"], + ["Farroupilha", "4307906"], + ["Faxinal do Soturno", "4308003"], + ["Faxinalzinho", "4308052"], + ["Fazenda Vilanova", "4308078"], + ["Feliz", "4308102"], + ["Flores da Cunha", "4308201"], + ["Floriano Peixoto", "4308250"], + ["Fontoura Xavier", "4308300"], + ["Formigueiro", "4308409"], + ["Forquetinha", "4308433"], + ["Fortaleza dos Valos", "4308458"], + ["Frederico Westphalen", "4308508"], + ["Garibaldi", "4308607"], + ["Garruchos", "4308656"], + ["Gaurama", "4308706"], + ["General Câmara", "4308805"], + ["Gentil", "4308854"], + ["Getúlio Vargas", "4308904"], + ["Giruá", "4309001"], + ["Glorinha", "4309050"], + ["Gramado", "4309100"], + ["Gramado dos Loureiros", "4309126"], + ["Gramado Xavier", "4309159"], + ["Gravataí", "4309209"], + ["Guabiju", "4309258"], + ["Guaíba", "4309308"], + ["Guaporé", "4309407"], + ["Guarani das Missões", "4309506"], + ["Harmonia", "4309555"], + ["Herval", "4307104"], + ["Herveiras", "4309571"], + ["Horizontina", "4309605"], + ["Hulha Negra", "4309654"], + ["Humaitá", "4309704"], + ["Ibarama", "4309753"], + ["Ibiaçá", "4309803"], + ["Ibiraiaras", "4309902"], + ["Ibirapuitã", "4309951"], + ["Ibirubá", "4310009"], + ["Igrejinha", "4310108"], + ["Ijuí", "4310207"], + ["Ilópolis", "4310306"], + ["Imbé", "4310330"], + ["Imigrante", "4310363"], + ["Independência", "4310405"], + ["Inhacorá", "4310413"], + ["Ipê", "4310439"], + ["Ipiranga do Sul", "4310462"], + ["Iraí", "4310504"], + ["Itaara", "4310538"], + ["Itacurubi", "4310553"], + ["Itapuca", "4310579"], + ["Itaqui", "4310603"], + ["Itati", "4310652"], + ["Itatiba do Sul", "4310702"], + ["Ivorá", "4310751"], + ["Ivoti", "4310801"], + ["Jaboticaba", "4310850"], + ["Jacuizinho", "4310876"], + ["Jacutinga", "4310900"], + ["Jaguarão", "4311007"], + ["Jaguari", "4311106"], + ["Jaquirana", "4311122"], + ["Jari", "4311130"], + ["Jóia", "4311155"], + ["Júlio de Castilhos", "4311205"], + ["Lagoa Bonita do Sul", "4311239"], + ["Lagoa dos Três Cantos", "4311270"], + ["Lagoa Vermelha", "4311304"], + ["Lagoão", "4311254"], + ["Lajeado", "4311403"], + ["Lajeado do Bugre", "4311429"], + ["Lavras do Sul", "4311502"], + ["Liberato Salzano", "4311601"], + ["Lindolfo Collor", "4311627"], + ["Linha Nova", "4311643"], + ["Maçambará", "4311718"], + ["Machadinho", "4311700"], + ["Mampituba", "4311734"], + ["Manoel Viana", "4311759"], + ["Maquiné", "4311775"], + ["Maratá", "4311791"], + ["Marau", "4311809"], + ["Marcelino Ramos", "4311908"], + ["Mariana Pimentel", "4311981"], + ["Mariano Moro", "4312005"], + ["Marques de Souza", "4312054"], + ["Mata", "4312104"], + ["Mato Castelhano", "4312138"], + ["Mato Leitão", "4312153"], + ["Mato Queimado", "4312179"], + ["Maximiliano de Almeida", "4312203"], + ["Minas do Leão", "4312252"], + ["Miraguaí", "4312302"], + ["Montauri", "4312351"], + ["Monte Alegre dos Campos", "4312377"], + ["Monte Belo do Sul", "4312385"], + ["Montenegro", "4312401"], + ["Mormaço", "4312427"], + ["Morrinhos do Sul", "4312443"], + ["Morro Redondo", "4312450"], + ["Morro Reuter", "4312476"], + ["Mostardas", "4312500"], + ["Muçum", "4312609"], + ["Muitos Capões", "4312617"], + ["Muliterno", "4312625"], + ["Não-Me-Toque", "4312658"], + ["Nicolau Vergueiro", "4312674"], + ["Nonoai", "4312708"], + ["Nova Alvorada", "4312757"], + ["Nova Araçá", "4312807"], + ["Nova Bassano", "4312906"], + ["Nova Boa Vista", "4312955"], + ["Nova Bréscia", "4313003"], + ["Nova Candelária", "4313011"], + ["Nova Esperança do Sul", "4313037"], + ["Nova Hartz", "4313060"], + ["Nova Pádua", "4313086"], + ["Nova Palma", "4313102"], + ["Nova Petrópolis", "4313201"], + ["Nova Prata", "4313300"], + ["Nova Ramada", "4313334"], + ["Nova Roma do Sul", "4313359"], + ["Nova Santa Rita", "4313375"], + ["Novo Barreiro", "4313490"], + ["Novo Cabrais", "4313391"], + ["Novo Hamburgo", "4313409"], + ["Novo Machado", "4313425"], + ["Novo Tiradentes", "4313441"], + ["Novo Xingu", "4313466"], + ["Osório", "4313508"], + ["Paim Filho", "4313607"], + ["Palmares do Sul", "4313656"], + ["Palmeira das Missões", "4313706"], + ["Palmitinho", "4313805"], + ["Panambi", "4313904"], + ["Pantano Grande", "4313953"], + ["Paraí", "4314001"], + ["Paraíso do Sul", "4314027"], + ["Pareci Novo", "4314035"], + ["Parobé", "4314050"], + ["Passa Sete", "4314068"], + ["Passo do Sobrado", "4314076"], + ["Passo Fundo", "4314100"], + ["Paulo Bento", "4314134"], + ["Paverama", "4314159"], + ["Pedras Altas", "4314175"], + ["Pedro Osório", "4314209"], + ["Pejuçara", "4314308"], + ["Pelotas", "4314407"], + ["Picada Café", "4314423"], + ["Pinhal", "4314456"], + ["Pinhal da Serra", "4314464"], + ["Pinhal Grande", "4314472"], + ["Pinheirinho do Vale", "4314498"], + ["Pinheiro Machado", "4314506"], + ["Pinto Bandeira", "4314548"], + ["Pirapó", "4314555"], + ["Piratini", "4314605"], + ["Planalto", "4314704"], + ["Poço das Antas", "4314753"], + ["Pontão", "4314779"], + ["Ponte Preta", "4314787"], + ["Portão", "4314803"], + ["Porto Alegre", "4314902"], + ["Porto Lucena", "4315008"], + ["Porto Mauá", "4315057"], + ["Porto Vera Cruz", "4315073"], + ["Porto Xavier", "4315107"], + ["Pouso Novo", "4315131"], + ["Presidente Lucena", "4315149"], + ["Progresso", "4315156"], + ["Protásio Alves", "4315172"], + ["Putinga", "4315206"], + ["Quaraí", "4315305"], + ["Quatro Irmãos", "4315313"], + ["Quevedos", "4315321"], + ["Quinze de Novembro", "4315354"], + ["Redentora", "4315404"], + ["Relvado", "4315453"], + ["Restinga Sêca", "4315503"], + ["Rio dos Índios", "4315552"], + ["Rio Grande", "4315602"], + ["Rio Pardo", "4315701"], + ["Riozinho", "4315750"], + ["Roca Sales", "4315800"], + ["Rodeio Bonito", "4315909"], + ["Rolador", "4315958"], + ["Rolante", "4316006"], + ["Ronda Alta", "4316105"], + ["Rondinha", "4316204"], + ["Roque Gonzales", "4316303"], + ["Rosário do Sul", "4316402"], + ["Sagrada Família", "4316428"], + ["Saldanha Marinho", "4316436"], + ["Salto do Jacuí", "4316451"], + ["Salvador das Missões", "4316477"], + ["Salvador do Sul", "4316501"], + ["Sananduva", "4316600"], + ["Sant'Ana do Livramento", "4317103"], + ["Santa Bárbara do Sul", "4316709"], + ["Santa Cecília do Sul", "4316733"], + ["Santa Clara do Sul", "4316758"], + ["Santa Cruz do Sul", "4316808"], + ["Santa Margarida do Sul", "4316972"], + ["Santa Maria", "4316907"], + ["Santa Maria do Herval", "4316956"], + ["Santa Rosa", "4317202"], + ["Santa Tereza", "4317251"], + ["Santa Vitória do Palmar", "4317301"], + ["Santana da Boa Vista", "4317004"], + ["Santiago", "4317400"], + ["Santo Ângelo", "4317509"], + ["Santo Antônio da Patrulha", "4317608"], + ["Santo Antônio das Missões", "4317707"], + ["Santo Antônio do Palma", "4317558"], + ["Santo Antônio do Planalto", "4317756"], + ["Santo Augusto", "4317806"], + ["Santo Cristo", "4317905"], + ["Santo Expedito do Sul", "4317954"], + ["São Borja", "4318002"], + ["São Domingos do Sul", "4318051"], + ["São Francisco de Assis", "4318101"], + ["São Francisco de Paula", "4318200"], + ["São Gabriel", "4318309"], + ["São Jerônimo", "4318408"], + ["São João da Urtiga", "4318424"], + ["São João do Polêsine", "4318432"], + ["São Jorge", "4318440"], + ["São José das Missões", "4318457"], + ["São José do Herval", "4318465"], + ["São José do Hortêncio", "4318481"], + ["São José do Inhacorá", "4318499"], + ["São José do Norte", "4318507"], + ["São José do Ouro", "4318606"], + ["São José do Sul", "4318614"], + ["São José dos Ausentes", "4318622"], + ["São Leopoldo", "4318705"], + ["São Lourenço do Sul", "4318804"], + ["São Luiz Gonzaga", "4318903"], + ["São Marcos", "4319000"], + ["São Martinho", "4319109"], + ["São Martinho da Serra", "4319125"], + ["São Miguel das Missões", "4319158"], + ["São Nicolau", "4319208"], + ["São Paulo das Missões", "4319307"], + ["São Pedro da Serra", "4319356"], + ["São Pedro das Missões", "4319364"], + ["São Pedro do Butiá", "4319372"], + ["São Pedro do Sul", "4319406"], + ["São Sebastião do Caí", "4319505"], + ["São Sepé", "4319604"], + ["São Valentim", "4319703"], + ["São Valentim do Sul", "4319711"], + ["São Valério do Sul", "4319737"], + ["São Vendelino", "4319752"], + ["São Vicente do Sul", "4319802"], + ["Sapiranga", "4319901"], + ["Sapucaia do Sul", "4320008"], + ["Sarandi", "4320107"], + ["Seberi", "4320206"], + ["Sede Nova", "4320230"], + ["Segredo", "4320263"], + ["Selbach", "4320305"], + ["Senador Salgado Filho", "4320321"], + ["Sentinela do Sul", "4320354"], + ["Serafina Corrêa", "4320404"], + ["Sério", "4320453"], + ["Sertão", "4320503"], + ["Sertão Santana", "4320552"], + ["Sete de Setembro", "4320578"], + ["Severiano de Almeida", "4320602"], + ["Silveira Martins", "4320651"], + ["Sinimbu", "4320677"], + ["Sobradinho", "4320701"], + ["Soledade", "4320800"], + ["Tabaí", "4320859"], + ["Tapejara", "4320909"], + ["Tapera", "4321006"], + ["Tapes", "4321105"], + ["Taquara", "4321204"], + ["Taquari", "4321303"], + ["Taquaruçu do Sul", "4321329"], + ["Tavares", "4321352"], + ["Tenente Portela", "4321402"], + ["Terra de Areia", "4321436"], + ["Teutônia", "4321451"], + ["Tio Hugo", "4321469"], + ["Tiradentes do Sul", "4321477"], + ["Toropi", "4321493"], + ["Torres", "4321501"], + ["Tramandaí", "4321600"], + ["Travesseiro", "4321626"], + ["Três Arroios", "4321634"], + ["Três Cachoeiras", "4321667"], + ["Três Coroas", "4321709"], + ["Três de Maio", "4321808"], + ["Três Forquilhas", "4321832"], + ["Três Palmeiras", "4321857"], + ["Três Passos", "4321907"], + ["Trindade do Sul", "4321956"], + ["Triunfo", "4322004"], + ["Tucunduva", "4322103"], + ["Tunas", "4322152"], + ["Tupanci do Sul", "4322186"], + ["Tupanciretã", "4322202"], + ["Tupandi", "4322251"], + ["Tuparendi", "4322301"], + ["Turuçu", "4322327"], + ["Ubiretama", "4322343"], + ["União da Serra", "4322350"], + ["Unistalda", "4322376"], + ["Uruguaiana", "4322400"], + ["Vacaria", "4322509"], + ["Vale do Sol", "4322533"], + ["Vale Real", "4322541"], + ["Vale Verde", "4322525"], + ["Vanini", "4322558"], + ["Venâncio Aires", "4322608"], + ["Vera Cruz", "4322707"], + ["Veranópolis", "4322806"], + ["Vespasiano Corrêa", "4322855"], + ["Viadutos", "4322905"], + ["Viamão", "4323002"], + ["Vicente Dutra", "4323101"], + ["Victor Graeff", "4323200"], + ["Vila Flores", "4323309"], + ["Vila Lângaro", "4323358"], + ["Vila Maria", "4323408"], + ["Vila Nova do Sul", "4323457"], + ["Vista Alegre", "4323507"], + ["Vista Alegre do Prata", "4323606"], + ["Vista Gaúcha", "4323705"], + ["Vitória das Missões", "4323754"], + ["Westfália", "4323770"], + ["Xangri-lá", "4323804"], ], SC: [ - "Abdon Batista", - "Abelardo Luz", - "Agrolândia", - "Agronômica", - "Água Doce", - "Águas de Chapecó", - "Águas Frias", - "Águas Mornas", - "Alfredo Wagner", - "Alto Bela Vista", - "Anchieta", - "Angelina", - "Anita Garibaldi", - "Anitápolis", - "Antônio Carlos", - "Apiúna", - "Arabutã", - "Araquari", - "Araranguá", - "Armazém", - "Arroio Trinta", - "Arvoredo", - "Ascurra", - "Atalanta", - "Aurora", - "Balneário Arroio do Silva", - "Balneário Barra do Sul", - "Balneário Camboriú", - "Balneário Gaivota", - "Balneário Piçarras", - "Balneário Rincão", - "Bandeirante", - "Barra Bonita", - "Barra Velha", - "Bela Vista do Toldo", - "Belmonte", - "Benedito Novo", - "Biguaçu", - "Blumenau", - "Bocaina do Sul", - "Bom Jardim da Serra", - "Bom Jesus", - "Bom Jesus do Oeste", - "Bom Retiro", - "Bombinhas", - "Botuverá", - "Braço do Norte", - "Braço do Trombudo", - "Brunópolis", - "Brusque", - "Caçador", - "Caibi", - "Calmon", - "Camboriú", - "Campo Alegre", - "Campo Belo do Sul", - "Campo Erê", - "Campos Novos", - "Canelinha", - "Canoinhas", - "Capão Alto", - "Capinzal", - "Capivari de Baixo", - "Catanduvas", - "Caxambu do Sul", - "Celso Ramos", - "Cerro Negro", - "Chapadão do Lageado", - "Chapecó", - "Cocal do Sul", - "Concórdia", - "Cordilheira Alta", - "Coronel Freitas", - "Coronel Martins", - "Correia Pinto", - "Corupá", - "Criciúma", - "Cunha Porã", - "Cunhataí", - "Curitibanos", - "Descanso", - "Dionísio Cerqueira", - "Dona Emma", - "Doutor Pedrinho", - "Entre Rios", - "Ermo", - "Erval Velho", - "Faxinal dos Guedes", - "Flor do Sertão", - "Florianópolis", - "Formosa do Sul", - "Forquilhinha", - "Fraiburgo", - "Frei Rogério", - "Galvão", - "Garopaba", - "Garuva", - "Gaspar", - "Governador Celso Ramos", - "Grão-Pará", - "Gravatal", - "Guabiruba", - "Guaraciaba", - "Guaramirim", - "Guarujá do Sul", - "Guatambú", - "Herval d'Oeste", - "Ibiam", - "Ibicaré", - "Ibirama", - "Içara", - "Ilhota", - "Imaruí", - "Imbituba", - "Imbuia", - "Indaial", - "Iomerê", - "Ipira", - "Iporã do Oeste", - "Ipuaçu", - "Ipumirim", - "Iraceminha", - "Irani", - "Irati", - "Irineópolis", - "Itá", - "Itaiópolis", - "Itajaí", - "Itapema", - "Itapiranga", - "Itapoá", - "Ituporanga", - "Jaborá", - "Jacinto Machado", - "Jaguaruna", - "Jaraguá do Sul", - "Jardinópolis", - "Joaçaba", - "Joinville", - "José Boiteux", - "Jupiá", - "Lacerdópolis", - "Lages", - "Laguna", - "Lajeado Grande", - "Laurentino", - "Lauro Müller", - "Lebon Régis", - "Leoberto Leal", - "Lindóia do Sul", - "Lontras", - "Luiz Alves", - "Luzerna", - "Macieira", - "Mafra", - "Major Gercino", - "Major Vieira", - "Maracajá", - "Maravilha", - "Marema", - "Massaranduba", - "Matos Costa", - "Meleiro", - "Mirim Doce", - "Modelo", - "Mondaí", - "Monte Carlo", - "Monte Castelo", - "Morro da Fumaça", - "Morro Grande", - "Navegantes", - "Nova Erechim", - "Nova Itaberaba", - "Nova Trento", - "Nova Veneza", - "Novo Horizonte", - "Orleans", - "Otacílio Costa", - "Ouro", - "Ouro Verde", - "Paial", - "Painel", - "Palhoça", - "Palma Sola", - "Palmeira", - "Palmitos", - "Papanduva", - "Paraíso", - "Passo de Torres", - "Passos Maia", - "Paulo Lopes", - "Pedras Grandes", - "Penha", - "Peritiba", - "Pescaria Brava", - "Petrolândia", - "Pinhalzinho", - "Pinheiro Preto", - "Piratuba", - "Planalto Alegre", - "Pomerode", - "Ponte Alta", - "Ponte Alta do Norte", - "Ponte Serrada", - "Porto Belo", - "Porto União", - "Pouso Redondo", - "Praia Grande", - "Presidente Castello Branco", - "Presidente Getúlio", - "Presidente Nereu", - "Princesa", - "Quilombo", - "Rancho Queimado", - "Rio das Antas", - "Rio do Campo", - "Rio do Oeste", - "Rio do Sul", - "Rio dos Cedros", - "Rio Fortuna", - "Rio Negrinho", - "Rio Rufino", - "Riqueza", - "Rodeio", - "Romelândia", - "Salete", - "Saltinho", - "Salto Veloso", - "Sangão", - "Santa Cecília", - "Santa Helena", - "Santa Rosa de Lima", - "Santa Rosa do Sul", - "Santa Terezinha", - "Santa Terezinha do Progresso", - "Santiago do Sul", - "Santo Amaro da Imperatriz", - "São Bento do Sul", - "São Bernardino", - "São Bonifácio", - "São Carlos", - "São Cristóvão do Sul", - "São Domingos", - "São Francisco do Sul", - "São João Batista", - "São João do Itaperiú", - "São João do Oeste", - "São João do Sul", - "São Joaquim", - "São José", - "São José do Cedro", - "São José do Cerrito", - "São Lourenço do Oeste", - "São Ludgero", - "São Martinho", - "São Miguel da Boa Vista", - "São Miguel do Oeste", - "São Pedro de Alcântara", - "Saudades", - "Schroeder", - "Seara", - "Serra Alta", - "Siderópolis", - "Sombrio", - "Sul Brasil", - "Taió", - "Tangará", - "Tigrinhos", - "Tijucas", - "Timbé do Sul", - "Timbó", - "Timbó Grande", - "Três Barras", - "Treviso", - "Treze de Maio", - "Treze Tílias", - "Trombudo Central", - "Tubarão", - "Tunápolis", - "Turvo", - "União do Oeste", - "Urubici", - "Urupema", - "Urussanga", - "Vargeão", - "Vargem", - "Vargem Bonita", - "Vidal Ramos", - "Videira", - "Vitor Meireles", - "Witmarsum", - "Xanxerê", - "Xavantina", - "Xaxim", - "Zortéa", + ["Abdon Batista", "4200051"], + ["Abelardo Luz", "4200101"], + ["Agrolândia", "4200200"], + ["Agronômica", "4200309"], + ["Água Doce", "4200408"], + ["Águas de Chapecó", "4200507"], + ["Águas Frias", "4200556"], + ["Águas Mornas", "4200606"], + ["Alfredo Wagner", "4200705"], + ["Alto Bela Vista", "4200754"], + ["Anchieta", "4200804"], + ["Angelina", "4200903"], + ["Anita Garibaldi", "4201000"], + ["Anitápolis", "4201109"], + ["Antônio Carlos", "4201208"], + ["Apiúna", "4201257"], + ["Arabutã", "4201273"], + ["Araquari", "4201307"], + ["Araranguá", "4201406"], + ["Armazém", "4201505"], + ["Arroio Trinta", "4201604"], + ["Arvoredo", "4201653"], + ["Ascurra", "4201703"], + ["Atalanta", "4201802"], + ["Aurora", "4201901"], + ["Balneário Arroio do Silva", "4201950"], + ["Balneário Barra do Sul", "4202057"], + ["Balneário Camboriú", "4202008"], + ["Balneário Gaivota", "4202073"], + ["Balneário Piçarras", "4212809"], + ["Balneário Rincão", "4220000"], + ["Bandeirante", "4202081"], + ["Barra Bonita", "4202099"], + ["Barra Velha", "4202107"], + ["Bela Vista do Toldo", "4202131"], + ["Belmonte", "4202156"], + ["Benedito Novo", "4202206"], + ["Biguaçu", "4202305"], + ["Blumenau", "4202404"], + ["Bocaina do Sul", "4202438"], + ["Bom Jardim da Serra", "4202503"], + ["Bom Jesus", "4202537"], + ["Bom Jesus do Oeste", "4202578"], + ["Bom Retiro", "4202602"], + ["Bombinhas", "4202453"], + ["Botuverá", "4202701"], + ["Braço do Norte", "4202800"], + ["Braço do Trombudo", "4202859"], + ["Brunópolis", "4202875"], + ["Brusque", "4202909"], + ["Caçador", "4203006"], + ["Caibi", "4203105"], + ["Calmon", "4203154"], + ["Camboriú", "4203204"], + ["Campo Alegre", "4203303"], + ["Campo Belo do Sul", "4203402"], + ["Campo Erê", "4203501"], + ["Campos Novos", "4203600"], + ["Canelinha", "4203709"], + ["Canoinhas", "4203808"], + ["Capão Alto", "4203253"], + ["Capinzal", "4203907"], + ["Capivari de Baixo", "4203956"], + ["Catanduvas", "4204004"], + ["Caxambu do Sul", "4204103"], + ["Celso Ramos", "4204152"], + ["Cerro Negro", "4204178"], + ["Chapadão do Lageado", "4204194"], + ["Chapecó", "4204202"], + ["Cocal do Sul", "4204251"], + ["Concórdia", "4204301"], + ["Cordilheira Alta", "4204350"], + ["Coronel Freitas", "4204400"], + ["Coronel Martins", "4204459"], + ["Correia Pinto", "4204558"], + ["Corupá", "4204509"], + ["Criciúma", "4204608"], + ["Cunha Porã", "4204707"], + ["Cunhataí", "4204756"], + ["Curitibanos", "4204806"], + ["Descanso", "4204905"], + ["Dionísio Cerqueira", "4205001"], + ["Dona Emma", "4205100"], + ["Doutor Pedrinho", "4205159"], + ["Entre Rios", "4205175"], + ["Ermo", "4205191"], + ["Erval Velho", "4205209"], + ["Faxinal dos Guedes", "4205308"], + ["Flor do Sertão", "4205357"], + ["Florianópolis", "4205407"], + ["Formosa do Sul", "4205431"], + ["Forquilhinha", "4205456"], + ["Fraiburgo", "4205506"], + ["Frei Rogério", "4205555"], + ["Galvão", "4205605"], + ["Garopaba", "4205704"], + ["Garuva", "4205803"], + ["Gaspar", "4205902"], + ["Governador Celso Ramos", "4206009"], + ["Grão-Pará", "4206108"], + ["Gravatal", "4206207"], + ["Guabiruba", "4206306"], + ["Guaraciaba", "4206405"], + ["Guaramirim", "4206504"], + ["Guarujá do Sul", "4206603"], + ["Guatambú", "4206652"], + ["Herval d'Oeste", "4206702"], + ["Ibiam", "4206751"], + ["Ibicaré", "4206801"], + ["Ibirama", "4206900"], + ["Içara", "4207007"], + ["Ilhota", "4207106"], + ["Imaruí", "4207205"], + ["Imbituba", "4207304"], + ["Imbuia", "4207403"], + ["Indaial", "4207502"], + ["Iomerê", "4207577"], + ["Ipira", "4207601"], + ["Iporã do Oeste", "4207650"], + ["Ipuaçu", "4207684"], + ["Ipumirim", "4207700"], + ["Iraceminha", "4207759"], + ["Irani", "4207809"], + ["Irati", "4207858"], + ["Irineópolis", "4207908"], + ["Itá", "4208005"], + ["Itaiópolis", "4208104"], + ["Itajaí", "4208203"], + ["Itapema", "4208302"], + ["Itapiranga", "4208401"], + ["Itapoá", "4208450"], + ["Ituporanga", "4208500"], + ["Jaborá", "4208609"], + ["Jacinto Machado", "4208708"], + ["Jaguaruna", "4208807"], + ["Jaraguá do Sul", "4208906"], + ["Jardinópolis", "4208955"], + ["Joaçaba", "4209003"], + ["Joinville", "4209102"], + ["José Boiteux", "4209151"], + ["Jupiá", "4209177"], + ["Lacerdópolis", "4209201"], + ["Lages", "4209300"], + ["Laguna", "4209409"], + ["Lajeado Grande", "4209458"], + ["Laurentino", "4209508"], + ["Lauro Müller", "4209607"], + ["Lebon Régis", "4209706"], + ["Leoberto Leal", "4209805"], + ["Lindóia do Sul", "4209854"], + ["Lontras", "4209904"], + ["Luiz Alves", "4210001"], + ["Luzerna", "4210035"], + ["Macieira", "4210050"], + ["Mafra", "4210100"], + ["Major Gercino", "4210209"], + ["Major Vieira", "4210308"], + ["Maracajá", "4210407"], + ["Maravilha", "4210506"], + ["Marema", "4210555"], + ["Massaranduba", "4210605"], + ["Matos Costa", "4210704"], + ["Meleiro", "4210803"], + ["Mirim Doce", "4210852"], + ["Modelo", "4210902"], + ["Mondaí", "4211009"], + ["Monte Carlo", "4211058"], + ["Monte Castelo", "4211108"], + ["Morro da Fumaça", "4211207"], + ["Morro Grande", "4211256"], + ["Navegantes", "4211306"], + ["Nova Erechim", "4211405"], + ["Nova Itaberaba", "4211454"], + ["Nova Trento", "4211504"], + ["Nova Veneza", "4211603"], + ["Novo Horizonte", "4211652"], + ["Orleans", "4211702"], + ["Otacílio Costa", "4211751"], + ["Ouro", "4211801"], + ["Ouro Verde", "4211850"], + ["Paial", "4211876"], + ["Painel", "4211892"], + ["Palhoça", "4211900"], + ["Palma Sola", "4212007"], + ["Palmeira", "4212056"], + ["Palmitos", "4212106"], + ["Papanduva", "4212205"], + ["Paraíso", "4212239"], + ["Passo de Torres", "4212254"], + ["Passos Maia", "4212270"], + ["Paulo Lopes", "4212304"], + ["Pedras Grandes", "4212403"], + ["Penha", "4212502"], + ["Peritiba", "4212601"], + ["Pescaria Brava", "4212650"], + ["Petrolândia", "4212700"], + ["Pinhalzinho", "4212908"], + ["Pinheiro Preto", "4213005"], + ["Piratuba", "4213104"], + ["Planalto Alegre", "4213153"], + ["Pomerode", "4213203"], + ["Ponte Alta", "4213302"], + ["Ponte Alta do Norte", "4213351"], + ["Ponte Serrada", "4213401"], + ["Porto Belo", "4213500"], + ["Porto União", "4213609"], + ["Pouso Redondo", "4213708"], + ["Praia Grande", "4213807"], + ["Presidente Castello Branco", "4213906"], + ["Presidente Getúlio", "4214003"], + ["Presidente Nereu", "4214102"], + ["Princesa", "4214151"], + ["Quilombo", "4214201"], + ["Rancho Queimado", "4214300"], + ["Rio das Antas", "4214409"], + ["Rio do Campo", "4214508"], + ["Rio do Oeste", "4214607"], + ["Rio do Sul", "4214805"], + ["Rio dos Cedros", "4214706"], + ["Rio Fortuna", "4214904"], + ["Rio Negrinho", "4215000"], + ["Rio Rufino", "4215059"], + ["Riqueza", "4215075"], + ["Rodeio", "4215109"], + ["Romelândia", "4215208"], + ["Salete", "4215307"], + ["Saltinho", "4215356"], + ["Salto Veloso", "4215406"], + ["Sangão", "4215455"], + ["Santa Cecília", "4215505"], + ["Santa Helena", "4215554"], + ["Santa Rosa de Lima", "4215604"], + ["Santa Rosa do Sul", "4215653"], + ["Santa Terezinha", "4215679"], + ["Santa Terezinha do Progresso", "4215687"], + ["Santiago do Sul", "4215695"], + ["Santo Amaro da Imperatriz", "4215703"], + ["São Bento do Sul", "4215802"], + ["São Bernardino", "4215752"], + ["São Bonifácio", "4215901"], + ["São Carlos", "4216008"], + ["São Cristóvão do Sul", "4216057"], + ["São Domingos", "4216107"], + ["São Francisco do Sul", "4216206"], + ["São João Batista", "4216305"], + ["São João do Itaperiú", "4216354"], + ["São João do Oeste", "4216255"], + ["São João do Sul", "4216404"], + ["São Joaquim", "4216503"], + ["São José", "4216602"], + ["São José do Cedro", "4216701"], + ["São José do Cerrito", "4216800"], + ["São Lourenço do Oeste", "4216909"], + ["São Ludgero", "4217006"], + ["São Martinho", "4217105"], + ["São Miguel da Boa Vista", "4217154"], + ["São Miguel do Oeste", "4217204"], + ["São Pedro de Alcântara", "4217253"], + ["Saudades", "4217303"], + ["Schroeder", "4217402"], + ["Seara", "4217501"], + ["Serra Alta", "4217550"], + ["Siderópolis", "4217600"], + ["Sombrio", "4217709"], + ["Sul Brasil", "4217758"], + ["Taió", "4217808"], + ["Tangará", "4217907"], + ["Tigrinhos", "4217956"], + ["Tijucas", "4218004"], + ["Timbé do Sul", "4218103"], + ["Timbó", "4218202"], + ["Timbó Grande", "4218251"], + ["Três Barras", "4218301"], + ["Treviso", "4218350"], + ["Treze de Maio", "4218400"], + ["Treze Tílias", "4218509"], + ["Trombudo Central", "4218608"], + ["Tubarão", "4218707"], + ["Tunápolis", "4218756"], + ["Turvo", "4218806"], + ["União do Oeste", "4218855"], + ["Urubici", "4218905"], + ["Urupema", "4218954"], + ["Urussanga", "4219002"], + ["Vargeão", "4219101"], + ["Vargem", "4219150"], + ["Vargem Bonita", "4219176"], + ["Vidal Ramos", "4219200"], + ["Videira", "4219309"], + ["Vitor Meireles", "4219358"], + ["Witmarsum", "4219408"], + ["Xanxerê", "4219507"], + ["Xavantina", "4219606"], + ["Xaxim", "4219705"], + ["Zortéa", "4219853"], ], SE: [ - "Amparo do São Francisco", - "Aquidabã", - "Aracaju", - "Arauá", - "Areia Branca", - "Barra dos Coqueiros", - "Boquim", - "Brejo Grande", - "Campo do Brito", - "Canhoba", - "Canindé de São Francisco", - "Capela", - "Carira", - "Carmópolis", - "Cedro de São João", - "Cristinápolis", - "Cumbe", - "Divina Pastora", - "Estância", - "Feira Nova", - "Frei Paulo", - "Gararu", - "General Maynard", - "Graccho Cardoso", - "Ilha das Flores", - "Indiaroba", - "Itabaiana", - "Itabaianinha", - "Itabi", - "Itaporanga d'Ajuda", - "Japaratuba", - "Japoatã", - "Lagarto", - "Laranjeiras", - "Macambira", - "Malhada dos Bois", - "Malhador", - "Maruim", - "Moita Bonita", - "Monte Alegre de Sergipe", - "Muribeca", - "Neópolis", - "Nossa Senhora Aparecida", - "Nossa Senhora da Glória", - "Nossa Senhora das Dores", - "Nossa Senhora de Lourdes", - "Nossa Senhora do Socorro", - "Pacatuba", - "Pedra Mole", - "Pedrinhas", - "Pinhão", - "Pirambu", - "Poço Redondo", - "Poço Verde", - "Porto da Folha", - "Propriá", - "Riachão do Dantas", - "Riachuelo", - "Ribeirópolis", - "Rosário do Catete", - "Salgado", - "Santa Luzia do Itanhy", - "Santa Rosa de Lima", - "Santana do São Francisco", - "Santo Amaro das Brotas", - "São Cristóvão", - "São Domingos", - "São Francisco", - "São Miguel do Aleixo", - "Simão Dias", - "Siriri", - "Telha", - "Tobias Barreto", - "Tomar do Geru", - "Umbaúba", + ["Amparo do São Francisco", "2800100"], + ["Aquidabã", "2800209"], + ["Aracaju", "2800308"], + ["Arauá", "2800407"], + ["Areia Branca", "2800506"], + ["Barra dos Coqueiros", "2800605"], + ["Boquim", "2800670"], + ["Brejo Grande", "2800704"], + ["Campo do Brito", "2801009"], + ["Canhoba", "2801108"], + ["Canindé de São Francisco", "2801207"], + ["Capela", "2801306"], + ["Carira", "2801405"], + ["Carmópolis", "2801504"], + ["Cedro de São João", "2801603"], + ["Cristinápolis", "2801702"], + ["Cumbe", "2801900"], + ["Divina Pastora", "2802007"], + ["Estância", "2802106"], + ["Feira Nova", "2802205"], + ["Frei Paulo", "2802304"], + ["Gararu", "2802403"], + ["General Maynard", "2802502"], + ["Graccho Cardoso", "2802601"], + ["Ilha das Flores", "2802700"], + ["Indiaroba", "2802809"], + ["Itabaiana", "2802908"], + ["Itabaianinha", "2803005"], + ["Itabi", "2803104"], + ["Itaporanga d'Ajuda", "2803203"], + ["Japaratuba", "2803302"], + ["Japoatã", "2803401"], + ["Lagarto", "2803500"], + ["Laranjeiras", "2803609"], + ["Macambira", "2803708"], + ["Malhada dos Bois", "2803807"], + ["Malhador", "2803906"], + ["Maruim", "2804003"], + ["Moita Bonita", "2804102"], + ["Monte Alegre de Sergipe", "2804201"], + ["Muribeca", "2804300"], + ["Neópolis", "2804409"], + ["Nossa Senhora Aparecida", "2804458"], + ["Nossa Senhora da Glória", "2804508"], + ["Nossa Senhora das Dores", "2804607"], + ["Nossa Senhora de Lourdes", "2804706"], + ["Nossa Senhora do Socorro", "2804805"], + ["Pacatuba", "2804904"], + ["Pedra Mole", "2805000"], + ["Pedrinhas", "2805109"], + ["Pinhão", "2805208"], + ["Pirambu", "2805307"], + ["Poço Redondo", "2805406"], + ["Poço Verde", "2805505"], + ["Porto da Folha", "2805604"], + ["Propriá", "2805703"], + ["Riachão do Dantas", "2805802"], + ["Riachuelo", "2805901"], + ["Ribeirópolis", "2806008"], + ["Rosário do Catete", "2806107"], + ["Salgado", "2806206"], + ["Santa Luzia do Itanhy", "2806305"], + ["Santa Rosa de Lima", "2806503"], + ["Santana do São Francisco", "2806404"], + ["Santo Amaro das Brotas", "2806602"], + ["São Cristóvão", "2806701"], + ["São Domingos", "2806800"], + ["São Francisco", "2806909"], + ["São Miguel do Aleixo", "2807006"], + ["Simão Dias", "2807105"], + ["Siriri", "2807204"], + ["Telha", "2807303"], + ["Tobias Barreto", "2807402"], + ["Tomar do Geru", "2807501"], + ["Umbaúba", "2807600"], ], SP: [ - "Adamantina", - "Adolfo", - "Aguaí", - "Águas da Prata", - "Águas de Lindóia", - "Águas de Santa Bárbara", - "Águas de São Pedro", - "Agudos", - "Alambari", - "Alfredo Marcondes", - "Altair", - "Altinópolis", - "Alto Alegre", - "Alumínio", - "Álvares Florence", - "Álvares Machado", - "Álvaro de Carvalho", - "Alvinlândia", - "Americana", - "Américo Brasiliense", - "Américo de Campos", - "Amparo", - "Analândia", - "Andradina", - "Angatuba", - "Anhembi", - "Anhumas", - "Aparecida", - "Aparecida d'Oeste", - "Apiaí", - "Araçariguama", - "Araçatuba", - "Araçoiaba da Serra", - "Aramina", - "Arandu", - "Arapeí", - "Araraquara", - "Araras", - "Arco-Íris", - "Arealva", - "Areias", - "Areiópolis", - "Ariranha", - "Artur Nogueira", - "Arujá", - "Aspásia", - "Assis", - "Atibaia", - "Auriflama", - "Avaí", - "Avanhandava", - "Avaré", - "Bady Bassitt", - "Balbinos", - "Bálsamo", - "Bananal", - "Barão de Antonina", - "Barbosa", - "Bariri", - "Barra Bonita", - "Barra do Chapéu", - "Barra do Turvo", - "Barretos", - "Barrinha", - "Barueri", - "Bastos", - "Batatais", - "Bauru", - "Bebedouro", - "Bento de Abreu", - "Bernardino de Campos", - "Bertioga", - "Bilac", - "Birigui", - "Biritiba Mirim", - "Boa Esperança do Sul", - "Bocaina", - "Bofete", - "Boituva", - "Bom Jesus dos Perdões", - "Bom Sucesso de Itararé", - "Borá", - "Boracéia", - "Borborema", - "Borebi", - "Botucatu", - "Bragança Paulista", - "Braúna", - "Brejo Alegre", - "Brodowski", - "Brotas", - "Buri", - "Buritama", - "Buritizal", - "Cabrália Paulista", - "Cabreúva", - "Caçapava", - "Cachoeira Paulista", - "Caconde", - "Cafelândia", - "Caiabu", - "Caieiras", - "Caiuá", - "Cajamar", - "Cajati", - "Cajobi", - "Cajuru", - "Campina do Monte Alegre", - "Campinas", - "Campo Limpo Paulista", - "Campos do Jordão", - "Campos Novos Paulista", - "Cananéia", - "Canas", - "Cândido Mota", - "Cândido Rodrigues", - "Canitar", - "Capão Bonito", - "Capela do Alto", - "Capivari", - "Caraguatatuba", - "Carapicuíba", - "Cardoso", - "Casa Branca", - "Cássia dos Coqueiros", - "Castilho", - "Catanduva", - "Catiguá", - "Cedral", - "Cerqueira César", - "Cerquilho", - "Cesário Lange", - "Charqueada", - "Chavantes", - "Clementina", - "Colina", - "Colômbia", - "Conchal", - "Conchas", - "Cordeirópolis", - "Coroados", - "Coronel Macedo", - "Corumbataí", - "Cosmópolis", - "Cosmorama", - "Cotia", - "Cravinhos", - "Cristais Paulista", - "Cruzália", - "Cruzeiro", - "Cubatão", - "Cunha", - "Descalvado", - "Diadema", - "Dirce Reis", - "Divinolândia", - "Dobrada", - "Dois Córregos", - "Dolcinópolis", - "Dourado", - "Dracena", - "Duartina", - "Dumont", - "Echaporã", - "Eldorado", - "Elias Fausto", - "Elisiário", - "Embaúba", - "Embu das Artes", - "Embu-Guaçu", - "Emilianópolis", - "Engenheiro Coelho", - "Espírito Santo do Pinhal", - "Espírito Santo do Turvo", - "Estiva Gerbi", - "Estrela d'Oeste", - "Estrela do Norte", - "Euclides da Cunha Paulista", - "Fartura", - "Fernando Prestes", - "Fernandópolis", - "Fernão", - "Ferraz de Vasconcelos", - "Flora Rica", - "Floreal", - "Flórida Paulista", - "Florínea", - "Franca", - "Francisco Morato", - "Franco da Rocha", - "Gabriel Monteiro", - "Gália", - "Garça", - "Gastão Vidigal", - "Gavião Peixoto", - "General Salgado", - "Getulina", - "Glicério", - "Guaiçara", - "Guaimbê", - "Guaíra", - "Guapiaçu", - "Guapiara", - "Guará", - "Guaraçaí", - "Guaraci", - "Guarani d'Oeste", - "Guarantã", - "Guararapes", - "Guararema", - "Guaratinguetá", - "Guareí", - "Guariba", - "Guarujá", - "Guarulhos", - "Guatapará", - "Guzolândia", - "Herculândia", - "Holambra", - "Hortolândia", - "Iacanga", - "Iacri", - "Iaras", - "Ibaté", - "Ibirá", - "Ibirarema", - "Ibitinga", - "Ibiúna", - "Icém", - "Iepê", - "Igaraçu do Tietê", - "Igarapava", - "Igaratá", - "Iguape", - "Ilha Comprida", - "Ilha Solteira", - "Ilhabela", - "Indaiatuba", - "Indiana", - "Indiaporã", - "Inúbia Paulista", - "Ipaussu", - "Iperó", - "Ipeúna", - "Ipiguá", - "Iporanga", - "Ipuã", - "Iracemápolis", - "Irapuã", - "Irapuru", - "Itaberá", - "Itaí", - "Itajobi", - "Itaju", - "Itanhaém", - "Itaoca", - "Itapecerica da Serra", - "Itapetininga", - "Itapeva", - "Itapevi", - "Itapira", - "Itapirapuã Paulista", - "Itápolis", - "Itaporanga", - "Itapuí", - "Itapura", - "Itaquaquecetuba", - "Itararé", - "Itariri", - "Itatiba", - "Itatinga", - "Itirapina", - "Itirapuã", - "Itobi", - "Itu", - "Itupeva", - "Ituverava", - "Jaborandi", - "Jaboticabal", - "Jacareí", - "Jaci", - "Jacupiranga", - "Jaguariúna", - "Jales", - "Jambeiro", - "Jandira", - "Jardinópolis", - "Jarinu", - "Jaú", - "Jeriquara", - "Joanópolis", - "João Ramalho", - "José Bonifácio", - "Júlio Mesquita", - "Jumirim", - "Jundiaí", - "Junqueirópolis", - "Juquiá", - "Juquitiba", - "Lagoinha", - "Laranjal Paulista", - "Lavínia", - "Lavrinhas", - "Leme", - "Lençóis Paulista", - "Limeira", - "Lindóia", - "Lins", - "Lorena", - "Lourdes", - "Louveira", - "Lucélia", - "Lucianópolis", - "Luís Antônio", - "Luiziânia", - "Lupércio", - "Lutécia", - "Macatuba", - "Macaubal", - "Macedônia", - "Magda", - "Mairinque", - "Mairiporã", - "Manduri", - "Marabá Paulista", - "Maracaí", - "Marapoama", - "Mariápolis", - "Marília", - "Marinópolis", - "Martinópolis", - "Matão", - "Mauá", - "Mendonça", - "Meridiano", - "Mesópolis", - "Miguelópolis", - "Mineiros do Tietê", - "Mira Estrela", - "Miracatu", - "Mirandópolis", - "Mirante do Paranapanema", - "Mirassol", - "Mirassolândia", - "Mococa", - "Mogi das Cruzes", - "Mogi Guaçu", - "Mogi Mirim", - "Mombuca", - "Monções", - "Mongaguá", - "Monte Alegre do Sul", - "Monte Alto", - "Monte Aprazível", - "Monte Azul Paulista", - "Monte Castelo", - "Monte Mor", - "Monteiro Lobato", - "Morro Agudo", - "Morungaba", - "Motuca", - "Murutinga do Sul", - "Nantes", - "Narandiba", - "Natividade da Serra", - "Nazaré Paulista", - "Neves Paulista", - "Nhandeara", - "Nipoã", - "Nova Aliança", - "Nova Campina", - "Nova Canaã Paulista", - "Nova Castilho", - "Nova Europa", - "Nova Granada", - "Nova Guataporanga", - "Nova Independência", - "Nova Luzitânia", - "Nova Odessa", - "Novais", - "Novo Horizonte", - "Nuporanga", - "Ocauçu", - "Óleo", - "Olímpia", - "Onda Verde", - "Oriente", - "Orindiúva", - "Orlândia", - "Osasco", - "Oscar Bressane", - "Osvaldo Cruz", - "Ourinhos", - "Ouro Verde", - "Ouroeste", - "Pacaembu", - "Palestina", - "Palmares Paulista", - "Palmeira d'Oeste", - "Palmital", - "Panorama", - "Paraguaçu Paulista", - "Paraibuna", - "Paraíso", - "Paranapanema", - "Paranapuã", - "Parapuã", - "Pardinho", - "Pariquera-Açu", - "Parisi", - "Patrocínio Paulista", - "Paulicéia", - "Paulínia", - "Paulistânia", - "Paulo de Faria", - "Pederneiras", - "Pedra Bela", - "Pedranópolis", - "Pedregulho", - "Pedreira", - "Pedrinhas Paulista", - "Pedro de Toledo", - "Penápolis", - "Pereira Barreto", - "Pereiras", - "Peruíbe", - "Piacatu", - "Piedade", - "Pilar do Sul", - "Pindamonhangaba", - "Pindorama", - "Pinhalzinho", - "Piquerobi", - "Piquete", - "Piracaia", - "Piracicaba", - "Piraju", - "Pirajuí", - "Pirangi", - "Pirapora do Bom Jesus", - "Pirapozinho", - "Pirassununga", - "Piratininga", - "Pitangueiras", - "Planalto", - "Platina", - "Poá", - "Poloni", - "Pompéia", - "Pongaí", - "Pontal", - "Pontalinda", - "Pontes Gestal", - "Populina", - "Porangaba", - "Porto Feliz", - "Porto Ferreira", - "Potim", - "Potirendaba", - "Pracinha", - "Pradópolis", - "Praia Grande", - "Pratânia", - "Presidente Alves", - "Presidente Bernardes", - "Presidente Epitácio", - "Presidente Prudente", - "Presidente Venceslau", - "Promissão", - "Quadra", - "Quatá", - "Queiroz", - "Queluz", - "Quintana", - "Rafard", - "Rancharia", - "Redenção da Serra", - "Regente Feijó", - "Reginópolis", - "Registro", - "Restinga", - "Ribeira", - "Ribeirão Bonito", - "Ribeirão Branco", - "Ribeirão Corrente", - "Ribeirão do Sul", - "Ribeirão dos Índios", - "Ribeirão Grande", - "Ribeirão Pires", - "Ribeirão Preto", - "Rifaina", - "Rincão", - "Rinópolis", - "Rio Claro", - "Rio das Pedras", - "Rio Grande da Serra", - "Riolândia", - "Riversul", - "Rosana", - "Roseira", - "Rubiácea", - "Rubinéia", - "Sabino", - "Sagres", - "Sales", - "Sales Oliveira", - "Salesópolis", - "Salmourão", - "Saltinho", - "Salto", - "Salto de Pirapora", - "Salto Grande", - "Sandovalina", - "Santa Adélia", - "Santa Albertina", - "Santa Bárbara d'Oeste", - "Santa Branca", - "Santa Clara d'Oeste", - "Santa Cruz da Conceição", - "Santa Cruz da Esperança", - "Santa Cruz das Palmeiras", - "Santa Cruz do Rio Pardo", - "Santa Ernestina", - "Santa Fé do Sul", - "Santa Gertrudes", - "Santa Isabel", - "Santa Lúcia", - "Santa Maria da Serra", - "Santa Mercedes", - "Santa Rita d'Oeste", - "Santa Rita do Passa Quatro", - "Santa Rosa de Viterbo", - "Santa Salete", - "Santana da Ponte Pensa", - "Santana de Parnaíba", - "Santo Anastácio", - "Santo André", - "Santo Antônio da Alegria", - "Santo Antônio de Posse", - "Santo Antônio do Aracanguá", - "Santo Antônio do Jardim", - "Santo Antônio do Pinhal", - "Santo Expedito", - "Santópolis do Aguapeí", - "Santos", - "São Bento do Sapucaí", - "São Bernardo do Campo", - "São Caetano do Sul", - "São Carlos", - "São Francisco", - "São João da Boa Vista", - "São João das Duas Pontes", - "São João de Iracema", - "São João do Pau d'Alho", - "São Joaquim da Barra", - "São José da Bela Vista", - "São José do Barreiro", - "São José do Rio Pardo", - "São José do Rio Preto", - "São José dos Campos", - "São Lourenço da Serra", - "São Luiz do Paraitinga", - "São Manuel", - "São Miguel Arcanjo", - "São Paulo", - "São Pedro", - "São Pedro do Turvo", - "São Roque", - "São Sebastião", - "São Sebastião da Grama", - "São Simão", - "São Vicente", - "Sarapuí", - "Sarutaiá", - "Sebastianópolis do Sul", - "Serra Azul", - "Serra Negra", - "Serrana", - "Sertãozinho", - "Sete Barras", - "Severínia", - "Silveiras", - "Socorro", - "Sorocaba", - "Sud Mennucci", - "Sumaré", - "Suzanápolis", - "Suzano", - "Tabapuã", - "Tabatinga", - "Taboão da Serra", - "Taciba", - "Taguaí", - "Taiaçu", - "Taiúva", - "Tambaú", - "Tanabi", - "Tapiraí", - "Tapiratiba", - "Taquaral", - "Taquaritinga", - "Taquarituba", - "Taquarivaí", - "Tarabai", - "Tarumã", - "Tatuí", - "Taubaté", - "Tejupá", - "Teodoro Sampaio", - "Terra Roxa", - "Tietê", - "Timburi", - "Torre de Pedra", - "Torrinha", - "Trabiju", - "Tremembé", - "Três Fronteiras", - "Tuiuti", - "Tupã", - "Tupi Paulista", - "Turiúba", - "Turmalina", - "Ubarana", - "Ubatuba", - "Ubirajara", - "Uchoa", - "União Paulista", - "Urânia", - "Uru", - "Urupês", - "Valentim Gentil", - "Valinhos", - "Valparaíso", - "Vargem", - "Vargem Grande do Sul", - "Vargem Grande Paulista", - "Várzea Paulista", - "Vera Cruz", - "Vinhedo", - "Viradouro", - "Vista Alegre do Alto", - "Vitória Brasil", - "Votorantim", - "Votuporanga", - "Zacarias", + ["Adamantina", "3500105"], + ["Adolfo", "3500204"], + ["Aguaí", "3500303"], + ["Águas da Prata", "3500402"], + ["Águas de Lindóia", "3500501"], + ["Águas de Santa Bárbara", "3500550"], + ["Águas de São Pedro", "3500600"], + ["Agudos", "3500709"], + ["Alambari", "3500758"], + ["Alfredo Marcondes", "3500808"], + ["Altair", "3500907"], + ["Altinópolis", "3501004"], + ["Alto Alegre", "3501103"], + ["Alumínio", "3501152"], + ["Álvares Florence", "3501202"], + ["Álvares Machado", "3501301"], + ["Álvaro de Carvalho", "3501400"], + ["Alvinlândia", "3501509"], + ["Americana", "3501608"], + ["Américo Brasiliense", "3501707"], + ["Américo de Campos", "3501806"], + ["Amparo", "3501905"], + ["Analândia", "3502002"], + ["Andradina", "3502101"], + ["Angatuba", "3502200"], + ["Anhembi", "3502309"], + ["Anhumas", "3502408"], + ["Aparecida", "3502507"], + ["Aparecida d'Oeste", "3502606"], + ["Apiaí", "3502705"], + ["Araçariguama", "3502754"], + ["Araçatuba", "3502804"], + ["Araçoiaba da Serra", "3502903"], + ["Aramina", "3503000"], + ["Arandu", "3503109"], + ["Arapeí", "3503158"], + ["Araraquara", "3503208"], + ["Araras", "3503307"], + ["Arco-Íris", "3503356"], + ["Arealva", "3503406"], + ["Areias", "3503505"], + ["Areiópolis", "3503604"], + ["Ariranha", "3503703"], + ["Artur Nogueira", "3503802"], + ["Arujá", "3503901"], + ["Aspásia", "3503950"], + ["Assis", "3504008"], + ["Atibaia", "3504107"], + ["Auriflama", "3504206"], + ["Avaí", "3504305"], + ["Avanhandava", "3504404"], + ["Avaré", "3504503"], + ["Bady Bassitt", "3504602"], + ["Balbinos", "3504701"], + ["Bálsamo", "3504800"], + ["Bananal", "3504909"], + ["Barão de Antonina", "3505005"], + ["Barbosa", "3505104"], + ["Bariri", "3505203"], + ["Barra Bonita", "3505302"], + ["Barra do Chapéu", "3505351"], + ["Barra do Turvo", "3505401"], + ["Barretos", "3505500"], + ["Barrinha", "3505609"], + ["Barueri", "3505708"], + ["Bastos", "3505807"], + ["Batatais", "3505906"], + ["Bauru", "3506003"], + ["Bebedouro", "3506102"], + ["Bento de Abreu", "3506201"], + ["Bernardino de Campos", "3506300"], + ["Bertioga", "3506359"], + ["Bilac", "3506409"], + ["Birigui", "3506508"], + ["Biritiba Mirim", "3506607"], + ["Boa Esperança do Sul", "3506706"], + ["Bocaina", "3506805"], + ["Bofete", "3506904"], + ["Boituva", "3507001"], + ["Bom Jesus dos Perdões", "3507100"], + ["Bom Sucesso de Itararé", "3507159"], + ["Borá", "3507209"], + ["Boracéia", "3507308"], + ["Borborema", "3507407"], + ["Borebi", "3507456"], + ["Botucatu", "3507506"], + ["Bragança Paulista", "3507605"], + ["Braúna", "3507704"], + ["Brejo Alegre", "3507753"], + ["Brodowski", "3507803"], + ["Brotas", "3507902"], + ["Buri", "3508009"], + ["Buritama", "3508108"], + ["Buritizal", "3508207"], + ["Cabrália Paulista", "3508306"], + ["Cabreúva", "3508405"], + ["Caçapava", "3508504"], + ["Cachoeira Paulista", "3508603"], + ["Caconde", "3508702"], + ["Cafelândia", "3508801"], + ["Caiabu", "3508900"], + ["Caieiras", "3509007"], + ["Caiuá", "3509106"], + ["Cajamar", "3509205"], + ["Cajati", "3509254"], + ["Cajobi", "3509304"], + ["Cajuru", "3509403"], + ["Campina do Monte Alegre", "3509452"], + ["Campinas", "3509502"], + ["Campo Limpo Paulista", "3509601"], + ["Campos do Jordão", "3509700"], + ["Campos Novos Paulista", "3509809"], + ["Cananéia", "3509908"], + ["Canas", "3509957"], + ["Cândido Mota", "3510005"], + ["Cândido Rodrigues", "3510104"], + ["Canitar", "3510153"], + ["Capão Bonito", "3510203"], + ["Capela do Alto", "3510302"], + ["Capivari", "3510401"], + ["Caraguatatuba", "3510500"], + ["Carapicuíba", "3510609"], + ["Cardoso", "3510708"], + ["Casa Branca", "3510807"], + ["Cássia dos Coqueiros", "3510906"], + ["Castilho", "3511003"], + ["Catanduva", "3511102"], + ["Catiguá", "3511201"], + ["Cedral", "3511300"], + ["Cerqueira César", "3511409"], + ["Cerquilho", "3511508"], + ["Cesário Lange", "3511607"], + ["Charqueada", "3511706"], + ["Chavantes", "3557204"], + ["Clementina", "3511904"], + ["Colina", "3512001"], + ["Colômbia", "3512100"], + ["Conchal", "3512209"], + ["Conchas", "3512308"], + ["Cordeirópolis", "3512407"], + ["Coroados", "3512506"], + ["Coronel Macedo", "3512605"], + ["Corumbataí", "3512704"], + ["Cosmópolis", "3512803"], + ["Cosmorama", "3512902"], + ["Cotia", "3513009"], + ["Cravinhos", "3513108"], + ["Cristais Paulista", "3513207"], + ["Cruzália", "3513306"], + ["Cruzeiro", "3513405"], + ["Cubatão", "3513504"], + ["Cunha", "3513603"], + ["Descalvado", "3513702"], + ["Diadema", "3513801"], + ["Dirce Reis", "3513850"], + ["Divinolândia", "3513900"], + ["Dobrada", "3514007"], + ["Dois Córregos", "3514106"], + ["Dolcinópolis", "3514205"], + ["Dourado", "3514304"], + ["Dracena", "3514403"], + ["Duartina", "3514502"], + ["Dumont", "3514601"], + ["Echaporã", "3514700"], + ["Eldorado", "3514809"], + ["Elias Fausto", "3514908"], + ["Elisiário", "3514924"], + ["Embaúba", "3514957"], + ["Embu das Artes", "3515004"], + ["Embu-Guaçu", "3515103"], + ["Emilianópolis", "3515129"], + ["Engenheiro Coelho", "3515152"], + ["Espírito Santo do Pinhal", "3515186"], + ["Espírito Santo do Turvo", "3515194"], + ["Estiva Gerbi", "3557303"], + ["Estrela d'Oeste", "3515202"], + ["Estrela do Norte", "3515301"], + ["Euclides da Cunha Paulista", "3515350"], + ["Fartura", "3515400"], + ["Fernando Prestes", "3515608"], + ["Fernandópolis", "3515509"], + ["Fernão", "3515657"], + ["Ferraz de Vasconcelos", "3515707"], + ["Flora Rica", "3515806"], + ["Floreal", "3515905"], + ["Flórida Paulista", "3516002"], + ["Florínea", "3516101"], + ["Franca", "3516200"], + ["Francisco Morato", "3516309"], + ["Franco da Rocha", "3516408"], + ["Gabriel Monteiro", "3516507"], + ["Gália", "3516606"], + ["Garça", "3516705"], + ["Gastão Vidigal", "3516804"], + ["Gavião Peixoto", "3516853"], + ["General Salgado", "3516903"], + ["Getulina", "3517000"], + ["Glicério", "3517109"], + ["Guaiçara", "3517208"], + ["Guaimbê", "3517307"], + ["Guaíra", "3517406"], + ["Guapiaçu", "3517505"], + ["Guapiara", "3517604"], + ["Guará", "3517703"], + ["Guaraçaí", "3517802"], + ["Guaraci", "3517901"], + ["Guarani d'Oeste", "3518008"], + ["Guarantã", "3518107"], + ["Guararapes", "3518206"], + ["Guararema", "3518305"], + ["Guaratinguetá", "3518404"], + ["Guareí", "3518503"], + ["Guariba", "3518602"], + ["Guarujá", "3518701"], + ["Guarulhos", "3518800"], + ["Guatapará", "3518859"], + ["Guzolândia", "3518909"], + ["Herculândia", "3519006"], + ["Holambra", "3519055"], + ["Hortolândia", "3519071"], + ["Iacanga", "3519105"], + ["Iacri", "3519204"], + ["Iaras", "3519253"], + ["Ibaté", "3519303"], + ["Ibirá", "3519402"], + ["Ibirarema", "3519501"], + ["Ibitinga", "3519600"], + ["Ibiúna", "3519709"], + ["Icém", "3519808"], + ["Iepê", "3519907"], + ["Igaraçu do Tietê", "3520004"], + ["Igarapava", "3520103"], + ["Igaratá", "3520202"], + ["Iguape", "3520301"], + ["Ilha Comprida", "3520426"], + ["Ilha Solteira", "3520442"], + ["Ilhabela", "3520400"], + ["Indaiatuba", "3520509"], + ["Indiana", "3520608"], + ["Indiaporã", "3520707"], + ["Inúbia Paulista", "3520806"], + ["Ipaussu", "3520905"], + ["Iperó", "3521002"], + ["Ipeúna", "3521101"], + ["Ipiguá", "3521150"], + ["Iporanga", "3521200"], + ["Ipuã", "3521309"], + ["Iracemápolis", "3521408"], + ["Irapuã", "3521507"], + ["Irapuru", "3521606"], + ["Itaberá", "3521705"], + ["Itaí", "3521804"], + ["Itajobi", "3521903"], + ["Itaju", "3522000"], + ["Itanhaém", "3522109"], + ["Itaoca", "3522158"], + ["Itapecerica da Serra", "3522208"], + ["Itapetininga", "3522307"], + ["Itapeva", "3522406"], + ["Itapevi", "3522505"], + ["Itapira", "3522604"], + ["Itapirapuã Paulista", "3522653"], + ["Itápolis", "3522703"], + ["Itaporanga", "3522802"], + ["Itapuí", "3522901"], + ["Itapura", "3523008"], + ["Itaquaquecetuba", "3523107"], + ["Itararé", "3523206"], + ["Itariri", "3523305"], + ["Itatiba", "3523404"], + ["Itatinga", "3523503"], + ["Itirapina", "3523602"], + ["Itirapuã", "3523701"], + ["Itobi", "3523800"], + ["Itu", "3523909"], + ["Itupeva", "3524006"], + ["Ituverava", "3524105"], + ["Jaborandi", "3524204"], + ["Jaboticabal", "3524303"], + ["Jacareí", "3524402"], + ["Jaci", "3524501"], + ["Jacupiranga", "3524600"], + ["Jaguariúna", "3524709"], + ["Jales", "3524808"], + ["Jambeiro", "3524907"], + ["Jandira", "3525003"], + ["Jardinópolis", "3525102"], + ["Jarinu", "3525201"], + ["Jaú", "3525300"], + ["Jeriquara", "3525409"], + ["Joanópolis", "3525508"], + ["João Ramalho", "3525607"], + ["José Bonifácio", "3525706"], + ["Júlio Mesquita", "3525805"], + ["Jumirim", "3525854"], + ["Jundiaí", "3525904"], + ["Junqueirópolis", "3526001"], + ["Juquiá", "3526100"], + ["Juquitiba", "3526209"], + ["Lagoinha", "3526308"], + ["Laranjal Paulista", "3526407"], + ["Lavínia", "3526506"], + ["Lavrinhas", "3526605"], + ["Leme", "3526704"], + ["Lençóis Paulista", "3526803"], + ["Limeira", "3526902"], + ["Lindóia", "3527009"], + ["Lins", "3527108"], + ["Lorena", "3527207"], + ["Lourdes", "3527256"], + ["Louveira", "3527306"], + ["Lucélia", "3527405"], + ["Lucianópolis", "3527504"], + ["Luís Antônio", "3527603"], + ["Luiziânia", "3527702"], + ["Lupércio", "3527801"], + ["Lutécia", "3527900"], + ["Macatuba", "3528007"], + ["Macaubal", "3528106"], + ["Macedônia", "3528205"], + ["Magda", "3528304"], + ["Mairinque", "3528403"], + ["Mairiporã", "3528502"], + ["Manduri", "3528601"], + ["Marabá Paulista", "3528700"], + ["Maracaí", "3528809"], + ["Marapoama", "3528858"], + ["Mariápolis", "3528908"], + ["Marília", "3529005"], + ["Marinópolis", "3529104"], + ["Martinópolis", "3529203"], + ["Matão", "3529302"], + ["Mauá", "3529401"], + ["Mendonça", "3529500"], + ["Meridiano", "3529609"], + ["Mesópolis", "3529658"], + ["Miguelópolis", "3529708"], + ["Mineiros do Tietê", "3529807"], + ["Mira Estrela", "3530003"], + ["Miracatu", "3529906"], + ["Mirandópolis", "3530102"], + ["Mirante do Paranapanema", "3530201"], + ["Mirassol", "3530300"], + ["Mirassolândia", "3530409"], + ["Mococa", "3530508"], + ["Mogi das Cruzes", "3530607"], + ["Mogi Guaçu", "3530706"], + ["Mogi Mirim", "3530805"], + ["Mombuca", "3530904"], + ["Monções", "3531001"], + ["Mongaguá", "3531100"], + ["Monte Alegre do Sul", "3531209"], + ["Monte Alto", "3531308"], + ["Monte Aprazível", "3531407"], + ["Monte Azul Paulista", "3531506"], + ["Monte Castelo", "3531605"], + ["Monte Mor", "3531803"], + ["Monteiro Lobato", "3531704"], + ["Morro Agudo", "3531902"], + ["Morungaba", "3532009"], + ["Motuca", "3532058"], + ["Murutinga do Sul", "3532108"], + ["Nantes", "3532157"], + ["Narandiba", "3532207"], + ["Natividade da Serra", "3532306"], + ["Nazaré Paulista", "3532405"], + ["Neves Paulista", "3532504"], + ["Nhandeara", "3532603"], + ["Nipoã", "3532702"], + ["Nova Aliança", "3532801"], + ["Nova Campina", "3532827"], + ["Nova Canaã Paulista", "3532843"], + ["Nova Castilho", "3532868"], + ["Nova Europa", "3532900"], + ["Nova Granada", "3533007"], + ["Nova Guataporanga", "3533106"], + ["Nova Independência", "3533205"], + ["Nova Luzitânia", "3533304"], + ["Nova Odessa", "3533403"], + ["Novais", "3533254"], + ["Novo Horizonte", "3533502"], + ["Nuporanga", "3533601"], + ["Ocauçu", "3533700"], + ["Óleo", "3533809"], + ["Olímpia", "3533908"], + ["Onda Verde", "3534005"], + ["Oriente", "3534104"], + ["Orindiúva", "3534203"], + ["Orlândia", "3534302"], + ["Osasco", "3534401"], + ["Oscar Bressane", "3534500"], + ["Osvaldo Cruz", "3534609"], + ["Ourinhos", "3534708"], + ["Ouro Verde", "3534807"], + ["Ouroeste", "3534757"], + ["Pacaembu", "3534906"], + ["Palestina", "3535002"], + ["Palmares Paulista", "3535101"], + ["Palmeira d'Oeste", "3535200"], + ["Palmital", "3535309"], + ["Panorama", "3535408"], + ["Paraguaçu Paulista", "3535507"], + ["Paraibuna", "3535606"], + ["Paraíso", "3535705"], + ["Paranapanema", "3535804"], + ["Paranapuã", "3535903"], + ["Parapuã", "3536000"], + ["Pardinho", "3536109"], + ["Pariquera-Açu", "3536208"], + ["Parisi", "3536257"], + ["Patrocínio Paulista", "3536307"], + ["Paulicéia", "3536406"], + ["Paulínia", "3536505"], + ["Paulistânia", "3536570"], + ["Paulo de Faria", "3536604"], + ["Pederneiras", "3536703"], + ["Pedra Bela", "3536802"], + ["Pedranópolis", "3536901"], + ["Pedregulho", "3537008"], + ["Pedreira", "3537107"], + ["Pedrinhas Paulista", "3537156"], + ["Pedro de Toledo", "3537206"], + ["Penápolis", "3537305"], + ["Pereira Barreto", "3537404"], + ["Pereiras", "3537503"], + ["Peruíbe", "3537602"], + ["Piacatu", "3537701"], + ["Piedade", "3537800"], + ["Pilar do Sul", "3537909"], + ["Pindamonhangaba", "3538006"], + ["Pindorama", "3538105"], + ["Pinhalzinho", "3538204"], + ["Piquerobi", "3538303"], + ["Piquete", "3538501"], + ["Piracaia", "3538600"], + ["Piracicaba", "3538709"], + ["Piraju", "3538808"], + ["Pirajuí", "3538907"], + ["Pirangi", "3539004"], + ["Pirapora do Bom Jesus", "3539103"], + ["Pirapozinho", "3539202"], + ["Pirassununga", "3539301"], + ["Piratininga", "3539400"], + ["Pitangueiras", "3539509"], + ["Planalto", "3539608"], + ["Platina", "3539707"], + ["Poá", "3539806"], + ["Poloni", "3539905"], + ["Pompéia", "3540002"], + ["Pongaí", "3540101"], + ["Pontal", "3540200"], + ["Pontalinda", "3540259"], + ["Pontes Gestal", "3540309"], + ["Populina", "3540408"], + ["Porangaba", "3540507"], + ["Porto Feliz", "3540606"], + ["Porto Ferreira", "3540705"], + ["Potim", "3540754"], + ["Potirendaba", "3540804"], + ["Pracinha", "3540853"], + ["Pradópolis", "3540903"], + ["Praia Grande", "3541000"], + ["Pratânia", "3541059"], + ["Presidente Alves", "3541109"], + ["Presidente Bernardes", "3541208"], + ["Presidente Epitácio", "3541307"], + ["Presidente Prudente", "3541406"], + ["Presidente Venceslau", "3541505"], + ["Promissão", "3541604"], + ["Quadra", "3541653"], + ["Quatá", "3541703"], + ["Queiroz", "3541802"], + ["Queluz", "3541901"], + ["Quintana", "3542008"], + ["Rafard", "3542107"], + ["Rancharia", "3542206"], + ["Redenção da Serra", "3542305"], + ["Regente Feijó", "3542404"], + ["Reginópolis", "3542503"], + ["Registro", "3542602"], + ["Restinga", "3542701"], + ["Ribeira", "3542800"], + ["Ribeirão Bonito", "3542909"], + ["Ribeirão Branco", "3543006"], + ["Ribeirão Corrente", "3543105"], + ["Ribeirão do Sul", "3543204"], + ["Ribeirão dos Índios", "3543238"], + ["Ribeirão Grande", "3543253"], + ["Ribeirão Pires", "3543303"], + ["Ribeirão Preto", "3543402"], + ["Rifaina", "3543600"], + ["Rincão", "3543709"], + ["Rinópolis", "3543808"], + ["Rio Claro", "3543907"], + ["Rio das Pedras", "3544004"], + ["Rio Grande da Serra", "3544103"], + ["Riolândia", "3544202"], + ["Riversul", "3543501"], + ["Rosana", "3544251"], + ["Roseira", "3544301"], + ["Rubiácea", "3544400"], + ["Rubinéia", "3544509"], + ["Sabino", "3544608"], + ["Sagres", "3544707"], + ["Sales", "3544806"], + ["Sales Oliveira", "3544905"], + ["Salesópolis", "3545001"], + ["Salmourão", "3545100"], + ["Saltinho", "3545159"], + ["Salto", "3545209"], + ["Salto de Pirapora", "3545308"], + ["Salto Grande", "3545407"], + ["Sandovalina", "3545506"], + ["Santa Adélia", "3545605"], + ["Santa Albertina", "3545704"], + ["Santa Bárbara d'Oeste", "3545803"], + ["Santa Branca", "3546009"], + ["Santa Clara d'Oeste", "3546108"], + ["Santa Cruz da Conceição", "3546207"], + ["Santa Cruz da Esperança", "3546256"], + ["Santa Cruz das Palmeiras", "3546306"], + ["Santa Cruz do Rio Pardo", "3546405"], + ["Santa Ernestina", "3546504"], + ["Santa Fé do Sul", "3546603"], + ["Santa Gertrudes", "3546702"], + ["Santa Isabel", "3546801"], + ["Santa Lúcia", "3546900"], + ["Santa Maria da Serra", "3547007"], + ["Santa Mercedes", "3547106"], + ["Santa Rita d'Oeste", "3547403"], + ["Santa Rita do Passa Quatro", "3547502"], + ["Santa Rosa de Viterbo", "3547601"], + ["Santa Salete", "3547650"], + ["Santana da Ponte Pensa", "3547205"], + ["Santana de Parnaíba", "3547304"], + ["Santo Anastácio", "3547700"], + ["Santo André", "3547809"], + ["Santo Antônio da Alegria", "3547908"], + ["Santo Antônio de Posse", "3548005"], + ["Santo Antônio do Aracanguá", "3548054"], + ["Santo Antônio do Jardim", "3548104"], + ["Santo Antônio do Pinhal", "3548203"], + ["Santo Expedito", "3548302"], + ["Santópolis do Aguapeí", "3548401"], + ["Santos", "3548500"], + ["São Bento do Sapucaí", "3548609"], + ["São Bernardo do Campo", "3548708"], + ["São Caetano do Sul", "3548807"], + ["São Carlos", "3548906"], + ["São Francisco", "3549003"], + ["São João da Boa Vista", "3549102"], + ["São João das Duas Pontes", "3549201"], + ["São João de Iracema", "3549250"], + ["São João do Pau d'Alho", "3549300"], + ["São Joaquim da Barra", "3549409"], + ["São José da Bela Vista", "3549508"], + ["São José do Barreiro", "3549607"], + ["São José do Rio Pardo", "3549706"], + ["São José do Rio Preto", "3549805"], + ["São José dos Campos", "3549904"], + ["São Lourenço da Serra", "3549953"], + ["São Luiz do Paraitinga", "3550001"], + ["São Manuel", "3550100"], + ["São Miguel Arcanjo", "3550209"], + ["São Paulo", "3550308"], + ["São Pedro", "3550407"], + ["São Pedro do Turvo", "3550506"], + ["São Roque", "3550605"], + ["São Sebastião", "3550704"], + ["São Sebastião da Grama", "3550803"], + ["São Simão", "3550902"], + ["São Vicente", "3551009"], + ["Sarapuí", "3551108"], + ["Sarutaiá", "3551207"], + ["Sebastianópolis do Sul", "3551306"], + ["Serra Azul", "3551405"], + ["Serra Negra", "3551603"], + ["Serrana", "3551504"], + ["Sertãozinho", "3551702"], + ["Sete Barras", "3551801"], + ["Severínia", "3551900"], + ["Silveiras", "3552007"], + ["Socorro", "3552106"], + ["Sorocaba", "3552205"], + ["Sud Mennucci", "3552304"], + ["Sumaré", "3552403"], + ["Suzanápolis", "3552551"], + ["Suzano", "3552502"], + ["Tabapuã", "3552601"], + ["Tabatinga", "3552700"], + ["Taboão da Serra", "3552809"], + ["Taciba", "3552908"], + ["Taguaí", "3553005"], + ["Taiaçu", "3553104"], + ["Taiúva", "3553203"], + ["Tambaú", "3553302"], + ["Tanabi", "3553401"], + ["Tapiraí", "3553500"], + ["Tapiratiba", "3553609"], + ["Taquaral", "3553658"], + ["Taquaritinga", "3553708"], + ["Taquarituba", "3553807"], + ["Taquarivaí", "3553856"], + ["Tarabai", "3553906"], + ["Tarumã", "3553955"], + ["Tatuí", "3554003"], + ["Taubaté", "3554102"], + ["Tejupá", "3554201"], + ["Teodoro Sampaio", "3554300"], + ["Terra Roxa", "3554409"], + ["Tietê", "3554508"], + ["Timburi", "3554607"], + ["Torre de Pedra", "3554656"], + ["Torrinha", "3554706"], + ["Trabiju", "3554755"], + ["Tremembé", "3554805"], + ["Três Fronteiras", "3554904"], + ["Tuiuti", "3554953"], + ["Tupã", "3555000"], + ["Tupi Paulista", "3555109"], + ["Turiúba", "3555208"], + ["Turmalina", "3555307"], + ["Ubarana", "3555356"], + ["Ubatuba", "3555406"], + ["Ubirajara", "3555505"], + ["Uchoa", "3555604"], + ["União Paulista", "3555703"], + ["Urânia", "3555802"], + ["Uru", "3555901"], + ["Urupês", "3556008"], + ["Valentim Gentil", "3556107"], + ["Valinhos", "3556206"], + ["Valparaíso", "3556305"], + ["Vargem", "3556354"], + ["Vargem Grande do Sul", "3556404"], + ["Vargem Grande Paulista", "3556453"], + ["Várzea Paulista", "3556503"], + ["Vera Cruz", "3556602"], + ["Vinhedo", "3556701"], + ["Viradouro", "3556800"], + ["Vista Alegre do Alto", "3556909"], + ["Vitória Brasil", "3556958"], + ["Votorantim", "3557006"], + ["Votuporanga", "3557105"], + ["Zacarias", "3557154"], ], TO: [ - "Abreulândia", - "Aguiarnópolis", - "Aliança do Tocantins", - "Almas", - "Alvorada", - "Ananás", - "Angico", - "Aparecida do Rio Negro", - "Aragominas", - "Araguacema", - "Araguaçu", - "Araguaína", - "Araguanã", - "Araguatins", - "Arapoema", - "Arraias", - "Augustinópolis", - "Aurora do Tocantins", - "Axixá do Tocantins", - "Babaçulândia", - "Bandeirantes do Tocantins", - "Barra do Ouro", - "Barrolândia", - "Bernardo Sayão", - "Bom Jesus do Tocantins", - "Brasilândia do Tocantins", - "Brejinho de Nazaré", - "Buriti do Tocantins", - "Cachoeirinha", - "Campos Lindos", - "Cariri do Tocantins", - "Carmolândia", - "Carrasco Bonito", - "Caseara", - "Centenário", - "Chapada da Natividade", - "Chapada de Areia", - "Colinas do Tocantins", - "Colméia", - "Combinado", - "Conceição do Tocantins", - "Couto Magalhães", - "Cristalândia", - "Crixás do Tocantins", - "Darcinópolis", - "Dianópolis", - "Divinópolis do Tocantins", - "Dois Irmãos do Tocantins", - "Dueré", - "Esperantina", - "Fátima", - "Figueirópolis", - "Filadélfia", - "Formoso do Araguaia", - "Goianorte", - "Goiatins", - "Guaraí", - "Gurupi", - "Ipueiras", - "Itacajá", - "Itaguatins", - "Itapiratins", - "Itaporã do Tocantins", - "Jaú do Tocantins", - "Juarina", - "Lagoa da Confusão", - "Lagoa do Tocantins", - "Lajeado", - "Lavandeira", - "Lizarda", - "Luzinópolis", - "Marianópolis do Tocantins", - "Mateiros", - "Maurilândia do Tocantins", - "Miracema do Tocantins", - "Miranorte", - "Monte do Carmo", - "Monte Santo do Tocantins", - "Muricilândia", - "Natividade", - "Nazaré", - "Nova Olinda", - "Nova Rosalândia", - "Novo Acordo", - "Novo Alegre", - "Novo Jardim", - "Oliveira de Fátima", - "Palmas", - "Palmeirante", - "Palmeiras do Tocantins", - "Palmeirópolis", - "Paraíso do Tocantins", - "Paranã", - "Pau D'Arco", - "Pedro Afonso", - "Peixe", - "Pequizeiro", - "Pindorama do Tocantins", - "Piraquê", - "Pium", - "Ponte Alta do Bom Jesus", - "Ponte Alta do Tocantins", - "Porto Alegre do Tocantins", - "Porto Nacional", - "Praia Norte", - "Presidente Kennedy", - "Pugmil", - "Recursolândia", - "Riachinho", - "Rio da Conceição", - "Rio dos Bois", - "Rio Sono", - "Sampaio", - "Sandolândia", - "Santa Fé do Araguaia", - "Santa Maria do Tocantins", - "Santa Rita do Tocantins", - "Santa Rosa do Tocantins", - "Santa Tereza do Tocantins", - "Santa Terezinha do Tocantins", - "São Bento do Tocantins", - "São Félix do Tocantins", - "São Miguel do Tocantins", - "São Salvador do Tocantins", - "São Sebastião do Tocantins", - "São Valério", - "Silvanópolis", - "Sítio Novo do Tocantins", - "Sucupira", - "Tabocão", - "Taguatinga", - "Taipas do Tocantins", - "Talismã", - "Tocantínia", - "Tocantinópolis", - "Tupirama", - "Tupiratins", - "Wanderlândia", - "Xambioá", + ["Abreulândia", "1700251"], + ["Aguiarnópolis", "1700301"], + ["Aliança do Tocantins", "1700350"], + ["Almas", "1700400"], + ["Alvorada", "1700707"], + ["Ananás", "1701002"], + ["Angico", "1701051"], + ["Aparecida do Rio Negro", "1701101"], + ["Aragominas", "1701309"], + ["Araguacema", "1701903"], + ["Araguaçu", "1702000"], + ["Araguaína", "1702109"], + ["Araguanã", "1702158"], + ["Araguatins", "1702208"], + ["Arapoema", "1702307"], + ["Arraias", "1702406"], + ["Augustinópolis", "1702554"], + ["Aurora do Tocantins", "1702703"], + ["Axixá do Tocantins", "1702901"], + ["Babaçulândia", "1703008"], + ["Bandeirantes do Tocantins", "1703057"], + ["Barra do Ouro", "1703073"], + ["Barrolândia", "1703107"], + ["Bernardo Sayão", "1703206"], + ["Bom Jesus do Tocantins", "1703305"], + ["Brasilândia do Tocantins", "1703602"], + ["Brejinho de Nazaré", "1703701"], + ["Buriti do Tocantins", "1703800"], + ["Cachoeirinha", "1703826"], + ["Campos Lindos", "1703842"], + ["Cariri do Tocantins", "1703867"], + ["Carmolândia", "1703883"], + ["Carrasco Bonito", "1703891"], + ["Caseara", "1703909"], + ["Centenário", "1704105"], + ["Chapada da Natividade", "1705102"], + ["Chapada de Areia", "1704600"], + ["Colinas do Tocantins", "1705508"], + ["Colméia", "1716703"], + ["Combinado", "1705557"], + ["Conceição do Tocantins", "1705607"], + ["Couto Magalhães", "1706001"], + ["Cristalândia", "1706100"], + ["Crixás do Tocantins", "1706258"], + ["Darcinópolis", "1706506"], + ["Dianópolis", "1707009"], + ["Divinópolis do Tocantins", "1707108"], + ["Dois Irmãos do Tocantins", "1707207"], + ["Dueré", "1707306"], + ["Esperantina", "1707405"], + ["Fátima", "1707553"], + ["Figueirópolis", "1707652"], + ["Filadélfia", "1707702"], + ["Formoso do Araguaia", "1708205"], + ["Goianorte", "1708304"], + ["Goiatins", "1709005"], + ["Guaraí", "1709302"], + ["Gurupi", "1709500"], + ["Ipueiras", "1709807"], + ["Itacajá", "1710508"], + ["Itaguatins", "1710706"], + ["Itapiratins", "1710904"], + ["Itaporã do Tocantins", "1711100"], + ["Jaú do Tocantins", "1711506"], + ["Juarina", "1711803"], + ["Lagoa da Confusão", "1711902"], + ["Lagoa do Tocantins", "1711951"], + ["Lajeado", "1712009"], + ["Lavandeira", "1712157"], + ["Lizarda", "1712405"], + ["Luzinópolis", "1712454"], + ["Marianópolis do Tocantins", "1712504"], + ["Mateiros", "1712702"], + ["Maurilândia do Tocantins", "1712801"], + ["Miracema do Tocantins", "1713205"], + ["Miranorte", "1713304"], + ["Monte do Carmo", "1713601"], + ["Monte Santo do Tocantins", "1713700"], + ["Muricilândia", "1713957"], + ["Natividade", "1714203"], + ["Nazaré", "1714302"], + ["Nova Olinda", "1714880"], + ["Nova Rosalândia", "1715002"], + ["Novo Acordo", "1715101"], + ["Novo Alegre", "1715150"], + ["Novo Jardim", "1715259"], + ["Oliveira de Fátima", "1715507"], + ["Palmas", "1721000"], + ["Palmeirante", "1715705"], + ["Palmeiras do Tocantins", "1713809"], + ["Palmeirópolis", "1715754"], + ["Paraíso do Tocantins", "1716109"], + ["Paranã", "1716208"], + ["Pau D'Arco", "1716307"], + ["Pedro Afonso", "1716505"], + ["Peixe", "1716604"], + ["Pequizeiro", "1716653"], + ["Pindorama do Tocantins", "1717008"], + ["Piraquê", "1717206"], + ["Pium", "1717503"], + ["Ponte Alta do Bom Jesus", "1717800"], + ["Ponte Alta do Tocantins", "1717909"], + ["Porto Alegre do Tocantins", "1718006"], + ["Porto Nacional", "1718204"], + ["Praia Norte", "1718303"], + ["Presidente Kennedy", "1718402"], + ["Pugmil", "1718451"], + ["Recursolândia", "1718501"], + ["Riachinho", "1718550"], + ["Rio da Conceição", "1718659"], + ["Rio dos Bois", "1718709"], + ["Rio Sono", "1718758"], + ["Sampaio", "1718808"], + ["Sandolândia", "1718840"], + ["Santa Fé do Araguaia", "1718865"], + ["Santa Maria do Tocantins", "1718881"], + ["Santa Rita do Tocantins", "1718899"], + ["Santa Rosa do Tocantins", "1718907"], + ["Santa Tereza do Tocantins", "1719004"], + ["Santa Terezinha do Tocantins", "1720002"], + ["São Bento do Tocantins", "1720101"], + ["São Félix do Tocantins", "1720150"], + ["São Miguel do Tocantins", "1720200"], + ["São Salvador do Tocantins", "1720259"], + ["São Sebastião do Tocantins", "1720309"], + ["São Valério", "1720499"], + ["Silvanópolis", "1720655"], + ["Sítio Novo do Tocantins", "1720804"], + ["Sucupira", "1720853"], + ["Tabocão", "1708254"], + ["Taguatinga", "1720903"], + ["Taipas do Tocantins", "1720937"], + ["Talismã", "1720978"], + ["Tocantínia", "1721109"], + ["Tocantinópolis", "1721208"], + ["Tupirama", "1721257"], + ["Tupiratins", "1721307"], + ["Wanderlândia", "1722081"], + ["Xambioá", "1722107"], ], -} as const; +}; diff --git a/src/get-cities/get-cities.test.ts b/src/get-cities/get-cities.test.ts index 75c82320..9c0fb2c8 100644 --- a/src/get-cities/get-cities.test.ts +++ b/src/get-cities/get-cities.test.ts @@ -3,28 +3,76 @@ import { describe, expect, it } from "../_internals/test/runtime"; import { getStates } from "../get-states/get-states"; import { getCities } from "./get-cities"; -/** - * https://cidades.ibge.gov.br/brasil/panorama - */ -const NUMBER_OF_BRAZILIAN_CITIES = 5570; +const NUMBER_OF_BRAZILIAN_CITIES = 5571; + +const NUMBER_OF_BRAZILIAN_STATES = 27; + +const KNOWN_STATE_CITY_COUNTS: Record = { + MG: 853, + RS: 497, + SP: 645, +}; describe("getCities", () => { it("should return cities of all states", () => { expect(getCities().length).toEqual(NUMBER_OF_BRAZILIAN_CITIES); }); + it("should sort the combined list with the pt-BR comparator", () => { + const cities = getCities(); + const sorted = [...cities].sort((a, b) => a.localeCompare(b, "pt-BR")); + + expect(cities).toEqual(sorted); + }); + it("should return empty array if state does not exist", () => { // @ts-expect-error expect(getCities("ACC")).toEqual([]); }); + it("should return empty array for inherited Object property names instead of throwing", () => { + // @ts-expect-error + expect(getCities("toString")).toEqual([]); + // @ts-expect-error + expect(getCities("constructor")).toEqual([]); + }); + + it("should return a fresh copy so mutating the result does not affect subsequent calls", () => { + const all = getCities(); + all.push("MUTATED CITY"); + + expect(getCities().length).toEqual(NUMBER_OF_BRAZILIAN_CITIES); + + const spCities = getCities("SP"); + spCities.push("MUTATED CITY"); + + expect(getCities("SP").length).toEqual(KNOWN_STATE_CITY_COUNTS.SP); + }); + + describe("data integrity (IBGE 2022, https://cidades.ibge.gov.br/brasil/panorama)", () => { + it(`should have exactly ${NUMBER_OF_BRAZILIAN_STATES} states with cities`, () => { + expect(Object.keys(DATA).length).toBe(NUMBER_OF_BRAZILIAN_STATES); + }); + + it(`should total exactly ${NUMBER_OF_BRAZILIAN_CITIES} cities across all states`, () => { + const total = Object.values(DATA).reduce((sum, cities) => sum + cities.length, 0); + expect(total).toBe(NUMBER_OF_BRAZILIAN_CITIES); + }); + + for (const [stateCode, expectedCount] of Object.entries(KNOWN_STATE_CITY_COUNTS)) { + it(`should have ${expectedCount} cities for ${stateCode}`, () => { + expect(getCities(stateCode as never).length).toBe(expectedCount); + }); + } + }); + describe("return cities from states", () => { const states = getStates(); for (const { code } of states) { it(`should return cities from code ${code}`, () => { - const stateCities = DATA[code]; - expect(getCities(code)).toMatchObject(stateCities); + const stateCityNames = DATA[code].map(([name]) => name); + expect(getCities(code)).toEqual(stateCityNames); }); } }); diff --git a/src/get-cities/get-cities.ts b/src/get-cities/get-cities.ts index 4e783642..cb94028c 100644 --- a/src/get-cities/get-cities.ts +++ b/src/get-cities/get-cities.ts @@ -1,23 +1,40 @@ import { DATA as CITIES_DATA } from "../_internals/constants/cities"; import type { StateCode } from "../_internals/constants/states"; +let allCitiesCache: string[] | undefined; + /** * Returns a list of city names for a given Brazilian state, or all cities if no state is specified. * * If a state code is provided, the function returns its cities sorted alphabetically. - * If no state is provided, it returns all cities from all states, sorted alphabetically. + * If no state is provided, it returns all cities from all states, sorted with + * `localeCompare` in the "pt-BR" locale so accented names land where a Brazilian reader + * expects them (the combined, sorted list is computed once and cached for subsequent calls). * * @param state - The code of the Brazilian state to filter cities by. Optional. * @returns An array of city names, sorted alphabetically. Returns an empty array if the state is not found. + * + * @example + * ```typescript + * getCities("SP")[0]; // "Adamantina" + * getCities().length; // every city of every state + * ``` + * + * @see Official: https://servicodados.ibge.gov.br/api/docs/localidades */ export const getCities = (state?: StateCode): string[] => { if (!state) { - return Object.values(CITIES_DATA) - .flat() - .sort((a, b) => a.localeCompare(b)); + if (!allCitiesCache) { + allCitiesCache = Object.values(CITIES_DATA) + .flat() + .map(([name]) => name) + .sort((a, b) => a.localeCompare(b, "pt-BR")); + } + + return [...allCitiesCache]; } - if (!(state in CITIES_DATA)) return []; + if (!Object.hasOwn(CITIES_DATA, state)) return []; - return [...CITIES_DATA[state]]; + return CITIES_DATA[state].map(([name]) => name); }; From 5213e653ad7aae6d7d5fc213098c6180d4c79a56 Mon Sep 17 00:00:00 2001 From: Hyan Mandian <5044101+hyanmandian@users.noreply.github.com> Date: Fri, 11 Sep 2026 17:32:07 -0300 Subject: [PATCH 06/13] fix(license-plate): strip mask characters and add Mercosul conversion isValidLicensePlate("abc 1234") -> true; mask characters are stripped from any position while extra characters are still rejected. Export the LicensePlateFormat type (GenerateLicensePlateFormat is now an alias of it). Add convertLicensePlateToMercosul: "LLLNNNN" -> "LLLNLNN" (5th digit -> letter 0->A..9->J), or null when the input is not an old-format plate. --- .../constants.ts | 18 ++++++ .../convert-license-plate-to-mercosul.test.ts | 64 +++++++++++++++++++ .../convert-license-plate-to-mercosul.ts | 35 ++++++++++ .../format-license-plate.test.ts | 6 ++ .../format-license-plate.ts | 22 ++++++- .../generate-license-plate.test.ts | 13 +++- .../generate-license-plate.ts | 33 +++++++++- src/get-format-license-plate/constants.ts | 3 +- .../get-format-license-plate.test.ts | 13 +++- .../get-format-license-plate.ts | 38 +++++++++-- .../is-valid-license-plate.test.ts | 19 +++++- .../is-valid-license-plate.ts | 26 ++++---- .../parse-license-plate.ts | 16 ++++- 13 files changed, 280 insertions(+), 26 deletions(-) create mode 100644 src/convert-license-plate-to-mercosul/constants.ts create mode 100644 src/convert-license-plate-to-mercosul/convert-license-plate-to-mercosul.test.ts create mode 100644 src/convert-license-plate-to-mercosul/convert-license-plate-to-mercosul.ts diff --git a/src/convert-license-plate-to-mercosul/constants.ts b/src/convert-license-plate-to-mercosul/constants.ts new file mode 100644 index 00000000..917fb309 --- /dev/null +++ b/src/convert-license-plate-to-mercosul/constants.ts @@ -0,0 +1,18 @@ +/** + * Official digit to letter conversion table used to turn an old format plate's 5th character + * into the Mercosul format's embedded letter (0=A, 1=B, ..., 9=J). + * + * @see Official: https://www.gov.br/transportes/pt-br/assuntos/denatran (Resolução CONTRAN 780/2019, anexo) + */ +export const DIGIT_TO_MERCOSUL_LETTER: Record = { + "0": "A", + "1": "B", + "2": "C", + "3": "D", + "4": "E", + "5": "F", + "6": "G", + "7": "H", + "8": "I", + "9": "J", +}; diff --git a/src/convert-license-plate-to-mercosul/convert-license-plate-to-mercosul.test.ts b/src/convert-license-plate-to-mercosul/convert-license-plate-to-mercosul.test.ts new file mode 100644 index 00000000..f3b5fcc9 --- /dev/null +++ b/src/convert-license-plate-to-mercosul/convert-license-plate-to-mercosul.test.ts @@ -0,0 +1,64 @@ +import { describe, expect, test } from "../_internals/test/runtime"; +import { convertLicensePlateToMercosul } from "./convert-license-plate-to-mercosul"; + +describe("convertLicensePlateToMercosul", () => { + describe("should return an empty string", () => { + test("when it is null", () => { + // @ts-expect-error + expect(convertLicensePlateToMercosul(null)).toBe(""); + }); + + test("when it is undefined", () => { + // @ts-expect-error + expect(convertLicensePlateToMercosul(undefined)).toBe(""); + }); + + test("when it is an empty string", () => { + expect(convertLicensePlateToMercosul("")).toBe(""); + }); + + test("when it is already a Mercosul car plate", () => { + expect(convertLicensePlateToMercosul("ABC1D23")).toBe(""); + }); + + test("when it is a Mercosul motorcycle plate", () => { + expect(convertLicensePlateToMercosul("ABC12D3")).toBe(""); + }); + + test("when it does not match any license plate format", () => { + expect(convertLicensePlateToMercosul("invalid")).toBe(""); + }); + + test("when it is an old format plate followed by extra characters", () => { + expect(convertLicensePlateToMercosul("ABC1234EXTRA")).toBe(""); + expect(convertLicensePlateToMercosul("ABC12345")).toBe(""); + }); + + test("when it is too short", () => { + expect(convertLicensePlateToMercosul("ABC123")).toBe(""); + }); + }); + + describe("should return the converted plate", () => { + test("for a plate ending in each digit, per the official conversion table", () => { + expect(convertLicensePlateToMercosul("ABC0000")).toBe("ABC0A00"); + expect(convertLicensePlateToMercosul("ABC1111")).toBe("ABC1B11"); + expect(convertLicensePlateToMercosul("ABC2222")).toBe("ABC2C22"); + expect(convertLicensePlateToMercosul("ABC3333")).toBe("ABC3D33"); + expect(convertLicensePlateToMercosul("ABC4444")).toBe("ABC4E44"); + expect(convertLicensePlateToMercosul("ABC5555")).toBe("ABC5F55"); + expect(convertLicensePlateToMercosul("ABC6666")).toBe("ABC6G66"); + expect(convertLicensePlateToMercosul("ABC7777")).toBe("ABC7H77"); + expect(convertLicensePlateToMercosul("ABC8888")).toBe("ABC8I88"); + expect(convertLicensePlateToMercosul("ABC9999")).toBe("ABC9J99"); + }); + + test("for a lowercase plate", () => { + expect(convertLicensePlateToMercosul("abc1234")).toBe("ABC1C34"); + }); + + test("for a plate with the old format hyphen mask", () => { + expect(convertLicensePlateToMercosul("ABC-1234")).toBe("ABC1C34"); + }); + }); +}); diff --git a/src/convert-license-plate-to-mercosul/convert-license-plate-to-mercosul.ts b/src/convert-license-plate-to-mercosul/convert-license-plate-to-mercosul.ts new file mode 100644 index 00000000..79171637 --- /dev/null +++ b/src/convert-license-plate-to-mercosul/convert-license-plate-to-mercosul.ts @@ -0,0 +1,35 @@ +import { getFormatLicensePlate } from "../get-format-license-plate/get-format-license-plate"; +import { parseLicensePlate } from "../parse-license-plate/parse-license-plate"; +import { DIGIT_TO_MERCOSUL_LETTER } from "./constants"; + +/** + * Converts an old format Brazilian license plate ("LLLNNNN") to the Mercosul format + * ("LLLNLNN"), following the official conversion table: the digit in the 5th position (the + * 2nd digit of the 4 digit number) becomes a letter, `0` through `9` mapping to `A` through + * `J`, and every other character is kept as is. + * + * @param {string} value - The old format license plate to be converted. + * @returns {string} The converted Mercosul format plate, or `""` when `value` is not a valid + * old format ("LLLNNNN") license plate. + * + * @example + * ```typescript + * convertLicensePlateToMercosul("ABC1234"); // "ABC1C34" + * convertLicensePlateToMercosul("abc-1234"); // "ABC1C34" + * convertLicensePlateToMercosul("ABC1D23"); // "" (already Mercosul) + * convertLicensePlateToMercosul("invalid"); // "" + * ``` + * + * @see Official: https://www.gov.br/transportes/pt-br/assuntos/denatran (Resolução CONTRAN 780/2019, anexo) + */ +export const convertLicensePlateToMercosul = (value: string): string => { + if (typeof value !== "string" || value === "") return ""; + + if (getFormatLicensePlate(value) !== "LLLNNNN") return ""; + + const parsed = parseLicensePlate(value); + + const letter = DIGIT_TO_MERCOSUL_LETTER[parsed[4]]; + + return `${parsed.slice(0, 4)}${letter}${parsed.slice(5)}`; +}; diff --git a/src/format-license-plate/format-license-plate.test.ts b/src/format-license-plate/format-license-plate.test.ts index 4fdf7de9..03cd617d 100644 --- a/src/format-license-plate/format-license-plate.test.ts +++ b/src/format-license-plate/format-license-plate.test.ts @@ -28,4 +28,10 @@ describe("formatLicensePlate", () => { expect(formatLicensePlate("abc-1234")).toBe("ABC-1234"); expect(formatLicensePlate("abc1-d23")).toBe("ABC1D23"); }); + + it("should return an empty string when the tail matches no supported format", () => { + expect(formatLicensePlate("abc1da2")).toBe(""); + expect(formatLicensePlate("abc12d")).toBe(""); + expect(formatLicensePlate("abc12d3")).toBe(""); + }); }); diff --git a/src/format-license-plate/format-license-plate.ts b/src/format-license-plate/format-license-plate.ts index b040b814..2ee3c880 100644 --- a/src/format-license-plate/format-license-plate.ts +++ b/src/format-license-plate/format-license-plate.ts @@ -2,6 +2,26 @@ import { getFormatLicensePlate } from "../get-format-license-plate/get-format-li import { parseLicensePlate } from "../parse-license-plate/parse-license-plate"; import { OLD_FORMAT_SEPARATOR_INDEX } from "./constants"; +/** + * Formats a Brazilian license plate (placa de carro ou moto). + * + * Old format plates ("LLLNNNN") are hyphenated, while Mercosul plates ("LLLNLNN") are + * returned without any separator. Partial values are formatted as far as they go, so the + * function can be used as an input mask. + * + * @param {string} value - The license plate to be formatted. + * @returns {string} The formatted license plate, or an empty string when the value cannot + * start a valid license plate. + * + * @example + * ```typescript + * formatLicensePlate("abc1234"); // "ABC-1234" + * formatLicensePlate("abc1d23"); // "ABC1D23" + * formatLicensePlate("1234567"); // "" + * ``` + * + * @see Official: https://www.gov.br/transportes/pt-br/assuntos/transito/conteudo-contran/resolucoes/resolucao9692022.pdf + */ export const formatLicensePlate = (value: string): string => { const parsed = parseLicensePlate(value); @@ -27,7 +47,7 @@ export const formatLicensePlate = (value: string): string => { return `${parsed.slice(0, OLD_FORMAT_SEPARATOR_INDEX)}-${tail}`; } - if (/^\d[A-Z]\d{0,2}$/.test(tail) || /^\d{2}[A-Z]\d?$/.test(tail)) { + if (/^\d[A-Z]\d{0,2}$/.test(tail)) { return parsed; } diff --git a/src/generate-license-plate/generate-license-plate.test.ts b/src/generate-license-plate/generate-license-plate.test.ts index 00dcf608..942a44d2 100644 --- a/src/generate-license-plate/generate-license-plate.test.ts +++ b/src/generate-license-plate/generate-license-plate.test.ts @@ -6,6 +6,17 @@ describe("generateLicensePlate", () => { it("should generate valid plates for all supported formats", () => { expect(isValidLicensePlate(generateLicensePlate("LLLNNNN"))).toBe(true); expect(isValidLicensePlate(generateLicensePlate("LLLNLNN"))).toBe(true); - expect(isValidLicensePlate(generateLicensePlate("LLLNNLN"))).toBe(true); + }); + + it("should default to the mercosul format", () => { + const plate = generateLicensePlate(); + + expect(isValidLicensePlate(plate)).toBe(true); + expect(plate).toMatch(/^[A-Z]{3}\d[A-Z]\d{2}$/); + }); + + it("should fall back to the mercosul format when the argument is not a string", () => { + // @ts-expect-error + expect(generateLicensePlate(123)).toMatch(/^[A-Z]{3}\d[A-Z]\d{2}$/); }); }); diff --git a/src/generate-license-plate/generate-license-plate.ts b/src/generate-license-plate/generate-license-plate.ts index d5597910..60fb6083 100644 --- a/src/generate-license-plate/generate-license-plate.ts +++ b/src/generate-license-plate/generate-license-plate.ts @@ -1,13 +1,40 @@ +import type { LicensePlateFormat } from "../get-format-license-plate/get-format-license-plate"; + const LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; -export type GenerateLicensePlateFormat = "LLLNNNN" | "LLLNLNN" | "LLLNNLN"; +const DEFAULT_FORMAT = "LLLNLNN"; + +export type GenerateLicensePlateFormat = LicensePlateFormat; const randomLetter = (): string => LETTERS[Math.floor(Math.random() * LETTERS.length)]; const randomDigit = (): string => Math.floor(Math.random() * 10).toString(); -export const generateLicensePlate = (format: GenerateLicensePlateFormat = "LLLNLNN"): string => { - return format +/** + * Generates a valid random Brazilian license plate (placa de carro ou moto). + * + * Uses `Math.random()` internally, so it is not cryptographically secure, do not use for + * security purposes. + * + * @param {GenerateLicensePlateFormat} format - The format to generate. Defaults to the + * Mercosul format ("LLLNLNN"), the single sequence Resolução CONTRAN nº 969/2022 defines + * for every vehicle, motorcycles included. + * @returns {string} A randomly generated license plate matching the requested format. + * + * @example + * ```typescript + * generateLicensePlate(); // "ABC1D23" (Mercosul) + * generateLicensePlate("LLLNNNN"); // "ABC1234" (old Brazilian format) + * ``` + * + * @see Official: https://www.gov.br/transportes/pt-br/assuntos/transito/conteudo-contran/resolucoes/resolucao9692022.pdf + */ +export const generateLicensePlate = ( + format: GenerateLicensePlateFormat = DEFAULT_FORMAT, +): string => { + const safeFormat = typeof format === "string" ? format : DEFAULT_FORMAT; + + return safeFormat .split("") .map((char) => (char === "L" ? randomLetter() : randomDigit())) .join(""); diff --git a/src/get-format-license-plate/constants.ts b/src/get-format-license-plate/constants.ts index 1f155194..c1b16fb9 100644 --- a/src/get-format-license-plate/constants.ts +++ b/src/get-format-license-plate/constants.ts @@ -1,3 +1,2 @@ export const OLD_FORMAT_REGEX = /^[A-Z]{3}[0-9]{4}$/; -export const MERCOSUL_CAR_REGEX = /^[A-Z]{3}[0-9][A-Z][0-9]{2}$/; -export const MERCOSUL_MOTORCYCLE_REGEX = /^[A-Z]{3}[0-9]{2}[A-Z][0-9]$/; +export const MERCOSUL_REGEX = /^[A-Z]{3}[0-9][A-Z][0-9]{2}$/; diff --git a/src/get-format-license-plate/get-format-license-plate.test.ts b/src/get-format-license-plate/get-format-license-plate.test.ts index 1515c964..e4000ffe 100644 --- a/src/get-format-license-plate/get-format-license-plate.test.ts +++ b/src/get-format-license-plate/get-format-license-plate.test.ts @@ -5,6 +5,17 @@ describe("getFormatLicensePlate", () => { it("should identify supported formats", () => { expect(getFormatLicensePlate("ABC1234")).toBe("LLLNNNN"); expect(getFormatLicensePlate("ABC1D23")).toBe("LLLNLNN"); - expect(getFormatLicensePlate("ABC12D3")).toBe("LLLNNLN"); + }); + + it("should return null for the withdrawn motorcycle sequence", () => { + expect(getFormatLicensePlate("ABC12D3")).toBeNull(); + }); + + it("should return null when the value has extra characters beyond the license plate length", () => { + expect(getFormatLicensePlate("ABC1234EXTRA")).toBeNull(); + }); + + it("should return null when the value does not match any supported format", () => { + expect(getFormatLicensePlate("invalid")).toBeNull(); }); }); diff --git a/src/get-format-license-plate/get-format-license-plate.ts b/src/get-format-license-plate/get-format-license-plate.ts index 0621d2c2..15458575 100644 --- a/src/get-format-license-plate/get-format-license-plate.ts +++ b/src/get-format-license-plate/get-format-license-plate.ts @@ -1,14 +1,44 @@ +import { sanitizeToAlphanumeric } from "../_internals/sanitize-to-alphanumeric/sanitize-to-alphanumeric"; +import { LENGTH } from "../parse-license-plate/constants"; import { parseLicensePlate } from "../parse-license-plate/parse-license-plate"; -import { MERCOSUL_CAR_REGEX, MERCOSUL_MOTORCYCLE_REGEX, OLD_FORMAT_REGEX } from "./constants"; +import { MERCOSUL_REGEX, OLD_FORMAT_REGEX } from "./constants"; -export type LicensePlateFormat = "LLLNNNN" | "LLLNLNN" | "LLLNNLN"; +export type LicensePlateFormat = "LLLNNNN" | "LLLNLNN"; +/** + * Identifies the format of a Brazilian license plate (placa de carro ou moto). + * + * Two formats are supported: the old Brazilian `LLLNNNN` and the single Mercosul sequence + * `LLLNLNN` that Resolução CONTRAN nº 969/2022 defines for every vehicle, motorcycles + * included. + * + * Returns `null` when the sanitized value does not have exactly 7 alphanumeric characters + * (e.g. it is too short, too long, or otherwise malformed) or does not match any of the + * supported formats. + * + * @param {string} value - The license plate value to be checked. + * @returns {LicensePlateFormat | null} The identified format, or `null` when it doesn't match + * any supported format. + * + * @example + * ```typescript + * getFormatLicensePlate("ABC1234"); // "LLLNNNN" + * getFormatLicensePlate("ABC1D23"); // "LLLNLNN" + * getFormatLicensePlate("ABC12D3"); // null (not a Mercosul sequence) + * getFormatLicensePlate("ABC1234EXTRA"); // null (too many characters) + * ``` + * + * @see Official: https://www.gov.br/transportes/pt-br/assuntos/transito/conteudo-contran/resolucoes/resolucao9692022.pdf + */ export const getFormatLicensePlate = (value: string): LicensePlateFormat | null => { + if (typeof value !== "string" || value === "") return null; + + if (sanitizeToAlphanumeric(value).length !== LENGTH) return null; + const parsed = parseLicensePlate(value); if (OLD_FORMAT_REGEX.test(parsed)) return "LLLNNNN"; - if (MERCOSUL_CAR_REGEX.test(parsed)) return "LLLNLNN"; - if (MERCOSUL_MOTORCYCLE_REGEX.test(parsed)) return "LLLNNLN"; + if (MERCOSUL_REGEX.test(parsed)) return "LLLNLNN"; return null; }; diff --git a/src/is-valid-license-plate/is-valid-license-plate.test.ts b/src/is-valid-license-plate/is-valid-license-plate.test.ts index fb4a65c5..59a249aa 100644 --- a/src/is-valid-license-plate/is-valid-license-plate.test.ts +++ b/src/is-valid-license-plate/is-valid-license-plate.test.ts @@ -40,6 +40,15 @@ describe("isValidLicensePlate", () => { expect(isValidLicensePlate("abcd1234")).toBe(false); expect(isValidLicensePlate("abcd234")).toBe(false); }); + + it("when it has extra characters beyond the license plate length", () => { + expect(isValidLicensePlate("ABC1234EXTRA")).toBe(false); + }); + + it("when it uses the withdrawn motorcycle sequence", () => { + expect(isValidLicensePlate("ABC12D3")).toBe(false); + expect(isValidLicensePlate("abc12d3")).toBe(false); + }); }); describe("should return true", () => { @@ -53,9 +62,15 @@ describe("isValidLicensePlate", () => { it("when mercosul license plate format is valid", () => { expect(isValidLicensePlate("abc1d23")).toBe(true); expect(isValidLicensePlate("ABC1D23")).toBe(true); + }); + + it("when it has a whitespace mask", () => { + expect(isValidLicensePlate("ABC 1234")).toBe(true); + expect(isValidLicensePlate(" abc1234 ")).toBe(true); + }); - expect(isValidLicensePlate("ABC12D3")).toBe(true); - expect(isValidLicensePlate("abc12d3")).toBe(true); + it("when the mercosul format has a hyphen mask", () => { + expect(isValidLicensePlate("ABC-1D23")).toBe(true); }); }); }); diff --git a/src/is-valid-license-plate/is-valid-license-plate.ts b/src/is-valid-license-plate/is-valid-license-plate.ts index 2c7dfce0..09028a7d 100644 --- a/src/is-valid-license-plate/is-valid-license-plate.ts +++ b/src/is-valid-license-plate/is-valid-license-plate.ts @@ -1,10 +1,13 @@ -const mercosulRegex = /^[a-z]{3}[0-9]{1}[a-z]{1}[0-9]{2}$|^[a-z]{3}[0-9]{2}[a-z]{1}[0-9]{1}$/i; - -const brazilianLicensePlateRegex = /^[a-z]{3}-?[0-9]{4}$/i; +import { getFormatLicensePlate } from "../get-format-license-plate/get-format-license-plate"; /** * Validates if a Brazilian license plate (placa de carro ou moto) is valid. - * Supports the old Brazilian format (ABC-1234) and the Mercosul format for cars (ABC1D23) and motorcycles (ABC12D3). + * + * Supports the old Brazilian format (ABC-1234) and the Mercosul format (ABC1D23), the single + * sequence Resolução CONTRAN nº 969/2022 defines for every vehicle, motorcycles included. + * Accepts the usual mask characters (hyphens, spaces) and is case-insensitive, mirroring + * `getFormatLicensePlate`/`parseLicensePlate` (single source of truth for the supported + * formats). * * @param {string} value - The license plate value to be validated. * @returns {boolean} True if the license plate is valid, false otherwise. @@ -13,13 +16,14 @@ const brazilianLicensePlateRegex = /^[a-z]{3}-?[0-9]{4}$/i; * ```typescript * isValidLicensePlate("abc1234"); // true (Brazilian format) * isValidLicensePlate("ABC-1234"); // true (Brazilian format with hyphen) - * isValidLicensePlate("abc1d23"); // true (Mercosul format - Car) - * isValidLicensePlate("ABC12D3"); // true (Mercosul format - Motorcycle) + * isValidLicensePlate("ABC 1234"); // true (whitespace mask) + * isValidLicensePlate("abc1d23"); // true (Mercosul format) + * isValidLicensePlate("ABC12D3"); // false (not a Mercosul sequence) + * isValidLicensePlate("ABC1234EXTRA"); // false (too many characters) * isValidLicensePlate("invalid"); // false * ``` + * + * @see Official: https://www.gov.br/transportes/pt-br/assuntos/transito/conteudo-contran/resolucoes/resolucao9692022.pdf */ -export const isValidLicensePlate = (value: string): boolean => { - if (!value || typeof value !== "string") return false; - - return mercosulRegex.test(value) || brazilianLicensePlateRegex.test(value); -}; +export const isValidLicensePlate = (value: string): boolean => + getFormatLicensePlate(value) !== null; diff --git a/src/parse-license-plate/parse-license-plate.ts b/src/parse-license-plate/parse-license-plate.ts index 3d79f4e6..5ac0ac24 100644 --- a/src/parse-license-plate/parse-license-plate.ts +++ b/src/parse-license-plate/parse-license-plate.ts @@ -1,7 +1,21 @@ import { sanitizeToAlphanumeric } from "../_internals/sanitize-to-alphanumeric/sanitize-to-alphanumeric"; import { LENGTH } from "./constants"; +/** + * Removes license plate formatting characters and returns only uppercase alphanumerics. + * + * @param {string} value - The license plate to be parsed. + * @returns {string} Up to 7 uppercase alphanumeric characters, or an empty string when the + * value is not a string. + * + * @example + * ```typescript + * parseLicensePlate("abc-1234"); // "ABC1234" + * ``` + * + * @see Official: https://www.gov.br/transportes/pt-br/assuntos/transito/conteudo-contran/resolucoes/resolucao9692022.pdf + */ export const parseLicensePlate = (value: string): string => { - if (!value || typeof value !== "string") return ""; + if (typeof value !== "string" || value === "") return ""; return sanitizeToAlphanumeric(value).slice(0, LENGTH); }; From 35bc26cb5cc18702e22e93a6b829f5c604e9b008 Mon Sep 17 00:00:00 2001 From: Hyan Mandian <5044101+hyanmandian@users.noreply.github.com> Date: Fri, 11 Sep 2026 17:32:07 -0300 Subject: [PATCH 07/13] =?UTF-8?q?fix(processo-juridico):=20use=20the=20CNJ?= =?UTF-8?q?=20Resolu=C3=A7=C3=A3o=2065/2008=20dot-separated=20mask?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mask was missing the dot between the justiça and tribunal/região digits: formatProcessoJuridico(...) -> "0002080-25.2012.5.15.0049" (was "...515.0049"). parseProcessoJuridico accepts both the old and the new mask as input. generateProcessoJuridico({ year: 99999 }) -> null; year must be between the current year and 9999 (was a 21-character string). Res. CNJ 65/2008. --- src/format-processo-juridico/constants.ts | 1 - .../format-processo-juridico.test.ts | 24 ++++--- .../format-processo-juridico.ts | 22 +++++-- .../generate-processo-juridico.test.ts | 64 +++++++++++++++++++ .../generate-processo-juridico.ts | 36 ++++++++++- src/is-valid-processo-juridico/constants.ts | 1 - .../is-valid-processo-juridico.test.ts | 10 ++- .../is-valid-processo-juridico.ts | 10 +-- 8 files changed, 141 insertions(+), 27 deletions(-) delete mode 100644 src/format-processo-juridico/constants.ts create mode 100644 src/generate-processo-juridico/generate-processo-juridico.test.ts diff --git a/src/format-processo-juridico/constants.ts b/src/format-processo-juridico/constants.ts deleted file mode 100644 index 47c191cd..00000000 --- a/src/format-processo-juridico/constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const LENGTH = 20; diff --git a/src/format-processo-juridico/format-processo-juridico.test.ts b/src/format-processo-juridico/format-processo-juridico.test.ts index 1cfdd549..6d659846 100644 --- a/src/format-processo-juridico/format-processo-juridico.test.ts +++ b/src/format-processo-juridico/format-processo-juridico.test.ts @@ -1,5 +1,5 @@ +import { PROCESSO_JURIDICO_LENGTH } from "../_internals/constants/processo-juridico"; import { describe, expect, it } from "../_internals/test/runtime"; -import { LENGTH } from "./constants"; import { formatProcessoJuridico } from "./format-processo-juridico"; describe("formatProcessoJuridico", () => { @@ -19,21 +19,25 @@ describe("formatProcessoJuridico", () => { expect(formatProcessoJuridico("000208025201")).toBe("0002080-25.201"); expect(formatProcessoJuridico("0002080252012")).toBe("0002080-25.2012"); expect(formatProcessoJuridico("00020802520125")).toBe("0002080-25.2012.5"); - expect(formatProcessoJuridico("000208025201251")).toBe("0002080-25.2012.51"); - expect(formatProcessoJuridico("0002080252012515")).toBe("0002080-25.2012.515"); - expect(formatProcessoJuridico("00020802520125150")).toBe("0002080-25.2012.515.0"); - expect(formatProcessoJuridico("000208025201251500")).toBe("0002080-25.2012.515.00"); - expect(formatProcessoJuridico("0002080252012515004")).toBe("0002080-25.2012.515.004"); - expect(formatProcessoJuridico("00020802520125150049")).toBe("0002080-25.2012.515.0049"); + expect(formatProcessoJuridico("000208025201251")).toBe("0002080-25.2012.5.1"); + expect(formatProcessoJuridico("0002080252012515")).toBe("0002080-25.2012.5.15"); + expect(formatProcessoJuridico("00020802520125150")).toBe("0002080-25.2012.5.15.0"); + expect(formatProcessoJuridico("000208025201251500")).toBe("0002080-25.2012.5.15.00"); + expect(formatProcessoJuridico("0002080252012515004")).toBe("0002080-25.2012.5.15.004"); + expect(formatProcessoJuridico("00020802520125150049")).toBe("0002080-25.2012.5.15.0049"); }); - it(`should NOT add digits after the processo juridico length (${LENGTH})`, () => { - expect(formatProcessoJuridico("00020802520125150049123123")).toBe("0002080-25.2012.515.0049"); + it("should follow the NNNNNNN-DD.AAAA.J.TR.OOOO pattern from Resolução CNJ 65/2008, keeping J (the Judiciary branch) and TR (tribunal) as separate fields", () => { + expect(formatProcessoJuridico("00020802520125150049")).toBe("0002080-25.2012.5.15.0049"); + }); + + it(`should NOT add digits after the processo juridico length (${PROCESSO_JURIDICO_LENGTH})`, () => { + expect(formatProcessoJuridico("00020802520125150049123123")).toBe("0002080-25.2012.5.15.0049"); }); it("should remove all non numeric characters", () => { expect(formatProcessoJuridico("0002080@$25201%!@2515.%0049123123")).toBe( - "0002080-25.2012.515.0049", + "0002080-25.2012.5.15.0049", ); }); }); diff --git a/src/format-processo-juridico/format-processo-juridico.ts b/src/format-processo-juridico/format-processo-juridico.ts index 04c8c980..864b32bd 100644 --- a/src/format-processo-juridico/format-processo-juridico.ts +++ b/src/format-processo-juridico/format-processo-juridico.ts @@ -1,4 +1,5 @@ import { type FormatParams, format } from "../_internals/format/format"; +import { isNullish } from "../_internals/is-nullish/is-nullish"; import { sanitizeToDigits } from "../_internals/sanitize-to-digits/sanitize-to-digits"; export type FormatProcessoJuridicoOptions = Pick; @@ -7,16 +8,25 @@ export type FormatProcessoJuridicoOptions = Pick; * Formats a legal process number (processo jurídico) according to a specific pattern. * * @param {string|number} value - The legal process number to be formatted. It can be a string or a number. - * @param {Object} options - Optional formatting options. + * @param {FormatProcessoJuridicoOptions} [options] - Optional formatting options. * @param {boolean} options.pad - If true, the value will be padded with leading zeros if necessary. * @returns {string} The formatted legal process number as a string. + * + * @example + * ```typescript + * formatProcessoJuridico("00020802520125150049"); // "0002080-25.2012.5.15.0049" + * ``` + * + * @see Official: https://atos.cnj.jus.br/atos/detalhar/119 Resolução CNJ nº 65/2008 */ export const formatProcessoJuridico = ( value: string | number, options?: FormatProcessoJuridicoOptions, ): string => - format({ - pad: options?.pad, - value: sanitizeToDigits(value), - pattern: "0000000-00.0000.000.0000", - }); + isNullish(value) + ? "" + : format({ + pad: options?.pad, + value: sanitizeToDigits(value), + pattern: "0000000-00.0000.0.00.0000", + }); diff --git a/src/generate-processo-juridico/generate-processo-juridico.test.ts b/src/generate-processo-juridico/generate-processo-juridico.test.ts new file mode 100644 index 00000000..4246da75 --- /dev/null +++ b/src/generate-processo-juridico/generate-processo-juridico.test.ts @@ -0,0 +1,64 @@ +import { PROCESSO_JURIDICO_LENGTH } from "../_internals/constants/processo-juridico"; +import { describe, expect, it } from "../_internals/test/runtime"; +import { isValidProcessoJuridico } from "../is-valid-processo-juridico/is-valid-processo-juridico"; +import { generateProcessoJuridico } from "./generate-processo-juridico"; + +const currentYear = new Date().getFullYear(); + +describe("generateProcessoJuridico", () => { + it("should generate a valid processo juridico", () => { + const value = generateProcessoJuridico(); + + expect(value).not.toBe(null); + expect((value as string).length).toBe(PROCESSO_JURIDICO_LENGTH); + expect(isValidProcessoJuridico(value as string)).toBe(true); + }); + + it("should generate valid values on every round trip", () => { + for (let i = 0; i < 200; i++) { + const value = generateProcessoJuridico(); + + expect(value).not.toBe(null); + expect((value as string).length).toBe(PROCESSO_JURIDICO_LENGTH); + expect(isValidProcessoJuridico(value as string)).toBe(true); + } + }); + + it("should honor the year and court options", () => { + const value = generateProcessoJuridico({ year: currentYear, court: 5 }); + + expect(value).not.toBe(null); + expect((value as string).substring(9, 13)).toBe(String(currentYear)); + expect((value as string).charAt(13)).toBe("5"); + expect(isValidProcessoJuridico(value as string)).toBe(true); + }); + + it("should return null for years before the current one", () => { + expect(generateProcessoJuridico({ year: currentYear - 1 })).toBe(null); + }); + + it("should return null for years above 9999", () => { + expect(generateProcessoJuridico({ year: 10_000 })).toBe(null); + expect(generateProcessoJuridico({ year: 99_999 })).toBe(null); + }); + + it("should accept the maximum supported year", () => { + const value = generateProcessoJuridico({ year: 9999 }); + + expect(value).not.toBe(null); + expect((value as string).length).toBe(PROCESSO_JURIDICO_LENGTH); + expect(isValidProcessoJuridico(value as string)).toBe(true); + }); + + it("should return null for invalid courts", () => { + expect(generateProcessoJuridico({ court: 0 })).toBe(null); + expect(generateProcessoJuridico({ court: 10 })).toBe(null); + expect(generateProcessoJuridico({ court: 1.5 })).toBe(null); + expect(generateProcessoJuridico({ court: -1 })).toBe(null); + }); + + it("should return null for non integer years", () => { + expect(generateProcessoJuridico({ year: currentYear + 0.5 })).toBe(null); + expect(generateProcessoJuridico({ year: Number.NaN })).toBe(null); + }); +}); diff --git a/src/generate-processo-juridico/generate-processo-juridico.ts b/src/generate-processo-juridico/generate-processo-juridico.ts index bd46f5cd..390fa9a0 100644 --- a/src/generate-processo-juridico/generate-processo-juridico.ts +++ b/src/generate-processo-juridico/generate-processo-juridico.ts @@ -1,27 +1,59 @@ import { generateRandomNumber } from "../_internals/generate-random-number/generate-random-number"; +import { isNullish } from "../_internals/is-nullish/is-nullish"; export type GenerateProcessoJuridicoOptions = { + /** Filing year, from the current year to 9999 (default: the current year). */ year?: number; + /** Court segment (J), from 1 to 9 (default: random). */ court?: number; }; +const MAX_YEAR = 9999; +const MIN_COURT = 1; +const MAX_COURT = 9; + const calculateCheckDigits = (base: string): string => { const checksum = 98n - ((BigInt(base) * 100n) % 97n); return checksum.toString().padStart(2, "0"); }; +/** + * Generates a random valid Brazilian Processo Jurídico (court case) number, + * following the `NNNNNNNDDAAAAJTROOOO` layout of Resolução CNJ nº 65/2008. + * + * Uses `Math.random()` internally, so it is not cryptographically secure, do not use for security purposes. + * + * @param {GenerateProcessoJuridicoOptions} [options] - Optional generation options. + * @param {number} options.year - The `AAAA` field. Must be an integer between the + * current year and 9999. Defaults to the current year. + * @param {number} options.court - The `J` field (segmento do Judiciário). Must be an + * integer between 1 and 9. Defaults to a random value. + * @returns {string|null} The generated number without formatting, or null when the options are invalid. + * + * @example + * ```typescript + * generateProcessoJuridico(); // "00020802520265150049" + * generateProcessoJuridico({ year: 2030, court: 5 }); // "12345672820305120049" + * generateProcessoJuridico({ year: 10000 }); // null + * ``` + * + * @see Official: https://atos.cnj.jus.br/atos/detalhar/119 Resolução CNJ nº 65/2008 + */ export const generateProcessoJuridico = ( options: GenerateProcessoJuridicoOptions = {}, ): string | null => { + if (isNullish(options) || typeof options !== "object") return null; + const { year = new Date().getFullYear(), court = Math.floor(Math.random() * 9) + 1 } = options; const currentYear = new Date().getFullYear(); if ( !Number.isInteger(year) || year < currentYear || + year > MAX_YEAR || !Number.isInteger(court) || - court < 1 || - court > 9 + court < MIN_COURT || + court > MAX_COURT ) { return null; } diff --git a/src/is-valid-processo-juridico/constants.ts b/src/is-valid-processo-juridico/constants.ts index 1dd9247a..21d4482c 100644 --- a/src/is-valid-processo-juridico/constants.ts +++ b/src/is-valid-processo-juridico/constants.ts @@ -1,4 +1,3 @@ -export const LENGTH = 20; export const CHECK_DIGIT_START_POSITION = 7; export const CHECK_DIGIT_LENGTH = 2; export const MOD_97_10_QUOTIENT = 97; diff --git a/src/is-valid-processo-juridico/is-valid-processo-juridico.test.ts b/src/is-valid-processo-juridico/is-valid-processo-juridico.test.ts index e93b7586..a54ec79a 100644 --- a/src/is-valid-processo-juridico/is-valid-processo-juridico.test.ts +++ b/src/is-valid-processo-juridico/is-valid-processo-juridico.test.ts @@ -1,5 +1,5 @@ +import { PROCESSO_JURIDICO_LENGTH } from "../_internals/constants/processo-juridico"; import { describe, expect, test } from "../_internals/test/runtime"; -import { LENGTH } from "./constants"; import { isValidProcessoJuridico } from "./is-valid-processo-juridico"; describe("isValidProcessoJuridico", () => { @@ -18,7 +18,7 @@ describe("isValidProcessoJuridico", () => { expect(isValidProcessoJuridico(undefined)).toBe(false); }); - test(`when length is less than ${LENGTH}`, () => { + test(`when length is less than ${PROCESSO_JURIDICO_LENGTH}`, () => { expect(isValidProcessoJuridico("123")).toBe(false); }); }); @@ -28,7 +28,11 @@ describe("isValidProcessoJuridico", () => { expect(isValidProcessoJuridico("00020802520125150049")).toBe(true); }); - test("when is a processo juridico valid with mask", () => { + test("when is a processo juridico valid with the CNJ mask", () => { + expect(isValidProcessoJuridico("0002080-25.2012.5.15.0049")).toBe(true); + }); + + test("when is a processo juridico valid with the legacy fused mask", () => { expect(isValidProcessoJuridico("0002080-25.2012.515.0049")).toBe(true); }); }); diff --git a/src/is-valid-processo-juridico/is-valid-processo-juridico.ts b/src/is-valid-processo-juridico/is-valid-processo-juridico.ts index 48f2a28f..9a956a1a 100644 --- a/src/is-valid-processo-juridico/is-valid-processo-juridico.ts +++ b/src/is-valid-processo-juridico/is-valid-processo-juridico.ts @@ -1,8 +1,8 @@ +import { PROCESSO_JURIDICO_LENGTH } from "../_internals/constants/processo-juridico"; import { sanitizeToDigits } from "../_internals/sanitize-to-digits/sanitize-to-digits"; import { CHECK_DIGIT_LENGTH, CHECK_DIGIT_START_POSITION, - LENGTH, MOD_97_10_QUOTIENT, MOD_97_10_SUM, } from "./constants"; @@ -45,15 +45,17 @@ const verifyCheckDigit = (value: string): boolean => { * @example * ```typescript * isValidProcessoJuridico("00020802520125150049"); // true - * isValidProcessoJuridico("0002080-25.2012.515.0049"); // true + * isValidProcessoJuridico("0002080-25.2012.5.15.0049"); // true * ``` + * + * @see Official: https://atos.cnj.jus.br/atos/detalhar/119 Resolução CNJ nº 65/2008 */ export const isValidProcessoJuridico = (value: string): boolean => { - if (!value || typeof value !== "string") return false; + if (typeof value !== "string" || value === "") return false; const digits = sanitizeToDigits(value); - if (digits.length !== LENGTH) return false; + if (digits.length !== PROCESSO_JURIDICO_LENGTH) return false; return verifyCheckDigit(digits); }; From 08c8ed58d69ce46c80fcfb999f40d7e29985793c Mon Sep 17 00:00:00 2001 From: Hyan Mandian <5044101+hyanmandian@users.noreply.github.com> Date: Fri, 11 Sep 2026 17:32:07 -0300 Subject: [PATCH 08/13] fix(capitalize): match user word lists case-insensitively and treat - and / as word boundaries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit upperCaseWords/lowerCaseWords are matched case-insensitively: capitalize("empresa ltda", { upperCaseWords: ["ltda"] }) -> "Empresa LTDA". The default word lists are unchanged ("empresa ltda" -> "Empresa Ltda"). Treat - and / as word boundaries: "MOGI-GUAÇU" -> "Mogi-Guaçu" (was "Mogi-guaçu"). Collapse runs of whitespace and case using the pt-BR locale. --- src/capitalize/capitalize.test.ts | 22 +++++++++ src/capitalize/capitalize.ts | 74 +++++++++++++++++++++---------- src/capitalize/constants.ts | 6 ++- 3 files changed, 77 insertions(+), 25 deletions(-) diff --git a/src/capitalize/capitalize.test.ts b/src/capitalize/capitalize.test.ts index c7c69f9b..e62fe071 100644 --- a/src/capitalize/capitalize.test.ts +++ b/src/capitalize/capitalize.test.ts @@ -39,5 +39,27 @@ describe("capitalize", () => { "José não ama Maria", ); }); + + test("when upper case words are provided in any case", () => { + expect(capitalize("empresa ltda")).toBe("Empresa Ltda"); + expect(capitalize("empresa ltda", { upperCaseWords: ["ltda"] })).toBe("Empresa LTDA"); + expect(capitalize("meu cpf e rg", { upperCaseWords: ["CPF", "Rg"] })).toBe("Meu CPF e RG"); + }); + + test("when the value contains whitespace other than a space", () => { + expect(capitalize("joao\tsilva")).toBe("Joao Silva"); + expect(capitalize("joao\n\nsilva")).toBe("Joao Silva"); + expect(capitalize(" joao \t\n silva ")).toBe("Joao Silva"); + }); + + test("when the value contains hyphens or slashes", () => { + expect(capitalize("MOGI-GUAÇU")).toBe("Mogi-Guaçu"); + expect(capitalize("SANTANA/RS")).toBe("Santana/Rs"); + expect(capitalize("SANTANA/RS", { upperCaseWords: ["rs"] })).toBe("Santana/RS"); + expect(capitalize("sÃo josÉ do rio-preto")).toBe("São José do Rio-Preto"); + expect(capitalize("de-facto")).toBe("De-Facto"); + expect(capitalize("rio-de-janeiro")).toBe("Rio-de-Janeiro"); + expect(capitalize("a - b")).toBe("A - B"); + }); }); }); diff --git a/src/capitalize/capitalize.ts b/src/capitalize/capitalize.ts index a698238e..052727f0 100644 --- a/src/capitalize/capitalize.ts +++ b/src/capitalize/capitalize.ts @@ -1,56 +1,84 @@ -import { ACRONYMS, PREPOSITIONS } from "./constants"; +import { PREPOSITIONS, SEPARATOR_REGEX, WHITESPACE_REGEX } from "./constants"; export type CapitalizeOptions = { + /** Words to keep in lower case when they are not the first word (default: the Portuguese prepositions). */ lowerCaseWords?: string[]; + /** Words to keep in upper case wherever they appear (default: `[]`). */ upperCaseWords?: string[]; }; /** * Capitalizes a given string according to specific rules for lower-case and upper-case words. * + * Words are separated by whitespace, by `-` and by `/`, so `"MOGI-GUAÇU"` becomes + * `"Mogi-Guaçu"` and `"SANTANA/RS"` becomes `"Santana/Rs"`. Hyphens and slashes are kept + * where they are, while every run of whitespace (spaces, tabs, newlines) collapses into a + * single space and the leading and trailing whitespace is dropped. + * * - Words listed in `lowerCaseWords` (default: `PREPOSITIONS`) will be converted to lower case, except for the first word. - * - Words listed in `upperCaseWords` (default: `ACRONYMS`) will be converted to upper case. + * - Words listed in `upperCaseWords` will be converted to upper case (none by default). The + * comparison ignores the case of the words given in both lists. * - All other words will be capitalized (first letter upper case, rest lower case). * * @param value - The input string to be capitalized. * @param options - Optional configuration for capitalization. * @param options.lowerCaseWords - Array of words to keep in lower case (default: `PREPOSITIONS`). - * @param options.upperCaseWords - Array of words to keep in upper case (default: `ACRONYMS`). + * @param options.upperCaseWords - Array of words to keep in upper case (default: `[]`). * @returns The capitalized string according to the specified rules. + * + * @example + * ```typescript + * capitalize("JOSÉ DA SILVA"); // "José da Silva" + * capitalize("empresa ltda"); // "Empresa Ltda" + * capitalize("empresa ltda", { upperCaseWords: ["ltda"] }); // "Empresa LTDA" + * capitalize("MOGI-GUAÇU"); // "Mogi-Guaçu" + * capitalize("SANTANA/RS"); // "Santana/Rs" + * capitalize("SANTANA/RS", { upperCaseWords: ["rs"] }); // "Santana/RS" + * capitalize("joao\tsilva"); // "Joao Silva" + * ``` */ -export const capitalize = ( - value: string, - { lowerCaseWords = PREPOSITIONS, upperCaseWords = ACRONYMS }: CapitalizeOptions = {}, -): string => { - const lowerCaseSet = new Set(lowerCaseWords); +export const capitalize = (value: string, options?: CapitalizeOptions): string => { + if (typeof value !== "string") return ""; - const upperCaseSet = new Set(upperCaseWords); + const { lowerCaseWords = PREPOSITIONS, upperCaseWords = [] } = options ?? {}; - const words = value.split(" "); + const lowerCaseSet = new Set(lowerCaseWords.map((word) => word.toLocaleLowerCase("pt-BR"))); - const result: string[] = []; + const upperCaseSet = new Set(upperCaseWords.map((word) => word.toLocaleUpperCase("pt-BR"))); - for (let i = 0, len = words.length; i < len; i++) { - const word = words[i]; + const tokens = value.trim().split(SEPARATOR_REGEX); - if (!word) continue; + let result = ""; + let wordIndex = 0; - const lowerCaseWord = word.toLocaleLowerCase(); + for (let i = 0, len = tokens.length; i < len; i++) { + const token = tokens[i]; - if (i > 0 && lowerCaseSet.has(lowerCaseWord)) { - result.push(lowerCaseWord); + if (!token) continue; + + if (WHITESPACE_REGEX.test(token)) { + result += " "; continue; } - const upperCaseWord = word.toLocaleUpperCase(); - - if (upperCaseSet.has(upperCaseWord)) { - result.push(upperCaseWord); + if (token === "-" || token === "/") { + result += token; continue; } - result.push(upperCaseWord.charAt(0) + lowerCaseWord.slice(1)); + const lowerCaseWord = token.toLocaleLowerCase("pt-BR"); + const upperCaseWord = token.toLocaleUpperCase("pt-BR"); + + if (wordIndex > 0 && lowerCaseSet.has(lowerCaseWord)) { + result += lowerCaseWord; + } else if (upperCaseSet.has(upperCaseWord)) { + result += upperCaseWord; + } else { + result += upperCaseWord.charAt(0) + lowerCaseWord.slice(1); + } + + wordIndex++; } - return result.join(" "); + return result; }; diff --git a/src/capitalize/constants.ts b/src/capitalize/constants.ts index cfd776f2..081cd985 100644 --- a/src/capitalize/constants.ts +++ b/src/capitalize/constants.ts @@ -1,5 +1,3 @@ -export const ACRONYMS = ["cia", "cnpj", "cpf", "ltda", "me", "rg"]; - export const PREPOSITIONS = [ "a", "com", @@ -18,3 +16,7 @@ export const PREPOSITIONS = [ "por", "sem", ]; + +export const SEPARATOR_REGEX = /(\s+|[-/])/; + +export const WHITESPACE_REGEX = /^\s+$/; From 0c6aec86067969383ef944432671d06b2304969f Mon Sep 17 00:00:00 2001 From: Hyan Mandian <5044101+hyanmandian@users.noreply.github.com> Date: Fri, 11 Sep 2026 17:32:07 -0300 Subject: [PATCH 09/13] fix(passport): accept lowercase and mask characters isValidPassport("ab123456") -> true (was false); accept mask characters such as "AB-123.456". --- src/_internals/constants/passport.ts | 2 ++ src/format-passport/constants.ts | 1 - src/format-passport/format-passport.ts | 8 +++++--- src/generate-passport/generate-passport.ts | 4 ++++ .../is-valid-passport.test.ts | 9 +++++++++ src/is-valid-passport/is-valid-passport.ts | 20 +++++++++++++------ src/parse-passport/constants.ts | 1 - src/parse-passport/parse-passport.ts | 8 +++++--- 8 files changed, 39 insertions(+), 14 deletions(-) create mode 100644 src/_internals/constants/passport.ts delete mode 100644 src/format-passport/constants.ts delete mode 100644 src/parse-passport/constants.ts diff --git a/src/_internals/constants/passport.ts b/src/_internals/constants/passport.ts new file mode 100644 index 00000000..27300e06 --- /dev/null +++ b/src/_internals/constants/passport.ts @@ -0,0 +1,2 @@ +/** Characters of a Brazilian passport number. */ +export const PASSPORT_LENGTH = 8; diff --git a/src/format-passport/constants.ts b/src/format-passport/constants.ts deleted file mode 100644 index 3122517e..00000000 --- a/src/format-passport/constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const LENGTH = 8; diff --git a/src/format-passport/format-passport.ts b/src/format-passport/format-passport.ts index 343a27f5..ad8ce902 100644 --- a/src/format-passport/format-passport.ts +++ b/src/format-passport/format-passport.ts @@ -1,5 +1,5 @@ +import { PASSPORT_LENGTH } from "../_internals/constants/passport"; import { sanitizeToAlphanumeric } from "../_internals/sanitize-to-alphanumeric/sanitize-to-alphanumeric"; -import { LENGTH } from "./constants"; /** * Formats a Brazilian passport number for display. @@ -12,8 +12,10 @@ import { LENGTH } from "./constants"; * formatPassport("ab123456") // "AB123456" * formatPassport("AB-123.456") // "AB123456" * formatPassport("") // "" + * + * @see Official: https://www.gov.br/pf/pt-br/assuntos/passaporte */ export const formatPassport = (passport: string): string => { - if (!passport || typeof passport !== "string") return ""; - return sanitizeToAlphanumeric(passport).slice(0, LENGTH); + if (typeof passport !== "string" || passport === "") return ""; + return sanitizeToAlphanumeric(passport).slice(0, PASSPORT_LENGTH); }; diff --git a/src/generate-passport/generate-passport.ts b/src/generate-passport/generate-passport.ts index 5967c6fd..827beb01 100644 --- a/src/generate-passport/generate-passport.ts +++ b/src/generate-passport/generate-passport.ts @@ -4,11 +4,15 @@ import { ALPHABET_LENGTH, CHAR_CODE_A, DIGITS_LENGTH, LETTERS_LENGTH } from "./c /** * Generates a random valid Brazilian passport number. * + * Uses `Math.random()` internally, so it is not cryptographically secure, do not use for security purposes. + * * @returns A random valid passport number string (e.g. "RY393097"). * * @example * generatePassport() // "RY393097" * generatePassport() // "ZS840088" + * + * @see Official: https://www.gov.br/pf/pt-br/assuntos/passaporte */ export const generatePassport = (): string => { const letters = Array.from({ length: LETTERS_LENGTH }, () => diff --git a/src/is-valid-passport/is-valid-passport.test.ts b/src/is-valid-passport/is-valid-passport.test.ts index 70ab07e0..3bad9ef9 100644 --- a/src/is-valid-passport/is-valid-passport.test.ts +++ b/src/is-valid-passport/is-valid-passport.test.ts @@ -36,5 +36,14 @@ describe("isValidPassport", () => { expect(isValidPassport("AA111111")).toBe(true); expect(isValidPassport("CL125167")).toBe(true); }); + + test("when passport is lowercase", () => { + expect(isValidPassport("ab123456")).toBe(true); + }); + + test("when passport contains mask symbols", () => { + expect(isValidPassport("AB-123456")).toBe(true); + expect(isValidPassport("AB.123.456")).toBe(true); + }); }); }); diff --git a/src/is-valid-passport/is-valid-passport.ts b/src/is-valid-passport/is-valid-passport.ts index bcbaad1d..5e8e00ce 100644 --- a/src/is-valid-passport/is-valid-passport.ts +++ b/src/is-valid-passport/is-valid-passport.ts @@ -1,9 +1,13 @@ +import { isNullish } from "../_internals/is-nullish/is-nullish"; +import { sanitizeToAlphanumeric } from "../_internals/sanitize-to-alphanumeric/sanitize-to-alphanumeric"; import { PASSPORT_REGEX } from "./constants"; /** * Checks if a Brazilian passport number is valid. - * To be considered valid, the input must be a string containing exactly two - * alphabetical characters followed by exactly six numerical digits. + * To be considered valid, the sanitized input must contain exactly two alphabetical + * characters followed by exactly six numerical digits. The input is case-insensitive and + * any non-alphanumeric characters (spaces, dots, hyphens, etc.) are ignored, mirroring the + * sanitization performed by `formatPassport`/`parsePassport`. * This function does not verify if the input is a real passport number, * as there are no checksums for the Brazilian passport. * @@ -11,12 +15,16 @@ import { PASSPORT_REGEX } from "./constants"; * @returns True if the passport number is valid (2 letters followed by 6 digits). * * @example - * isValidPassport("Ab123456") // false - must be uppercase * isValidPassport("AB123456") // true + * isValidPassport("ab123456") // true (case-insensitive) + * isValidPassport("AB-123.456") // true (symbols are ignored) * isValidPassport("12345678") // false - * isValidPassport("DC-221345") // false + * isValidPassport("DC-221345extra") // false + * + * @see Official: https://www.gov.br/pf/pt-br/assuntos/passaporte */ export const isValidPassport = (passport: string | number): boolean => { - if (passport === null || passport === undefined) return false; - return PASSPORT_REGEX.test(String(passport)); + if (isNullish(passport) || passport === "") return false; + + return PASSPORT_REGEX.test(sanitizeToAlphanumeric(passport)); }; diff --git a/src/parse-passport/constants.ts b/src/parse-passport/constants.ts deleted file mode 100644 index 3122517e..00000000 --- a/src/parse-passport/constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const LENGTH = 8; diff --git a/src/parse-passport/parse-passport.ts b/src/parse-passport/parse-passport.ts index 042afe22..7cc946fb 100644 --- a/src/parse-passport/parse-passport.ts +++ b/src/parse-passport/parse-passport.ts @@ -1,5 +1,5 @@ +import { PASSPORT_LENGTH } from "../_internals/constants/passport"; import { sanitizeToAlphanumeric } from "../_internals/sanitize-to-alphanumeric/sanitize-to-alphanumeric"; -import { LENGTH } from "./constants"; /** * Removes non-alphanumeric characters from a passport number, uppercases it, and caps it to 8 characters. @@ -11,8 +11,10 @@ import { LENGTH } from "./constants"; * parsePassport("Ab123456") // "AB123456" * parsePassport("Ab-123456") // "AB123456" * parsePassport("Ab -. 123456") // "AB123456" + * + * @see Official: https://www.gov.br/pf/pt-br/assuntos/passaporte */ export const parsePassport = (passport: string): string => { - if (!passport || typeof passport !== "string") return ""; - return sanitizeToAlphanumeric(passport).slice(0, LENGTH); + if (typeof passport !== "string" || passport === "") return ""; + return sanitizeToAlphanumeric(passport).slice(0, PASSPORT_LENGTH); }; From 1b3e2ca945bde2336797d5dc9a0b8b56cbc56f2a Mon Sep 17 00:00:00 2001 From: Hyan Mandian <5044101+hyanmandian@users.noreply.github.com> Date: Fri, 11 Sep 2026 17:32:07 -0300 Subject: [PATCH 10/13] fix(pis): accept alphanumeric-style separators and avoid repeated-digit bases isValidPis accepts whitespace and the common separators between groups. generatePis retries when the generated base would be a repeated-digit sequence. --- src/generate-pis/generate-pis.test.ts | 33 +++++++++++++++++++++++++++ src/generate-pis/generate-pis.ts | 29 +++++++++++++++++++---- src/is-valid-pis/constants.ts | 2 -- src/is-valid-pis/is-valid-pis.test.ts | 13 +++++++++-- src/is-valid-pis/is-valid-pis.ts | 27 +++++++++++----------- 5 files changed, 82 insertions(+), 22 deletions(-) create mode 100644 src/generate-pis/generate-pis.test.ts diff --git a/src/generate-pis/generate-pis.test.ts b/src/generate-pis/generate-pis.test.ts new file mode 100644 index 00000000..e9112dd5 --- /dev/null +++ b/src/generate-pis/generate-pis.test.ts @@ -0,0 +1,33 @@ +import { describe, expect, test } from "../_internals/test/runtime"; +import { isValidPis } from "../is-valid-pis/is-valid-pis"; +import { generatePis } from "./generate-pis"; + +describe("generatePis", () => { + test("should have the right length without mask (11)", () => { + expect(generatePis()).toHaveLength(11); + expect(/^\d{11}$/.test(generatePis())).toBe(true); + }); + + test("should always generate a valid PIS", () => { + for (let i = 0; i < 1000; i++) { + expect(isValidPis(generatePis())).toBe(true); + } + }); + + test("should regenerate the base when it comes out with repeated digits", () => { + const digits = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; + const originalRandom = Math.random; + let call = 0; + + Math.random = () => (digits[call++] + 0.5) / 10; + + try { + const pis = generatePis(); + + expect(pis.slice(0, 10)).toBe("1234567890"); + expect(isValidPis(pis)).toBe(true); + } finally { + Math.random = originalRandom; + } + }); +}); diff --git a/src/generate-pis/generate-pis.ts b/src/generate-pis/generate-pis.ts index f5fbc033..6df9757f 100644 --- a/src/generate-pis/generate-pis.ts +++ b/src/generate-pis/generate-pis.ts @@ -1,14 +1,35 @@ +import { PIS_WEIGHTS } from "../_internals/constants/pis"; import { generateRandomNumber } from "../_internals/generate-random-number/generate-random-number"; - -const WEIGHTS = [3, 2, 9, 8, 7, 6, 5, 4, 3, 2] as const; +import { isRepeatedDigits } from "../_internals/is-repeated-digits/is-repeated-digits"; const calculateCheckDigit = (base: string): string => { - const sum = base.split("").reduce((acc, digit, index) => acc + Number(digit) * WEIGHTS[index], 0); + const sum = base + .split("") + .reduce((acc, digit, index) => acc + Number(digit) * PIS_WEIGHTS[index], 0); const digit = 11 - (sum % 11); return digit >= 10 ? "0" : digit.toString(); }; +/** + * Generates a valid random Brazilian PIS (Programa de Integração Social) number. + * + * Uses `Math.random()` internally, so it is not cryptographically secure, do not use for security purposes. + * + * @returns {string} A valid 11-digit PIS string without formatting. + * + * @example + * ```typescript + * generatePis(); // "12056874107" + * ``` + * + * @see Official: https://www.gov.br/inss/pt-br/direitos-e-deveres/inscricao-e-contribuicao/inscricao + */ export const generatePis = (): string => { - const base = generateRandomNumber(10); + let base = generateRandomNumber(10); + + while (isRepeatedDigits(base)) { + base = generateRandomNumber(10); + } + return `${base}${calculateCheckDigit(base)}`; }; diff --git a/src/is-valid-pis/constants.ts b/src/is-valid-pis/constants.ts index 571f25d0..3be42751 100644 --- a/src/is-valid-pis/constants.ts +++ b/src/is-valid-pis/constants.ts @@ -1,5 +1,3 @@ -export const LENGTH = 11; - export const RESERVED_NUMBERS = [ "00000000000", "11111111111", diff --git a/src/is-valid-pis/is-valid-pis.test.ts b/src/is-valid-pis/is-valid-pis.test.ts index 6cf7a30e..53da1898 100644 --- a/src/is-valid-pis/is-valid-pis.test.ts +++ b/src/is-valid-pis/is-valid-pis.test.ts @@ -1,5 +1,6 @@ +import { PIS_LENGTH } from "../_internals/constants/pis"; import { describe, expect, test } from "../_internals/test/runtime"; -import { LENGTH, RESERVED_NUMBERS } from "./constants"; +import { RESERVED_NUMBERS } from "./constants"; import { isValidPis } from "./is-valid-pis"; describe("isValidPis", () => { @@ -41,7 +42,7 @@ describe("isValidPis", () => { expect(isValidPis([])).toBe(false); }); - test(`when dont match with PIS length (${LENGTH})`, () => { + test(`when dont match with PIS length (${PIS_LENGTH})`, () => { expect(isValidPis("123456")).toBe(false); }); @@ -68,6 +69,14 @@ describe("isValidPis", () => { expect(isValidPis("120.5641.284-7")).toBe(true); }); + test("when is valid PIS with a slash mask", () => { + expect(isValidPis("120/56874/10-7")).toBe(true); + }); + + test("when is valid PIS with a whitespace mask", () => { + expect(isValidPis("120 56874 10 7")).toBe(true); + }); + test("when is a valid PIS with last digit 0", () => { expect(isValidPis("120.1213.266-0")).toBe(true); expect(isValidPis("120.7041.469-0")).toBe(true); diff --git a/src/is-valid-pis/is-valid-pis.ts b/src/is-valid-pis/is-valid-pis.ts index 06232acc..2cb5f8d0 100644 --- a/src/is-valid-pis/is-valid-pis.ts +++ b/src/is-valid-pis/is-valid-pis.ts @@ -1,13 +1,11 @@ +import { PIS_LENGTH, PIS_WEIGHTS } from "../_internals/constants/pis"; import { generateChecksum } from "../_internals/generate-checksum/generate-checksum"; import { sanitizeToDigits } from "../_internals/sanitize-to-digits/sanitize-to-digits"; -import { LENGTH, RESERVED_NUMBERS } from "./constants"; - -const RESERVED_SET = new Set(RESERVED_NUMBERS); - -const WEIGHTS = [3, 2, 9, 8, 7, 6, 5, 4, 3, 2]; +import { RESERVED_NUMBERS } from "./constants"; /** * Validates a Brazilian PIS (Programa de Integração Social) number. + * Accepts the usual mask characters (`.`, `-`, `/`, `(`, `)`, `,`, `*`) and whitespace. * * @param {string} pis - The PIS number to validate. * @returns {boolean} True if the PIS number is valid, false otherwise. @@ -15,29 +13,30 @@ const WEIGHTS = [3, 2, 9, 8, 7, 6, 5, 4, 3, 2]; * @example * ```typescript * isValidPis("120.56874.10-7"); // true + * isValidPis("120/56874/10-7"); // true * isValidPis("12056874107"); // true * isValidPis("00000000000"); // false (reserved number) * ``` + * + * @see Official: https://www.gov.br/inss/pt-br/direitos-e-deveres/inscricao-e-contribuicao/inscricao */ export const isValidPis = (pis: string): boolean => { - if (!pis || typeof pis !== "string") return false; + if (typeof pis !== "string" || pis === "") return false; - // Check if contains invalid characters (letters that are not separators) - // Valid separators: space, parentheses, dots, commas, asterisks, hyphens - const hasInvalidChars = /[^0-9 ().,*-]/.test(pis); + const hasInvalidChars = /[^0-9\s().,*/-]/.test(pis); if (hasInvalidChars) return false; const digits = sanitizeToDigits(pis); - if (digits.length !== LENGTH) return false; + if (digits.length !== PIS_LENGTH) return false; - if (RESERVED_SET.has(digits)) return false; + if (RESERVED_NUMBERS.includes(digits)) return false; - const base = digits.substring(0, LENGTH - 1); - const checkDigit = digits.charCodeAt(LENGTH - 1) - 48; + const base = digits.substring(0, PIS_LENGTH - 1); + const checkDigit = digits.charCodeAt(PIS_LENGTH - 1) - 48; - const weightedChecksum = generateChecksum({ base, weight: WEIGHTS }); + const weightedChecksum = generateChecksum({ base, weight: PIS_WEIGHTS }); const calculatedDigit = 11 - (weightedChecksum % 11); const finalDigit = calculatedDigit >= 10 ? 0 : calculatedDigit; From de95486cf222ebdfd8f0c762d37dee9de947a8bf Mon Sep 17 00:00:00 2001 From: Hyan Mandian <5044101+hyanmandian@users.noreply.github.com> Date: Fri, 11 Sep 2026 17:32:07 -0300 Subject: [PATCH 11/13] =?UTF-8?q?fix(legal-nature):=20replace=20the=20tabl?= =?UTF-8?q?e=20with=20IBGE/CONCLA=20Natureza=20Jur=C3=ADdica=202021?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the outdated, unaccented 60-code table with the official 2021 table (92 official codes + 8 legacy codes kept for compatibility, 100 total). Descriptions now use the official accented wording. --- scripts/legal-natures.ts | 177 ++++++++++++++++++ .../format-legal-nature.ts | 24 ++- .../generate-legal-nature.ts | 22 ++- .../get-legal-natures.test.ts | 15 +- src/get-legal-natures/get-legal-natures.ts | 25 ++- src/is-valid-legal-nature/constants.ts | 139 +++++++++----- .../is-valid-legal-nature.test.ts | 34 ++++ .../is-valid-legal-nature.ts | 31 ++- 8 files changed, 408 insertions(+), 59 deletions(-) create mode 100644 scripts/legal-natures.ts diff --git a/scripts/legal-natures.ts b/scripts/legal-natures.ts new file mode 100644 index 00000000..bb65444a --- /dev/null +++ b/scripts/legal-natures.ts @@ -0,0 +1,177 @@ +#!/usr/bin/env node + +import { writeFile } from "node:fs/promises"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { inflateSync } from "node:zlib"; + +import { fetchWithRetry } from "../src/_internals/fetch-with-retry/fetch-with-retry.ts"; + +const scriptsDir = dirname(fileURLToPath(import.meta.url)); + +const SOURCE_URL = + "https://concla.ibge.gov.br/images/concla/documentacao/CONCLA-TNJ2021-EstruturaDetalhada.pdf"; + +const SOURCE_PAGE_URL = + "https://concla.ibge.gov.br/estrutura/natjur-estrutura/natureza-juridica-2021"; + +const OUTPUT_PATH = "./src/is-valid-legal-nature/constants.ts"; + +const EXPECTED_CODES = 92; + +const LEGACY_LEGAL_NATURE: Record = { + "2076": "Sociedade Empresária em Nome Coletivo", + "2100": "Sociedade Mercantil de Capital e Indústria (extinta pelo NCC/2002)", + "2208": "Entidade Binacional Itaipu", + "3042": "Organização Social", + "3050": "Organização da Sociedade Civil de Interesse Público (Oscip)", + "3093": "Unidade Executora (Programa Dinheiro Direto na Escola)", + "3123": "Partido Político", + "5002": "Organização Internacional e Outras Instituições Extraterritoriais", +}; + +const TYPO_FIXES: Record = { + "Frente Plebiscitária ou Referendaria": "Frente Plebiscitária ou Referendária", +}; + +const inflateStreams = (pdf: Buffer): string[] => { + const streams: string[] = []; + let cursor = 0; + + while (cursor < pdf.length) { + const start = pdf.indexOf("stream", cursor); + if (start < 0) break; + + let contentStart = start + "stream".length; + if (pdf[contentStart] === 0x0d) contentStart += 1; + if (pdf[contentStart] === 0x0a) contentStart += 1; + + const end = pdf.indexOf("endstream", contentStart); + if (end < 0) break; + + try { + streams.push(inflateSync(pdf.subarray(contentStart, end)).toString("latin1")); + } catch {} + + cursor = end + "endstream".length; + } + + return streams; +}; + +const unescapePdfString = (value: string): string => + value.replace(/\\([0-7]{1,3})|\\(.)/g, (_match, octal?: string, char?: string) => { + if (octal) return String.fromCharCode(Number.parseInt(octal, 8)); + if (char === "n") return "\n"; + if (char === "r") return "\r"; + if (char === "t") return "\t"; + return char ?? ""; + }); + +const extractLines = (streams: string[]): string[] => { + const lines: string[] = []; + + for (const stream of streams) { + const rows = new Map>(); + + for (const block of stream.matchAll(/BT([\s\S]*?)ET/g)) { + const body = block[1]; + const matrix = [...body.matchAll(/([-\d.]+)\s+([-\d.]+)\s+Tm/g)].pop(); + if (!matrix) continue; + + let text = ""; + for (const array of body.matchAll(/\[((?:[^[\]\\]|\\.)*)\]\s*TJ/g)) { + for (const chunk of array[1].matchAll(/\(((?:[^()\\]|\\.)*)\)/g)) { + text += unescapePdfString(chunk[1]); + } + } + for (const chunk of body.matchAll(/\(((?:[^()\\]|\\.)*)\)\s*Tj/g)) { + text += unescapePdfString(chunk[1]); + } + if (!text) continue; + + const x = Number.parseFloat(matrix[1]); + const y = Math.round(Number.parseFloat(matrix[2]) * 10) / 10; + + const row = rows.get(y) ?? []; + row.push([x, text]); + rows.set(y, row); + } + + for (const y of [...rows.keys()].sort((a, b) => b - a)) { + const row = rows.get(y) ?? []; + lines.push( + row + .sort(([a], [b]) => a - b) + .map(([, text]) => text) + .join(""), + ); + } + } + + return lines; +}; + +const parseLegalNatures = (lines: string[]): Record => { + const legalNatures: Record = {}; + + for (const line of lines) { + const match = line.match(/^\s*(\d{3})-(\d)\s*-\s*(.+?)\s*$/); + if (!match) continue; + + const code = `${match[1]}${match[2]}`; + const description = match[3].replace(/\s+/g, " ").trim(); + + legalNatures[code] = TYPO_FIXES[description] ?? description; + } + + return legalNatures; +}; + +const stringifyEntries = (entries: Record): string => + Object.entries(entries) + .map(([code, description]) => `\t${JSON.stringify(code)}: ${JSON.stringify(description)},`) + .join("\n"); + +const main = async () => { + const response = await fetchWithRetry(SOURCE_URL); + + if (!response.ok) { + throw new Error(`CONCLA legal natures request failed with status ${response.status}`); + } + + const pdf = Buffer.from(await response.arrayBuffer()); + const current = parseLegalNatures(extractLines(inflateStreams(pdf))); + const codes = Object.keys(current); + + if (codes.length !== EXPECTED_CODES) { + throw new Error(`Expected ${EXPECTED_CODES} legal natures, got ${codes.length}`); + } + + const legacy = Object.fromEntries( + Object.entries(LEGACY_LEGAL_NATURE).filter(([code]) => !(code in current)), + ); + + await writeFile( + resolve(scriptsDir, "..", OUTPUT_PATH), + `/** + * Tabela de Natureza Jurídica 2021 (IBGE/CONCLA), indexed by the four digit code. + * + * Generated by \`node ./scripts/legal-natures.ts\`. Do not edit by hand. + * + * @see ${SOURCE_PAGE_URL} + * @see ${SOURCE_URL} + */ +export const LEGAL_NATURE: Record = { +${stringifyEntries(current)} + +${stringifyEntries(legacy)} +}; +`, + ); +}; + +await main().catch((error) => { + console.error(error instanceof Error ? error.message : error); + process.exit(1); +}); diff --git a/src/format-legal-nature/format-legal-nature.ts b/src/format-legal-nature/format-legal-nature.ts index cf2c6568..3c75dbe3 100644 --- a/src/format-legal-nature/format-legal-nature.ts +++ b/src/format-legal-nature/format-legal-nature.ts @@ -1,8 +1,24 @@ import { format } from "../_internals/format/format"; +import { isNullish } from "../_internals/is-nullish/is-nullish"; import { sanitizeToDigits } from "../_internals/sanitize-to-digits/sanitize-to-digits"; +/** + * Formats a Brazilian legal nature (natureza jurídica) code. + * + * @param {string|number} value - The legal nature code to be formatted. + * @returns {string} The formatted code, or an empty string when there is nothing to format. + * + * @example + * ```typescript + * formatLegalNature("2062"); // "206-2" + * ``` + * + * @see Official: https://concla.ibge.gov.br/estrutura/natjur-estrutura/natureza-juridica-2021 + */ export const formatLegalNature = (value: string | number): string => - format({ - value: sanitizeToDigits(value), - pattern: "000-0", - }); + isNullish(value) + ? "" + : format({ + value: sanitizeToDigits(value), + pattern: "000-0", + }); diff --git a/src/generate-legal-nature/generate-legal-nature.ts b/src/generate-legal-nature/generate-legal-nature.ts index 70eff652..920122a2 100644 --- a/src/generate-legal-nature/generate-legal-nature.ts +++ b/src/generate-legal-nature/generate-legal-nature.ts @@ -1,6 +1,20 @@ import { LEGAL_NATURE } from "../is-valid-legal-nature/constants"; -const LEGAL_NATURE_CODES = Object.keys(LEGAL_NATURE); - -export const generateLegalNature = (): string => - LEGAL_NATURE_CODES[Math.floor(Math.random() * LEGAL_NATURE_CODES.length)]; +/** + * Generates a random valid Brazilian legal nature (natureza jurídica) code. + * + * Uses `Math.random()` internally, so it is not cryptographically secure, do not use for security purposes. + * + * @returns {string} One of the legal nature codes published by the CONCLA. + * + * @example + * ```typescript + * generateLegalNature(); // "2062" + * ``` + * + * @see Official: https://concla.ibge.gov.br/estrutura/natjur-estrutura/natureza-juridica-2021 + */ +export const generateLegalNature = (): string => { + const legalNatureCodes = Object.keys(LEGAL_NATURE); + return legalNatureCodes[Math.floor(Math.random() * legalNatureCodes.length)]; +}; diff --git a/src/get-legal-natures/get-legal-natures.test.ts b/src/get-legal-natures/get-legal-natures.test.ts index bbc2ea0c..3c3b479c 100644 --- a/src/get-legal-natures/get-legal-natures.test.ts +++ b/src/get-legal-natures/get-legal-natures.test.ts @@ -3,6 +3,19 @@ import { getLegalNatures } from "./get-legal-natures"; describe("getLegalNatures", () => { it("should return legal nature entries", () => { - expect(getLegalNatures()["2062"]).toBe("Sociedade Empresaria Limitada"); + expect(getLegalNatures()["2062"]).toBe("Sociedade Empresária Limitada"); + }); + + it("should use the descriptions of the 2021 CONCLA table", () => { + expect(getLegalNatures()["1015"]).toBe("Órgão Público do Poder Executivo Federal"); + expect(getLegalNatures()["5010"]).toBe("Organização Internacional"); + expect(getLegalNatures()["3999"]).toBe("Associação Privada"); + }); + + it("should return a copy, so mutating the result does not change the table", () => { + const legalNatures = getLegalNatures(); + legalNatures["2062"] = "changed"; + + expect(getLegalNatures()["2062"]).toBe("Sociedade Empresária Limitada"); }); }); diff --git a/src/get-legal-natures/get-legal-natures.ts b/src/get-legal-natures/get-legal-natures.ts index d3511505..6b18b36d 100644 --- a/src/get-legal-natures/get-legal-natures.ts +++ b/src/get-legal-natures/get-legal-natures.ts @@ -1,3 +1,26 @@ import { LEGAL_NATURE } from "../is-valid-legal-nature/constants"; -export const getLegalNatures = (): Record => ({ ...LEGAL_NATURE }); +/** + * Returns every Brazilian legal nature (natureza jurídica) published by the CONCLA. + * + * @returns {Record} A fresh object mapping each 4 digit code to its description. + * + * @example + * ```typescript + * getLegalNatures()["2062"]; // "Sociedade Empresária Limitada" + * ``` + * + * @see Official: https://concla.ibge.gov.br/estrutura/natjur-estrutura/natureza-juridica-2021 + */ +export const getLegalNatures = (): Record => { + const entries = Object.entries(LEGAL_NATURE); + + const result: Record = {}; + + for (let i = 0; i < entries.length; i++) { + const [code, description] = entries[i]; + result[code] = description; + } + + return result; +}; diff --git a/src/is-valid-legal-nature/constants.ts b/src/is-valid-legal-nature/constants.ts index 884a236c..0786e941 100644 --- a/src/is-valid-legal-nature/constants.ts +++ b/src/is-valid-legal-nature/constants.ts @@ -1,62 +1,111 @@ +/** + * Tabela de Natureza Jurídica 2021 (IBGE/CONCLA), indexed by the four digit code. + * + * Generated by `node ./scripts/legal-natures.ts`. Do not edit by hand. + * + * @see https://concla.ibge.gov.br/estrutura/natjur-estrutura/natureza-juridica-2021 + * @see https://concla.ibge.gov.br/images/concla/documentacao/CONCLA-TNJ2021-EstruturaDetalhada.pdf + */ export const LEGAL_NATURE: Record = { - "1015": "Orgao Publico do Poder Executivo Federal", - "1023": "Orgao Publico do Poder Executivo Estadual ou do Distrito Federal", - "1031": "Orgao Publico do Poder Executivo Municipal", - "1040": "Orgao Publico do Poder Legislativo Federal", - "1058": "Orgao Publico do Poder Legislativo Estadual ou do Distrito Federal", - "1066": "Orgao Publico do Poder Legislativo Municipal", - "1074": "Orgao Publico do Poder Judiciario Federal", - "1082": "Orgao Publico do Poder Judiciario Estadual", + "1015": "Órgão Público do Poder Executivo Federal", + "1023": "Órgão Público do Poder Executivo Estadual ou do Distrito Federal", + "1031": "Órgão Público do Poder Executivo Municipal", + "1040": "Órgão Público do Poder Legislativo Federal", + "1058": "Órgão Público do Poder Legislativo Estadual ou do Distrito Federal", + "1066": "Órgão Público do Poder Legislativo Municipal", + "1074": "Órgão Público do Poder Judiciário Federal", + "1082": "Órgão Público do Poder Judiciário Estadual", "1104": "Autarquia Federal", "1112": "Autarquia Estadual ou do Distrito Federal", "1120": "Autarquia Municipal", - "1139": "Fundacao Federal", - "1147": "Fundacao Estadual ou do Distrito Federal", - "1155": "Fundacao Municipal", - "1163": "Orgao Publico Autonomo da Uniao", - "1171": "Orgao Publico Autonomo Estadual ou do Distrito Federal", - "1180": "Orgao Publico Autonomo Municipal", - "2011": "Empresa Publica", + "1139": "Fundação Pública de Direito Público Federal", + "1147": "Fundação Pública de Direito Público Estadual ou do Distrito Federal", + "1155": "Fundação Pública de Direito Público Municipal", + "1163": "Órgão Público Autônomo Federal", + "1171": "Órgão Público Autônomo Estadual ou do Distrito Federal", + "1180": "Órgão Público Autônomo Municipal", + "1198": "Comissão Polinacional", + "1210": "Consórcio Público de Direito Público (Associação Pública)", + "1228": "Consórcio Público de Direito Privado", + "1236": "Estado ou Distrito Federal", + "1244": "Município", + "1252": "Fundação Pública de Direito Privado Federal", + "1260": "Fundação Pública de Direito Privado Estadual ou do Distrito Federal", + "1279": "Fundação Pública de Direito Privado Municipal", + "1287": "Fundo Público da Administração Indireta Federal", + "1295": "Fundo Público da Administração Indireta Estadual ou do Distrito Federal", + "1309": "Fundo Público da Administração Indireta Municipal", + "1317": "Fundo Público da Administração Direta Federal", + "1325": "Fundo Público da Administração Direta Estadual ou do Distrito Federal", + "1333": "Fundo Público da Administração Direta Municipal", + "1341": "União", + "2011": "Empresa Pública", "2038": "Sociedade de Economia Mista", - "2046": "Sociedade Anonima Aberta", - "2054": "Sociedade Anonima Fechada", - "2062": "Sociedade Empresaria Limitada", - "2076": "Sociedade Empresaria em Nome Coletivo", - "2089": "Sociedade Empresaria em Comandita Simples", - "2097": "Sociedade Empresaria em Comandita por Acoes", - "2100": "Sociedade Mercantil de Capital e Industria (extinta pelo NCC/2002)", - "2127": "Sociedade Empresaria em Conta de Participacao", - "2135": "Empresario (Individual)", + "2046": "Sociedade Anônima Aberta", + "2054": "Sociedade Anônima Fechada", + "2062": "Sociedade Empresária Limitada", + "2070": "Sociedade Empresária em Nome Coletivo", + "2089": "Sociedade Empresária em Comandita Simples", + "2097": "Sociedade Empresária em Comandita por Ações", + "2127": "Sociedade em Conta de Participação", + "2135": "Empresário (Individual)", "2143": "Cooperativa", - "2151": "Consorcio de Sociedades", + "2151": "Consórcio de Sociedades", "2160": "Grupo de Sociedades", "2178": "Estabelecimento, no Brasil, de Sociedade Estrangeira", "2194": "Estabelecimento, no Brasil, de Empresa Binacional Argentino-Brasileira", - "2208": "Entidade Binacional Itaipu", "2216": "Empresa Domiciliada no Exterior", "2224": "Clube/Fundo de Investimento", "2232": "Sociedade Simples Pura", "2240": "Sociedade Simples Limitada", - "2259": "Sociedade em Nome Coletivo", - "2267": "Sociedade em Comandita Simples", - "2275": "Sociedade Simples em Conta de Participacao", - "2305": "Empresa Individual de Responsabilidade Limitada", - "3034": "Servico Notarial e Registral (Cartorio)", - "3042": "Organizacao Social", - "3050": "Organizacao da Sociedade Civil de Interesse Publico (Oscip)", - "3069": "Outras Formas de Fundacoes Mantidas com Recursos Privados", - "3077": "Servico Social Autonomo", - "3085": "Condominio Edilicios", - "3093": "Unidade Executora (Programa Dinheiro Direto na Escola)", - "3107": "Comissao de Conciliacao Previa", - "3115": "Entidade de Mediacao e Arbitragem", - "3123": "Partido Politico", + "2259": "Sociedade Simples em Nome Coletivo", + "2267": "Sociedade Simples em Comandita Simples", + "2275": "Empresa Binacional", + "2283": "Consórcio de Empregadores", + "2291": "Consórcio Simples", + "2305": "Empresa Individual de Responsabilidade Limitada (de Natureza Empresária)", + "2313": "Empresa Individual de Responsabilidade Limitada (de Natureza Simples)", + "2321": "Sociedade Unipessoal de Advogados", + "2330": "Cooperativas de Consumo", + "2348": "Empresa Simples de Inovação - Inova Simples", + "2356": "Investidor Não Residente", + "3034": "Serviço Notarial e Registral (Cartório)", + "3069": "Fundação Privada", + "3077": "Serviço Social Autônomo", + "3085": "Condomínio Edilício", + "3107": "Comissão de Conciliação Prévia", + "3115": "Entidade de Mediação e Arbitragem", "3131": "Entidade Sindical", - "3204": "Estabelecimento, no Brasil, de Fundacao ou Associacao Estrangeiras", - "3212": "Fundacao ou Associacao Domiciliada no Exterior", - "3999": "Outras Formas de Associacao", - "4014": "Empresa Individual Imobiliaria", + "3204": "Estabelecimento, no Brasil, de Fundação ou Associação Estrangeiras", + "3212": "Fundação ou Associação Domiciliada no Exterior", + "3220": "Organização Religiosa", + "3239": "Comunidade Indígena", + "3247": "Fundo Privado", + "3255": "Órgão de Direção Nacional de Partido Político", + "3263": "Órgão de Direção Regional de Partido Político", + "3271": "Órgão de Direção Local de Partido Político", + "3280": "Comitê Financeiro de Partido Político", + "3298": "Frente Plebiscitária ou Referendária", + "3301": "Organização Social (OS)", + "3310": "Demais Condomínios", + "3328": "Plano de Benefícios de Previdência Complementar Fechada", + "3999": "Associação Privada", + "4014": "Empresa Individual Imobiliária", "4022": "Segurado Especial", "4081": "Contribuinte individual", - "5002": "Organizacao Internacional e Outras Instituicoes Extraterritoriais", + "4090": "Candidato a Cargo Político Eletivo", + "4111": "Leiloeiro", + "4120": "Produtor Rural (Pessoa Física)", + "5010": "Organização Internacional", + "5029": "Representação Diplomática Estrangeira", + "5037": "Outras Instituições Extraterritoriais", + + "2076": "Sociedade Empresária em Nome Coletivo", + "2100": "Sociedade Mercantil de Capital e Indústria (extinta pelo NCC/2002)", + "2208": "Entidade Binacional Itaipu", + "3042": "Organização Social", + "3050": "Organização da Sociedade Civil de Interesse Público (Oscip)", + "3093": "Unidade Executora (Programa Dinheiro Direto na Escola)", + "3123": "Partido Político", + "5002": "Organização Internacional e Outras Instituições Extraterritoriais", }; diff --git a/src/is-valid-legal-nature/is-valid-legal-nature.test.ts b/src/is-valid-legal-nature/is-valid-legal-nature.test.ts index 44abe67f..d92d19e8 100644 --- a/src/is-valid-legal-nature/is-valid-legal-nature.test.ts +++ b/src/is-valid-legal-nature/is-valid-legal-nature.test.ts @@ -7,4 +7,38 @@ describe("isValidLegalNature", () => { expect(isValidLegalNature("206-2")).toBe(true); expect(isValidLegalNature("9999")).toBe(false); }); + + it("should validate codes introduced by the 2021 CONCLA table", () => { + expect(isValidLegalNature("5010")).toBe(true); + expect(isValidLegalNature("1244")).toBe(true); + expect(isValidLegalNature("1252")).toBe(true); + expect(isValidLegalNature("2348")).toBe(true); + expect(isValidLegalNature("2356")).toBe(true); + expect(isValidLegalNature("3301")).toBe(true); + expect(isValidLegalNature("4090")).toBe(true); + expect(isValidLegalNature("4111")).toBe(true); + expect(isValidLegalNature("4120")).toBe(true); + }); + + it("should keep validating codes extinguished by CONCLA", () => { + expect(isValidLegalNature("5002")).toBe(true); + expect(isValidLegalNature("3123")).toBe(true); + expect(isValidLegalNature("2076")).toBe(true); + }); + + it("should reject codes with a length different from 4", () => { + expect(isValidLegalNature("206")).toBe(false); + expect(isValidLegalNature("20620")).toBe(false); + expect(isValidLegalNature("")).toBe(false); + // @ts-expect-error not a string + expect(isValidLegalNature(null)).toBe(false); + }); + + it("should reject any character other than digits and the mask", () => { + expect(isValidLegalNature("2062a")).toBe(false); + expect(isValidLegalNature("a2062")).toBe(false); + expect(isValidLegalNature("20/62")).toBe(false); + expect(isValidLegalNature(" 206-2 ")).toBe(true); + expect(isValidLegalNature("206.2")).toBe(true); + }); }); diff --git a/src/is-valid-legal-nature/is-valid-legal-nature.ts b/src/is-valid-legal-nature/is-valid-legal-nature.ts index b652d004..1d02444e 100644 --- a/src/is-valid-legal-nature/is-valid-legal-nature.ts +++ b/src/is-valid-legal-nature/is-valid-legal-nature.ts @@ -1,10 +1,33 @@ -import { sanitizeToDigits } from "../_internals/sanitize-to-digits/sanitize-to-digits"; import { LEGAL_NATURE } from "./constants"; +const MASK_REGEX = /[-.\s]/g; + +const CODE_REGEX = /^\d{4}$/; + +/** + * Validates if a Brazilian legal nature (natureza jurídica) code exists. + * + * Only the usual mask characters (hyphens, dots, whitespace) are tolerated around the 4 + * digits. Any other character makes the value invalid, so `"2062a"` is rejected instead of + * being read as `"2062"`. + * + * @param {string} code - The legal nature code to be validated, with or without formatting. + * @returns {boolean} True when the code is a known 4 digit legal nature, false otherwise. + * + * @example + * ```typescript + * isValidLegalNature("2062"); // true + * isValidLegalNature("206-2"); // true + * isValidLegalNature("2062a"); // false + * isValidLegalNature("0000"); // false + * ``` + * + * @see Official: https://concla.ibge.gov.br/estrutura/natjur-estrutura/natureza-juridica-2021 + */ export const isValidLegalNature = (code: string): boolean => { - if (!code || typeof code !== "string") return false; + if (typeof code !== "string" || code === "") return false; - const normalized = sanitizeToDigits(code); + const normalized = code.replace(MASK_REGEX, ""); - return normalized.length === 4 && normalized in LEGAL_NATURE; + return CODE_REGEX.test(normalized) && normalized in LEGAL_NATURE; }; From 98f9b6f2fbd0c39135b3605d805c9d09cfe0a6e6 Mon Sep 17 00:00:00 2001 From: Hyan Mandian <5044101+hyanmandian@users.noreply.github.com> Date: Fri, 11 Sep 2026 17:32:07 -0300 Subject: [PATCH 12/13] fix(holidays): audit every state holiday against its law MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every STATE_HOLIDAYS entry now carries a @see citation to the law that created it, extracted into a new get-holidays/constants.ts. Added: AP Consciência Negra (2007-2023), PB 26/07, PR 15/11, TO 18/03, ES Nossa Senhora da Penha (Easter+8). Removed (no law found / not an actual holiday): CE 19/03, ES 23/05, GO 05/07 and 26/07, MT 09/05, MG 21/07, RJ 20/01. Fixed: RN 07/09 -> 07/08 (was duplicating Independência do Brasil); the MT/RJ entry is renamed to "Dia da Consciência Negra"; AL 16/09 is now marked optional. Each Holiday carries a type ("national"|"state"|"optional"|"religious") and results are memoized per year/stateCode, returning fresh copies on every call. --- src/_internals/constants/holidays.ts | 5 + src/get-holidays/constants.ts | 176 +++++++++++++++ src/get-holidays/get-holidays.test.ts | 235 ++++++++++++++++---- src/get-holidays/get-holidays.ts | 304 ++++++++++++-------------- 4 files changed, 506 insertions(+), 214 deletions(-) create mode 100644 src/_internals/constants/holidays.ts create mode 100644 src/get-holidays/constants.ts diff --git a/src/_internals/constants/holidays.ts b/src/_internals/constants/holidays.ts new file mode 100644 index 00000000..1c9ba42b --- /dev/null +++ b/src/_internals/constants/holidays.ts @@ -0,0 +1,5 @@ +/** First year `getHolidays` can compute, and therefore the first year the business-day utils accept. */ +export const HOLIDAYS_MIN_YEAR = 1900; + +/** Last year `getHolidays` can compute, and therefore the last year the business-day utils accept. */ +export const HOLIDAYS_MAX_YEAR = 2099; diff --git a/src/get-holidays/constants.ts b/src/get-holidays/constants.ts new file mode 100644 index 00000000..04aeb4e9 --- /dev/null +++ b/src/get-holidays/constants.ts @@ -0,0 +1,176 @@ +import type { StateCode } from "../_internals/constants/states"; +import type { HolidayType } from "./get-holidays"; + +export type StateHolidayEntry = { + name: string; + day?: number; + month?: number; + easterOffset?: number; + type?: HolidayType; + since?: number; + until?: number; +}; + +export const FIXED_HOLIDAYS = { + "Ano novo": { day: 1, month: 1 }, + Tiradentes: { day: 21, month: 4 }, + "Dia do trabalhador": { day: 1, month: 5 }, + "Independência do Brasil": { day: 7, month: 9 }, + "Nossa Senhora Aparecida": { day: 12, month: 10 }, + Finados: { day: 2, month: 11 }, + "Proclamação da República": { day: 15, month: 11 }, + Natal: { day: 25, month: 12 }, +} as const; + +export const CONSCIENCIA_NEGRA_NATIONAL_SINCE_YEAR = 2024; + +export const CONSCIENCIA_NEGRA_HOLIDAY_NAME = "Dia da Consciência Negra"; + +export const LEGACY_CONSCIENCIA_NEGRA_HOLIDAY_NAME = "Consciência Negra"; + +/** + * Feriados estaduais por lei estadual, um por UF (uma UF pode ter mais de um `@see`). + * + * @see https://pt.wikipedia.org/wiki/Acre Lei AC nº 1.538/2004, Dia do Evangélico + * @see https://pt.wikipedia.org/wiki/Acre Lei AC nº 1.411/2001, Dia Internacional da Mulher + * @see https://pt.wikipedia.org/wiki/Acre Lei AC nº 14/1964, Aniversário do Acre + * @see https://pt.wikipedia.org/wiki/Acre Lei AC nº 1.526/2004, Dia da Amazônia + * @see https://pt.wikipedia.org/wiki/Acre Lei AC nº 57/1965, Assinatura do Tratado de Petrópolis + * @see https://pt.wikipedia.org/wiki/Alagoas Lei AL nº 5.508/1993, São João + * @see https://pt.wikipedia.org/wiki/Alagoas Lei AL nº 5.509/1993, São Pedro + * @see https://pt.wikipedia.org/wiki/Feriados_no_Brasil Decreto AL nº 68.782/2019 (ponto facultativo), Emancipação Política de Alagoas + * @see https://pt.wikipedia.org/wiki/Feriados_no_Brasil Lei AP nº 667/2002, Dia de São José + * @see https://pt.wikipedia.org/wiki/Amap%C3%A1 Constituição Estadual do AP, Criação do Território Federal do Amapá + * @see https://pt.wikipedia.org/wiki/Dia_Nacional_de_Zumbi_e_da_Consci%C3%AAncia_Negra Lei AP nº 1.169/2007, Dia Estadual da Consciência Negra (state holiday until it became national in 2024) + * @see https://sapl.al.am.leg.br/norma/8919 Lei AM nº 25/1977, Elevação do Amazonas à categoria de Província (05/09) + * @see https://sapl.al.am.leg.br/norma/2873 Lei AM nº 84/2010, Dia da Consciência Negra (state holiday until it became national in 2024) + * @see https://pt.wikipedia.org/wiki/Feriados_no_Brasil Constituição Estadual da BA, Independência da Bahia + * @see https://pt.wikipedia.org/wiki/Cear%C3%A1 Constituição Estadual do CE (Data Magna), Abolição da Escravidão no Ceará + * @see https://pt.wikipedia.org/wiki/Feriados_no_Brasil Lei Distrital nº 963/1995, Dia do Evangélico (DF) + * @see https://pt.wikipedia.org/wiki/Feriados_no_Brasil Fundação de Brasília (21/4, Lei Orgânica do DF) + * @see https://pt.wikipedia.org/wiki/Esp%C3%ADrito_Santo_(estado) Lei ES nº 11.010/2019, Nossa Senhora da Penha (padroeira do estado, 8º dia após a Páscoa) + * @see https://pt.wikipedia.org/wiki/Feriados_no_Brasil Lei MA nº 2.457/1964, Adesão do Maranhão à Independência + * @see https://pt.wikipedia.org/wiki/Mato_Grosso Lei MT nº 1.587/2002, Dia da Consciência Negra (state holiday until it became national in 2024) + * @see https://pt.wikipedia.org/wiki/Feriados_no_Brasil Lei MS nº 10/1979, Criação do Estado de Mato Grosso do Sul + * @see https://pt.wikipedia.org/wiki/Feriados_no_Brasil Lei PA nº 5.999/1996, Adesão do Pará à Independência + * @see https://pt.wikipedia.org/wiki/Para%C3%ADba Lei PB nº 10.601/2015, Fundação do Estado e Dia de Nossa Senhora das Neves + * @see https://pt.wikipedia.org/wiki/Para%C3%ADba Lei PB nº 3.489/1967, art. 2º, Morte de João Pessoa + * @see https://pt.wikipedia.org/wiki/Paran%C3%A1 Lei PR nº 18.384/2014 (ponto facultativo), Emancipação Política do Paraná + * @see https://pt.wikipedia.org/wiki/Feriados_no_Brasil Lei PE nº 13.835/2009, Revolução Pernambucana + * @see https://pt.wikipedia.org/wiki/Feriados_no_Brasil Lei PI nº 176/1937, Dia do Piauí + * @see http://alerjln1.alerj.rj.gov.br/CONTLEI.NSF/c8aa0900025feef6032564ec0060dfff/1baf90ca125ff96f8325740a00776600 Lei RJ nº 5.198/2008, São Jorge + * @see http://alerjln1.alerj.rj.gov.br/CONTLEI.NSF/69d90307244602bb032567e800668618/80a541c3a5a9d63183256c7d0057bf25 Lei RJ nº 4.007/2002, Dia da Consciência Negra (state holiday until it became national in 2024; ADI 4.131 pending at the STF) + * @see https://pt.wikipedia.org/wiki/Feriados_no_Brasil Lei RN nº 8.913/2006, Mártires de Cunhaú e Uruaçu + * @see https://pt.wikipedia.org/wiki/Feriados_no_Brasil Constituição Estadual do RS, Revolução Farroupilha + * @see https://pt.wikipedia.org/wiki/Feriados_no_Brasil Lei RO nº 3.170/2013, Criação do Estado de Rondônia + * @see https://pt.wikipedia.org/wiki/Feriados_no_Brasil Constituição Estadual de RR, Criação do Estado de Roraima + * @see https://pt.wikipedia.org/wiki/Santa_Catarina Lei SC nº 16.719/2015 (consolida e revoga as Leis nº 10.306/1996 e 12.906/2004), Criação da Capitania de Santa Catarina + * @see https://pt.wikipedia.org/wiki/Santa_Catarina Lei SC nº 16.719/2015, Dia de Santa Catarina de Alexandria + * @see https://www.al.sp.gov.br/repositorio/legislacao/lei/1995/lei-710-25.04.1995.html Lei SP nº 710/1995, Revolução Constitucionalista + * @see https://www.al.sp.gov.br/repositorio/legislacao/lei/2023/lei-17746-12.09.2023.html Lei SP nº 17.746/2023, Dia da Consciência Negra (state holiday in 2023, national since 2024) + * @see https://pt.wikipedia.org/wiki/Feriados_no_Brasil Constituição Estadual de SE, Emancipação Política de Sergipe + * @see https://pt.wikipedia.org/wiki/Feriados_no_Brasil Lei TO nº 960/1998, Autonomia do Estado do Tocantins + * @see https://pt.wikipedia.org/wiki/Feriados_no_Brasil Lei TO nº 627/1993, Padroeira do Estado (Nossa Senhora da Natividade) + * @see https://pt.wikipedia.org/wiki/Feriados_no_Brasil Lei TO nº 98/1989, Criação do Estado do Tocantins + */ +export const STATE_HOLIDAYS: Partial> = { + AC: [ + { name: "Dia do Evangélico", day: 23, month: 1 }, + { name: "Dia Internacional da Mulher", day: 8, month: 3 }, + { name: "Aniversário do Acre", day: 15, month: 6 }, + { name: "Dia da Amazônia", day: 5, month: 9 }, + { name: "Assinatura do Tratado de Petrópolis", day: 17, month: 11 }, + ], + AL: [ + { name: "São João", day: 24, month: 6 }, + { name: "São Pedro", day: 29, month: 6 }, + { name: "Emancipação Política de Alagoas", day: 16, month: 9, type: "optional" }, + ], + AP: [ + { name: "Dia de São José", day: 19, month: 3 }, + { name: "Criação do Território Federal do Amapá", day: 13, month: 9 }, + { + name: "Dia Estadual da Consciência Negra", + day: 20, + month: 11, + since: 2007, + until: CONSCIENCIA_NEGRA_NATIONAL_SINCE_YEAR, + }, + ], + AM: [ + { name: "Elevação do Amazonas à categoria de Província", day: 5, month: 9 }, + { + name: CONSCIENCIA_NEGRA_HOLIDAY_NAME, + day: 20, + month: 11, + since: 2010, + until: CONSCIENCIA_NEGRA_NATIONAL_SINCE_YEAR, + }, + ], + BA: [{ name: "Independência da Bahia", day: 2, month: 7 }], + CE: [{ name: "Abolição da Escravidão no Ceará", day: 25, month: 3 }], + DF: [ + { name: "Fundação de Brasília", day: 21, month: 4 }, + { name: "Dia do Evangélico", day: 30, month: 11 }, + ], + ES: [{ name: "Nossa Senhora da Penha", easterOffset: 8 }], + MA: [{ name: "Adesão do Maranhão à Independência", day: 28, month: 7 }], + MT: [ + { + name: LEGACY_CONSCIENCIA_NEGRA_HOLIDAY_NAME, + day: 20, + month: 11, + until: CONSCIENCIA_NEGRA_NATIONAL_SINCE_YEAR, + }, + ], + MS: [{ name: "Criação do Estado de Mato Grosso do Sul", day: 11, month: 10 }], + PA: [{ name: "Adesão do Pará à Independência", day: 15, month: 8 }], + PB: [ + { + name: "Fundação do Estado e Dia de Nossa Senhora das Neves", + day: 5, + month: 8, + }, + { name: "Morte de João Pessoa", day: 26, month: 7 }, + ], + PR: [{ name: "Emancipação Política do Paraná", day: 19, month: 12, type: "optional" }], + PE: [{ name: "Revolução Pernambucana", day: 6, month: 3 }], + PI: [{ name: "Dia do Piauí", day: 19, month: 10 }], + RJ: [ + { name: "São Jorge", day: 23, month: 4 }, + { + name: LEGACY_CONSCIENCIA_NEGRA_HOLIDAY_NAME, + day: 20, + month: 11, + until: CONSCIENCIA_NEGRA_NATIONAL_SINCE_YEAR, + }, + ], + RN: [{ name: "Mártires de Cunhaú e Uruaçu", day: 3, month: 10 }], + RS: [{ name: "Revolução Farroupilha", day: 20, month: 9 }], + RO: [{ name: "Criação do Estado de Rondônia", day: 4, month: 1 }], + RR: [{ name: "Criação do Estado de Roraima", day: 5, month: 10 }], + SC: [ + { name: "Criação da Capitania de Santa Catarina", day: 11, month: 8 }, + { name: "Dia de Santa Catarina de Alexandria", day: 25, month: 11 }, + ], + SP: [ + { name: "Revolução Constitucionalista", day: 9, month: 7 }, + { + name: CONSCIENCIA_NEGRA_HOLIDAY_NAME, + day: 20, + month: 11, + since: 2023, + until: CONSCIENCIA_NEGRA_NATIONAL_SINCE_YEAR, + }, + ], + SE: [{ name: "Emancipação Política de Sergipe", day: 8, month: 7 }], + TO: [ + { name: "Autonomia do Estado do Tocantins", day: 18, month: 3 }, + { + name: "Padroeira do Estado (Nossa Senhora da Natividade)", + day: 8, + month: 9, + }, + { name: "Criação do Estado do Tocantins", day: 5, month: 10 }, + ], +}; diff --git a/src/get-holidays/get-holidays.test.ts b/src/get-holidays/get-holidays.test.ts index 230e81ac..742a07d5 100644 --- a/src/get-holidays/get-holidays.test.ts +++ b/src/get-holidays/get-holidays.test.ts @@ -1,4 +1,5 @@ import { describe, expect, test } from "../_internals/test/runtime"; +import { STATE_HOLIDAYS } from "./constants"; import { getHolidays } from "./get-holidays"; describe("getHolidays", () => { @@ -7,57 +8,68 @@ describe("getHolidays", () => { const holidays = getHolidays(year); const fixedHolidays = [ - { name: "Ano novo", date: new Date(year, 0, 1) }, - { name: "Tiradentes", date: new Date(year, 3, 21) }, - { name: "Dia do trabalhador", date: new Date(year, 4, 1) }, - { name: "Independência do Brasil", date: new Date(year, 8, 7) }, - { name: "Nossa Senhora Aparecida", date: new Date(year, 9, 12) }, - { name: "Finados", date: new Date(year, 10, 2) }, - { name: "Proclamação da República", date: new Date(year, 10, 15) }, - { name: "Dia da Consciência Negra", date: new Date(year, 10, 20) }, - { name: "Natal", date: new Date(year, 11, 25) }, + { name: "Ano novo", date: new Date(year, 0, 1), type: "national" }, + { name: "Tiradentes", date: new Date(year, 3, 21), type: "national" }, + { name: "Dia do trabalhador", date: new Date(year, 4, 1), type: "national" }, + { name: "Independência do Brasil", date: new Date(year, 8, 7), type: "national" }, + { name: "Nossa Senhora Aparecida", date: new Date(year, 9, 12), type: "national" }, + { name: "Finados", date: new Date(year, 10, 2), type: "national" }, + { name: "Proclamação da República", date: new Date(year, 10, 15), type: "national" }, + { name: "Dia da Consciência Negra", date: new Date(year, 10, 20), type: "national" }, + { name: "Natal", date: new Date(year, 11, 25), type: "national" }, ]; - fixedHolidays.forEach(({ name, date }) => { - expect(holidays).toContainEqual({ name, date }); + fixedHolidays.forEach((holiday) => { + expect(holidays).toContainEqual(holiday); }); }); - test("should calculate Easter-related holidays correctly", () => { + test("should not include Dia da Consciência Negra as a national holiday before 2024 (Lei nº 14.759/2023 made it national only from 2024 onward)", () => { + const holidays = getHolidays(2023); + + expect( + holidays.find( + (holiday) => holiday.name === "Dia da Consciência Negra" && holiday.type === "national", + ), + ).toBeUndefined(); + }); + + test("should calculate Easter-related holidays correctly, including Corpus Christi 60 days after Easter Sunday (not 70, so Thu 2024-05-30)", () => { const year = 2024; const holidays = getHolidays(year); - const easterDate = new Date(2024, 2, 31); // Easter Sunday 2024 + const easterDate = new Date(2024, 2, 31); const expectedHolidays = [ - { name: "Páscoa", date: easterDate }, - { name: "Carnaval (terça-feira)", date: new Date(2024, 1, 13) }, - { name: "Sexta-feira Santa", date: new Date(2024, 2, 29) }, - { name: "Corpus Christi", date: new Date(2024, 5, 9) }, + { name: "Páscoa", date: easterDate, type: "religious" }, + { name: "Carnaval (terça-feira)", date: new Date(2024, 1, 13), type: "optional" }, + { name: "Sexta-feira Santa", date: new Date(2024, 2, 29), type: "national" }, + { name: "Corpus Christi", date: new Date(2024, 4, 30), type: "optional" }, ]; - expectedHolidays.forEach(({ name, date }) => { - expect(holidays).toContainEqual({ name, date }); + expectedHolidays.forEach((holiday) => { + expect(holidays).toContainEqual(holiday); }); }); - test("should return the correct number of holidays", () => { + test("should return 13 holidays for 2024: 9 fixed holidays (including Consciência Negra) plus 4 Easter-related holidays", () => { const year = 2024; const holidays = getHolidays(year); - expect(holidays.length).toBe(13); // 9 fixed + 4 Easter-related holidays + expect(holidays.length).toBe(13); }); - test("should work for leap years", () => { + test("should work for leap years, computing Easter Sunday 2020 correctly", () => { const year = 2020; const holidays = getHolidays(year); expect(holidays).toContainEqual({ name: "Ano novo", date: new Date(year, 0, 1), + type: "national", }); - const easterDate = new Date(2020, 3, 12); // Easter Sunday 2020 - expect(holidays).toContainEqual({ name: "Páscoa", date: easterDate }); + const easterDate = new Date(2020, 3, 12); + expect(holidays).toContainEqual({ name: "Páscoa", date: easterDate, type: "religious" }); }); test("should accept year as number parameter", () => { @@ -72,68 +84,201 @@ describe("getHolidays", () => { expect(holidays.every((h) => h.date.getFullYear() === 2024)).toBe(true); }); - test("should include state-specific holidays when stateCode is provided", () => { + test("should include state-specific holidays when stateCode is provided, still containing national holidays, with more than 13 total", () => { const holidays = getHolidays({ year: 2024, stateCode: "SP" }); - // Should include national holidays expect(holidays).toContainEqual({ name: "Ano novo", date: new Date(2024, 0, 1), + type: "national", }); - // Should include state holiday expect(holidays).toContainEqual({ name: "Revolução Constitucionalista", date: new Date(2024, 6, 9), + type: "state", }); - // Should have more holidays than just national ones expect(holidays.length).toBeGreaterThan(13); }); - test("should include multiple state holidays for states with multiple holidays", () => { + test("should include RJ's São Jorge (Lei nº 5.198/2008) and treat Dia da Consciência Negra as the unified national entry from 2024 on, not a separate RJ entry; São Sebastião is a municipal holiday of the city of Rio de Janeiro, not a state law, so it is not included", () => { const holidays = getHolidays({ year: 2024, stateCode: "RJ" }); - // Should include São Sebastião - expect(holidays).toContainEqual({ - name: "São Sebastião", - date: new Date(2024, 0, 20), - }); - - // Should include São Jorge expect(holidays).toContainEqual({ name: "São Jorge", date: new Date(2024, 3, 23), + type: "state", }); - // Should include Consciência Negra (state-specific) + expect(holidays.some((h) => h.name === "São Sebastião")).toBe(false); + expect(holidays).toContainEqual({ - name: "Consciência Negra", + name: "Dia da Consciência Negra", date: new Date(2024, 10, 20), + type: "national", + }); + }); + + test("should not duplicate Consciência Negra for states with their own entry from 2024 on", () => { + const rjHolidays = getHolidays({ year: 2024, stateCode: "RJ" }); + const mtHolidays = getHolidays({ year: 2024, stateCode: "MT" }); + + expect(rjHolidays.filter((h) => h.name === "Dia da Consciência Negra")).toHaveLength(1); + expect(mtHolidays.filter((h) => h.name === "Dia da Consciência Negra")).toHaveLength(1); + }); + + test("should keep the state-specific Consciência Negra entry before 2024", () => { + const rjHolidays = getHolidays({ year: 2023, stateCode: "RJ" }); + const mtHolidays = getHolidays({ year: 2023, stateCode: "MT" }); + + expect(rjHolidays).toContainEqual({ + name: "Consciência Negra", + date: new Date(2023, 10, 20), + type: "state", + }); + expect(mtHolidays).toContainEqual({ + name: "Consciência Negra", + date: new Date(2023, 10, 20), + type: "state", }); }); - test("should return only national holidays when stateCode is not provided", () => { + test("should return only national holidays when stateCode is not provided, while SP's holidays still contain every national holiday plus extras", () => { const nationalHolidays = getHolidays(2024); const spHolidays = getHolidays({ year: 2024, stateCode: "SP" }); - // SP holidays should include all national holidays nationalHolidays.forEach((nationalHoliday) => { expect(spHolidays).toContainEqual(nationalHoliday); }); - // SP holidays should have more than national holidays expect(spHolidays.length).toBeGreaterThan(nationalHolidays.length); }); - test("should work with different state codes", () => { + test("should work with different state codes: RS includes Revolução Farroupilha (Constituição Estadual), while MG has no state-specific entry list since its Data Magna (21/4) coincides with the national Tiradentes holiday and there is no law backing a separate 'Aniversário de Minas Gerais' on 21/7", () => { const rsHolidays = getHolidays({ year: 2024, stateCode: "RS" }); const mgHolidays = getHolidays({ year: 2024, stateCode: "MG" }); - // RS should have Revolução Farroupilha expect(rsHolidays.some((h) => h.name === "Revolução Farroupilha")).toBe(true); - // MG should have Aniversário de Minas Gerais - expect(mgHolidays.some((h) => h.name === "Aniversário de Minas Gerais")).toBe(true); + expect(mgHolidays.some((h) => h.name === "Aniversário de Minas Gerais")).toBe(false); + }); + + test("should ignore an unknown stateCode and return national-only holidays", () => { + const nationalHolidays = getHolidays(2024); + // @ts-expect-error + const holidays = getHolidays({ year: 2024, stateCode: "XX" }); + + expect(holidays).toEqual(nationalHolidays); + }); + + test("should ignore a non-string stateCode and return national-only holidays", () => { + const nationalHolidays = getHolidays(2024); + // @ts-expect-error + const holidays = getHolidays({ year: 2024, stateCode: 123 }); + + expect(holidays).toEqual(nationalHolidays); + }); + + test("should return a fresh copy on every call so mutation cannot leak between calls", () => { + const first = getHolidays(2024); + first[0].name = "MUTATED"; + first[0].date.setFullYear(1900); + + const second = getHolidays(2024); + expect(second[0].name).not.toBe("MUTATED"); + expect(second[0].date.getFullYear()).toBe(2024); + }); + test("should apply state laws for Consciência Negra before it became national", () => { + const name = "Dia da Consciência Negra"; + + expect(getHolidays({ year: 2009, stateCode: "AM" }).map((h) => h.name)).not.toContain(name); + expect(getHolidays({ year: 2010, stateCode: "AM" }).map((h) => h.name)).toContain(name); + expect(getHolidays({ year: 2022, stateCode: "SP" }).map((h) => h.name)).not.toContain(name); + expect(getHolidays({ year: 2023, stateCode: "SP" }).map((h) => h.name)).toContain(name); + expect( + getHolidays({ year: 2024, stateCode: "SP" }).filter((h) => h.name === name), + ).toHaveLength(1); + }); + + test("should include state holidays added after the 2026 legal audit: PB's Morte de João Pessoa (Lei nº 3.489/1967, art. 2º), TO's Autonomia do Estado do Tocantins (Lei nº 960/1998), and AP's Dia Estadual da Consciência Negra (Lei nº 1.169/2007, until superseded by the 2024 national holiday)", () => { + const pbHolidays = getHolidays({ year: 2024, stateCode: "PB" }); + const toHolidays = getHolidays({ year: 2024, stateCode: "TO" }); + const apHolidays2023 = getHolidays({ year: 2023, stateCode: "AP" }); + const apHolidays2024 = getHolidays({ year: 2024, stateCode: "AP" }); + + expect(pbHolidays).toContainEqual({ + name: "Morte de João Pessoa", + date: new Date(2024, 6, 26), + type: "state", + }); + + expect(toHolidays).toContainEqual({ + name: "Autonomia do Estado do Tocantins", + date: new Date(2024, 2, 18), + type: "state", + }); + + expect(apHolidays2023).toContainEqual({ + name: "Dia Estadual da Consciência Negra", + date: new Date(2023, 10, 20), + type: "state", + }); + + expect(apHolidays2024.some((h) => h.name === "Dia Estadual da Consciência Negra")).toBe(false); + }); + + test("should compute ES's Nossa Senhora da Penha (Lei nº 11.010/2019) as a movable state holiday, 8 days after Easter Sunday, replacing the removed 'Dia do Estado do Espírito Santo' which was only a municipal ponto facultativo", () => { + const holidays = getHolidays({ year: 2024, stateCode: "ES" }); + + expect(holidays).toContainEqual({ + name: "Nossa Senhora da Penha", + date: new Date(2024, 3, 8), + type: "state", + }); + + expect(holidays.some((h) => h.name === "Dia do Estado do Espírito Santo")).toBe(false); + }); + + test("should not list commemorative dates as state holidays: PR's Dia de Nossa Senhora do Rocio (Lei nº 22.360/2025 only adds it to the state events calendar; 15 de novembro is already the national Proclamação da República) and RN's Dia do Rio Grande do Norte (Lei nº 7.831/2000 creates a commemorative date, not a holiday)", () => { + const prHolidays = getHolidays({ year: 2024, stateCode: "PR" }); + const rnHolidays = getHolidays({ year: 2024, stateCode: "RN" }); + + expect(prHolidays.filter((h) => h.date.getMonth() === 10 && h.date.getDate() === 15)).toEqual([ + { name: "Proclamação da República", date: new Date(2024, 10, 15), type: "national" }, + ]); + expect(prHolidays.some((h) => h.name === "Dia de Nossa Senhora do Rocio")).toBe(false); + expect(rnHolidays.some((h) => h.name === "Dia do Rio Grande do Norte")).toBe(false); + expect(rnHolidays).toContainEqual({ + name: "Mártires de Cunhaú e Uruaçu", + date: new Date(2024, 9, 3), + type: "state", + }); + }); + + test("should no longer include state holidays that lack a statewide legal basis: CE's São José (municipal, Fortaleza's patron saint), GO's Dia do Estado and Nossa Senhora Sant'Ana (no state law found), MT's Criação do Estado de Mato Grosso (Mato Grosso's only state holiday by law is Dia da Consciência Negra), and RJ's São Sebastião (municipal, city of Rio de Janeiro's patron saint)", () => { + const ceHolidays = getHolidays({ year: 2024, stateCode: "CE" }); + const goHolidays = getHolidays({ year: 2024, stateCode: "GO" }); + const mtHolidays = getHolidays({ year: 2024, stateCode: "MT" }); + const rjHolidays = getHolidays({ year: 2024, stateCode: "RJ" }); + + expect(ceHolidays.some((h) => h.name === "Dia de São José")).toBe(false); + expect(goHolidays.some((h) => h.name === "Dia do Estado de Goiás")).toBe(false); + expect(goHolidays.some((h) => h.name === "Nossa Senhora Sant'Ana")).toBe(false); + expect(mtHolidays.some((h) => h.name === "Criação do Estado de Mato Grosso")).toBe(false); + expect(rjHolidays.some((h) => h.name === "São Sebastião")).toBe(false); + }); + + test("should throw when a state holiday entry defines neither easterOffset nor both day and month", () => { + const entries = STATE_HOLIDAYS.AC ?? []; + entries.push({ name: "Feriado estadual sem data" }); + + try { + expect(() => getHolidays({ year: 2098, stateCode: "AC" })).toThrow( + "State holiday entry must define either `easterOffset` or both `day` and `month`", + ); + } finally { + entries.pop(); + } }); }); diff --git a/src/get-holidays/get-holidays.ts b/src/get-holidays/get-holidays.ts index 4999aa27..308cca22 100644 --- a/src/get-holidays/get-holidays.ts +++ b/src/get-holidays/get-holidays.ts @@ -1,127 +1,32 @@ +import { HOLIDAYS_MAX_YEAR, HOLIDAYS_MIN_YEAR } from "../_internals/constants/holidays"; import type { StateCode } from "../_internals/constants/states"; +import { isNullish } from "../_internals/is-nullish/is-nullish"; +import { + CONSCIENCIA_NEGRA_HOLIDAY_NAME, + CONSCIENCIA_NEGRA_NATIONAL_SINCE_YEAR, + FIXED_HOLIDAYS, + type StateHolidayEntry, + STATE_HOLIDAYS, +} from "./constants"; + +export type HolidayType = "national" | "state" | "optional" | "religious"; export type Holiday = { + /** The holiday name in Brazilian Portuguese, e.g. `"Sexta-feira Santa"`. */ name: string; + /** The holiday date, at local midnight of the requested year. */ date: Date; + /** How the holiday is observed: national, state, optional (ponto facultativo) or religious. */ + type: HolidayType; }; export type GetHolidaysOptions = { + /** The four digit year to list holidays for. Must be an integer between 1900 and 2099. */ year: number; + /** Two letter state code whose state holidays are added to the national ones (default: national holidays only). */ stateCode?: StateCode; }; -/** - * Fixed holidays that occur on the same date every year. - * Month is 1-based (1 = January, 12 = December). - */ -const FIXED_HOLIDAYS = { - "Ano novo": { day: 1, month: 1 }, - Tiradentes: { day: 21, month: 4 }, - "Dia do trabalhador": { day: 1, month: 5 }, - "Independência do Brasil": { day: 7, month: 9 }, - "Nossa Senhora Aparecida": { day: 12, month: 10 }, - Finados: { day: 2, month: 11 }, - "Proclamação da República": { day: 15, month: 11 }, - "Dia da Consciência Negra": { day: 20, month: 11 }, - Natal: { day: 25, month: 12 }, -} as const; - -/** - * State-specific holidays by state code. - * Month is 1-based (1 = January, 12 = December). - */ -const STATE_HOLIDAYS: Partial< - Record> -> = { - AC: [ - { name: "Dia do Evangélico", day: 23, month: 1 }, - { name: "Dia Internacional da Mulher", day: 8, month: 3 }, - { name: "Aniversário do Acre", day: 15, month: 6 }, - { name: "Dia da Amazônia", day: 5, month: 9 }, - { name: "Assinatura do Tratado de Petrópolis", day: 17, month: 11 }, - ], - AL: [ - { name: "São João", day: 24, month: 6 }, - { name: "São Pedro", day: 29, month: 6 }, - { name: "Emancipação Política de Alagoas", day: 16, month: 9 }, - ], - AP: [ - { name: "Dia de São José", day: 19, month: 3 }, - { name: "Criação do Território Federal do Amapá", day: 13, month: 9 }, - ], - AM: [ - { name: "Elevação do Amazonas à categoria de Província", day: 5, month: 9 }, - { name: "Nossa Senhora da Conceição", day: 8, month: 12 }, - ], - BA: [{ name: "Independência da Bahia", day: 2, month: 7 }], - CE: [ - { name: "Dia de São José", day: 19, month: 3 }, - { name: "Abolição da Escravidão no Ceará", day: 25, month: 3 }, - ], - DF: [ - { name: "Fundação de Brasília", day: 21, month: 4 }, - { name: "Dia do Evangélico", day: 30, month: 11 }, - ], - ES: [{ name: "Dia do Estado do Espírito Santo", day: 23, month: 5 }], - GO: [ - { name: "Dia do Estado de Goiás", day: 5, month: 7 }, - { name: "Nossa Senhora Sant'Ana", day: 26, month: 7 }, - ], - MA: [{ name: "Adesão do Maranhão à Independência", day: 28, month: 7 }], - MT: [ - { name: "Criação do Estado de Mato Grosso", day: 9, month: 5 }, - { name: "Consciência Negra", day: 20, month: 11 }, - ], - MS: [{ name: "Criação do Estado de Mato Grosso do Sul", day: 11, month: 10 }], - MG: [{ name: "Aniversário de Minas Gerais", day: 21, month: 7 }], - PA: [{ name: "Adesão do Pará à Independência", day: 15, month: 8 }], - PB: [ - { - name: "Fundação do Estado e Dia de Nossa Senhora das Neves", - day: 5, - month: 8, - }, - ], - PR: [{ name: "Emancipação Política do Paraná", day: 19, month: 12 }], - PE: [{ name: "Revolução Pernambucana", day: 6, month: 3 }], - PI: [{ name: "Dia do Piauí", day: 19, month: 10 }], - RJ: [ - { name: "São Sebastião", day: 20, month: 1 }, - { name: "São Jorge", day: 23, month: 4 }, - { name: "Consciência Negra", day: 20, month: 11 }, - ], - RN: [ - { name: "Mártires de Cunhaú e Uruaçu", day: 3, month: 10 }, - { name: "Dia do Rio Grande do Norte", day: 7, month: 9 }, - ], - RS: [{ name: "Revolução Farroupilha", day: 20, month: 9 }], - RO: [ - { name: "Criação do Estado de Rondônia", day: 4, month: 1 }, - { name: "Dia do Evangélico", day: 18, month: 6 }, - ], - RR: [{ name: "Criação do Estado de Roraima", day: 5, month: 10 }], - SC: [ - { name: "Criação da Capitania de Santa Catarina", day: 11, month: 8 }, - { name: "Dia de Santa Catarina de Alexandria", day: 25, month: 11 }, - ], - SP: [{ name: "Revolução Constitucionalista", day: 9, month: 7 }], - SE: [{ name: "Emancipação Política de Sergipe", day: 8, month: 7 }], - TO: [ - { - name: "Padroeira do Estado (Nossa Senhora da Natividade)", - day: 8, - month: 9, - }, - { name: "Criação do Estado do Tocantins", day: 5, month: 10 }, - ], -} as const; - -/** - * Calculates the date of Easter Sunday for a given year using the Computus algorithm. - * - * @param {number} year - The year for which to calculate Easter - * @returns {Date} The date of Easter Sunday - */ function calculateEaster(year: number): Date { const a = year % 19; const b = Math.floor(year / 100); @@ -136,19 +41,12 @@ function calculateEaster(year: number): Date { const l = (32 + 2 * e + 2 * i - h - k) % 7; const m = Math.floor((a + 11 * h + 22 * l) / 451); - const month = Math.floor((h + l - 7 * m + 114) / 31) - 1; // 0-based month + const month = Math.floor((h + l - 7 * m + 114) / 31) - 1; const day = ((h + l - 7 * m + 114) % 31) + 1; return new Date(year, month, day); } -/** - * Calculates a holiday date based on Easter Sunday with a given offset in days. - * - * @param {number} year - The year for which to calculate the holiday - * @param {number} offset - The number of days to add or subtract from Easter (negative for before, positive for after) - * @returns {Date} The date of the holiday - */ function calculateHolidayFromEaster(year: number, offset: number): Date { const easterDate = calculateEaster(year); const holidayDate = new Date(easterDate); @@ -156,99 +54,167 @@ function calculateHolidayFromEaster(year: number, offset: number): Date { return holidayDate; } -/** - * Retrieves all Brazilian holidays for a given year. - * - * The function returns both fixed holidays (that occur on the same date every year) - * and movable holidays (that are calculated based on Easter Sunday). - * If a state code is provided, state-specific holidays are also included. - * - * Holidays are returned sorted by date (chronological order). - * - * @param {number} year - The year for which to retrieve holidays (must be between 1900 and 2099) - * @returns {Holiday[]} An array of holidays sorted by date - * - * @example - * ```typescript - * // Get all national holidays - * const holidays = getHolidays(2024); - * - * // Get holidays for a specific state - * const spHolidays = getHolidays({ year: 2024, stateCode: 'SP' }); - * ``` - */ -export function getHolidays(year: number): Holiday[]; -export function getHolidays(options: GetHolidaysOptions): Holiday[]; -export function getHolidays(yearOrOptions: number | GetHolidaysOptions): Holiday[] { - let year: number; - let stateCode: StateCode | undefined; - - // Handle overload: either a number (year) or an options object - if (typeof yearOrOptions === "number") { - year = yearOrOptions; - stateCode = undefined; - } else { - year = yearOrOptions.year; - stateCode = yearOrOptions.stateCode; +function resolveStateHolidayDate( + year: number, + { day, month, easterOffset }: Pick, +): Date { + if (easterOffset !== undefined) { + return calculateHolidayFromEaster(year, easterOffset); } - // Validate year - if (typeof year !== "number" || !Number.isInteger(year) || year < 1900 || year > 2099) { - return []; + if (day !== undefined && month !== undefined) { + return new Date(year, month - 1, day); } + throw new Error( + "State holiday entry must define either `easterOffset` or both `day` and `month`", + ); +} + +let cache: Map | undefined; + +const cloneHolidays = (holidays: Holiday[]): Holiday[] => + holidays.map((holiday) => ({ ...holiday, date: new Date(holiday.date) })); + +const computeHolidays = (year: number, stateCode: StateCode | undefined): Holiday[] => { const holidays: Holiday[] = []; - // Add fixed national holidays for (const [name, { day, month }] of Object.entries(FIXED_HOLIDAYS)) { holidays.push({ name, date: new Date(year, month - 1, day), + type: "national", + }); + } + + if (year >= CONSCIENCIA_NEGRA_NATIONAL_SINCE_YEAR) { + holidays.push({ + name: CONSCIENCIA_NEGRA_HOLIDAY_NAME, + date: new Date(year, 10, 20), + type: "national", }); } - // Calculate and add movable holidays based on Easter const easterDate = calculateEaster(year); - // Carnaval (Shrove Tuesday) - 47 days before Easter holidays.push({ name: "Carnaval (terça-feira)", date: calculateHolidayFromEaster(year, -47), + type: "optional", }); - // Sexta-feira Santa (Good Friday) - 2 days before Easter holidays.push({ name: "Sexta-feira Santa", date: calculateHolidayFromEaster(year, -2), + type: "national", }); - // Páscoa (Easter Sunday) holidays.push({ name: "Páscoa", date: easterDate, + type: "religious", }); - // Corpus Christi - 60 days after Easter Sunday holidays.push({ name: "Corpus Christi", - date: calculateHolidayFromEaster(year, 70), + date: calculateHolidayFromEaster(year, 60), + type: "optional", }); - // Add state-specific holidays if stateCode is provided - if (stateCode) { + if (stateCode !== undefined) { const stateHolidays = STATE_HOLIDAYS[stateCode]; if (stateHolidays) { - for (const { name, day, month } of stateHolidays) { + for (const entry of stateHolidays) { + const { name, type, since, until } = entry; + if (since !== undefined && year < since) continue; + if (until !== undefined && year >= until) continue; + holidays.push({ name, - date: new Date(year, month - 1, day), + date: resolveStateHolidayDate(year, entry), + type: type ?? "state", }); } } } - // Sort holidays by date (chronological order) holidays.sort((a, b) => a.date.getTime() - b.date.getTime()); return holidays; +}; + +/** + * Retrieves all Brazilian holidays for a given year. + * + * The function returns both fixed holidays (that occur on the same date every year) + * and movable holidays (that are calculated based on Easter Sunday). + * If a state code is provided, state-specific holidays are also included. + * + * Holidays are returned sorted by date (chronological order). Results are memoized + * per `year`/`stateCode` combination; the returned array (and each `Holiday.date`) is + * always a fresh copy, so mutating it never affects subsequent calls. + * + * If `stateCode` is provided but is not a valid/known state code, it is ignored and + * only national holidays are returned (this mirrors passing no `stateCode` at all, + * and is kept for backwards compatibility). + * + * @param {number} year - The year for which to retrieve holidays (must be between 1900 and 2099) + * @returns {Holiday[]} An array of holidays sorted by date + * + * @example + * ```typescript + * // Get all national holidays + * const holidays = getHolidays(2024); + * + * // Get holidays for a specific state + * const spHolidays = getHolidays({ year: 2024, stateCode: 'SP' }); + * ``` + * + * @see Official: https://www.planalto.gov.br/ccivil_03/leis/l0662.htm National holidays law + * (fixed and movable national holidays). + * @see Official: state holiday laws are cited individually, one `@see` per holiday, in + * `src/get-holidays/constants.ts`. + * @see Based on: https://pt.wikipedia.org/wiki/Feriados_no_Brasil Used as secondary evidence for + * some state holidays where no official law text was located (see constants.ts for which). + */ +export function getHolidays(year: number): Holiday[]; +export function getHolidays(options: GetHolidaysOptions): Holiday[]; +export function getHolidays(yearOrOptions: number | GetHolidaysOptions): Holiday[] { + let year: number; + let stateCode: StateCode | undefined; + + if (typeof yearOrOptions === "number") { + year = yearOrOptions; + stateCode = undefined; + } else if (isNullish(yearOrOptions) || typeof yearOrOptions !== "object") { + return []; + } else { + year = yearOrOptions.year; + stateCode = yearOrOptions.stateCode; + } + + if ( + typeof year !== "number" || + !Number.isInteger(year) || + year < HOLIDAYS_MIN_YEAR || + year > HOLIDAYS_MAX_YEAR + ) { + return []; + } + + const normalizedStateCode = typeof stateCode === "string" ? stateCode : undefined; + + const cacheKey = `${year}|${normalizedStateCode ?? ""}`; + + cache ??= new Map(); + + const cached = cache.get(cacheKey); + if (cached) { + return cloneHolidays(cached); + } + + const holidays = computeHolidays(year, normalizedStateCode); + cache.set(cacheKey, holidays); + + return cloneHolidays(holidays); } From 957db805250e899337df6e88087227e841b80dc9 Mon Sep 17 00:00:00 2001 From: Hyan Mandian <5044101+hyanmandian@users.noreply.github.com> Date: Fri, 11 Sep 2026 17:32:08 -0300 Subject: [PATCH 13/13] fix(holidays): isHoliday returns a boolean and never throws isHoliday(null) / isHoliday() threw a TypeError; it now returns false instead of throwing or returning the matched Holiday, so it can'"'"'t be used to read holiday details (use getHolidays for that). The comparison uses the Date'"'"'s local calendar date. --- src/is-holiday/is-holiday.test.ts | 62 +++++++++++++++++++++++++++++++ src/is-holiday/is-holiday.ts | 44 ++++++++++++++++++++-- 2 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 src/is-holiday/is-holiday.test.ts diff --git a/src/is-holiday/is-holiday.test.ts b/src/is-holiday/is-holiday.test.ts new file mode 100644 index 00000000..0305bad7 --- /dev/null +++ b/src/is-holiday/is-holiday.test.ts @@ -0,0 +1,62 @@ +import { describe, expect, it } from "../_internals/test/runtime"; +import { isHoliday } from "./is-holiday"; + +describe("isHoliday", () => { + it("should return true for a national holiday built from local date components", () => { + expect(isHoliday({ targetDate: new Date(2024, 0, 1) })).toBe(true); + }); + + it("should return true for Corpus Christi 2024 (Easter + 60 days)", () => { + expect(isHoliday({ targetDate: new Date(2024, 4, 30) })).toBe(true); + }); + + it("should return false for a non-holiday date", () => { + expect(isHoliday({ targetDate: new Date(2024, 5, 10) })).toBe(false); + }); + + it("should return true for a state holiday when stateCode is provided", () => { + expect(isHoliday({ targetDate: new Date(2024, 6, 9), stateCode: "SP" })).toBe(true); + }); + + it("should return false for a state holiday of another state when stateCode is not provided", () => { + expect(isHoliday({ targetDate: new Date(2024, 6, 9) })).toBe(false); + }); + + it("should return false when called without arguments", () => { + expect(isHoliday()).toBe(false); + }); + + it("should return false when targetDate is an invalid Date", () => { + expect(isHoliday({ targetDate: new Date("not a date") })).toBe(false); + }); + + it("should return false when targetDate is a string instead of a Date", () => { + // @ts-expect-error + expect(isHoliday({ targetDate: "2024-01-01" })).toBe(false); + }); + + it("should return false when stateCode is not a string", () => { + // @ts-expect-error + expect(isHoliday({ targetDate: new Date(2024, 0, 1), stateCode: 123 })).toBe(false); + }); + + it("should ignore an unknown stateCode and fall back to national holidays", () => { + // @ts-expect-error + expect(isHoliday({ targetDate: new Date(2024, 0, 1), stateCode: "XX" })).toBe(true); + // @ts-expect-error + expect(isHoliday({ targetDate: new Date(2024, 5, 10), stateCode: "XX" })).toBe(false); + }); + + describe("local calendar date vs UTC instant", () => { + it("should read the local calendar day of a UTC-midnight instant, not its UTC day, deriving the expectation from the ambient zone (e.g. '2024-12-25' is local 2024-12-24 in America/Sao_Paulo, UTC-3) so the test is deterministic under vitest, bun and deno", () => { + const utcMidnight = new Date("2024-12-25"); + const isLocallyChristmas = utcMidnight.getMonth() === 11 && utcMidnight.getDate() === 25; + + expect(isHoliday({ targetDate: utcMidnight })).toBe(isLocallyChristmas); + }); + + it("should return true when the date is built from local components instead", () => { + expect(isHoliday({ targetDate: new Date(2024, 11, 25) })).toBe(true); + }); + }); +}); diff --git a/src/is-holiday/is-holiday.ts b/src/is-holiday/is-holiday.ts index 4308d2dc..db746842 100644 --- a/src/is-holiday/is-holiday.ts +++ b/src/is-holiday/is-holiday.ts @@ -1,18 +1,56 @@ import type { StateCode } from "../_internals/constants/states"; +import { isNullish } from "../_internals/is-nullish/is-nullish"; import { getHolidays } from "../get-holidays/get-holidays"; export type IsHolidayOptions = { + /** The date to check, read by its local calendar day. */ targetDate: Date; + /** Two letter state code whose state holidays are also considered (default: national holidays only). */ stateCode?: StateCode; }; -export const isHoliday = ({ targetDate, stateCode }: IsHolidayOptions): boolean | null => { +/** + * Checks whether a given date is a Brazilian holiday. + * + * The check is based on `targetDate`'s **local calendar date** (its local year/month/day, + * as read by `Date#getFullYear`/`getMonth`/`getDate`), not its underlying UTC instant. + * This matters because `new Date("2024-12-25")` (a date-only ISO string) is parsed as UTC + * midnight, which in timezones behind UTC (e.g. America/Sao_Paulo, UTC-3) represents + * "2024-12-24" in local time, so build `targetDate` from local components + * (`new Date(2024, 11, 25)`) or from a full ISO datetime when you mean a specific local day. + * + * If `stateCode` is provided but is not a valid/known state code, it is ignored and only + * national holidays are considered (same behavior as `getHolidays`). + * + * @param {IsHolidayOptions} [options] - Options for the check. + * @param {Date} options.targetDate - The date to check. + * @param {StateCode} [options.stateCode] - Optional Brazilian state code to also consider state holidays. + * @returns {boolean} True when the date is a holiday, false otherwise. Bad input also returns + * false: missing `options`, a `targetDate` that is not a valid `Date`, or a non-string + * `stateCode`. + * + * @example + * ```typescript + * isHoliday({ targetDate: new Date(2024, 0, 1) }); // true (Ano novo) + * isHoliday({ targetDate: new Date(2024, 5, 10) }); // false + * isHoliday(); // false + * ``` + * + * @see Official: https://www.planalto.gov.br/ccivil_03/leis/l0662.htm + */ +export const isHoliday = (options?: IsHolidayOptions): boolean => { + if (isNullish(options) || typeof options !== "object") { + return false; + } + + const { targetDate, stateCode } = options; + if (!(targetDate instanceof Date) || Number.isNaN(targetDate.getTime())) { - return null; + return false; } if (stateCode !== undefined && typeof stateCode !== "string") { - return null; + return false; } return getHolidays({ year: targetDate.getFullYear(), stateCode }).some((holiday) => {