# 如何使用 DateTime 類(處理轉換、格式、差異和時區) (# How to use the DateTime class (Dealing with Conversions, Formatting, Diffs, and Time zones))


問題描述

如何使用 DateTime 類(處理轉換、格式、差異和時區) (# How to use the DateTime class (Dealing with Conversions, Formatting, Diffs, and Time zones))

每個人都必須在編程的某個階段處理日期/時間格式、時區、奇怪的時間格式等。PHP 處理這些問題的方法很少,其中最值得注意的是 PHP 內置的 DateTime 類。這是因為 DateTime 類作為所有這些問題的一體化解決方案。

但是,DateTime 類有一個缺陷: 如果你還不熟悉它,使用它可能會讓人感到困惑。

這篇文章是為了幫助那些想了解更多關於DateTime 類和/或發現自己在問以下問題之一:

  1. 如何將字符串轉換為可修改的日期/時間?
  2. 如何格式化日期/時間字符串?
  3. 如何獲得 2 次之間的差異?
  4. 我如何計算時區?

請注意:

這篇文章不會涉及date()time()strtotime() 或它們的任何相關函數。這篇文章純粹是為了解釋 DateTime 及其相關類在 PHP 中的正確用法。雖然其中許多答案也可以通過 date() 及其相關函數來實現,但 DateTime 將所有這些功能包裝到幾個乾淨的類中;


參考解法

方法 1:

Most of the below information can be gotten from various parts of PHP's documentation of the DateTime class. However, this answer is formatted in a way that should answer most people's questions regarding the DateTime class, and should apply to most of its use cases.

If you are trying to do something more advanced with Dates/Times such as creating a DateTime wrapper, using immutable DateTime instances, or something else that's specific to your applications needs I highly suggest you checkout the full Date and Time documentation.


1. How do I convert a string to a modifiable date/time?

One of the hardest things to do in programming is to try and make end‑user input usable. However, when it comes to dates and times, the DateTime class makes this practically child's play.

How

DateTime's constructor uses a powerful parser that accepts most widely known formats including relative formats.

$datetime = new DateTime($datetime_string);

From there you can use any of the following methods to modify the time:

To see the full list of formats that DateTime::__construct() supports check out: Supported Date and Time Formats.

Example ‑ Interpreting End‑User Input

Lets say you have form that allows users to say what day they want to make an appointment, but this input is not a date picker with a forced format, but is instead a plain text input.

A typical end‑user will put something like these in that input and a typical programmer will respond in the following ways when asked to support it:

  • 12/31/2000 ‑ "OK"
  • 2000‑12‑31 ‑ "Sure"
  • Today ‑ "Um, I guess we could support that?"
  • Tomorrow ‑ "I guess we should support that too."
  • wednesday next week ‑ "No."

After a while you either force a specific format (which you should always do anyway) or weep at your poor form design. However, DateTime allows all of these as valid inputs and interprets them flawlessly.

// 2000‑12‑31 00:00:00.000000
new DateTime('12/31/2000');

// 2000‑12‑31 00:00:00.000000
new DateTime('2000‑12‑31');

// 2000‑12‑31 00:00:00.000000
new DateTime('Today');

// 2001‑01‑01 00:00:00.000000
new DateTime('Tomorrow');

// 2001‑01‑03 00:00:00.000000
new DateTime('wednesday next week');

However, like most things the DateTime class is not perfect, and doesn't support every format. Which is why you should always use try ... catch blocks with DateTime and confirm with your end‑user that the date you interpreted is what the end‑user desired. One great example is European date formats:

try {
    new DateTime('31/12/2000');
} catch (Exception $e) {
    echo $e‑>getMessage();
}

Output:

DateTime::__construct(): Failed to parse time string (31/12/2000) at position 0 (3): Unexpected character

Example ‑ Modifying A Date/Time

You can adjust any date/time easily with the $datetime‑>modify() method:

$datetime = new DateTime('2001‑01‑01');

// 2001‑01‑04 00:00:00.000000
$datetime‑>modify('+3 days');

// 2001‑02‑04 00:00:00.000000
$datetime‑>modify('+1 month');

// 2001‑02‑03 23:59:00.000000
$datetime‑>modify('‑60 seconds');

// 2001‑02‑02 00:00:00.000000
$datetime‑>modify('yesterday');

// 2001‑02‑02 18:00:00.000000
$datetime‑>modify('6pm');

The $datetime‑>modify() method is the easiest way to modify any instance of DateTime.

However, due to parsing it is somewhat inefficient. If you are modifying 1000's of dates/times and you need better performance, then use add(), sub(), setDate(), setISODate(), setTime(), and setTimestamp() instead of modify().

$datetime = new DateTime('2001‑01‑01');

// 2001‑06‑01 00:00:00.000000
$datetime‑>setDate(2001, 6, 1);

// 2001‑06‑01 06:30:00.000000
$datetime‑>setTime(6, 30, 0);

// No sane person should ever do the below when they could just add 10,000
// seconds, but it's a good way to test how fast your system will handle
// updating DateTime.
$timestamp = $datetime‑>getTimestamp();
foreach (range(1, 10000) as $i) {
    $timestamp++;
    $datetime‑>setTimestamp($timestamp);
}
// 2001‑06‑01 09:16:40.000000

2. How do I format a date/time string?

It's common that you need to take 1 date/time string and format it as another date/time string, or even just take and existing date/time and update it. The DateTime class makes this simple as well.

How

DateTime has the method format() which returns the date/time as a formatted string.

$datetime = new DateTime;
$format   = 'Y‑m‑d H:i:s';
echo $datetime‑>format($format);

We are only going to use a small subset of the formatting options available in these examples, so I highly encourage you to checkout the documentation on formatting dates/times as well as the predefined DateTime constants.

Notice: Be aware that if you attempt to escape a character that could be PHP string escape character you may get unexpected results.

Incorrect Result

// output: Da   e 2000‑12‑31
echo $datetime‑>format("\D\a\t\e\: Y‑m‑d").PHP_EOL;

Correct Result

// output: Date 2000‑12‑31
echo $datetime‑>format("\D\a\\t\e\: Y‑m‑d").PHP_EOL;

// output: Date 2000‑12‑31
echo $datetime‑>format('\D\a\t\e\: Y‑m‑d').PHP_EOL;

Examples

These are some common formats that you may need:

SQL Dates/Times

// output: 2000‑12‑31
echo $datetime‑>format('Y‑m‑d').PHP_EOL;

// output: 23:59:59
echo $datetime‑>format('H:i:s').PHP_EOL;

// output: 2000‑12‑31 23:59:59
echo $datetime‑>format('Y‑m‑d H:i:s').PHP_EOL;

End‑User Readable Dates/Times

// output: 12/31/2000
echo $datetime‑>format('n/j/Y').PHP_EOL;

// output: 11:59pm
echo $datetime‑>format('g:ia').PHP_EOL;

// output: 12/31/2000 at 11:59pm
echo $datetime‑>format('n/j/Y \a\t g:ia').PHP_EOL;

// output: Sunday the 31st of December 2000 at 11:59:59 PM
echo $datetime‑>format('l \t\h\e jS \o\f F Y \a\t g:i:s A').PHP_EOL;

Dates/Times With Time zones

date_default_timezone_set('America/New_York');
$datetime = new DateTime('2000‑12‑31 23:59:59');

// output: 2000‑12‑31 23:59:59 America/New_York
echo $datetime‑>format('Y‑m‑d H:i:s e').PHP_EOL;

// output: 2000‑12‑31 23:59:59 EST
echo $datetime‑>format('Y‑m‑d H:i:s T').PHP_EOL;

// output: 2000‑12‑31 23:59:59 ‑0500
echo $datetime‑>format('Y‑m‑d H:i:s O').PHP_EOL;

3. How do I get the difference between 2 times?

It is common to need to know the difference in time between 2 dates/times. With DateTime there are actually 3 different ways to achieve this, and which one you will want to use will depend on your needs.

How (with Examples)

Scenario 1: You only need to know if $datetime1 is greater than, less than, or equal to $datetime2

In this case, you can simply directly compare the instances of DateTime.

$datetime1 = new DateTime;
sleep(1);
$datetime2 = new DateTime;

var_dump($datetime1 > $datetime2); // FALSE
var_dump($datetime1 < $datetime2); // TRUE
var_dump($datetime1 == $datetime2); // FALSE

Scenario 2: You need the difference between $datetime1 and $datetime2 expressed as broken‑down years/months/days/etc.

This will work for most cases, however the DateInterval instance you get back from $datetime‑>diff() has its own "gotchas" and may not work for your specific use case.

$datetime1 = new DateTime('2000‑01‑01 00:00:00.000000');
$datetime2 = new DateTime('2001‑02‑03 04:05:06.789012');
$diff      = $datetime1‑>diff($datetime2);

// output: 1 Years, 1 Months, 2 Days, 4 Hours, 5 Minutes, 6 Seconds
echo $diff‑>format('%y Years, %m Months, %d Days, %h Hours, %i Minutes, %s Seconds');

Scenario 3: You need the difference between $datetime1 and $datetime2 expressed in another way.

This will work in any context at the cost of a little extra code.

$interval   = 60 * 60 * 24; // 1 day in seconds
$datetime1  = new DateTime('2000‑01‑01');
$datetime2  = new DateTime;
$diff       = $datetime2‑>getTimestamp() ‑ $datetime1‑>getTimestamp();

// output: It has been 6956 days since 2000‑01‑01!
printf('It has been %s days since %s!', floor($diff / $interval), $datetime1‑>format('Y‑m‑d'));

4. How do I account for time zones?

When it comes to dealing with time in programming, by far the worst part is dealing with time zones. Fortunately, this is something else that the DateTime class handles gracefully.

How

DateTime's constructor allows you to specify the source time zone in either the date/time string or as the 2nd Argument. After that, just set a new timezone with $datetime‑>setTimezone() and DateTime will take care of the rest.

// These 2 lines are functionally identical
$datetime = new DateTime('2001‑01‑01 00:00:00', new DateTimeZone('UTC')); // recommended, may be faster
$datetime = new DateTime('2001‑01‑01 00:00:00 UTC');

$datetime‑>setTimezone(new DateTimeZone('EST'));

I recommend checking out the full list of PHP's supported time zones as well as the docs on the DateTimeZone class.

Example

Lets say you want to show your end‑users the time your customer support line opens in their time zone. With DateTime the code would look something like this:

$support_opens      = new DateTime('08:00:00', new DateTimeZone('America/New_York'));
$customer_timezones = array('America/New_York', 'America/Chicago', 'America/Denver', 'America/Phoenix', 'America/Los_Angeles', 'America/Anchorage', 'America/Adak', 'Pacific/Honolulu');

echo "Today we open at the following times:".PHP_EOL;
foreach ($customer_timezones as $timezone) {
    $support_opens‑>setTimezone(new DateTimeZone($timezone));
    echo '* '.$support_opens‑>format('g:ia \f\o\r \t\h\e e').' time zone'.PHP_EOL;
}

Output:

Today we open at the following times:
* 8:00am for the America/New_York time zone
* 7:00am for the America/Chicago time zone
* 6:00am for the America/Denver time zone
* 6:00am for the America/Phoenix time zone
* 5:00am for the America/Los_Angeles time zone
* 4:00am for the America/Anchorage time zone
* 3:00am for the America/Adak time zone
* 3:00am for the Pacific/Honolulu time zone

Notice: If you supply a time zone in both the date/time string and as the second argument, the argument time zone will be ignored.

$datetime = new DateTime('2001‑01‑01 00:00:00 EST', new DateTimeZone('UTC'));

// output: 2001‑01‑01 00:00:00 EST
echo $datetime1‑>format('Y‑m‑d H:i:s');

(by Nicholas SummersNicholas Summers)

參考文件

  1. # How to use the DateTime class (Dealing with Conversions, Formatting, Diffs, and Time zones) (CC BY‑SA 2.5/3.0/4.0)

#datetime #datetime-format #PHP #timezone #timestamp






相關問題

NHibernate:HQL:從日期字段中刪除時間部分 (NHibernate:HQL: Remove time part from date field)

如何獲得在給定時間內發送超過 X 個數據包的 IP (How do I get IPs that sent more than X packets in less than a given time)

Памылка дадання даты пры адніманні ад 0:00 (Dateadd error when subtracting from 0:00)

查找與日曆相比缺失的日期 (Find missing date as compare to calendar)

CodeReview:java Dates diff(以天為單位) (CodeReview: java Dates diff (in day resolution))

顯示兩個給定時間之間的 15 分鐘步長 (display 15-minute steps between two given times)

如何在 C# 中獲取月份名稱? (How to get the month name in C#?)

fromtimestamp() 的反義詞是什麼? (What is the opposite of fromtimestamp()?)

構建 JavaScript 時缺少模塊 (Missing Module When Building JavaScript)

setTimeout 一天中的特定時間,然後停止直到下一個特定時間 (setTimeout for specific hours of day and then stop until next specific time)

將浮點數轉換為 datatime64[ns] (Converting float into datatime64[ns])

Python Dataframe 在連接時防止重複 (Python Dataframe prevent duplicates while concating)







留言討論