stubbles/date

简化日期和日期范围的处理。

v9.0.0 2023-12-31 12:49 UTC

README

以不可变的方式处理日期和日期范围,使用美观的API。

构建状态

Tests

Latest Stable Version Latest Unstable Version

安装

stubbles/dateComposer 包的形式分发。要将它作为你的包的依赖项安装,请使用以下命令

composer require "stubbles/date": "^9.0"

需求

stubbles/date 至少需要 PHP 8.2。

简介

这个库中的类起源于 PHP 的面向对象日期/时间处理类不可变的时代。在此期间,PHP 中已经添加了它们的不可变版本,但我们更喜欢这里类的 API,因为它们在使用它们的代码中导致了更好的语法。

底层实现使用 PHP 的面向对象日期/时间处理类,但将它们抽象化,使得 stubbles/date 实例是不可变的。对日期的任何更改都会导致一个新实例。尽管日期类公开了底层句柄,但用户将只收到它的克隆,因此修改这样的句柄不会改变从该句柄获取的日期实例。

stubbles\date\Date

请注意,这个类不仅覆盖了一个日期,还包括了一个具体的时间点。每个实例还有一个时间部分。如果您只对具体的一天感兴趣,请使用 stubbles\date\span\Day(见下文)。

创建一个新实例

在创建新的日期实例时,有不同方式设置实际日期

  • 整数,解释为时间戳:new Date(1187872547)
  • 字符串,解析为日期:new Date('2007-01-01 01:00:00 Europe/Berlin')
  • \DateTime 对象,将直接使用:new Date(new \DateTime())
  • 没有参数,创建代表当前时间的日期:new Date(),等同于 Date::now()

构造函数的第二个参数是可选的时区。时区分配遵循以下规则

  • 如果时间以字符串形式给出且包含可解析的时区标识符,则使用该标识符。
  • 如果无法确定时区,则使用第二个构造函数参数给出的时区。
  • 如果未给出第二个参数,则使用系统的默认时区。

自由类型提示

如果您有一个接受 stubbles\date\Date 实例的方法,您可以选择在您接受的内容上更加宽容。您的函数可以不针对具体类型进行类型提示,而是将提供的值转换为实例

/**
 * does something cool
 *
 * @param  int|string|stubbles\date\Date  $date
 */
function doSomething($date)
{
    $date = Date::castFrom($date);
    // now do something with $date, which is an instance of stubbles\date\Date
}

Date::castFrom(); 接受四种不同的值类型

  • 整数,解释为 Unix 时间戳:Date:castFrom(1187872547)
  • 字符串,解析为日期:Date:castFrom('2007-01-01 01:00:00 Europe/Berlin')
  • \DateTime 对象:Date:castFrom(new \DateTime())
  • stubbles\date\Date 的实例

stubbles\date\Date 的实例将始终以原样返回,其他允许的值将导致创建一个 stubbles\date\Date 实例。传递任何其他值将导致抛出 \InvalidArgumentException

更改日期

要更改日期,可以调用 Date::change() 方法。这将返回一个 stubbles\date\DateModifier 实例,它提供了多种不同的方法来更改日期和/或时间。所有更改都将导致创建一个新的 stubbles\date\Date 实例,原始调用 Date::change() 的实例保持不变

$currentDate = Date::now();
// create new date with current time but 48 hours ago, this will not change $currentDate
$newDate = $currentDate->change()->byHours(-48);

以下是 stubbles\date\DateModifier 提供的列表方法

  • to($target): 使用strtotime()函数接受相对格式改变日期,详见strtotime()
  • timeTo($time): 保持日期(即日、月、年),但将时间改为给定值,格式必须为HH:mm:ss
  • createDateWithNewTime($hour, $minute, $second): 与上述功能相同,但具有所有值的单独参数
  • timeToStartOfDay(): timeTo('00:00:00')的别名
  • timeToEndOfDay(): timeTo('23:59:59')的别名
  • hourTo($hour): 保持日、月、年、分钟和秒,但将小时改为给定值
  • byHours($hours): 将给定的小时数添加到当前日期和时间。负值将减去小时数
  • minuteTo($minute): 保持日、月、年、小时和秒,但将分钟改为给定值
  • byMinutes($minutes): 将给定分钟的分钟数添加到当前日期和时间。负值将减去分钟数。
  • secondTo($second): 保持日、月、年、小时和分钟,但将秒改为给定值
  • bySeconds($seconds): 将给定秒数的秒数添加到当前日期和时间。负值将减去秒数。
  • dateTo($date): 改变日期但保持时间,给定日期必须是格式YYYY-MM-DD
  • yearTo($year): 保持日、月、小时、分钟和秒,但将年改为给定值
  • byYears($years): 将给定年数添加到当前日期和时间。负值将减去年数。
  • monthTo($month): 保持日、年、小时、分钟和秒,但将月改为给定值
  • byMonths($months): 将给定月数添加到当前日期和时间。负值将减去月数。
  • dayTo($day): 保持月、年、小时、分钟和秒,但将日改为给定值
  • byDays($days): 将给定日数添加到当前日期和时间。负值将减去日数。

日期比较

stubbles\date\Date的实例可以相互比较

$date = Date::now();
if ($date->isBefore('2017-01-01 00:00:00')) {
    // execute when current date is before 2017
}
$date = Date::now();
if ($date->isAfter('2017-01-01 00:00:00')) {
    // execute when current date is after beginning of 2017
}

比较基于Unix时间戳。

isBefore()isAfter()接受Date::castFrom()接受的所有值,见上文。

日期格式化

可以通过格式化将日期显示为字符串

echo 'Current date and time in system timezone: ' . Date::now()->format('Y-m-d H:i:s') . PHP_EOL;
echo 'Current date and time in timezone Europe/Berlin: ' . Date::now()->format('Y-m-d H:i:s', new TimeZone('Europe/Berlin')) . PHP_EOL;

当实例被转换为字符串时,输出格式将为Y-m-d H:i:sO

日期范围

有时需要不覆盖特定日期,而是覆盖两个时间点之间的范围。最值得注意的是这些是诸如单日、月份、周甚至年份的事物。由于始终携带此类范围的起始点和结束点是不切实际的,stubbles/date提供了stubbles\date\span\Datespan接口和不同的实现。

每个日期范围的方法

  • start(): 返回范围的精确起始点
  • startsBefore($date): 检查日期范围是否在给定点之前开始
  • startsAfter($date): 检查日期范围是否在给定点之后开始
  • end(): 返回范围的精确结束点
  • endsBefore($date): 检查日期范围是否在给定点之前结束
  • endsAfter($date): 检查日期范围是否在给定点之后结束
  • formatStart($format, TimeZone $timeZone = null): 以给定格式格式化起始点
  • formatEnd($format, TimeZone $timeZone = null): 以给定格式格式化结束点
  • amountOfDays(): 返回日期范围覆盖的天数
  • days(): 返回一个迭代器,允许在日期范围内迭代每一天
  • isInFuture(): 根据当前日期和时间检查日期范围是否完全在未来
  • containsDate($date): 检查给定的日期和时间是否包含在日期范围内

所有具有$date参数的方法都接受Date::castFrom()接受的所有值,见上文。

提供日期范围实现的列表

stubbles\dates\span\Day

覆盖整个一天,从00:00:00开始,到23:59:59结束。

// create without argument always points to current day
$today = new Day();

// create with given date
$another = new Day('2016-06-27');

// create day from given stubbles\date\Date instance
$oneMore = new Day(new Date('2013-05-28'));

// creates a new instance representing tomorrow
$tomorrow = Day::tomorrow();

// creates a new instance representing yesterday
$yesterday = Day::yesterday();

额外方法

  • next():创建一个代表表示日期后一天的新的实例
  • before():创建一个代表表示日期前一天的新实例
  • isToday():检查日期是否代表当前日期

stubbles\dates\span\Week

覆盖整个一周,从给定日期的00:00:00开始,到七天后23:59:59结束。

// create a week starting today
$week1 = new Week(Date::now());

// create a week which starts tomorrow
$week2 = new Week('tomorrow');

// create a week which represents the 5th calender week of 2016
$week3 = Week::fromString('2016-W05')

额外方法

  • number():返回周数

stubbles\dates\span\Month

覆盖一个月,从月初00:00:00开始,到月底23:59:59结束。

// creates instance representing the current month
$currentMonth = new Month();

// creates instance with current month but in the year 2014-05
$currentMonthIn2015 = new Month(2015);

// create instance representing June 2016
$exactMonth = new Month(2016, 6);

// create instance representing month given as string, format must be YYYY-MM
$otherMonth = Month::fromString('2016-07');

// creates instance representing the month before current month
$lastMonth = Month::last();

// creates instance for current month execpt when today is the first day of a
// month, the the instance represents the month before
// ideally suited when creating reports, as most often the report created on the
// first month of a day should be for the last month instead of for the current
// month
$reportingMonth = Month::currentOrLastWhenFirstDay()

额外方法

  • next():创建一个表示当前表示月份后一个月的实例
  • before():创建一个表示当前表示月份前一个月的实例
  • year():返回月份所在的年份
  • isCurrentMonth():检查月份实例是否代表当前月份

stubbles\dates\span\Year

覆盖整个一年,从一月一日 00:00:00开始,到十二月三十一日 23:59:59结束。

// create instance representing the current year
$currentYear = new Year();

// creates instance representing the year 2015
$year2015 = new Year(2015);

额外方法

  • months():返回一个迭代器,包含该年的所有stubbles\dates\span\Month实例
  • isLeapYear():检查年份是否是闰年
  • isCurrentYear():检查年份是否代表当前年份

stubbles\dates\span\CustomDatespan

覆盖自定义日期范围,从给定日期的00:00:00开始,到给定日期的23:59:59结束。

// create a span from 2006-04-04 00:00:00 to 2006-04-20 23:59:59
$custom = new CustomDatespan('2006-04-04', '2006-04-20');

构造函数参数接受所有Date::castFrom()接受的值,见上文。请注意,开始时间总是设置为00:00:00,结束时间总是设置为23:59:59。无法更改为其他时间。

bovigo/assert集成

如果您想对代码进行单元测试并需要测试日期相等性,可以使用bovigo/assert进行断言。stubbles/date提供了一个谓词stubbles\date\assert\equalsDate(),可以用来检查日期的相等性。它可以接受stubbles\date\Date接受的任何参数,并比较Unix时间戳与实际值。如果它们不指向同一时间点,错误信息将包含一个diff,以人类可读的形式显示两个日期。