Skip to content

Commit

Permalink
481/add native date to result (#482)
Browse files Browse the repository at this point in the history
* feat(#481): add aditional arg

* test(#481): date instead of string

* test(#481): consolidate tests

* refactor(#481): improve api

* docs(#481): include returnNativeDate option

* refactor(#481): naming

* test(#481): add more cases

* docs(#481): remove unnecessary stuff
  • Loading branch information
MauricioRobayo authored Feb 9, 2023
1 parent b4104e7 commit dc02e50
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 44 deletions.
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@ The content of the `colombianHolidays2015` variable will be the following array:
```
<!-- prettier-ignore-end -->

You can opt-in to have the function return native JavaScript dates instead of strings for the `date` and `celebrationDate` properties by using the `returnNativeDate` option:

<!-- prettier-ignore-start -->

```js
const colombianHolidays2015 = colombianHolidays(2015, { returnNativeDate });
```

If the year is omitted, by default the function will return the holidays for the current year:

```js
Expand Down Expand Up @@ -124,14 +132,6 @@ const holidays = holidaysWithinInterval({ start, end });

The module is written in TypeScript and type definitions files are included.

## Contributing

Contributions, issues and feature requests are welcome!

## Show your support

Give a ⭐️ if you like this project!

## License

[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FMauricioRobayo%2Fcolombian-holidays.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2FMauricioRobayo%2Fcolombian-holidays?ref=badge_large)
14 changes: 3 additions & 11 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
module.exports = {
coverageThreshold: {
global: {
branches: 100,
functions: 100,
lines: 100,
statements: 100,
},
},
collectCoverageFrom: ['src/**/*'],
preset: 'ts-jest',
testEnvironment: 'node',
collectCoverageFrom: ["src/**/*"],
preset: "ts-jest",
testEnvironment: "node",
};
51 changes: 41 additions & 10 deletions src/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import pascua from "pascua";
import type { Holiday, EasterHoliday, ColombianHoliday } from "./types";
import type {
Holiday,
EasterHoliday,
ColombianHoliday,
ColombianHolidayWithNativeDate,
} from "./types";

// 1984 is the year when the current holidays scheme is enforced
// http://www.alcaldiabogota.gov.co/sisjur/normas/Norma1.jsp?i=4954
export const NEW_HOLIDAY_SCHEMA_START_YEAR = 1984;

function getNextDayOfWeek(date: Date, dayOfWeek: number): Date {
const resultDate = new Date(date);
resultDate.setDate(date.getDate() + ((7 + dayOfWeek - date.getDay()) % 7));
resultDate.setUTCDate(
date.getUTCDate() + ((7 + dayOfWeek - date.getUTCDay()) % 7)
);
return resultDate;
}

Expand All @@ -23,29 +30,53 @@ function isEasterHoliday(holiday: Holiday): holiday is EasterHoliday {
function getHolidayDate(holiday: Holiday, year: number): Date {
if (isEasterHoliday(holiday)) {
const { month, day } = pascua(year);
return new Date(year, month - 1, day + holiday.offset);
const date = new Date(`${year}-${String(month).padStart(2, "0")}-01`);
date.setUTCDate(day + holiday.offset);
return date;
}

const [month, day] = holiday.date.split("-");
return new Date(year, Number(month) - 1, Number(day));
return new Date(`${year}-${month.padStart(2, "0")}-${day.padStart(2, "0")}`);
}

function formatDate(date: Date): string {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
const year = date.getUTCFullYear();
const month = String(date.getUTCMonth() + 1).padStart(2, "0");
const day = String(date.getUTCDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
}

function getHoliday(holiday: Holiday, year: number): ColombianHoliday {
function getHoliday(
holiday: Holiday,
year: number,
options?: undefined | { returnNativeDate?: false | undefined }
): ColombianHoliday;
function getHoliday(
holiday: Holiday,
year: number,
options?: { returnNativeDate: true }
): ColombianHolidayWithNativeDate;
function getHoliday(
holiday: Holiday,
year: number,
options?: { returnNativeDate?: boolean }
): ColombianHoliday | ColombianHolidayWithNativeDate;
function getHoliday(
holiday: Holiday,
year: number,
{ returnNativeDate = false }: { returnNativeDate?: boolean } = {}
): unknown {
const holidayDate = getHolidayDate(holiday, year);
const celebrationDate =
year >= NEW_HOLIDAY_SCHEMA_START_YEAR && holiday.nextMonday
? getNextMonday(holidayDate)
: holidayDate;

return {
date: formatDate(holidayDate),
celebrationDate: formatDate(celebrationDate),
date: returnNativeDate ? holidayDate : formatDate(holidayDate),
celebrationDate: returnNativeDate
? celebrationDate
: formatDate(celebrationDate),
name: holiday.name,
nextMonday: year >= NEW_HOLIDAY_SCHEMA_START_YEAR && holiday.nextMonday,
};
Expand Down
10 changes: 5 additions & 5 deletions src/holidays.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ import type { DateHoliday, EasterHoliday } from "./types";

const dateHolidays: DateHoliday[] = [
{ date: "01-01", name: "Año Nuevo", nextMonday: false },
{ date: "05-01", name: "Día del Trabajo", nextMonday: false },
{ date: "07-20", name: "Grito de la Independencia", nextMonday: false },
{ date: "08-07", name: "Batalla de Boyacá", nextMonday: false },
{ date: "12-08", name: "Inmaculada Concepción", nextMonday: false },
{ date: "12-25", name: "Navidad", nextMonday: false },
{ date: "01-06", name: "Reyes Magos", nextMonday: true },
{ date: "03-19", name: "San José", nextMonday: true },
{ date: "05-01", name: "Día del Trabajo", nextMonday: false },
{ date: "06-29", name: "San Pedro y San Pablo", nextMonday: true },
{ date: "07-20", name: "Grito de la Independencia", nextMonday: false },
{ date: "08-07", name: "Batalla de Boyacá", nextMonday: false },
{ date: "08-15", name: "Asunción de la Virgen", nextMonday: true },
{ date: "10-12", name: "Día de la Raza", nextMonday: true },
{ date: "11-01", name: "Todos los Santos", nextMonday: true },
{ date: "11-11", name: "Independencia de Cartagena", nextMonday: true },
{ date: "12-08", name: "Inmaculada Concepción", nextMonday: false },
{ date: "12-25", name: "Navidad", nextMonday: false },
];

// We could simplify the calculation by setting the offset to match Monday.
Expand Down
45 changes: 40 additions & 5 deletions src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -459,11 +459,46 @@ afterEach(() => {
});

describe.each(years)("Gets all holidays for %p", (year) => {
it.each(timezones)("Should return holidays for %p", (timezone) => {
timezone_mock.register(timezone);
expect(colombianHolidays(year).length).toBe(holidaysYears[year].length);
expect(colombianHolidays(year)).toEqual(holidaysYears[year]);
});
it.each(timezones)(
"Should return holidays formatted as string for %p if no options given",
(timezone) => {
timezone_mock.register(timezone);
expect(colombianHolidays(year)).toEqual(holidaysYears[year]);
}
);

it.each(timezones)(
"Should return holidays formatted as string for %p if options is empty",
(timezone) => {
timezone_mock.register(timezone);
expect(colombianHolidays(year, {})).toEqual(holidaysYears[year]);
}
);

it.each(timezones)(
"Should return holidays formatted as string for %p if returnNativeDate is set to false",
(timezone) => {
timezone_mock.register(timezone);
expect(colombianHolidays(year, { returnNativeDate: false })).toEqual(
holidaysYears[year]
);
}
);

it.each(timezones)(
"Should return holidays with native JS date for %p if returnNativeDate is set to true",
(timezone) => {
timezone_mock.register(timezone);
expect(colombianHolidays(year, { returnNativeDate: true })).toEqual(
holidaysYears[year].map((holiday) => ({
date: new Date(holiday.date),
celebrationDate: new Date(holiday.celebrationDate),
name: holiday.name,
nextMonday: holiday.nextMonday,
}))
);
}
);
});

describe("Gets all holidays for the current year", () => {
Expand Down
39 changes: 34 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,52 @@
import getHoliday from "./helpers";
import holidays from "./holidays";
import { ColombianHoliday } from "./types";
import { ColombianHoliday, ColombianHolidayWithNativeDate } from "./types";

// pascua package year limits
export const FIRST_HOLIDAY_YEAR = 1583;
export const LAST_HOLIDAY_YEAR = 4099;

function colombianHolidays(
year: number = new Date().getFullYear()
): ColombianHoliday[] {
year?: number,
options?: undefined | { returnNativeDate?: false | undefined }
): ColombianHoliday[];
function colombianHolidays(
year?: number,
options?: { returnNativeDate?: true }
): ColombianHolidayWithNativeDate[];
function colombianHolidays(
year?: number,
options?: { returnNativeDate?: boolean }
): (ColombianHoliday | ColombianHolidayWithNativeDate)[];
function colombianHolidays(
year: number = new Date().getFullYear(),
{ returnNativeDate = false }: { returnNativeDate?: boolean } = {}
): unknown[] {
if (year < FIRST_HOLIDAY_YEAR || year > LAST_HOLIDAY_YEAR) {
throw new Error(
`The year should be between ${FIRST_HOLIDAY_YEAR} and ${LAST_HOLIDAY_YEAR}`
);
}

return holidays
.map((holiday) => getHoliday(holiday, year))
.sort((a, b) => a.celebrationDate.localeCompare(b.celebrationDate));
.map((holiday) => getHoliday(holiday, year, { returnNativeDate }))
.sort((a, b) => {
if (
a.celebrationDate instanceof Date &&
b.celebrationDate instanceof Date
) {
return a.celebrationDate.getTime() - b.celebrationDate.getTime();
}

if (
typeof a.celebrationDate === "string" &&
typeof b.celebrationDate === "string"
) {
return a.celebrationDate.localeCompare(b.celebrationDate);
}

throw new Error("Invariant violation: this state is not possible.");
});
}

export default colombianHolidays;
5 changes: 5 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,9 @@ export interface ColombianHoliday extends BasicHoliday {
celebrationDate: string;
}

export interface ColombianHolidayWithNativeDate extends BasicHoliday {
date: Date;
celebrationDate: Date;
}

export type Holiday = DateHoliday | EasterHoliday;

0 comments on commit dc02e50

Please sign in to comment.