
在构建历史数据库、文化遗产项目,或者任何需要记录不确定、模糊或近似日期的系统时,PHP原生的DateTime类往往显得力不从心。说实话,我曾为此头疼不已。比如,一个博物馆藏品可能只知道是“19世纪90年代”的作品,或者一份文献的日期是“大约1950年”,甚至更复杂的“1900年到1910年之间,但确切日期未知”。
面对这些情况,我们常用的new DateTime('1950')会把它解释为1950年1月1日0时0分0秒,这显然丢失了“大约”或“年代”这种关键信息。如果直接存储为字符串,那么后续的查询、排序和展示将变得异常复杂和容易出错。我尝试过各种笨拙的方法:增加额外的字段来标记日期的不确定性,或者编写复杂的正则表达式来解析日期字符串,但结果往往是代码冗余、逻辑混乱,而且维护起来更是噩梦。
直到我遇到了 Extended Date/Time Format (EDTF) 规范,以及它的PHP实现——professional-wiki/edtf这个Composer库。这简直是雪中送炭!
什么是 EDTF?
EDTF 是一种 ISO 标准,旨在提供一种灵活且机器可读的方式来表达具有不同精度、不确定性和近似性的日期和时间。它允许我们表达诸如:
-
不确定日期:
1950?(可能是1950年,但不确定) -
近似日期:
1950~(大约1950年) -
未指定部分:
198X(1980年代的某个年份) -
日期区间:
1900/1910(1900年至1910年之间) -
日期集合:
{1900, 1910, 1920}(1900、1910或1920年)
这些复杂的日期表达方式,正是原生DateTime无法直接处理的。
professional-wiki/edtf:优雅的解决方案
professional-wiki/edtf库为PHP开发者提供了一个强大的工具集,用于解析、表示和操作符合EDTF规范的日期。它的引入,让我在处理这些复杂日期时,终于有了清晰的思路和高效的工具。
1. 安装:简单快捷的Composer体验
使用Composer安装这个库非常简单,只需一行命令:
composer require professional-wiki/edtf
几秒钟,库文件就准备就绪,你可以立即在项目中使用。
2. 解析 (Parsing):将EDTF字符串转化为对象
现在,我可以轻松地将一个EDTF字符串解析成一个可操作的PHP对象,而不再需要手动拆分和判断。
use EDTF\EdtfFactory;
$parser = EdtfFactory::newParser();
$parsingResult = $parser->parse('1985-04-12T23:20:30');
if ($parsingResult->isValid()) {
$edtfValue = $parsingResult->getEdtfValue(); // 获取 EDTF 对象
echo "解析成功,输入: " . $parsingResult->getInput() . "\n";
// 现在 $edtfValue 是一个可操作的日期对象
} else {
echo "EDTF字符串无效。\n";
}
// 尝试解析一个复杂的EDTF
$complexResult = $parser->parse('198X?~'); // 1980年代的某个年份,不确定且近似
if ($complexResult->isValid()) {
$complexEdtf = $complexResult->getEdtfValue();
// 我们可以通过对象模型来获取更多信息
// ...
}3. 验证 (Validating):确保EDTF字符串的合法性
在接收用户输入或处理外部数据时,验证EDTF字符串的合法性至关重要。
use EDTF\EdtfFactory;
$validator = EdtfFactory::newValidator();
echo "1985-04-12T23:20:30 是否有效? " . ($validator->isValidEdtf('1985-04-12T23:20:30') ? '是' : '否') . "\n"; // 是
echo "无效日期字符串是否有效? " . ($validator->isValidEdtf('invalid-date') ? '是' : '否') . "\n"; // 否4. 对象模型 (Object model):深入操作日期数据
解析后的EdtfValue对象提供了丰富的API,让我能够以编程的方式访问和操作日期的各个方面,而不仅仅是简单的字符串。
use EDTF\EdtfFactory;
use EDTF\ExtDate;
$parser = EdtfFactory::newParser();
$parsingResult = $parser->parse('1980/1990'); // 一个日期区间
$edtfValue = $parsingResult->getEdtfValue();
// 获取日期的最小和最大Unix时间戳
echo "日期区间最小时间戳: " . $edtfValue->getMin() . "\n";
echo "日期区间最大时间戳: " . $edtfValue->getMax() . "\n";
// 检查一个日期是否覆盖另一个日期
$anotherResult = $parser->parse('1985');
$anotherValue = $anotherResult->getEdtfValue();
echo "1980/1990 是否覆盖 1985? " . ($edtfValue->covers($anotherValue) ? '是' : '否') . "\n"; // 是
// 对于单个日期,我们可以获取年份等信息
$singleDateResult = $parser->parse('1950?');
$singleDateValue = $singleDateResult->getEdtfValue();
if ($singleDateValue instanceof ExtDate) { // 确保是 ExtDate 类型
echo "年份: " . $singleDateValue->getYear() . "\n"; // 1950
echo "是否不确定? " . ($singleDateValue->isUncertain() ? '是' : '否') . "\n"; // 是
}5. 人性化展示 (Humanizing):将EDTF对象转化为可读文本
这是我最喜欢的功能之一!它能将复杂的EDTF对象转换成易于人类理解的自然语言描述,并且支持多语言。这对于前端展示和用户报告来说,简直是福音。
use EDTF\EdtfFactory;
$parser = EdtfFactory::newParser();
$humanizer = EdtfFactory::newHumanizerForLanguage('zh'); // 指定中文
$edtfValue1 = $parser->parse('1950?')->getEdtfValue();
echo "1950? 人性化显示: " . $humanizer->humanize($edtfValue1) . "\n"; // 约1950年
$edtfValue2 = $parser->parse('198X')->getEdtfValue();
echo "198X 人性化显示: " . $humanizer->humanize($edtfValue2) . "\n"; // 1980年代
$edtfValue3 = $parser->parse('1900/1910')->getEdtfValue();
echo "1900/1910 人性化显示: " . $humanizer->humanize($edtfValue3) . "\n"; // 1900年至1910年优势与实际应用效果
- 数据一致性与准确性: 通过EDTF规范,我们能够以标准化的方式存储和处理那些非精确日期,避免了信息丢失和歧义。
- 开发效率大幅提升: 不再需要手动编写复杂的日期解析逻辑,库已经帮我们做好了这一切。
- 强大的可操作性: 解析后的对象模型提供了丰富的API,方便我们进行日期比较、区间判断、属性查询等高级操作。
- 友好的用户体验: 人性化展示功能让复杂的日期信息变得易于理解,大大提升了用户界面的友好度。
-
国际化支持:
humanizer支持多语言,让我们的应用能够更好地服务全球用户。
自从在项目中使用professional-wiki/edtf以来,我感觉在处理历史数据和文化遗产信息时,效率和准确性都得到了质的飞跃。它让原本棘手的日期问题变得简单而优雅,也让我的代码更加健壮和易于维护。
总结
如果你也曾为PHP中复杂、不确定或近似日期的处理而烦恼,那么professional-wiki/edtf这个Composer库绝对值得你尝试。它通过实现EDTF规范,为我们提供了一个全面、强大且易于使用的解决方案,让你能够从繁琐的日期处理细节中解脱出来,专注于业务逻辑的实现。赶紧用Composer把它引入你的项目吧,相信你也会爱上它!










