Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ itself:
form: area codes and their states (Anatel, `area-codes.ts`), service phone prefixes (Anatel,
`service-phone.ts`), national and state holidays (`holidays.ts`), the órgãos and tribunals of the
processo number (Resolução CNJ nº 65/2008, `processo-juridico.ts`), IBAN lengths per country
(`iban.ts`), IBGE state codes (`ibge-uf-codes.ts`), legal nature categories, the CST and CSOSN
(`iban.ts`), IBGE state codes (`ibge-uf-codes.ts`), the state capitals and the regions (IBGE,
`state-capitals.ts`, `regions.ts`), legal nature categories, the CST and CSOSN
tables (`src/is-valid-cst`, `src/is-valid-csosn`), the professional councils
(`src/is-valid-registro-profissional`), the região fiscal digit of each state
(`src/_internals/constants/cpf.ts`) and the voter ID state codes (`src/is-valid-voter-id`). A change to one of
Expand Down
52 changes: 52 additions & 0 deletions docs/pt-br/utilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -1780,6 +1780,58 @@ getStateNameByCode(' Rj '); // 'Rio de Janeiro'
getStateNameByCode('ZZ'); // null
```

### getStateCapital

Retorna a capital de um estado brasileiro, no mesmo formato `{ code, name, stateCode }` (`Municipality`) que o `getMunicipalityByCode` retorna para ela.

- A busca ignora maiúsculas e minúsculas e os espaços nas pontas. Retorna `null` quando nenhum estado corresponde.
- Para o Distrito Federal, que não tem municípios, a capital é Brasília, com o código que o IBGE dá ao distrito todo.

```javascript
import { getStateCapital } from '@brazilian-utils/brazilian-utils';

getStateCapital('SP'); // { code: '3550308', name: 'São Paulo', stateCode: 'SP' }
getStateCapital('to'); // { code: '1721000', name: 'Palmas', stateCode: 'TO' }
getStateCapital('ZZ'); // null
```

Fonte: [IBGE, Anuário Estatístico do Brasil, tabela 1.1.1.2 (capitais, 2025)](https://anuario.ibge.gov.br/2024/territorio/posicao-e-extensao.html).

### getRegions

Retorna as cinco Grandes Regiões do Brasil, cada uma com o código (o mesmo `regionCode` de cada estado), o nome e o identificador do IBGE, na ordem desse identificador. Exporta os tipos `Region` e `RegionCode`.

```javascript
import { getRegions } from '@brazilian-utils/brazilian-utils';

getRegions();
// [
// { code: 'N', name: 'Norte', ibgeCode: 1 },
// { code: 'NE', name: 'Nordeste', ibgeCode: 2 },
// { code: 'SE', name: 'Sudeste', ibgeCode: 3 },
// { code: 'S', name: 'Sul', ibgeCode: 4 },
// { code: 'CO', name: 'Centro-Oeste', ibgeCode: 5 },
// ]
```

Fonte: [IBGE, API de Localidades, `regioes`](https://servicodados.ibge.gov.br/api/v1/localidades/regioes).

### getStatesByRegion

Retorna os estados de uma região, dado o código dela (`'N'`, `'NE'`, `'SE'`, `'S'` ou `'CO'`), em ordem alfabética, como o `getStates` ordena.

- A busca ignora maiúsculas e minúsculas e os espaços nas pontas. Retorna `[]` quando nenhuma região corresponde.

```javascript
import { getStatesByRegion } from '@brazilian-utils/brazilian-utils';

getStatesByRegion('S').map((state) => state.code); // ['PR', 'RS', 'SC']
getStatesByRegion('co').map((state) => state.code); // ['DF', 'GO', 'MT', 'MS']
getStatesByRegion('X'); // []
```

Fonte: [IBGE, API de Localidades, `estados`](https://servicodados.ibge.gov.br/api/v1/localidades/estados).

### getTimezoneByState

Retorna o nome do fuso horário IANA (zona do tzdata) de um estado brasileiro: o fuso da sua capital.
Expand Down
52 changes: 52 additions & 0 deletions docs/utilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -1780,6 +1780,58 @@ getStateNameByCode(' Rj '); // 'Rio de Janeiro'
getStateNameByCode('ZZ'); // null
```

### getStateCapital

Get the capital of a Brazilian state, as the same `{ code, name, stateCode }` (`Municipality`) that `getMunicipalityByCode` returns for it.

- The match ignores case and surrounding whitespace. Returns `null` when no state matches.
- For the Distrito Federal, which has no municipalities, the capital is Brasília, with the code the IBGE gives the whole district.

```javascript
import { getStateCapital } from '@brazilian-utils/brazilian-utils';

getStateCapital('SP'); // { code: '3550308', name: 'São Paulo', stateCode: 'SP' }
getStateCapital('to'); // { code: '1721000', name: 'Palmas', stateCode: 'TO' }
getStateCapital('ZZ'); // null
```

Source: [IBGE, Anuário Estatístico do Brasil, table 1.1.1.2 (state capitals, 2025)](https://anuario.ibge.gov.br/2024/territorio/posicao-e-extensao.html).

### getRegions

Get the five Brazilian regions (Grandes Regiões), each with its code (the same `regionCode` every state carries), name and IBGE identifier, in the order of that identifier. Exports the `Region` and `RegionCode` types.

```javascript
import { getRegions } from '@brazilian-utils/brazilian-utils';

getRegions();
// [
// { code: 'N', name: 'Norte', ibgeCode: 1 },
// { code: 'NE', name: 'Nordeste', ibgeCode: 2 },
// { code: 'SE', name: 'Sudeste', ibgeCode: 3 },
// { code: 'S', name: 'Sul', ibgeCode: 4 },
// { code: 'CO', name: 'Centro-Oeste', ibgeCode: 5 },
// ]
```

Source: [IBGE, API de Localidades, `regioes`](https://servicodados.ibge.gov.br/api/v1/localidades/regioes).

### getStatesByRegion

Get the states of a region, given its code (`'N'`, `'NE'`, `'SE'`, `'S'` or `'CO'`), sorted by name the way `getStates` sorts them.

- The match ignores case and surrounding whitespace. Returns `[]` when no region matches.

```javascript
import { getStatesByRegion } from '@brazilian-utils/brazilian-utils';

getStatesByRegion('S').map((state) => state.code); // ['PR', 'RS', 'SC']
getStatesByRegion('co').map((state) => state.code); // ['DF', 'GO', 'MT', 'MS']
getStatesByRegion('X'); // []
```

Source: [IBGE, API de Localidades, `estados`](https://servicodados.ibge.gov.br/api/v1/localidades/estados).

### getTimezoneByState

Get the IANA time zone name (tzdata zone) of a Brazilian state: the zone of its capital.
Expand Down
3 changes: 3 additions & 0 deletions jsr.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,15 @@
"./get-nfse-key-info": "./src/get-nfse-key-info/get-nfse-key-info.ts",
"./get-pix-key-info": "./src/get-pix-key-info/get-pix-key-info.ts",
"./get-pix-payload-info": "./src/get-pix-payload-info/get-pix-payload-info.ts",
"./get-regions": "./src/get-regions/get-regions.ts",
"./get-service-item": "./src/get-service-item/get-service-item.ts",
"./get-state-by-cep": "./src/get-state-by-cep/get-state-by-cep.ts",
"./get-state-by-ibge-code": "./src/get-state-by-ibge-code/get-state-by-ibge-code.ts",
"./get-state-capital": "./src/get-state-capital/get-state-capital.ts",
"./get-state-code-by-name": "./src/get-state-code-by-name/get-state-code-by-name.ts",
"./get-state-name-by-code": "./src/get-state-name-by-code/get-state-name-by-code.ts",
"./get-states": "./src/get-states/get-states.ts",
"./get-states-by-region": "./src/get-states-by-region/get-states-by-region.ts",
"./get-timezone-by-state": "./src/get-timezone-by-state/get-timezone-by-state.ts",
"./is-business-day": "./src/is-business-day/is-business-day.ts",
"./is-holiday": "./src/is-holiday/is-holiday.ts",
Expand Down
31 changes: 31 additions & 0 deletions src/_internals/constants/regions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* One of the five Brazilian regions (Grandes Regiões), as `getRegions` returns it: the code the
* IBGE abbreviates it with (`code`, the same as `State["regionCode"]`), its name and its IBGE
* identifier (`ibgeCode`). Every region is its own member of the union, so narrowing by `code`
* narrows the other fields too.
*/
export type Region =
| { readonly code: "N"; readonly name: "Norte"; readonly ibgeCode: 1 }
| { readonly code: "NE"; readonly name: "Nordeste"; readonly ibgeCode: 2 }
| { readonly code: "SE"; readonly name: "Sudeste"; readonly ibgeCode: 3 }
| { readonly code: "S"; readonly name: "Sul"; readonly ibgeCode: 4 }
| { readonly code: "CO"; readonly name: "Centro-Oeste"; readonly ibgeCode: 5 };

/** The two letter or one letter code of a region, e.g. `"NE"`. */
export type RegionCode = Region["code"];

/**
* The five regions in the order of their IBGE identifier, the first digit of the code of each of
* their states.
*
* @see Official: https://servicodados.ibge.gov.br/api/v1/localidades/regioes
* IBGE, API de Localidades, `regioes`: `[{"id":1,"sigla":"N","nome":"Norte"}, ... ,
* {"id":5,"sigla":"CO","nome":"Centro-Oeste"}]`.
*/
export const REGIONS: readonly Region[] = [
{ code: "N", name: "Norte", ibgeCode: 1 },
{ code: "NE", name: "Nordeste", ibgeCode: 2 },
{ code: "SE", name: "Sudeste", ibgeCode: 3 },
{ code: "S", name: "Sul", ibgeCode: 4 },
{ code: "CO", name: "Centro-Oeste", ibgeCode: 5 },
];
44 changes: 44 additions & 0 deletions src/_internals/constants/state-capitals.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { type StateCode } from "./states";

/**
* The capital of each state, as `[name, ibgeCode]`: the municipality name as the IBGE spells it
* and its 7 digit IBGE code, the same pair `getMunicipalityByCode` finds for that code. For the
* Distrito Federal, whose territory is not divided into municipalities, the entry is Brasília,
* the code the IBGE gives the whole district.
*
* @see Official: https://anuario.ibge.gov.br/2024/territorio/posicao-e-extensao.html
* IBGE, Anuário Estatístico do Brasil, Tabela 1.1.1.2, "Localização geográfica, altitude e
* distância a Brasília, segundo os Municípios das Capitais - 2025": one capital per state,
* written "Porto Velho (RO)" to "Brasília (DF)".
* @see Official: https://geoftp.ibge.gov.br/organizacao_do_territorio/estrutura_territorial/divisao_territorial/2025/DTB_2025.zip
* IBGE, Divisão Territorial Brasileira 2025, the municipality codes.
*/
export const STATE_CAPITALS: Record<StateCode, readonly [name: string, ibgeCode: string]> = {
AC: ["Rio Branco", "1200401"],
AL: ["Maceió", "2704302"],
AM: ["Manaus", "1302603"],
AP: ["Macapá", "1600303"],
BA: ["Salvador", "2927408"],
CE: ["Fortaleza", "2304400"],
DF: ["Brasília", "5300108"],
ES: ["Vitória", "3205309"],
GO: ["Goiânia", "5208707"],
MA: ["São Luís", "2111300"],
MG: ["Belo Horizonte", "3106200"],
MS: ["Campo Grande", "5002704"],
MT: ["Cuiabá", "5103403"],
PA: ["Belém", "1501402"],
PB: ["João Pessoa", "2507507"],
PE: ["Recife", "2611606"],
PI: ["Teresina", "2211001"],
PR: ["Curitiba", "4106902"],
RJ: ["Rio de Janeiro", "3304557"],
RN: ["Natal", "2408102"],
RO: ["Porto Velho", "1100205"],
RR: ["Boa Vista", "1400100"],
RS: ["Porto Alegre", "4314902"],
SC: ["Florianópolis", "4205407"],
SE: ["Aracaju", "2800308"],
SP: ["São Paulo", "3550308"],
TO: ["Palmas", "1721000"],
};
49 changes: 49 additions & 0 deletions src/get-regions/get-regions.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { describe, expect, expectTypeOf, it, test } from "../_internals/test/runtime";
import { getStates } from "../get-states/get-states";
import { getRegions, type Region } from "./get-regions";

describe("getRegions", () => {
it("should return the five regions in the order of their IBGE identifier", () => {
expect(getRegions()).toEqual([
{ code: "N", name: "Norte", ibgeCode: 1 },
{ code: "NE", name: "Nordeste", ibgeCode: 2 },
{ code: "SE", name: "Sudeste", ibgeCode: 3 },
{ code: "S", name: "Sul", ibgeCode: 4 },
{ code: "CO", name: "Centro-Oeste", ibgeCode: 5 },
]);
});

it("should match the region code and name every state carries", () => {
const regions = getRegions();

for (const state of getStates()) {
expect(regions.find((region) => region.code === state.regionCode)?.name).toBe(
state.regionName,
);
}
});

it("should give each region the first digit of the IBGE code of its states", () => {
const regions = getRegions();

for (const state of getStates()) {
expect(regions.find((region) => region.code === state.regionCode)?.ibgeCode).toBe(
Math.floor(state.ibgeCode / 10),
);
}
});

it("should return fresh objects, so mutating one leaves a later call intact", () => {
const [first] = getRegions();

if (first !== undefined) Object.assign(first, { name: "MUTATED" });

expect(getRegions()[0]?.name).toBe("Norte");
});
});

describe("getRegions types", () => {
test("should return Region[]", () => {
expectTypeOf(getRegions).returns.toEqualTypeOf<Region[]>();
});
});
24 changes: 24 additions & 0 deletions src/get-regions/get-regions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { REGIONS, type Region } from "../_internals/constants/regions";

export type { Region, RegionCode } from "../_internals/constants/regions";

/**
* Retrieves the five Brazilian regions (Grandes Regiões) with their code, name and IBGE
* identifier, in the order of that identifier: Norte (1), Nordeste (2), Sudeste (3), Sul (4) and
* Centro-Oeste (5). The `code` is the same `State["regionCode"]` every state carries.
*
* Each call returns a fresh array of fresh objects, so mutating the result never affects the
* underlying data or later calls.
*
* @returns {Region[]} The five regions.
*
* @example
* ```typescript
* getRegions()[0]; // { code: "N", name: "Norte", ibgeCode: 1 }
* getRegions().map((region) => region.code); // ["N", "NE", "SE", "S", "CO"]
* ```
*
* @see Official: https://servicodados.ibge.gov.br/api/v1/localidades/regioes
* IBGE, API de Localidades, `regioes`.
*/
export const getRegions = (): Region[] => REGIONS.map((region) => Object.assign({}, region));
67 changes: 67 additions & 0 deletions src/get-state-capital/get-state-capital.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { STATE_CODES } from "../_internals/constants/state-codes";
import { anyGarbage } from "../_internals/test/arbitraries";
import { expectNeverThrows } from "../_internals/test/properties";
import { describe, expect, expectTypeOf, it, test } from "../_internals/test/runtime";
import {
getMunicipalityByCode,
type Municipality,
} from "../get-municipality-by-code/get-municipality-by-code";
import { getStateCapital } from "./get-state-capital";

describe("getStateCapital", () => {
it("should return the capital of a state, per the IBGE Anuário table of the state capitals", () => {
expect(getStateCapital("SP")).toEqual({ code: "3550308", name: "São Paulo", stateCode: "SP" });
expect(getStateCapital("TO")).toEqual({ code: "1721000", name: "Palmas", stateCode: "TO" });
expect(getStateCapital("RR")).toEqual({ code: "1400100", name: "Boa Vista", stateCode: "RR" });
});

it("should return Brasília for the Distrito Federal", () => {
expect(getStateCapital("DF")).toEqual({ code: "5300108", name: "Brasília", stateCode: "DF" });
});

it("should match case-insensitively and ignore surrounding whitespace", () => {
expect(getStateCapital(" mg ")).toEqual({
code: "3106200",
name: "Belo Horizonte",
stateCode: "MG",
});
});

it("should return a fresh object, so mutating it leaves a later call intact", () => {
const capital = getStateCapital("PE");

if (capital !== null) capital.name = "MUTATED";

expect(getStateCapital("PE")?.name).toBe("Recife");
});

it("should return null for a code that is not a state", () => {
expect(getStateCapital("ZZ")).toBeNull();
expect(getStateCapital("")).toBeNull();
expect(getStateCapital("__proto__")).toBeNull();
// @ts-expect-error: intentionally invalid input
expect(getStateCapital(35)).toBeNull();
});

describe("properties", () => {
test("should never throw, regardless of the input", () => {
expectNeverThrows(getStateCapital, anyGarbage);
});

test("should give every state a capital that getMunicipalityByCode finds in that state", () => {
for (const stateCode of STATE_CODES) {
const capital = getStateCapital(stateCode);

expect(capital).not.toBeNull();
expect(getMunicipalityByCode(capital?.code ?? "")).toEqual(capital);
}
});
});
});

describe("getStateCapital types", () => {
test("should take a string and return a Municipality or null", () => {
expectTypeOf(getStateCapital).parameter(0).toEqualTypeOf<string>();
expectTypeOf(getStateCapital).returns.toEqualTypeOf<Municipality | null>();
});
});
Loading
Loading