Skip to content

Commit 85faa89

Browse files
committed
feat: add Colombia holiday provider
- 18 public holidays per Ley 51/1983 (Ley Emiliani) - 10 Emiliani holidays moved to following Monday - 8 fixed-date holidays (including Independence Day and Battle of Boyacá) - 6 new/updated translation files - 18 test files with per-holiday test classes Signed-off-by: Giovanny Rodriguez <giovanny.rodriguez@imagunet.com>
1 parent 398caf6 commit 85faa89

27 files changed

Lines changed: 1906 additions & 0 deletions

src/Yasumi/Provider/Colombia.php

Lines changed: 373 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,373 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
/**
6+
* This file is part of the 'Yasumi' package.
7+
*
8+
* The easy PHP Library for calculating holidays.
9+
*
10+
* Copyright (c) 2015 - 2026 AzuyaLabs
11+
*
12+
* For the full copyright and license information, please view the LICENSE
13+
* file that was distributed with this source code.
14+
*
15+
* @author Sacha Telgenhof <me at sachatelgenhof dot com>
16+
*/
17+
18+
namespace Yasumi\Provider;
19+
20+
use Yasumi\Exception\UnknownLocaleException;
21+
use Yasumi\Holiday;
22+
23+
/**
24+
* Provider for all holidays in Colombia.
25+
*
26+
* Colombia observes 18 public holidays. Ten of them are "Emiliani" holidays:
27+
* when the canonical date falls on a day other than Monday they are moved
28+
* to the following Monday (Ley 51 of 1983, known as "Ley Emiliani").
29+
*
30+
* @see https://en.wikipedia.org/wiki/Public_holidays_in_Colombia
31+
*/
32+
class Colombia extends AbstractProvider
33+
{
34+
use CommonHolidays;
35+
use ChristianHolidays;
36+
37+
/** Year in which Colombia declared independence from Spain. */
38+
public const INDEPENDENCE_YEAR = 1810;
39+
40+
/** Year of the Battle of Boyacá, which secured independence. */
41+
public const BATTLE_OF_BOYACA_YEAR = 1819;
42+
43+
/**
44+
* Code to identify this Holiday Provider. Typically, this is the ISO3166
45+
* code corresponding to the respective country or sub-region.
46+
*/
47+
public const ID = 'CO';
48+
49+
/**
50+
* Initialize holidays for Colombia.
51+
*
52+
* @throws \InvalidArgumentException
53+
* @throws UnknownLocaleException
54+
* @throws \Exception
55+
*/
56+
public function initialize(): void
57+
{
58+
$this->timezone = 'America/Bogota';
59+
60+
// Fixed holidays (always on their canonical date)
61+
$this->addHoliday($this->newYearsDay($this->year, $this->timezone, $this->locale));
62+
$this->addHoliday($this->maundyThursday($this->year, $this->timezone, $this->locale));
63+
$this->addHoliday($this->goodFriday($this->year, $this->timezone, $this->locale));
64+
$this->addHoliday($this->internationalWorkersDay($this->year, $this->timezone, $this->locale));
65+
$this->addHoliday($this->immaculateConception($this->year, $this->timezone, $this->locale));
66+
$this->addHoliday($this->christmasDay($this->year, $this->timezone, $this->locale));
67+
68+
$this->addIndependenceDay();
69+
$this->addBattleOfBoyacaDay();
70+
71+
// Emiliani holidays (moved to the following Monday when not on a Monday)
72+
$this->addEpiphany();
73+
$this->addStJosephsDay();
74+
$this->addAscensionDay();
75+
$this->addCorpusChristi();
76+
$this->addSacredHeartDay();
77+
$this->addSaintsPeterAndPaulDay();
78+
$this->addAssumptionOfMary();
79+
$this->addColumbusDay();
80+
$this->addAllSaintsDay();
81+
$this->addIndependenceOfCartagenaDay();
82+
}
83+
84+
/**
85+
* The source of the holidays.
86+
*
87+
* @return string[] The source URLs
88+
*/
89+
public function getSources(): array
90+
{
91+
return [
92+
'https://en.wikipedia.org/wiki/Public_holidays_in_Colombia',
93+
'https://www.funcionpublica.gov.co/eva/gestornormativo/norma.php?i=4954',
94+
'https://www.secretariasenado.gov.co/senado/basedoc/ley_0051_1983.html',
95+
'https://www.dian.gov.co/dian/entidad/Paginas/Calendario-Tributario.aspx',
96+
];
97+
}
98+
99+
/**
100+
* Moves a date to the following Monday if it does not already fall on a Monday.
101+
*
102+
* This rule is established by Ley 51 of 1983 (Ley Emiliani) and applies to
103+
* ten specific public holidays in Colombia.
104+
*
105+
* @see https://www.secretariasenado.gov.co/senado/basedoc/ley_0051_1983.html
106+
*/
107+
protected function nextMonday(\DateTime $date): \DateTime
108+
{
109+
$result = clone $date;
110+
$dayOfWeek = (int) $result->format('w'); // 0 = Sunday, 1 = Monday
111+
112+
if (1 !== $dayOfWeek) {
113+
$daysUntilMonday = (8 - $dayOfWeek) % 7;
114+
if (0 === $daysUntilMonday) {
115+
$daysUntilMonday = 7;
116+
}
117+
$result->add(new \DateInterval("P{$daysUntilMonday}D"));
118+
}
119+
120+
return $result;
121+
}
122+
123+
/**
124+
* Epiphany (Emiliani).
125+
*
126+
* Known in Colombia as "Día de los Reyes Magos". Observed on the first
127+
* Monday on or after 6 January.
128+
*
129+
* @see https://en.wikipedia.org/wiki/Epiphany_(holiday)
130+
*
131+
* @throws \Exception
132+
*/
133+
protected function addEpiphany(): void
134+
{
135+
$date = $this->nextMonday(new \DateTime("{$this->year}-01-06", DateTimeZoneFactory::getDateTimeZone($this->timezone)));
136+
137+
$this->addHoliday(new Holiday(
138+
'epiphany',
139+
['es' => 'Día de los Reyes Magos', 'en' => 'Epiphany'],
140+
$date,
141+
$this->locale
142+
));
143+
}
144+
145+
/**
146+
* St. Joseph's Day (Emiliani).
147+
*
148+
* "Día de San José". Observed on the first Monday on or after 19 March.
149+
*
150+
* @throws \Exception
151+
*/
152+
protected function addStJosephsDay(): void
153+
{
154+
$date = $this->nextMonday(new \DateTime("{$this->year}-03-19", DateTimeZoneFactory::getDateTimeZone($this->timezone)));
155+
156+
$this->addHoliday(new Holiday(
157+
'stJosephsDay',
158+
['es' => 'Día de San José'],
159+
$date,
160+
$this->locale
161+
));
162+
}
163+
164+
/**
165+
* Ascension Day (Emiliani).
166+
*
167+
* "Ascensión del Señor". Observed on the Monday following the canonical
168+
* Thursday (Easter + 39 days).
169+
*
170+
* @throws \Exception
171+
*/
172+
protected function addAscensionDay(): void
173+
{
174+
$easter = $this->calculateEaster($this->year, $this->timezone);
175+
$canonical = (clone $easter)->add(new \DateInterval('P39D'));
176+
$date = $this->nextMonday(\DateTime::createFromInterface($canonical));
177+
178+
$this->addHoliday(new Holiday(
179+
'ascensionDay',
180+
['es' => 'Ascensión del Señor', 'en' => 'Ascension Day'],
181+
$date,
182+
$this->locale
183+
));
184+
}
185+
186+
/**
187+
* Corpus Christi (Emiliani).
188+
*
189+
* "Corpus Christi". Observed on the Monday following the canonical
190+
* Thursday (Easter + 60 days).
191+
*
192+
* @throws \Exception
193+
*/
194+
protected function addCorpusChristi(): void
195+
{
196+
$easter = $this->calculateEaster($this->year, $this->timezone);
197+
$canonical = (clone $easter)->add(new \DateInterval('P60D'));
198+
$date = $this->nextMonday(\DateTime::createFromInterface($canonical));
199+
200+
$this->addHoliday(new Holiday(
201+
'corpusChristi',
202+
['es' => 'Corpus Christi', 'en' => 'Corpus Christi'],
203+
$date,
204+
$this->locale
205+
));
206+
}
207+
208+
/**
209+
* Sacred Heart of Jesus (Emiliani).
210+
*
211+
* "Sagrado Corazón de Jesús". Observed on the Monday following the canonical
212+
* Friday (Easter + 68 days).
213+
*
214+
* @throws \Exception
215+
*/
216+
protected function addSacredHeartDay(): void
217+
{
218+
$easter = $this->calculateEaster($this->year, $this->timezone);
219+
$canonical = (clone $easter)->add(new \DateInterval('P68D'));
220+
$date = $this->nextMonday(\DateTime::createFromInterface($canonical));
221+
222+
$this->addHoliday(new Holiday(
223+
'sacredHeartDay',
224+
['es' => 'Sagrado Corazón de Jesús', 'en' => 'Sacred Heart of Jesus'],
225+
$date,
226+
$this->locale
227+
));
228+
}
229+
230+
/**
231+
* Saints Peter and Paul Day (Emiliani).
232+
*
233+
* "San Pedro y San Pablo". Observed on the first Monday on or after 29 June.
234+
*
235+
* @throws \Exception
236+
*/
237+
protected function addSaintsPeterAndPaulDay(): void
238+
{
239+
$date = $this->nextMonday(new \DateTime("{$this->year}-06-29", DateTimeZoneFactory::getDateTimeZone($this->timezone)));
240+
241+
$this->addHoliday(new Holiday(
242+
'saintsPeterAndPaulDay',
243+
['es' => 'San Pedro y San Pablo', 'en' => 'Saints Peter and Paul Day'],
244+
$date,
245+
$this->locale
246+
));
247+
}
248+
249+
/**
250+
* Independence Day.
251+
*
252+
* "Día de la Independencia". Commemorates the Declaration of Independence
253+
* on 20 July 1810. Fixed date, not subject to the Emiliani rule.
254+
*
255+
* @see https://en.wikipedia.org/wiki/Colombian_Declaration_of_Independence
256+
*
257+
* @throws \Exception
258+
*/
259+
protected function addIndependenceDay(): void
260+
{
261+
if ($this->year >= self::INDEPENDENCE_YEAR) {
262+
$this->addHoliday(new Holiday(
263+
'independenceDay',
264+
['es' => 'Día de la Independencia', 'en' => 'Independence Day'],
265+
new \DateTime("{$this->year}-07-20", DateTimeZoneFactory::getDateTimeZone($this->timezone)),
266+
$this->locale
267+
));
268+
}
269+
}
270+
271+
/**
272+
* Battle of Boyacá Day.
273+
*
274+
* "Batalla de Boyacá". Commemorates the decisive battle of 7 August 1819
275+
* that secured Colombian independence. Fixed date, not subject to the Emiliani rule.
276+
*
277+
* @see https://en.wikipedia.org/wiki/Battle_of_Boyac%C3%A1
278+
*
279+
* @throws \Exception
280+
*/
281+
protected function addBattleOfBoyacaDay(): void
282+
{
283+
if ($this->year >= self::BATTLE_OF_BOYACA_YEAR) {
284+
$this->addHoliday(new Holiday(
285+
'battleOfBoyacaDay',
286+
['es' => 'Batalla de Boyacá', 'en' => 'Battle of Boyacá Day'],
287+
new \DateTime("{$this->year}-08-07", DateTimeZoneFactory::getDateTimeZone($this->timezone)),
288+
$this->locale
289+
));
290+
}
291+
}
292+
293+
/**
294+
* Assumption of Mary (Emiliani).
295+
*
296+
* "Asunción de la Virgen". Observed on the first Monday on or after 15 August.
297+
*
298+
* @throws \Exception
299+
*/
300+
protected function addAssumptionOfMary(): void
301+
{
302+
$date = $this->nextMonday(new \DateTime("{$this->year}-08-15", DateTimeZoneFactory::getDateTimeZone($this->timezone)));
303+
304+
$this->addHoliday(new Holiday(
305+
'assumptionOfMary',
306+
['es' => 'Asunción de la Virgen', 'en' => 'Assumption of Mary'],
307+
$date,
308+
$this->locale
309+
));
310+
}
311+
312+
/**
313+
* Columbus Day / Day of Race (Emiliani).
314+
*
315+
* "Día de la Raza". Observed on the first Monday on or after 12 October.
316+
*
317+
* @see https://en.wikipedia.org/wiki/Columbus_Day
318+
*
319+
* @throws \Exception
320+
*/
321+
protected function addColumbusDay(): void
322+
{
323+
$date = $this->nextMonday(new \DateTime("{$this->year}-10-12", DateTimeZoneFactory::getDateTimeZone($this->timezone)));
324+
325+
$this->addHoliday(new Holiday(
326+
'columbusDay',
327+
['es' => 'Día de la Raza', 'en' => 'Columbus Day'],
328+
$date,
329+
$this->locale
330+
));
331+
}
332+
333+
/**
334+
* All Saints' Day (Emiliani).
335+
*
336+
* "Día de Todos los Santos". Observed on the first Monday on or after 1 November.
337+
*
338+
* @throws \Exception
339+
*/
340+
protected function addAllSaintsDay(): void
341+
{
342+
$date = $this->nextMonday(new \DateTime("{$this->year}-11-01", DateTimeZoneFactory::getDateTimeZone($this->timezone)));
343+
344+
$this->addHoliday(new Holiday(
345+
'allSaintsDay',
346+
['es' => 'Día de Todos los Santos'],
347+
$date,
348+
$this->locale
349+
));
350+
}
351+
352+
/**
353+
* Independence of Cartagena (Emiliani).
354+
*
355+
* "Independencia de Cartagena". Commemorates the independence of Cartagena
356+
* on 11 November 1811. Observed on the first Monday on or after 11 November.
357+
*
358+
* @see https://en.wikipedia.org/wiki/Cartagena,_Colombia#Independence
359+
*
360+
* @throws \Exception
361+
*/
362+
protected function addIndependenceOfCartagenaDay(): void
363+
{
364+
$date = $this->nextMonday(new \DateTime("{$this->year}-11-11", DateTimeZoneFactory::getDateTimeZone($this->timezone)));
365+
366+
$this->addHoliday(new Holiday(
367+
'independenceOfCartagenaDay',
368+
['es' => 'Independencia de Cartagena', 'en' => 'Independence of Cartagena'],
369+
$date,
370+
$this->locale
371+
));
372+
}
373+
}

src/Yasumi/data/translations/ascensionDay.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
'de_CH' => 'Auffahrt',
2323
'el' => 'Ανάληψη του Χριστού',
2424
'en' => 'Ascension Day',
25+
'es' => 'Ascensión del Señor',
2526
'fi' => 'Helatorstai',
2627
'fr' => 'Ascension',
2728
'it' => 'Ascensione',

0 commit comments

Comments
 (0)