umulmrum / holiday
一个用于计算假期的PHP库。这很有趣 :-)
Requires
- php: >=8.2
Requires (Dev)
- ext-json: *
- friendsofphp/php-cs-fixer: ^3.58
- phpstan/phpstan: ^1.11
- phpunit/phpunit: ~11.1
- symfony/console: ~5.4|~6.4|~7.0
- symfony/translation: ~5.4|~6.4|~7.0
README
Holiday是一个非常灵活地计算假期的库。可以根据各种标准过滤假期列表并以不同的方式格式化它们。很容易添加更多的假期提供者、过滤器和格式化工具,因此即使这个库不完全符合您的需求,您也可以简单地扩展它。
需求
- PHP >= 8.2
这就是全部。
安装
使用Composer安装库。
composer require umulmrum/holiday
使用示例
简单示例
<?php require 'vendor/autoload.php'; use Umulmrum\Holiday\HolidayCalculator; use Umulmrum\Holiday\Provider\Germany\Bavaria; $holidayCalculator = new HolidayCalculator(); $holidays = $holidayCalculator->calculate(Bavaria::class, 2020);
这会得到一个包含2020年所有假期的HolidayList
,这个列表可以用于输出或进一步计算。假期总是计算一整年,然后可以通过过滤器缩小范围。
更复杂示例
<?php require 'vendor/autoload.php'; use Umulmrum\Holiday\HolidayCalculator; use Umulmrum\Holiday\Filter\IncludeTimespanFilter; use Umulmrum\Holiday\Formatter\DateFormatter; $holidayCalculator = new HolidayCalculator(); $holidays = $holidayCalculator->calculate('DE-BY', 2020); // Apply filters, e.g. restrict to one month. $firstDay = new \DateTime('2020-12-01'); $lastDay = new \DateTime('2020-12-31'); $holidays = $holidays->filter(new IncludeTimespanFilter($firstDay, $lastDay)); // Format the results. $formattedHolidays = $holidays->format(new DateFormatter());
这会得到一个日期字符串数组。请注意,我们使用了ISO-3166-2代码DE-BY
来获取巴伐利亚州的假期。有关请求假期的不同方法的详细信息,请参见下面的解析假期提供者
部分。
还有一些辅助方法可以简化一些常见的假期计算。使用GetHolidayForMonth
辅助方法,上面的示例可以替换为以下内容
<?php require 'vendor/autoload.php'; use Umulmrum\Holiday\Formatter\DateFormatter; use Umulmrum\Holiday\Helper\GetHolidaysForMonth; use Umulmrum\Holiday\Provider\Germany\Bavaria; $formattedHolidays = (new GetHolidaysForMonth())(Bavaria::class, 2020, 12)->format(new DateFormatter());
最后一个示例展示了如何计算2020年和2021年巴登-符腾堡州的全部假期、周六和周日,按日期排序并将结果格式化为JSON(全部在两行代码中)
<?php require 'vendor/autoload.php'; use Umulmrum\Holiday\HolidayCalculator; use Umulmrum\Holiday\Filter\SortByDateFilter; use Umulmrum\Holiday\Formatter\JsonFormatter; use Umulmrum\Holiday\Provider\Germany\BadenWuerttemberg; use Umulmrum\Holiday\Provider\Weekday\Saturdays; use Umulmrum\Holiday\Provider\Weekday\Sundays; $calculator = new HolidayCalculator(); $holidays = $calculator->calculate([BadenWuerttemberg::class, Saturdays::class, Sundays::class], [2020, 2021]); $formattedHolidays = $holidays->filter(new SortByDateFilter())->format(new JsonFormatter());
详细使用方法
首先通过使用HolidayCalculator
创建一个或多个年份的HolidayList
。向计算器传递一个或多个假期提供者以获取这些提供者的假期,例如国家、宗教或特定的工作日。基本上,HolidayList
是一个增强的Holiday
对象数组。
use Umulmrum\Holiday\HolidayCalculator; use Umulmrum\Holiday\Provider\Luxembourg\Luxembourg; use Umulmrum\Holiday\Provider\Weekday\Saturdays; use Umulmrum\Holiday\Provider\Weekday\Sundays; $calculator = new HolidayCalculator(); $holidays = $calculator->calculate(Luxembourg::class, 2020); // or multiple providers at once by passing an array of providers $holidays = $calculator->calculate([Luxembourg::class, Saturdays::class, Sundays::class], 2020); // or multiple years at once by passing an array of years (can be combined) $holidays = $calculator->calculate(Luxembourg::class, [2020, 2021]);
在src/Provider
下查看内置提供者的完整列表,或者将它们用作创建自己的示例。请注意,假期不是一个等同于休息日的日子 - 本库中的假期是指由提供者定义的“特殊日子”,可能是传统或宗教节日,可能不影响工作时间。要限制HolidayList
只包含休息日,请使用下面在过滤器
部分中描述的IncludeTypeFilter
。
然后可以询问列表信息或添加/删除假期,列表可以过滤或格式化。
// Get the number of holidays in the list. HolidayList also implements \Countable $holidays->count();
// Get the list of holidays as array. HolidayList also implements \IteratorAggregate $holidays->getList(); $holidays->getIterator(); $holidays->getByName($holidayName)
// Modify the list. $holidays->add($anotherHoliday); $holidays->addAll($anotherHolidayList); $holidays->removeByName($holidayName); $holidays->removeByIndex(3); $holidays->replaceByNameAndDate($anotherHoliday); $holidays->replaceByIndex(0, $anotherHoliday);
// Check if a given date is in the list $holidays->isHoliday(new \DateTime('2020-12-01'));
解析假期提供者
上面的示例已经展示了两种不同的方式来指定应计算哪个地区或其他“实体”的假期。还有更多方法,也可以完全自定义 - 让我们看看所有这些方法
-
完全限定类名
$calculator->calculate(\Umulmrum\Holiday\Provider\France\France::class, 2020);
建议使用默认方式,因为您只需要记住要请求的区域,IDE应提供自动补全以节省按键。
-
实例化类
$calculator->calculate(new \Umulmrum\Holiday\Provider\Weekday\Sundays(\Umulmrum\Holiday\Constant\HolidayType::DAY_OFF), 2020);
使用与使用方式1指定的相同类型的实例化类。如果提供者有构造函数参数,例如在示例中看到的额外假期类型(星期天不是自动休息日),则执行此操作。
-
ISO-3166名称
$calculator->calculate('AT-9', 2020); // AT-9 = Vienna in Austria
使用ISO-3166-1国家代码或ISO-3166-2地区代码,例如如果您的应用程序已经使用它们来简化与假期库的集成和解耦。支持的国家和地区的列表可以在
src/Resolver/isoData.php
中找到。如果找不到地区代码,将回退到基本国家 - 请参阅IsoResolver
类中的注释以获取详细信息。 -
常用缩写
$calculator->calculate('Christian', 2020);
在
MiscResolver
类中定义了额外的简短字符串。这些包括用于基督教假期的Christian
,以及用于星期的Sun
、Mon
、Tue
、Wed
、Thu
、Fri
和Sat
。 -
自定义
$calculator = new \Umulmrum\Holiday\HolidayCalculator(new \Umulmrum\Holiday\Resolver\ResolverHandler([new MyCustomResolver()]));
使用此功能添加自定义解析逻辑。
ResolverHandler
接受一个ProviderResolverInterface
数组(并实现了ResolverHandlerInterface
,因此还可以用更自定义的代码替换)。
过滤器
使用过滤器来缩小假期的列表到所需的子集。
use Umulmrum\Holiday\Constant\HolidayType; use Umulmrum\Holiday\Filter\IncludeTimespanFilter; use Umulmrum\Holiday\Filter\IncludeTypeFilter; // Keep only work-free days (as defined in the provider(s) the list is derived from). $holidays->filter(new IncludeTypeFilter(HolidayType::DAY_OFF)); /* * Filters modify the original list. The following example shows how to chain filters to get holidays with type DAY_OFF * in January 2020. */ $holidays ->filter(new IncludeTimespanFilter(new \DateTime('2020-01-01'), new \DateTime('2020-01-31'))) ->filter(new IncludeTypeFilter(HolidayType::DAY_OFF)) ;
在src/Filter
下查看内置过滤器的完整列表,或作为示例创建自己的过滤器(扩展AbstractFilter
或实现HolidayFilterInterface
)。
格式化器
使用格式化器来格式化HolidayList
和Holiday
。
use Umulmrum\Holiday\Formatter\JsonFormatter; $formattedList = $holidayList->format(new JsonFormatter());
格式化器可以返回一个字符串或一个字符串数组。
在src/Formatter
下查看内置格式化器的完整列表,或作为示例创建自己的格式化器(实现HolidayFormatterInterface
)。
假日类型
HolidayList
包含Holiday
对象。除了(技术)名称和日期外,每个假日还有一个或多个类型。通过调用Holiday::getType()
返回这些类型作为整数位掩码,或使用Holiday::hasType()
请求特定类型。
类型通常是以下组合
- 假日的起源,例如宗教或传统,
- 假日的法律状态(是否官方),
- 假日对工作时间的影响(例如,如果是全天或半天休假,学校或政府机构是否关闭)。
查看HolidayType
类以获取可用的类型。此类还包含这些类型的翻译键,然后可以将其翻译为本地化可读名称 - 例如,查看JsonFormatter
的详细信息。
返回哪些假日
HolidayCalculator
只返回在所选假日提供者范围内法定定义的假日,或者如果存在“世俗影响”,例如,它是一个休息日。这意味着提供者之间存在差异;例如,复活节星期日在勃兰登堡/德国是官方假日,因此包括在内。在德国的其他地区(以及大多数其他国家)它不是假日,因为它本来就是星期日,因此不包括在内。
同样,如果假日提供者范围内没有官方假日,星期日通常不包括在内,尽管在西方半球它们是休息日。要找出哪些天是休息日,请提供相应的假日提供者和星期日的提供者(或使用GetNoWorkDaysForTimeSpan()
)。
如果您的用例需要其他假日列表,请考虑组合多个提供者(例如,Switzerland
和ChristianHolidays
),或使用过滤器删除一些(例如,使用IncludeTypeFilter
和InverseFilter
)从Brandenburg
列表中删除星期日。
翻译
每个假日都将提供一个类似英语名称的技术名称,例如new_year
。要将这些技术名称翻译成口语,请使用Translator
。此类包含所有假日的英语和德语名称,但可以添加其他语言(欢迎贡献,但翻译文件也可以位于此库之外)。
TranslateFilter
可以一次性替换HolidayList
中所有假日的名称。如下所示使用它
use \Umulmrum\Holiday\Filter\TranslateFilter; $holidayList->filter(new TranslateFilter(locale: 'en'));
TranslateFilter
使用内置翻译的Translator
内部,但可以进行自定义 - 请参阅所用类的构造函数。
某些格式化器,如JsonFormatter
,也可以使用可选的翻译器进行初始化。
Translator
使用两个级别的回退来处理传递的locale
- 首先,它会尝试查找特定地区的翻译,例如
en_US
。 - 如果没有找到,它会尝试基本语言,例如
en
。 - 如果这也找不到,它会尝试配置的备用语言,这是可配置的(默认
en
)。 - 如果这也找不到,它将返回空字符串。
翻译文件可以在 res/trans
下找到。它们是用 PHP 编写的,与 Symfony 翻译组件兼容,因此可以直接使用 Symfony 的 PhpFileLoader
加载。
还有一个 SymfonyBridgeTranslator
,它允许使用现有的 Symfony 翻译器(翻译文件需要单独注册)。
辅助工具
为了简化一些常见的与假日相关的任务,请参阅 src/Helper
下的辅助工具(例如,GetNoWorkDaysForTimeSpan
辅助工具接受假日提供者和时间段,并计算出所有这些参数的休假日列表)。
支持的日历
目前仅支持公历,对于年份 < 10000。
支持的国家
(由 simplemaps.com 创建的地图 - MIT 许可)
- 澳大利亚
- 奥地利(包括布尔根兰、卡林西亚、下奥地利、萨尔茨堡、施蒂里亚、蒂罗尔、上奥地利、维也纳、福拉尔贝格)
- 白俄罗斯
- 比利时
- 巴西
- 保加利亚
- 加拿大
- 捷克共和国
- 丹麦
- 爱沙尼亚
- 芬兰
- 法国(包括下莱茵、法属圭亚那、瓜德罗普、上莱茵、马提尼克、摩泽尔、留尼汪)
- 德国(包括巴登-符腾堡、拜仁、柏林、勃兰登堡、不来梅、汉堡、黑森、下萨克森、梅克伦堡-前波莫瑞、北莱茵-威斯特法伦、莱茵兰-普法尔茨、萨尔兰、萨克森、萨axed、石勒苏益格-荷尔斯泰因、图林根)
- 格陵兰
- 冰岛
- 爱尔兰
- 意大利(包括南蒂罗尔)
- 拉脱维亚
- 列支敦士登
- 立陶宛
- 卢森堡
- 墨西哥
- 荷兰
- 挪威
- 波兰
- 葡萄牙(包括亚速尔群岛、马德拉群岛,不包括市政假日)
- 俄罗斯
- 西班牙(目前只有公共假日)
- 瑞典
- 瑞士(包括阿劳、阿彭策尔-外阿彭策尔、阿彭策尔-内阿彭策尔、巴塞尔-兰德、巴塞尔城、伯尔尼、弗里堡、日内瓦、格拉鲁斯、格律森、汝拉、卢塞恩、讷沙泰尔、尼德瓦尔登、奥伯瓦尔登、沙夫豪森、施维茨、索洛图恩、圣加伦、图尔高、提契诺、乌里、瓦莱、沃、苏黎世、楚格)
- 土耳其(目前还没有伊斯兰假日)
- 乌克兰
- 联合王国(包括英格兰、北爱尔兰、苏格兰(不包括地方假日)、威尔士;目前只有大陆)
- 美国(目前只有联邦假日)
要创建自己的假日提供者,请查看现有代码,它应该是自解释的。我非常愿意合并支持更多国家的拉取请求(见下文)。
加拿大假日的说明
- 对于非加拿大人来说,在添加假日时很难把握加拿大的假日(对于非加拿大人)。假日有多级灵活性(省份/地区的差异、政府/私营部门的角色、替代假日的协商性)。因此,当前状态肯定不是完美的,进一步的建议非常受欢迎。
德国假日的说明
- 复活节星期日/Ostersonntag 和圣灵降临节星期日/Pfingstsonntag 在大多数州不是公共假日。
- 在黑森州,每个星期天都是公共假日。
- 在巴伐利亚州,大约有1700个左右的2000个社区将圣母升天节作为公共假日。这被实现为部分假日,因此您可能需要添加自己的逻辑来过滤这个日期。
- 在巴伐利亚州,学校和赎罪与祈祷日在圣母升天节关闭。对此“行为”没有特殊处理。
- 圣体节/Fronleichnam 在萨克森和图林根的一些社区中是公共假日。
- 尚未考虑具有传统上(但不是公开)有限营业时间的地区假日(例如狂欢节日)。
- 一些德国海洋港口将某些假日视为“大斋节”。通常不允许在这些日子里工作,并且前一天的工作时间在中午结束。这尚未考虑。
- 此外,还有一些不是公共假期的安静日子,存在于各个州。这还没有被考虑。
- 德国的新州被视为自始至终都是联邦共和国的一部分。因此,在1990年之前几年的假期是不正确的。
瑞士假期的注意事项
- 瑞士的假期非常复杂,因为在庆祝假期的方式上存在广泛的碎片化。实施的规则是这个库(由非瑞士人编写)范围内的最佳猜测。我欢迎改进。
乌克兰假期的注意事项
- 乌克兰近年来一直在使用修正的儒略历。这个日历不支持,但它与格里历在2800年之前是相同的。
- 复活节在5月1日星期日(例如2016年)的年份在补偿假期方面似乎不清楚。对这些年份持保留态度。
基督教假期的注意事项
- 只考虑了我认为最重要的假期,这很可能远非准确;请自由贡献更改。
基督教东正教假期的注意事项
- 东正教复活节日期的计算在1900年至2099年之间是准确的。
关于过去或未来假期的注意事项
如果计算过去年份的假期,请注意它们可能不准确。所有假期都曾在某个时间引入,因此这个库可能返回那些实际上不存在的年份的假期,也可能省略已经很久没有庆祝的假期。
同样,假期可能会在未来发生变化。此外,由于天文原因,复活节日期计算将在未来发生变化。
这个库旨在至少对“近期”年份准确,大约从第二次世界大战后到现在的年份。再次欢迎贡献。
版本控制
这个库遵循语义版本控制。库中的所有内容都可以被视为公共API,除了
- 带有
@internal
注释的代码, - 测试,
- 只能通过反射访问的代码(例如私有方法)。
请注意,这个库仍然处于0.*版本,这意味着公共API可以随时更改。
贡献
高度欢迎贡献。请按照以下规则提交PR
- 模仿现有代码的风格和结构。
- 为所有代码添加单元测试;通过调用
composer test
在本地运行测试。 - 理想情况下,也运行
composer analyze
并修复报告的任何错误。 - 最后,通过调用
composer cs
修复代码风格(如果您赶时间,可以省略这一步)。
通过提交PR,您同意您贡献的代码可以按照MIT许可证使用。
如何添加新的国家或地区
- 在
src/Provider/...
内部添加该国家或地区的提供者。 - 如果您需要添加特定国家的假期名称,请将其添加到
Constant\HolidayName
,并在umulmrum_holiday.en.php
中提供英文翻译。 - 查找您国家或地区的ISO代码,并将其添加到
Resolver\isoData.php
。如果您添加的提供者不添加国家数据,请将其添加到MiscResolver
类中的列表中。 - 如果国家提供任何形式的替代/补偿假期,以便在假期发生在周末时,请按照以下步骤操作
- 让提供者类实现
CompensatoryHolidayProviderInterface
而不是默认的HolidayProviderInterface
。除此之外,最初添加不覆盖补偿天的假期。 - 实现一个名为
getCompensatoryDaysCalculators
的方法,该方法应返回一个CompensatoryDayCalculator
对象数组。这些对象自动处理完成假期列表的补偿天数。默认情况下,对所有假期计算补偿天数,并假设假期如果落在周六或周日,则移至周一。可以通过传递给CompensatoryDayCalculator
构造函数的参数来调整此行为。例如,请参阅英国或美国的假日提供商以获取示例。
- 让提供者类实现
- 在
tests/Provider/...
内提供测试,理想情况下应涵盖所有边缘情况。可以按以下方式执行- 首先编写假日提供商本身
- 然后运行一个类似于以下命令的命令:
tests/console test:generate DE-BY -y 2018 -y 2025
。将DE-BY
替换为您新提供商的 ISO 代码。将-y 2018 -y 2025
替换为对您的提供商有意义的年份。 - 测试将计算选定年份的假期,并在
tests/Provider/Data
目录中生成 markdown 表格。在该目录中找到文件并检查您是否对结果满意,但不要更改文件的格式,因为测试运行器将期望它完全如此。 - 如果您需要无法用这种方式表达的测试(例如,瑞典),请手动编写这些特定测试。
许可证
假日项目受 MIT 许可证的许可。有关详细信息,请参阅 LICENSE。
简而言之,此许可证允许您几乎将其用于任何您喜欢的目的。如果您用某种方式利用它来压制、伤害、杀害、间谍或任何其他被认为邪恶的行为,本许可证中没有任何条款可以阻止您。但无论如何,都去你的吧。
对于所有人,我希望这个小库能帮助您,也许甚至给您带来一些乐趣。