<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          C++ 使用 chrono 庫處理日期和時間

          共 22682字,需瀏覽 46分鐘

           ·

          2021-09-08 10:16

          C++11 中提供了日期和時間相關(guān)的庫 chrono,通過 chrono 庫可以很方便地處理日期和時間,為程序的開發(fā)提供了便利。chrono 庫主要包含三種類型的類:時間間隔duration、時鐘clocks、時間點time point。

          1. 時間間隔 duration

          1.1 常用類成員

          duration表示一段時間間隔,用來記錄時間長度,可以表示幾秒、幾分鐘、幾個小時的時間間隔。duration 的原型如下:

          // 定義于頭文件 <chrono>
          template<
              class Rep,
              class Period = std::ratio<1>
          > class duration;

          ratio 類表示每個時鐘周期的秒數(shù),其中第一個模板參數(shù) Num 代表分子,Denom 代表分母,該分母值默認為 1,因此,ratio 代表的是一個分子除以分母的數(shù)值,比如:ratio<2> 代表一個時鐘周期是 2 秒,ratio<60 > 代表一分鐘,ratio<60*60 > 代表一個小時,ratio<60*60*24 > 代表一天。而 ratio<1,1000 > 代表的是 1/1000 秒,也就是 1 毫秒,ratio<1,1000000 > 代表一微秒,ratio<1,1000000000 > 代表一納秒。

          為了方便使用,在標準庫中定義了一些常用的時間間隔,比如:時、分、秒、毫秒、微秒、納秒,它們都位于 chrono 命名空間下,定義如下:

          類型定義
          納秒:std::chrono::nanosecondsduration<Rep/ 至少 64 位的有符號整數(shù)類型 /, std::nano>
          微秒:std::chrono::microsecondsduration<Rep/ 至少 55 位的有符號整數(shù)類型 /, std::micro>
          毫秒:std::chrono::millisecondsduration<Rep/ 至少 45 位的有符號整數(shù)類型 /, std::milli>
          秒:std::chrono::secondsduration<Rep/ 至少 35 位的有符號整數(shù)類型 />
          分鐘:std::chrono::minutesduration<Rep/ 至少 29 位的有符號整數(shù)類型 /, std::ratio<60>>
          小時:std::chrono::hoursduration<Rep/ 至少 23 位的有符號整數(shù)類型 /, std::ratio<3600>>

          注意:到 hours 為止的每個預定義時長類型至少涵蓋 ±292 年的范圍。

          duration 類的構(gòu)造函數(shù)原型如下:

          // 1. 拷貝構(gòu)造函數(shù)
          duration( const duration& ) = default;
          // 2. 通過指定時鐘周期的類型來構(gòu)造對象
          template< class Rep2 >
          constexpr explicit duration( const Rep2& r );
          // 3. 通過指定時鐘周期類型,和時鐘周期長度來構(gòu)造對象
          template< class Rep2, class Period2 >
          constexpr duration( const duration<Rep2,Period2>& d );

          為了更加方便的進行 duration 對象之間的操作,類內(nèi)部進行了操作符重載:

          操作符重載描述
          operator=賦值內(nèi)容 (公開成員函數(shù))
          operator+ operator-賦值內(nèi)容 (公開成員函數(shù))
          operator++ operator++(int) operator– operator–(int)遞增或遞減周期計數(shù) (公開成員函數(shù))
          operator+= operator-= operator*= operator/= operator%=實現(xiàn)二個時長間的復合賦值 (公開成員函數(shù))

          duration 類還提供了獲取時間間隔的時鐘周期數(shù)的方法 count (),函數(shù)原型如下:

          constexpr rep count() const;

          1.2 類的使用

          通過構(gòu)造函數(shù)構(gòu)造事件間隔對象示例代碼如下:

          #include <chrono>
          #include <iostream>
          using namespace std;
          int main()
          {
              chrono::hours h(1);                          // 一小時
              chrono::milliseconds ms{ 3 };                // 3 毫秒
              chrono::duration<int, ratio<1000>> ks(3);    // 3000 秒

              // chrono::duration<int, ratio<1000>> d3(3.5);  // error
              chrono::duration<double> dd(6.6);               // 6.6 秒

              // 使用小數(shù)表示時鐘周期的次數(shù)
              chrono::duration<double, std::ratio<1, 30>> hz(3.5);
          }
          • h(1) 時鐘周期為 1 小時,共有 1 個時鐘周期,所以 h 表示的時間間隔為 1 小時
          • ms(3) 時鐘周期為 1 毫秒,共有 3 個時鐘周期,所以 ms 表示的時間間隔為 3 毫秒
          • ks(3) 時鐘周期為 1000 秒,一共有三個時鐘周期,所以 ks 表示的時間間隔為 3000 秒
          • d3(3.5) 時鐘周期為 1000 秒,時鐘周期數(shù)量只能用整形來表示,但是此處指定的是浮點數(shù),因此語法錯誤
          • dd(6.6) 時鐘周期為默認的 1 秒,共有 6.6 個時鐘周期,所以 dd 表示的時間間隔為 6.6 秒
          • hz(3.5) 時鐘周期為 1/30 秒,共有 3.5 個時鐘周期,所以 hz 表示的時間間隔為 1/30*3.5 秒

          chrono 庫中根據(jù) duration 類封裝了不同長度的時鐘周期(也可以自定義),基于這個時鐘周期再進行周期次數(shù)的設(shè)置就可以得到總的時間間隔了(時鐘周期 * 周期次數(shù) = 總的時間間隔)。

          示例代碼如下:

          #include <chrono>
          #include <iostream>
          int main()
          {
              std::chrono::milliseconds ms{3};         // 3 毫秒
              std::chrono::microseconds us = 2*ms;     // 6000 微秒
              // 時間間隔周期為 1/30 秒
              std::chrono::duration<double, std::ratio<1, 30>> hz(3.5);
           
              std::cout <<  "3 ms duration has " << ms.count() << " ticks\n"
                        <<  "6000 us duration has " << us.count() << " ticks\n"
                        <<  "3.5 hz duration has " << hz.count() << " ticks\n";       
          }

          輸出的結(jié)果為:

          3 ms duration has 3 ticks
          6000 us duration has 6000 ticks
          3.5 hz duration has 3.5 ticks
          • ms 時間單位為毫秒,初始化操作 ms{3} 表示時間間隔為 3 毫秒,一共有 3 個時間周期,每個周期為 1 毫秒
          • us 時間單位為微秒,初始化操作 2*ms 表示時間間隔為 6000 微秒,一共有 6000 個時間周期,每個周期為 1 微秒
          • hz 時間單位為秒,初始化操作 hz(3.5) 表示時間間隔為 1/30*3.5 秒,一共有 3.5 個時間周期,每個周期為 1/30 秒

          由于在 duration 類內(nèi)部做了操作符重載,因此時間間隔之間可以直接進行算術(shù)運算,比如我們要計算兩個時間間隔的差值,就可以在代碼中做如下處理:

          #include <iostream>
          #include <chrono>
          using namespace std;

          int main()
          {
              chrono::minutes t1(10);
              chrono::seconds t2(60);
              chrono::seconds t3 = t1 - t2;
              cout << t3.count() << " second" << endl;
          }

          程序輸出的結(jié)果:

          540 second

          在上面的測試程序中,t1 代表 10 分鐘,t2 代表 60 秒,t3 是 t1 減去 t2,也就是 60*10-60=540,這個 540 表示的時鐘周期,每個時鐘周期是 1 秒,因此兩個時間間隔之間的差值為 540 秒。

          注意事項:duration 的加減運算有一定的規(guī)則,當兩個 duration 時鐘周期不相同的時候,會先統(tǒng)一成一種時鐘,然后再進行算術(shù)運算,統(tǒng)一的規(guī)則如下:假設(shè)有 ratio<x1,y1> 和 ratio<x2,y2 > 兩個時鐘周期,首先需要求出 x1,x2 的最大公約數(shù) X,然后求出 y1,y2 的最小公倍數(shù) Y,統(tǒng)一之后的時鐘周期 ratio 為 ratio<X,Y>。

          #include <iostream>
          #include <chrono>
          using namespace std;

          int main()
          {
              chrono::duration<double, ratio<9, 7>> d1(3);
              chrono::duration<double, ratio<6, 5>> d2(1);
              // d1 和 d2 統(tǒng)一之后的時鐘周期
              chrono::duration<double, ratio<3, 35>> d3 = d1 - d2;
          }

          對于分子 6,、9 最大公約數(shù)為 3,對于分母 7、5 最小公倍數(shù)為 35,因此推導出的時鐘周期為 ratio<3,35>

          2. 時間點 time point

          chrono 庫中提供了一個表示時間點的類 time_point,該類的定義如下:

          // 定義于頭文件 <chrono>
          template<
              class Clock,
              class Duration = typename Clock::duration
          > class time_point;

          它被實現(xiàn)成如同存儲一個 Duration 類型的自 Clock 的紀元起始開始的時間間隔的值,通過這個類最終可以得到時間中的某一個時間點。

          • Clock:此時間點在此時鐘上計量
          • Duration:用于計量從紀元起時間的 std::chrono::duration 類型

          time_point 類的構(gòu)造函數(shù)原型如下:

          // 1. 構(gòu)造一個以新紀元(epoch,即:1970.1.1)作為值的對象,需要和時鐘類一起使用,不能單獨使用該無參構(gòu)造函數(shù)
          time_point();
          // 2. 構(gòu)造一個對象,表示一個時間點,其中d的持續(xù)時間從epoch開始,需要和時鐘類一起使用,不能單獨使用該構(gòu)造函數(shù)
          explicit time_point( const duration& d );
          // 3. 拷貝構(gòu)造函數(shù),構(gòu)造與t相同時間點的對象,使用的時候需要指定模板參數(shù)
          template< class Duration2 >
          time_point( const time_point<Clock,Duration2>& t );

          在這個類中除了構(gòu)造函數(shù)還提供了另外一個 time_since_epoch() 函數(shù),用來獲得 1970 年 1 月 1 日到 time_point 對象中記錄的時間經(jīng)過的時間間隔(duration),函數(shù)原型如下:

          duration time_since_epoch() const;

          除此之外,時間點 time_point 對象和時間段對象 duration 之間還支持直接進行算術(shù)運算(即加減運算),時間點對象之間可以進行邏輯運算,具體細節(jié)可以參考下面的表格:

          其中 tp 和 tp2 是 time_point 類型的對象, dtn 是 duration 類型的對象。

          描述操作返回值
          復合賦值 (成員函數(shù)) operator+=tp += dtn*this
          復合賦值 (成員函數(shù))  operator-=tp -= dtn*this
          算術(shù)運算符 (非成員函數(shù)) operator+tp + dtna time_point value
          算術(shù)運算符 (非成員函數(shù)) operator+dtn + tpa time_point value
          算術(shù)運算符 (非成員函數(shù)) operator-tp - dtna time_point value
          算術(shù)運算符 (非成員函數(shù)) operator-ttp - tp2aduration value
          關(guān)系操作符 (非成員函數(shù)) operator==tp == tp2a bool value
          關(guān)系操作符 (非成員函數(shù)) operator!=tp != tp2a bool value
          關(guān)系操作符 (非成員函數(shù)) operator<tp < tp2a bool value
          關(guān)系操作符 (非成員函數(shù)) operator>tp > tp2a bool value
          關(guān)系操作符 (非成員函數(shù)) operator>=tp >= tp2a bool value
          關(guān)系操作符 (非成員函數(shù)) operator<=tp <= tp2a bool value

          由于該時間點類經(jīng)常和下面要介紹的時鐘類一起使用,所以在此先不舉例,在時鐘類的示例代碼中會涉及到時間點類的使用,到此為止只需要搞明白時間點類的提供的這幾個函數(shù)的作用就可以了。

          3. 時鐘 clocks

          chrono 庫中提供了獲取當前的系統(tǒng)時間的時鐘類,包含的時鐘一共有三種:

          • system_clock:系統(tǒng)的時鐘,系統(tǒng)的時鐘可以修改,甚至可以網(wǎng)絡(luò)對時,因此使用系統(tǒng)時間計算時間差可能不準。
          • steady_clock:是固定的時鐘,相當于秒表。開始計時后,時間只會增長并且不能修改,適合用于記錄程序耗時
          • high_resolution_clock:和時鐘類 steady_clock 是等價的(是它的別名)。

          在這些時鐘類的內(nèi)部有 time_point、duration、Rep、Period 等信息,基于這些信息來獲取當前時間,以及實現(xiàn) time_t 和 time_point 之間的相互轉(zhuǎn)換。

          時鐘類成員類型描述
          rep表示時鐘周期次數(shù)的有符號算術(shù)類型
          period表示時鐘計次周期的 std::ratio 類型
          duration時間間隔,可以表示負時長
          time_point表示在當前時鐘里邊記錄的時間點

          在使用chrono提供的時鐘類的時候,不需要創(chuàng)建類對象,直接調(diào)用類的靜態(tài)方法就可以得到想要的時間了。

          3.1 system_clock

          具體來說,時鐘類 system_clock 是一個系統(tǒng)范圍的實時時鐘。system_clock 提供了對當前時間點 time_point 的訪問,將得到時間點轉(zhuǎn)換為 time_t 類型的時間對象,就可以基于這個時間對象獲取到當前的時間信息了。

          system_clock 時鐘類在底層源碼中的定義如下:

          struct system_clock { // wraps GetSystemTimePreciseAsFileTime/GetSystemTimeAsFileTime
              using rep                       = long long;
              using period                    = ratio<1, 10'000'000>; // 100 nanoseconds
              using duration                  = chrono::duration<rep, period>;
              using time_point                = chrono::time_point<system_clock>;
              static constexpr bool is_steady = false;

              _NODISCARD static time_point now() noexcept 
              { // get current time
                  return time_point(duration(_Xtime_get_ticks()));
              }

              _NODISCARD static __time64_t to_time_t(const time_point& _Time) noexcept 
              { // convert to __time64_t
                  return duration_cast<seconds>(_Time.time_since_epoch()).count();
              }

              _NODISCARD static time_point from_time_t(__time64_t _Tm) noexcept 
              { // convert from __time64_t
                  return time_point{seconds{_Tm}};
              }
          };

          通過以上源碼可以了解到在 system_clock 類中的一些細節(jié)信息:

          • rep:時鐘周期次數(shù)是通過整形來記錄的 long long
          • period:一個時鐘周期是 100 納秒 ratio<1, 10'000'000>
          • duration:時間間隔為 rep*period 納秒 chrono::duration<rep, period>
          • time_point:時間點通過系統(tǒng)時鐘做了初始化 chrono::time_p- oint<system_clock>,里面記錄了新紀元時間點

          另外還可以看到 system_clock 類一共提供了三個靜態(tài)成員函數(shù):

          // 返回表示當前時間的時間點。
          static std::chrono::time_point<std::chrono::system_clock> now() noexcept;
          // 將 time_point 時間點類型轉(zhuǎn)換為 std::time_t 類型
          static std::time_t to_time_t( const time_point& t ) noexcept;
          // 將 std::time_t 類型轉(zhuǎn)換為 time_point 時間點類型
          static std::chrono::system_clock::time_point from_time_t( std::time_t t ) noexcept;

          比如,我們要獲取當前的系統(tǒng)時間,并且需要將其以能夠識別的方式打印出來,示例代碼如下:

          #include <chrono>
          #include <iostream>
          using namespace std;
          using namespace std::chrono;
          int main()
          {
              // 新紀元1970.1.1時間
              system_clock::time_point epoch;

              duration<int, ratio<60*60*24>> day(1);
              // 新紀元1970.1.1時間 + 1天
              system_clock::time_point ppt(day);

              using dday = duration<int, ratio<60 * 60 * 24>>;
              // 新紀元1970.1.1時間 + 10天
              time_point<system_clock, dday> t(dday(10));

              // 系統(tǒng)當前時間
              system_clock::time_point today = system_clock::now();
              
              // 轉(zhuǎn)換為time_t時間類型
              time_t tm = system_clock::to_time_t(today);
              cout << "今天的日期是:    " << ctime(&tm);

              time_t tm1 = system_clock::to_time_t(today+day);
              cout << "明天的日期是:    " << ctime(&tm1);

              time_t tm2 = system_clock::to_time_t(epoch);
              cout << "新紀元時間:      " << ctime(&tm2);

              time_t tm3 = system_clock::to_time_t(ppt);
              cout << "新紀元時間+1天:  " << ctime(&tm3);

              time_t tm4 = system_clock::to_time_t(t);
              cout << "新紀元時間+10天: " << ctime(&tm4);
          }

          示例代碼打印的結(jié)果為:

          今天的日期是:    Thu Apr  8 11:09:49 2021
          明天的日期是:    Fri Apr  9 11:09:49 2021
          新紀元時間:      Thu Jan  1 08:00:00 1970
          新紀元時間+1天:  Fri Jan  2 08:00:00 1970
          新紀元時間+10天: Sun Jan 11 08:00:00 1970

          3.2 steady_clock

          如果我們通過時鐘不是為了獲取當前的系統(tǒng)時間,而是進行程序耗時的時長,此時使用 syetem_clock 就不合適了,因為這個時間可以跟隨系統(tǒng)的設(shè)置發(fā)生變化。在 C++11 中提供的時鐘類 steady_clock 相當于秒表,只要啟動就會進行時間的累加,并且不能被修改,非常適合于進行耗時的統(tǒng)計。

          steady_clock 時鐘類在底層源碼中的定義如下:

          struct steady_clock { // wraps QueryPerformanceCounter
              using rep                       = long long;
              using period                    = nano;
              using duration                  = nanoseconds;
              using time_point                = chrono::time_point<steady_clock>;
              static constexpr bool is_steady = true;

              // get current time
              _NODISCARD static time_point now() noexcept 
              { 
                  // doesn't change after system boot
                  const long long _Freq = _Query_perf_frequency(); 
                  const long long _Ctr  = _Query_perf_counter();
                  static_assert(period::num == 1, "This assumes period::num == 1.");
                  const long long _Whole = (_Ctr / _Freq) * period::den;
                  const long long _Part  = (_Ctr % _Freq) * period::den / _Freq;
                  return time_point(duration(_Whole + _Part));
              }
          };

          通過以上源碼可以了解到在 steady_clock 類中的一些細節(jié)信息:

          • rep:時鐘周期次數(shù)是通過整形來記錄的 long long
          • period:一個時鐘周期是 1 納秒 nano
          • duration:時間間隔為 1 納秒 nanoseconds
          • time_point:時間點通過系統(tǒng)時鐘做了初始化 chrono::time_point<steady_clock>

          另外,在這個類中也提供了一個靜態(tài)的 now () 方法,用于得到當前的時間點,函數(shù)原型如下:

          static std::chrono::time_point<std::chrono::steady_clock> now() noexcept;

          假設(shè)要測試某一段程序的執(zhí)行效率,可以計算它執(zhí)行期間消耗的總時長,示例代碼如下:

          #include <chrono>
          #include <iostream>
          using namespace std;
          using namespace std::chrono;
          int main()
          {
              // 獲取開始時間點
              steady_clock::time_point start = steady_clock::now();
              // 執(zhí)行業(yè)務(wù)流程
              cout << "print 1000 stars ...." << endl;
              for (int i = 0; i < 1000; ++i)
              {
                  cout << "*";
              }
              cout << endl;
              // 獲取結(jié)束時間點
              steady_clock::time_point last = steady_clock::now();
              // 計算差值
              auto dt = last - start;
              cout << "總共耗時: " << dt.count() << "納秒" << endl;
          }

          3.3 high_resolution_clock

          high_resolution_clock 提供的時鐘精度比 system_clock 要高,它也是不可以修改的。在底層源碼中,這個類其實是 steady_clock 類的別名。

          using high_resolution_clock = steady_clock;

          因此 high_resolution_clock 的使用方式和 steady_clock 是一樣的,在此就不再過多進行贅述了。

          4. 轉(zhuǎn)換函數(shù)

          4.1 duration_cast

          duration_cast 是 chrono 庫提供的一個模板函數(shù),這個函數(shù)不屬于 duration 類。通過這個函數(shù)可以對 duration 類對象內(nèi)部的時鐘周期 Period,和周期次數(shù)的類型 Rep 進行修改,該函數(shù)原型如下:

          template <class ToDuration, class Rep, class Period>
            constexpr ToDuration duration_cast (const duration<Rep,Period>& dtn);

          在源周期能準確地為目標周期所整除的場合(例如小時到分鐘),浮點時長和整數(shù)時長間轉(zhuǎn)型能隱式進行無需使用 duration_cast ,其他情況下都需要通過函數(shù)進行轉(zhuǎn)換。

          我們可以修改一下上面測試程序執(zhí)行時間的代碼,在代碼中修改 duration 對象的屬性:

          #include <iostream>
          #include <chrono>
          using namespace std;
          using namespace std::chrono;

          void f()
          {
              cout << "print 1000 stars ...." << endl;
              for (int i = 0; i < 1000; ++i)
              {
                  cout << "*";
              }
              cout << endl;
          }

          int main()
          {
              auto t1 = steady_clock::now();
              f();
              auto t2 = steady_clock::now();

              // 整數(shù)時長:要求 duration_cast
              auto int_ms = duration_cast<chrono::milliseconds>(t2 - t1);

              // 小數(shù)時長:不要求 duration_cast
              duration<double, ratio<1, 1000>> fp_ms = t2 - t1;

              cout << "f() took " << fp_ms.count() << " ms, "
                  << "or " << int_ms.count() << " whole milliseconds\n";
          }

          示例代碼輸出的結(jié)果:

          print 1000 stars ....
          ****************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
          f() took 40.2547 ms, or 40 whole milliseconds

          4.2 time_point_cast

          time_point_cast 也是 chrono 庫提供的一個模板函數(shù),這個函數(shù)不屬于 time_point 類。函數(shù)的作用是對時間點進行轉(zhuǎn)換,因為不同的時間點對象內(nèi)部的時鐘周期 Period,和周期次數(shù)的類型 Rep 可能也是不同的,一般情況下它們之間可以進行隱式類型轉(zhuǎn)換,也可以通過該函數(shù)顯示的進行轉(zhuǎn)換,函數(shù)原型如下:

          template <class ToDuration, class Clock, class Duration>
          time_point<Clock, ToDuration> time_point_cast(const time_point<Clock, Duration> &t);

          關(guān)于函數(shù)的使用,示例代碼如下:

          #include <chrono>
          #include <iostream>
          using namespace std;

          using Clock = chrono::high_resolution_clock;
          using Ms = chrono::milliseconds;
          using Sec = chrono::seconds;
          template<class Duration>
          using TimePoint = chrono::time_point<Clock, Duration>;

          void print_ms(const TimePoint<Ms>& time_point)
          {
              std::cout << time_point.time_since_epoch().count() << " ms\n";
          }

          int main()
          {
              TimePoint<Sec> time_point_sec(Sec(6));
              // 無精度損失, 可以進行隱式類型轉(zhuǎn)換
              TimePoint<Ms> time_point_ms(time_point_sec);
              print_ms(time_point_ms);    // 6000 ms

              time_point_ms = TimePoint<Ms>(Ms(6789));
              // error,會損失精度,不允許進行隱式的類型轉(zhuǎn)換
              TimePoint<Sec> sec(time_point_ms);

              // 顯示類型轉(zhuǎn)換,會損失精度。6789 truncated to 6000
              time_point_sec = std::chrono::time_point_cast<Sec>(time_point_ms);
              print_ms(time_point_sec); // 6000 ms
          }

          注意事項:關(guān)于時間點的轉(zhuǎn)換如果沒有沒有精度的損失可以直接進行隱式類型轉(zhuǎn)換,如果會損失精度只能通過顯示類型轉(zhuǎn)換,也就是調(diào)用 time_point_cast 函數(shù)來完成該操作。

          文章鏈接: https://subingwen.com/cpp/chrono/

          瀏覽 25
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  天天综合天天做天天综合 | 老师的粉嫩小又紧水又多A片视频 | 青春草亚洲视频在线观看 | 无码人妻精品一区二区蜜桃在线 | 国产精品成人免费精品自在线观看 |