# 第?10?章?日期與時間
### 目錄
* [10.1 概述](datetime.html#datetime_general)
* [10.2 歷法日期](datetime.html#datetime_calendar)
* [10.3 位置無關的時間](datetime.html#datetime_location_independent_times)
* [10.4 位置相關的時間](datetime.html#datetime_location_dependent_times)
* [10.5 格式化輸入輸出](datetime.html#datetime_formatted_io)
* [10.6 練習](datetime.html#datetime_exercises)
[](http://creativecommons.org/licenses/by-nc-nd/3.0/de/deed.zh) 該書采用 [Creative Commons License](http://creativecommons.org/licenses/by-nc-nd/3.0/de/deed.zh) 授權
## 10.1.?概述
庫 [Boost.DateTime](http://www.boost.org/libs/date_time/) 可用于處理時間數據,如歷法日期和時間。 另外,Boost.DateTime 還提供了擴展來處理時區的問題,且支持歷法日期和時間的格式化輸入與輸出。 本章將覆蓋 Boost.DateTime 的各個部分。
## 10.2.?歷法日期
Boost.DateTime 只支持基于格里歷的歷法日期,這通常不成問題,因為這是最廣泛使用的歷法。 如果你與其它國家的某人有個會議,時間在2010年1月5日,你可以期望無需與對方確認這個日期是否基于格里歷。
格里歷是教皇 Gregory XIII 在1582年頒發的。 嚴格來說,Boost.DateTime 支持由1400年至9999年的歷法日期,這意味著它支持1582年以前的日期。 因此,Boost.DateTime 可用于任一歷法日期,只要該日期在轉換為格里歷后是在1400年之后。 如果需要更早的年份,就必須使用其它庫來代替。
用于處理歷法日期的類和函數位于名字空間 `boost::gregorian` 中,定義于 `boost/date_time/gregorian/gregorian.hpp`。 要創建一個日期,請使用 `boost::gregorian::date` 類。
```
#include <boost/date_time/gregorian/gregorian.hpp>
#include <iostream>
int main()
{
boost::gregorian::date d(2010, 1, 30);
std::cout << d.year() << std::endl;
std::cout << d.month() << std::endl;
std::cout << d.day() << std::endl;
std::cout << d.day_of_week() << std::endl;
std::cout << d.end_of_month() << std::endl;
}
```
* [下載源代碼](src/10.2.1/main.cpp)
`boost::gregorian::date` 提供了多個構造函數來進行日期的創建。 最基本的構造函數接受一個年份、一個月份和一個日期作為參數。 如果給定的是一個無效值,則將分別拋出 `boost::gregorian::bad_year`, `boost::gregorian::bad_month` 或 `boost::gregorian::bad_day_of_month` 類型的異常,這些異常均派生自 `std::out_of_range`。
正如在這個例子中所示的, 有多個方法用于訪問一個日期。 象 `year()`, `month()` 和 `day()` 這些方法訪問用于初始化的初始值,象 `day_of_week()` 和 `end_of_month()` 這些方法則訪問計算得到的值。
而 `boost::gregorian::date` 的構造函數則接受年份、月份和日期的值來設定一個日期,調用 `month()` 方法實際上會顯示 `Jan`,而調用 `day_of_week()` 則顯示 `Sat`。 它們不是普通的數字值,而分別是 `boost::gregorian::date::month_type` 和 `boost::gregorian::date::day_of_week_type` 類型的值。 不過,Boost.DateTime 為格式化的輸入輸出提供了全面的支持,可以將以上輸出從 `Jan` 調整為 `1`。
請留意,`boost::gregorian::date` 的缺省構造函數會創建一個無效的日期。 這樣的無效日期也可以通過將 `boost::date_time::not_a_date_time` 作為單一參數傳遞給構造函數來顯式地創建。
除了直接調用構造函數,也可以通過自由函數或其它對象的方法來創建一個 `boost::gregorian::date` 類型的對象。
```
#include <boost/date_time/gregorian/gregorian.hpp>
#include <iostream>
int main()
{
boost::gregorian::date d = boost::gregorian::day_clock::universal_day();
std::cout << d.year() << std::endl;
std::cout << d.month() << std::endl;
std::cout << d.day() << std::endl;
d = boost::gregorian::date_from_iso_string("20100131");
std::cout << d.year() << std::endl;
std::cout << d.month() << std::endl;
std::cout << d.day() << std::endl;
}
```
* [下載源代碼](src/10.2.2/main.cpp)
這個例子使用了 `boost::gregorian::day_clock` 類,它是一個返回當前日期的時鐘類。 方法 `universal_day()` 返回一個與時區及夏時制無關的 UTC 日期。 UTC 是世界時(universal time)的國際縮寫。 `boost::gregorian::day_clock` 還提供了另一個方法 `local_day()`,它接受本地設置。 要取出本地時區的當前日期,必須使用 `local_day()`。
名字空間 `boost::gregorian` 中包含了許多其它自由函數,將保存在字符串中的日期轉換為 `boost::gregorian::date` 類型的對象。 這個例子實際上是通過 `boost::gregorian::date_from_iso_string()` 函數對一個以 ISO 8601 格式給出的日期進行轉換。 還有其它相類似的函數,如 `boost::gregorian::from_simple_string()` 和 `boost::gregorian::from_us_string()`。
`boost::gregorian::date` 表示的是一個特定的時間點,而 `boost::gregorian::date_duration` 則表示了一段時間。
```
#include <boost/date_time/gregorian/gregorian.hpp>
#include <iostream>
int main()
{
boost::gregorian::date d1(2008, 1, 31);
boost::gregorian::date d2(2008, 8, 31);
boost::gregorian::date_duration dd = d2 - d1;
std::cout << dd.days() << std::endl;
}
```
* [下載源代碼](src/10.2.3/main.cpp)
由于 `boost::gregorian::date` 重載了 `operator-()` 操作符,所以兩個時間點可以如上所示那樣相減。 返回值的類型為 `boost::gregorian::date_duration`,表示了兩個日期之間的時間長度。
`boost::gregorian::date_duration` 所提供的最重要的方法是 `days()`,它返回一段時間內所包含的天數。
我們也可以通過傳遞一個天數作為構造函數的唯一參數,來顯式創建 `boost::gregorian::date_duration` 類型的對象。 要創建涉及星期數、月份數或年數的時間段,可以相應使用 `boost::gregorian::weeks`, `boost::gregorian::months` 和 `boost::gregorian::years`。
```
#include <boost/date_time/gregorian/gregorian.hpp>
#include <iostream>
int main()
{
boost::gregorian::date_duration dd(4);
std::cout << dd.days() << std::endl;
boost::gregorian::weeks ws(4);
std::cout << ws.days() << std::endl;
boost::gregorian::months ms(4);
std::cout << ms.number_of_months() << std::endl;
boost::gregorian::years ys(4);
std::cout << ys.number_of_years() << std::endl;
}
```
* [下載源代碼](src/10.2.4/main.cpp)
`boost::gregorian::months` 和 `boost::gregorian::years` 都無法確定其天數,因為某月或某年所含天數是可長的。 不過,這些類的用法還是可以從以下例子中看出。
```
#include <boost/date_time/gregorian/gregorian.hpp>
#include <iostream>
int main()
{
boost::gregorian::date d(2009, 1, 31);
boost::gregorian::months ms(1);
boost::gregorian::date d2 = d + ms;
std::cout << d2 << std::endl;
boost::gregorian::date d3 = d2 - ms;
std::cout << d3 << std::endl;
}
```
* [下載源代碼](src/10.2.5/main.cpp)
該程序將一個月加到給定的日期 January 31, 2009 上,得到 `d2`,其為 February 28, 2009。 接著,再減回一個月得到 `d3`,又重新變回 January 31, 2009。 如上所示,時間點和時間長度可用于計算。 不過,需要考慮具體的情況。 例如,從某月的最后一天開始計算,`boost::gregorian::months` 總是會到達另一個月的最后一天,如果反復前后跳,就可能得到令人驚訝的結果。
```
#include <boost/date_time/gregorian/gregorian.hpp>
#include <iostream>
int main()
{
boost::gregorian::date d(2009, 1, 30);
boost::gregorian::months ms(1);
boost::gregorian::date d2 = d + ms;
std::cout << d2 << std::endl;
boost::gregorian::date d3 = d2 - ms;
std::cout << d3 << std::endl;
}
```
* [下載源代碼](src/10.2.6/main.cpp)
這個例子與前一個例子的不同之處在于,初始的日期是 January 30, 2009。 雖然這不是 January 的最后一天,但是向前跳一個月后得到的 `d2` 還是 February 28, 2009,因為沒有 February 30 這一天。 不過,當我們再往回跳一個月,這次得到的 `d3` 就變成 January 31, 2009! 因為 February 28, 2009 是當月的最后一天,往回跳實際上是返回到 January 的最后一天。
如果你覺得這種行為過于混亂,可以通過取消 `BOOST_DATE_TIME_OPTIONAL_GREGORIAN_TYPES` 宏的定義來改變這種行為。 取消該宏后,`boost::gregorian::weeks`, `boost::gregorian::months` 和 `boost::gregorian::years` 類都不再可用。 唯一剩下的類是 `boost::gregorian::date_duration`,只能指定前向或后向的跳過的天數,這樣就不會再有意外的結果了。
`boost::gregorian::date_duration` 表示的是時間長度,而 `boost::gregorian::date_period` 則提供了對兩個日期之間區間的支持。
```
#include <boost/date_time/gregorian/gregorian.hpp>
#include <iostream>
int main()
{
boost::gregorian::date d1(2009, 1, 30);
boost::gregorian::date d2(2009, 10, 31);
boost::gregorian::date_period dp(d1, d2);
boost::gregorian::date_duration dd = dp.length();
std::cout << dd.days() << std::endl;
}
```
* [下載源代碼](src/10.2.7/main.cpp)
兩個類型為 `boost::gregorian::date` 的參數指定了開始和結束的日期,它們被傳遞給 `boost::gregorian::date_period` 的構造函數。 此外,也可以指定一個開始日期和一個類型為 `boost::gregorian::date_duration` 的時間長度。 請注意,結束日期的前一天才是這個時間區間的最后一天,這對于理解以下例子的輸出非常重要。
```
#include <boost/date_time/gregorian/gregorian.hpp>
#include <iostream>
int main()
{
boost::gregorian::date d1(2009, 1, 30);
boost::gregorian::date d2(2009, 10, 31);
boost::gregorian::date_period dp(d1, d2);
std::cout << dp.contains(d1) << std::endl;
std::cout << dp.contains(d2) << std::endl;
}
```
* [下載源代碼](src/10.2.8/main.cpp)
這個程序用 `contains()` 方法來檢查某個給定的日期是否包含在時間區間內。 雖然 `d1` 和 `d2` 都是被傳遞給 `boost::gregorian::date_period` 的構造函數的,但是 `contains()` 僅對第一個返回 `true`。 因為結束日期不是區間的一部分,所以以 `d2` 調用 `contains()` 會返回 `false`。
`boost::gregorian::date_period` 還提供了其它方法,如移動一個區間,或計算兩個重疊區間的交集。
除了日期類、時間長度類和時間區間類,Boost.DateTime 還提供了迭代器和其它有用的自由函數,如下例所示。
```
#include <boost/date_time/gregorian/gregorian.hpp>
#include <iostream>
int main()
{
boost::gregorian::date d(2009, 1, 5);
boost::gregorian::day_iterator it(d);
std::cout << *++it << std::endl;
std::cout << boost::date_time::next_weekday(*it, boost::gregorian::greg_weekday(boost::date_time::Friday)) << std::endl;
}
```
* [下載源代碼](src/10.2.9/main.cpp)
為了從一個指定日期向前或向后一天一天地跳,可以使用迭代器 `boost::gregorian::day_iterator`。 還有 `boost::gregorian::week_iterator`, `boost::gregorian::month_iterator` 和 `boost::gregorian::year_iterator` 分別提供了按周、按月或是按年跳的迭代器。
這個例子還使用了 `boost::date_time::next_weekday()`,它基于一個給定的日期返回下一個星期幾的日期。 以下程序將顯示 `2009-Jan-09`,因為它是 January 6, 2009 之的第一個Friday。
## 10.3.?位置無關的時間
`boost::gregorian::date` 用于創建日期,`boost::posix_time::ptime` 則用于定義一個位置無關的時間。 `boost::posix_time::ptime` 會存取 `boost::gregorian::date` 且額外保存一個時間。
為了使用 `boost::posix_time::ptime`,必須包含頭文件 `boost/date_time/posix_time/posix_time.hpp`。
```
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/date_time/gregorian/gregorian.hpp>
#include <iostream>
int main()
{
boost::posix_time::ptime pt(boost::gregorian::date(2009, 1, 5), boost::posix_time::time_duration(12, 0, 0));
boost::gregorian::date d = pt.date();
std::cout << d << std::endl;
boost::posix_time::time_duration td = pt.time_of_day();
std::cout << td << std::endl;
}
```
* [下載源代碼](src/10.3.1/main.cpp)
要初始化一個 `boost::posix_time::ptime` 類型的對象,要把一個類型為 `boost::gregorian::date` 的日期和一個類型為 `boost::posix_time::time_duration` 的時間長度作為第一和第二參數傳遞給構造函數。 傳給 `boost::posix_time::time_duration` 構造函數的三個參數決定了時間點。 以上程序指定的時間點是 January 5, 2009 的 12 PM。
要查詢日期和時間,可以使用 `date()` 和 `time_of_day()` 方法。
象 `boost::gregorian::date` 的缺省構造函數會創建一個無效日期一樣,如果使用缺省構造函數,`boost::posix_time::ptime` 類型的對象也是無效的。 也可以通過傳遞一個 `boost::date_time::not_a_date_time` 給構造函數來顯式創建一個無效時間。
和使用自由函數或其它對象的方法來創建 `boost::gregorian::date` 類型的歷法日期一樣,Boost.DateTime 也提供了相應的自由函數和對象來創建時間。
```
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/date_time/gregorian/gregorian.hpp>
#include <iostream>
int main()
{
boost::posix_time::ptime pt = boost::posix_time::second_clock::universal_time();
std::cout << pt.date() << std::endl;
std::cout << pt.time_of_day() << std::endl;
pt = boost::posix_time::from_iso_string("20090105T120000");
std::cout << pt.date() << std::endl;
std::cout << pt.time_of_day() << std::endl;
}
```
* [下載源代碼](src/10.3.2/main.cpp)
類 `boost::posix_time::second_clock` 表示一個返回當前時間的時鐘。 `universal_time()` 方法返回 UTC 時間,如上例所示。 如果需要本地時間,則必須使用 `local_time()`。
Boost.DateTime 還提供了一個名為 `boost::posix_time::microsec_clock` 的類,它返回包含微秒在內的當前時間,供需要更高精度時使用。
要將一個保存在字符串中的時間點轉換為類型為 `boost::posix_time::ptime` 的對象,可以用 `boost::posix_time::from_iso_string()` 這樣的自由函數,它要求傳入的時間點以 ISO 8601 格式提供。
除了 `boost::posix_time::ptime`, Boost.DateTime 也提供了 `boost::posix_time::time_duration` 類,用于指定一個時間長度。 這個類前面已經提到過,因為 `boost::posix_time::ptime` 的構造函數實際上需要一個 `boost::posix_time::time_duration` 類型的對象作為其第二個參數。 當然,`boost::posix_time::time_duration` 也可以單獨使用。
```
#include <boost/date_time/posix_time/posix_time.hpp>
#include <iostream>
int main()
{
boost::posix_time::time_duration td(16, 30, 0);
std::cout << td.hours() << std::endl;
std::cout << td.minutes() << std::endl;
std::cout << td.seconds() << std::endl;
std::cout << td.total_seconds() << std::endl;
}
```
* [下載源代碼](src/10.3.3/main.cpp)
`hours()`, `minutes()` 和 `seconds()` 均返回傳給構造函數的各個值,而象 `total_seconds()` 這樣的方法則返回總的秒數,以簡單的方式為你提供額外的信息。
可以傳遞任意值給 `boost::posix_time::time_duration`,因為沒有象24小時這樣的上限存在。
和歷法日期一樣,時間點與時間長度也可以執行運算。
```
#include <boost/date_time/posix_time/posix_time.hpp>
#include <iostream>
int main()
{
boost::posix_time::ptime pt1(boost::gregorian::date(2009, 1, 05), boost::posix_time::time_duration(12, 0, 0));
boost::posix_time::ptime pt2(boost::gregorian::date(2009, 1, 05), boost::posix_time::time_duration(18, 30, 0));
boost::posix_time::time_duration td = pt2 - pt1;
std::cout << td.hours() << std::endl;
std::cout << td.minutes() << std::endl;
std::cout << td.seconds() << std::endl;
}
```
* [下載源代碼](src/10.3.4/main.cpp)
如果兩個 `boost::posix_time::ptime` 類型的時間點相減,結果將是一個 `boost::posix_time::time_duration` 類型的對象,給出兩個時間點之間的時間長度。
```
#include <boost/date_time/posix_time/posix_time.hpp>
#include <iostream>
int main()
{
boost::posix_time::ptime pt1(boost::gregorian::date(2009, 1, 05), boost::posix_time::time_duration(12, 0, 0));
boost::posix_time::time_duration td(6, 30, 0);
boost::posix_time::ptime pt2 = pt1 + td;
std::cout << pt2.time_of_day() << std::endl;
}
```
* [下載源代碼](src/10.3.5/main.cpp)
正如這個例子所示,時間長度可以被增加至一個時間點上,以得到一個新的時間點。 以上程序將打印 `18:30:00` 到標準輸出流。
你可能已經留意到,Boost.DateTime 對于歷法日期和時間使用了相同的概念。 就象有時間類和時間長度類一樣,也有一個時間區間的類。 對于歷法日期,這個類是 `boost::gregorian::date_period`; 對于時間則是 `boost::posix_time::time_period`。 這兩個類均要求傳入兩個參數給構造函數: `boost::gregorian::date_period` 要求兩個歷法日期,而 `boost::posix_time::time_period` 則要求兩個時間。
```
#include <boost/date_time/posix_time/posix_time.hpp>
#include <iostream>
int main()
{
boost::posix_time::ptime pt1(boost::gregorian::date(2009, 1, 05), boost::posix_time::time_duration(12, 0, 0));
boost::posix_time::ptime pt2(boost::gregorian::date(2009, 1, 05), boost::posix_time::time_duration(18, 30, 0));
boost::posix_time::time_period tp(pt1, pt2);
std::cout << tp.contains(pt1) << std::endl;
std::cout << tp.contains(pt2) << std::endl;
}
```
* [下載源代碼](src/10.3.6/main.cpp)
大致上說,`boost::posix_time::time_period` 非常象 `boost::gregorian::date_period`。 它提供了一個名為 `contains()` 的方法,對于位于該時間區間內的每一個時間點,它返回 `true`。 由于傳給 `boost::posix_time::time_period` 的構造函數的結束時間不是時間區間的一部分,所以上例中第二個 `contains()` 調用將返回 `false`。
`boost::posix_time::time_period` 還提供了其它方法,如 `intersection()` 和 `merge()` 分別用于計算兩個重疊時間區間的交集,以及合并兩個相交區間。
最后,迭代器 `boost::posix_time::time_iterator` 用于對時間點進行迭代。
```
#include <boost/date_time/local_time/local_time.hpp>
#include <iostream>
int main()
{
boost::posix_time::ptime pt(boost::gregorian::date(2009, 1, 05), boost::posix_time::time_duration(12, 0, 0));
boost::posix_time::time_iterator it(pt, boost::posix_time::time_duration(6, 30, 0));
std::cout << *++it << std::endl;
std::cout << *++it << std::endl;
}
```
* [下載源代碼](src/10.3.7/main.cpp)
以上程序使用了迭代器 `it` 從時間點 `pt` 開始向前跳6.5個小時 。 由于迭代器被遞增兩次,所以相應的輸出分別為 `2009-Jan-05 18:30:00` 和 `2009-Jan-06 01:00:00`。
## 10.4.?位置相關的時間
和前一節所介紹的位置無關時間不一樣,位置相關時間是要考慮時區的。 為此,Boost.DateTime 提供了 `boost::local_time::local_date_time` 類,它定義于 `boost/date_time/local_time/local_time.hpp`, 并使用 `boost::local_time::posix_time_zone` 來保存時區信息。
```
#include <boost/date_time/local_time/local_time.hpp>
#include <iostream>
int main()
{
boost::local_time::time_zone_ptr tz(new boost::local_time::posix_time_zone("CET+1"));
boost::posix_time::ptime pt(boost::gregorian::date(2009, 1, 5), boost::posix_time::time_duration(12, 0, 0));
boost::local_time::local_date_time dt(pt, tz);
std::cout << dt.utc_time() << std::endl;
std::cout << dt << std::endl;
std::cout << dt.local_time() << std::endl;
std::cout << dt.zone_name() << std::endl;
}
```
* [下載源代碼](src/10.4.1/main.cpp)
`boost::local_time::local_date_time` 的構造函數要求一個 `boost::posix_time::ptime` 類型的對象作為其第一個參數,以及一個 `boost::local_time::time_zone_ptr` 類型的對象作為第二個參數。 后者只不過是 `boost::shared_ptr<boost::local_time::posix_time_zone>` 的類型定義。 換句話說,并不是傳遞一個 `boost::local_time::posix_time_zone` 對象,而是傳遞一個指向該對象的智能指針。 這樣,多個 `boost::local_time::local_date_time` 類型的對象就可以共享時區信息。 只有當最后一個對象被銷毀時,相應的表示時區的對象才會被自動釋放。
要創建一個 `boost::local_time::posix_time_zone` 類型的對象,就要將一個描述該時區的字符串作為單一參數傳遞給構造函數。 以上例子指定了歐洲中部時區:CET 是歐洲中部時間(Central European Time)的縮寫。 由于 CET 比 UTC 早一個小時,所以時差以 +1 表示。 Boost.DateTime 并不能解釋時區的縮寫,也就不知道 CET 的意思。 所以,必須以小時數給出時差;傳入 +0 表示沒有時差。
在執行時,該程序將打印 `2009-Jan-05 12:00:00`, `2009-Jan-05 13:00:00 CET`, `2009-Jan-05 13:00:00` 和 `CET` 到標準輸出流。 用以初始化 `boost::posix_time::ptime` 和 `boost::local_time::local_date_time` 類型的值缺省總是與 UTC 時區相關的。 只有當一個 `boost::local_time::local_date_time` 類型的對象被寫出至標準輸出流時,或者調用 `local_time()` 方法時,才使用時差來計算本地時間。
```
#include <boost/date_time/local_time/local_time.hpp>
#include <iostream>
int main()
{
boost::local_time::time_zone_ptr tz(new boost::local_time::posix_time_zone("CET+1"));
boost::posix_time::ptime pt(boost::gregorian::date(2009, 1, 5), boost::posix_time::time_duration(12, 0, 0));
boost::local_time::local_date_time dt(pt, tz);
std::cout << dt.local_time() << std::endl;
boost::local_time::time_zone_ptr tz2(new boost::local_time::posix_time_zone("EET+2"));
std::cout << dt.local_time_in(tz2).local_time() << std::endl;
}
```
* [下載源代碼](src/10.4.2/main.cpp)
通過使用 `local_time()` 方法,時區的偏差才被考慮進來。 為了計算 CET 時間,需要往保存在 `dt` 中的 UTC 時間 12 PM 上加一個小時,因為 CET 比 UTC 早一個小時。 `local_time()` 會相應地輸出 `2009-Jan-05 13:00:00` 到標準輸出流。
相比之下,`local_time_in()` 方法是在所傳入參數的時區內解釋保存在 `dt` 中的時間。 這意味著 12 PM UTC 相當于 2 PM EET,即東部歐洲時間,它比 UTC 早兩個小時。
最后,Boost.DateTime 通過 `boost::local_time::local_time_period` 類提供了位置相關的時間區間。
```
#include <boost/date_time/local_time/local_time.hpp>
#include <iostream>
int main()
{
boost::local_time::time_zone_ptr tz(new boost::local_time::posix_time_zone("CET+0"));
boost::posix_time::ptime pt1(boost::gregorian::date(2009, 1, 5), boost::posix_time::time_duration(12, 0, 0));
boost::local_time::local_date_time dt1(pt1, tz);
boost::posix_time::ptime pt2(boost::gregorian::date(2009, 1, 5), boost::posix_time::time_duration(18, 0, 0));
boost::local_time::local_date_time dt2(pt2, tz);
boost::local_time::local_time_period tp(dt1, dt2);
std::cout << tp.contains(dt1) << std::endl;
std::cout << tp.contains(dt2) << std::endl;
}
```
* [下載源代碼](src/10.4.3/main.cpp)
`boost::local_time::local_time_period` 的構造函數要求兩個類型為 `boost::local_time::local_date_time` 的參數。 和其它類型的時間區間一樣,第二個參數所表示的結束時間并不包含在區間之內。 通過如 `contains()`, `intersection()`, `merge()` 以及其它的方法,時間區間可以與其它 `boost::local_time::local_time_period` 相互操作。
## 10.5.?格式化輸入輸出
本章中的所有例子在執行后都提供形如 `2009-Jan-07` 這樣的輸出結果。 有的人可能更喜歡用其它格式來顯示結果。 Boost.DateTime 允許 `boost::date_time::date_facet` 和 `boost::date_time::time_facet` 類來格式化歷法日期和時間。
```
#include <boost/date_time/gregorian/gregorian.hpp>
#include <iostream>
#include <locale>
int main()
{
boost::gregorian::date d(2009, 1, 7);
boost::gregorian::date_facet *df = new boost::gregorian::date_facet("%A, %d %B %Y");
std::cout.imbue(std::locale(std::cout.getloc(), df));
std::cout << d << std::endl;
}
```
* [下載源代碼](src/10.5.1/main.cpp)
Boost.DateTime 使用了 locales 的概念,它來自于 C++ 標準,在 [第?5?章 _字符串處理_](stringhandling.html "第?5?章?字符串處理") 中有概括的介紹。 要格式化一個歷法日期,必須創建一個 `boost::date_time::date_facet` 類型的對象并安裝在一個 locale 內。 一個描述新格式的字符串被傳遞給 `boost::date_time::date_facet` 的構造函數。 上面的例子傳遞的是 `%A, %d %B %Y`,指定格式為:星期幾后跟日月年全名: `Wednesday, 07 January 2009`。
Boost.DateTime 提供了多個格式化標志,標志由一個百分號后跟一個字符組成。 Boost.DateTime 的文檔中對于所支持的所有標志有一個完整的介紹。 例如,%A 表示星期幾的全名。
如果應用程序的基本用戶是位于德國或德語國家,最好可以用德語而不是英語來顯示星期幾和月份。
```
#include <boost/date_time/gregorian/gregorian.hpp>
#include <iostream>
#include <locale>
#include <string>
#include <vector>
int main()
{
std::locale::global(std::locale("German"));
std::string months[12] = { "Januar", "Februar", "M?rz", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember" };
std::string weekdays[7] = { "Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag" };
boost::gregorian::date d(2009, 1, 7);
boost::gregorian::date_facet *df = new boost::gregorian::date_facet("%A, %d. %B %Y");
df->long_month_names(std::vector<std::string>(months, months + 12));
df->long_weekday_names(std::vector<std::string>(weekdays, weekdays + 7));
std::cout.imbue(std::locale(std::cout.getloc(), df));
std::cout << d << std::endl;
}
```
* [下載源代碼](src/10.5.2/main.cpp)
星期幾和月份的名字可以通過分別傳入兩個數組給 `boost::date_time::date_facet` 類的 `long_month_names()` 和 `long_weekday_names()` 方法來修改,這兩個數組分別包含了相應的名字。 以上例子現在將打印 `Mittwoch, 07\. Januar 2009` 到標準輸出流。
Boost.DateTime 在格式化輸入輸出方面是非常靈活的。 除了輸出類 `boost::date_time::date_facet` 和 `boost::date_time::time_facet` 以外,類 `boost::date_time::date_input_facet` 和 `boost::date_time::time_input_facet` 可用于格式化輸入。 所有這四個類都提供了許多方法,來為 Boost.DateTime 所提供的各種不同對象配置輸入和輸出的方式。 例如,可以指定 `boost::gregorian::date_period` 類型的時間長度如何輸入和輸出。 要弄清楚各種格式化輸入輸出的可能性,請參考 Boost.DateTime 的文檔。
## 10.6.?練習
You can buy [solutions to all exercises](http://en.highscore.de/shop/index.php?p=boost-solution) in this book as a ZIP file.
1. 創建一個程序,打印下一個 Christmas Eve, Christmas Day 及其后一天是星期幾到標準輸出流。
2. 以天數計算你的年齡。 該程序應該自動根據當前日期來計算。