小巧、快速、可靠。
擇三。
SQL 語言表達式

1. 語法

表達式

literal-value bind-parameter schema-name . table-name . column-name unary-operator expr expr binary-operator expr function-name ( function-arguments ) filter-clause over-clause ( expr ) , CAST ( expr AS type-name ) expr COLLATE collation-name expr NOT LIKE GLOB REGEXP MATCH expr expr ESCAPE expr expr ISNULL NOTNULL NOT NULL expr IS NOT DISTINCT FROM expr expr NOT BETWEEN expr AND expr expr NOT IN ( select-stmt ) expr , schema-name . table-function ( expr ) table-name , NOT EXISTS ( select-stmt ) CASE expr WHEN expr THEN expr ELSE expr END raise-function

過濾子句

函式參數

字面值

over 子句

raise 函式

select 陳述式

類型名稱

2. 運算子與影響解析的屬性

SQLite 理解以下運算子,依優先順序1排列
(由上至下 / 由高至低)

運算子 2
[表達式]    + [表達式]    - [表達式]
[表達式] COLLATE (排序規則名稱) 3
||   ->   ->>
*   /   %
+   -
&  |   <<  >>
[表達式] ESCAPE [跳脫字元表達式] 4
<  >  <=  >=
=  ==  <>  !=  IS   IS NOT
IS DISTINCT FROM   IS NOT DISTINCT FROM
[表達式] BETWEEN5 [表達式] AND [表達式]
IN5  MATCH5  LIKE5  REGEXP5  GLOB5
[表達式] ISNULL  [表達式] NOTNULL   [表達式] NOT NULL
NOT [表達式]
AND
OR

  1. 顯示在同一個表格儲存格中的運算子具有相同的優先順序。
  2. "[表達式]" 表示非二元運算子的運算元位置。
      沒有 "[表達式]" 附加項的運算子是二元且左結合的。
  3. COLLATE 子句(及其排序規則名稱)作為單個後置運算子。
  4. ESCAPE 子句(及其跳脫字元)作為單個後置運算子。
      它只能綁定到前面的 [表達式] LIKE [表達式] 表達式。
  5. (BETWEEN IN GLOB LIKE MATCH REGEXP) 中的每個關鍵字都可以在前面加上 NOT,保留原始運算子的優先順序和結合性。
      

COLLATE 運算子是一個一元後置運算子,它將排序規則指派給表達式。COLLATE 運算子設定的排序規則會覆蓋資料表欄位定義中 COLLATE 子句所決定的排序規則。有關其他資訊,請參閱SQLite3 中的資料類型文件中關於排序規則的詳細討論

一元運算子+是一個無運算的運算子。它可以應用於字串、數字、BLOB 或 NULL,並且它總是返回與運算元值相同的結果。

請注意,等於和不等於運算子有兩種變體。等於可以是===。不等於運算子可以是!=<>||運算子「concatenate」會將兩個運算元的字串連接起來。->->>運算子為「extract」;它們會從 LHS 中提取 RHS 元件。範例請見 JSON 子元件提取

%運算子會將其兩個運算元 強制轉型 為 INTEGER 類型,然後計算左整數除以右整數的餘數。其他算術運算子,如果兩個運算元都是整數且不會導致溢位,則執行整數運算;如果任一運算元是實數值或整數運算會產生溢位,則根據 IEEE 標準 754 執行浮點運算。整數除法會產生向零捨去的整數結果。

除了||串接運算子和->->>提取運算子(它們可以返回任何類型的值)之外,任何二元運算子的結果都是數值或 NULL。

除了下述特定例外情況外,所有運算子在任何運算元為 NULL 時通常都會計算為 NULL。這符合 SQL92 標準。

與 NULL 配對時,
  AND在另一個運算元為 false 時計算為 0(false);而
  OR在另一個運算元為 true 時計算為 1(true)。

ISIS NOT運算子的運作方式類似於=!=,除非其中一個或兩個運算元為 NULL。在這種情況下,如果兩個運算元都是 NULL,則 IS 運算子計算為 1 (true),而 IS NOT 運算子計算為 0 (false)。如果一個運算元是 NULL 而另一個不是,則 IS 運算子計算為 0 (false),而 IS NOT 運算子為 1 (true)。IS 或 IS NOT 表達式不可能計算為 NULL。

IS NOT DISTINCT FROM運算子是IS運算子的另一種寫法。同樣地,IS DISTINCT FROM運算子的意思與IS NOT相同。標準 SQL 不支援精簡的 IS 和 IS NOT 表示法。這些精簡形式是 SQLite 的擴充功能。您必須在其他 SQL 資料庫引擎上使用冗長且可讀性較差的 IS NOT DISTINCT FROM 和 IS DISTINCT FROM 運算子。

3. 字面值(常數)

字面值表示常數。字面值可以是整數、浮點數、字串、BLOB 或 NULL。

整數和浮點數字面值(統稱為「數值字面值」)的語法如下圖所示

numeric-literal(數值字面值)

digit _ . E e digit _ . digit _ - digit _ + 0x 0X hexdigit _

如果數值字面值有小數點或指數子句,或者它小於 -9223372036854775808 或大於 9223372036854775807,則它是浮點數字面值。否則,它是整數字面值。浮點數字面值的指數子句開頭的「E」字元可以是大寫或小寫。「.」字元始終用作小數點,即使地區設定指定「,」作為此角色也是如此 - 使用「,」作為小數點會導致語法上的歧義。

從 SQLite 3.46.0 版 (2024-05-23) 開始,可以在任何兩個數字之間新增一個額外的底線 ("_") 字元。底線純粹是為了提高人類可讀性,SQLite 會忽略它們。

十六進位整數字面值遵循 C 語言的表示法,以 "0x" 或 "0X" 開頭,後接十六進位數字。例如,0x1234 與 4660 相同,而 0x8000000000000000 與 -9223372036854775808 相同。十六進位整數字面值會被解讀為 64 位元二補數整數,因此精度限制為十六個有效數字。SQLite 從 3.8.6 版 (2014-08-15) 開始支援十六進位整數。為了向下相容,只有 SQL 語言剖析器能理解 "0x" 十六進位整數表示法,型別轉換例程則無法理解。含有十六進位整數格式文字的字串變數,在透過 CAST 運算式欄位親和性 轉換為整數值時,或是執行數值運算或任何其他執行時期轉換之前,都不會被解讀為十六進位整數。將十六進位整數格式的字串值強制轉換為整數值時,轉換過程會在遇到 'x' 字元時停止,因此產生的整數值永遠為零。SQLite 僅在 SQL 陳述式文字中出現十六進位整數表示法時才能理解,而當它作為資料庫內容的一部分出現時則無法理解。

字串常數是由將字串用單引號 (') 括起來所組成。字串中的單引號可以透過連續兩個單引號來編碼,就像 Pascal 語言一樣。不支援使用反斜線字元的 C 語言風格跳脫字元,因為它們不符合標準 SQL。

BLOB 字面值是包含十六進位資料並以單個 "x" 或 "X" 字元為前綴的字串字面值。範例:X'53514C697465'

字面值也可以是標記 "NULL"。

4. 參數

「變數」或「參數」標記指定運算式中在執行階段使用 sqlite3_bind() 系列 C/C++ 介面填入值的佔位符。參數可以採用幾種形式:

?NNN 問號後接數字 NNN 代表第 NNN 個參數的佔位符。NNN 必須介於 1 和 SQLITE_MAX_VARIABLE_NUMBER 之間。
? 後面沒有接數字的問號會建立一個參數,其編號比已指派的最大的參數編號大一。如果這表示參數編號大於 SQLITE_MAX_VARIABLE_NUMBER,則會發生錯誤。提供這種參數格式是為了與其他資料庫引擎相容。但由於容易算錯問號數量,因此不建議使用這種參數格式。鼓勵程式設計師使用以下其中一種符號格式或上述的 ?NNN 格式。
:AAAA 冒號後接識別碼名稱代表名為 :AAAA 的 具名參數 的佔位符。具名參數也有編號。指派的編號比已指派的最大的參數編號大一。如果這表示參數將被指派一個大於 SQLITE_MAX_VARIABLE_NUMBER 的編號,則會發生錯誤。為避免混淆,最好避免混合使用具名參數和編號參數。
@AAAA 「@」符號的功能與冒號完全相同,只是建立的參數名稱為 @AAAA。
$AAAA 錢字符號後接識別碼名稱也代表名為 $AAAA 的具名參數的佔位符。在這種情況下,識別碼名稱可以包含一個或多個 "::",以及用 "(...)" 括起來的後綴,其中可以包含任何文字。這種語法是 Tcl 程式語言 中變數名稱的形式。這種語法的存在是因為 SQLite 實際上是一個已經「逃逸到野外」的 Tcl 擴充功能

未透過 sqlite3_bind() 指派值的參數會被視為 NULL。可以使用 sqlite3_bind_parameter_index() 介面將符號參數名稱轉換為其對應的數字索引。

最大參數數量在編譯時期由 SQLITE_MAX_VARIABLE_NUMBER 巨集設定。個別的 資料庫連線 D 可以使用 sqlite3_limit(D, SQLITE_LIMIT_VARIABLE_NUMBER,...) 介面將其最大參數數量減少到低於編譯時期的最大值。

5. LIKE、GLOB、REGEXP、MATCH 和提取運算子

LIKE 運算子會執行模式匹配比較。LIKE 運算子右側的運算元包含模式,而左側的運算元包含要與模式匹配的字串。LIKE 模式中的百分比符號 ("%") 匹配字串中零個或多個字元的任何序列。LIKE 模式中的底線 ("_") 匹配字串中的任何單個字元。任何其他字元都匹配自身或其大小寫等效字元(即不區分大小寫的匹配)。重要注意事項:SQLite 預設情況下僅理解 ASCII 字元的大小寫。對於超出 ASCII 範圍的 Unicode 字元,LIKE 運算子預設區分大小寫。例如,表達式 'a' LIKE 'A' 為 TRUE,但 'æ' LIKE 'Æ' 為 FALSE。SQLite 的 ICU 擴展包含增強版的 LIKE 運算子,可在所有 Unicode 字元上進行大小寫摺疊。

如果存在可選的 ESCAPE 子句,則 ESCAPE 關鍵字後面的表達式必須計算結果為一個單個字元的字串。此字元可用於 LIKE 模式中,以包含字面百分比或底線字元。逸出字元後跟百分比符號 (%)、底線 (_) 或逸出字元本身的第二個實例分別匹配字面百分比符號、底線或單個逸出字元。

中綴 LIKE 運算子是透過呼叫應用程式定義的 SQL 函數 like(Y,X)like(Y,X,Z) 來實現的。

可以使用 case_sensitive_like pragma 使 LIKE 運算子區分大小寫。

GLOB 運算子類似於 LIKE,但使用 Unix 檔案通配符語法作為其通配符。此外,與 LIKE 不同,GLOB 區分大小寫。GLOB 和 LIKE 的前面都可以加上 NOT 關鍵字來反轉測試的意義。中綴 GLOB 運算子是透過呼叫函數 glob(Y,X) 來實現的,並且可以透過覆寫該函數來修改。

REGEXP 運算子是 regexp() 使用者函數的特殊語法。預設情況下未定義 regexp() 使用者函數,因此使用 REGEXP 運算子通常會導致錯誤訊息。如果在執行階段添加名為 "regexp" 的 應用程式定義的 SQL 函數,則 "X REGEXP Y" 運算子將作為對 "regexp(Y,X)" 的呼叫來實現。

MATCH 運算子是 match() 應用程式定義函數的特殊語法。預設的 match() 函數實現會引發異常,實際上沒有任何用處。但擴展可以覆寫 match() 函數以提供更有用的邏輯。

提取運算子充當函數 "->"() 和 "->>"() 的特殊語法。這些函數的預設實現會執行 JSON 子元件提取,但擴展可以將它們覆寫以用於其他目的。

6. BETWEEN 運算子

BETWEEN 運算子在邏輯上等效於一對比較。"x BETWEEN y AND z" 等效於 "x>=y AND x<=z",不同之處在於使用 BETWEEN 時,x 表達式只計算一次。

7. CASE 表達式

CASE 表達式的作用類似於其他程式語言中的 IF-THEN-ELSE。

CASE 關鍵字和第一個 WHEN 關鍵字之間出現的可選表達式稱為「基底」表達式。CASE 表達式有兩種基本形式:有基底表達式和沒有基底表達式。

在沒有基礎表達式 (base expression) 的 CASE 陳述式中,會從最左邊開始依序評估每個 WHEN 表達式的結果,並將結果視為布林值。CASE 表達式的結果是第一個評估為 true 的 WHEN 表達式對應的 THEN 表達式的評估結果。或者,如果沒有任何 WHEN 表達式評估為 true,則結果為 ELSE 表達式(如果有的話)的評估結果。如果沒有 ELSE 表達式,且沒有任何 WHEN 表達式為 true,則整體結果為 NULL。

在評估 WHEN 條件時,NULL 結果會被視為 false。

在具有基礎表達式的 CASE 陳述式中,基礎表達式只會評估一次,其結果會與從左到右的每個 WHEN 表達式的評估結果進行比較。CASE 表達式的結果是第一個比較結果為 true 的 WHEN 表達式對應的 THEN 表達式的評估結果。或者,如果沒有任何 WHEN 表達式的評估值等於基礎表達式,則結果為 ELSE 表達式(如果有的話)的評估結果。如果沒有 ELSE 表達式,且沒有任何 WHEN 表達式的結果等於基礎表達式,則整體結果為 NULL。

當比較基礎表達式和 WHEN 表達式時,會套用與將基礎表達式和 WHEN 表達式分別作為=運算子的左運算元和右運算元時相同的排序序列、親和性和 NULL 處理規則。

如果基礎表達式為 NULL,則 CASE 的結果永遠是 ELSE 表達式(如果存在)的評估結果,如果不存在 ELSE 表達式,則結果為 NULL。

兩種形式的 CASE 表達式都使用惰性求值或短路求值。

以下兩個 CASE 表達式的唯一區別在於,第一個範例中的 *x* 表達式只會評估一次,而第二個範例中可能會評估多次

內建的 iif(x,y,z) SQL 函式 在邏輯上等同於「CASE WHEN x THEN y ELSE z END」。 iif() 函式可在 SQL Server 中找到,並包含在 SQLite 中以確保相容性。有些開發人員偏好使用 iif() 函式,因為它更簡潔。

8. IN 和 NOT IN 運算子

IN 和 NOT IN 運算子接受左邊的表達式和右邊的值列表或子查詢。當 IN 或 NOT IN 運算子的右運算元是子查詢時,子查詢的欄位數必須與左運算元的 列值 的欄位數相同。如果左表達式不是 列值 表達式,則 IN 或 NOT IN 運算子右側的子查詢必須是純量子查詢。如果 IN 或 NOT IN 運算子的右運算元是值列表,則每個值都必須是純量,左表達式也必須是純量。IN 或 NOT IN 運算子的右側可以是表格 *名稱* 或 表格值函式 *名稱*,在這種情況下,右側被理解為形式為「(SELECT * FROM *名稱*)」的子查詢。當右運算元是空集合時,IN 的結果為 false,NOT IN 的結果為 true,無論左運算元為何,即使左運算元為 NULL 也是如此。

IN 或 NOT IN 運算子的結果由以下矩陣決定

左運算元
為 NULL
右運算元
包含 NULL
右運算元
為空集合
左運算元存在於
右運算元內
IN 運算子的
結果
IN 運算子的
NOT IN 運算子的結果
falsetrue
無關緊要falsetrue
無關緊要truefalse
NULLNULL
無關緊要無關緊要NULLNULL

請注意,SQLite 允許 IN 或 NOT IN 運算子右側括號中的純量值列表為空列表,但大多數其他 SQL 資料庫引擎和 SQL92 標準要求列表中至少包含一個元素。

9. 資料表欄位名稱

欄位名稱可以是 CREATE TABLE 陳述式中定義的任何名稱,或是下列特殊識別碼之一:「ROWID」、「OID」或「_ROWID_」。這三個特殊識別碼描述與每個資料表的每一列關聯的唯一整數鍵值(rowid),因此在 WITHOUT ROWID 資料表中無法使用。如果 CREATE TABLE 陳述式沒有定義具有相同名稱的實際欄位,則特殊識別碼才會參考列鍵值。rowid 的使用方式與一般欄位相同。

10. EXISTS 運算子

EXISTS 運算子永遠會評估為整數值 0 或 1。如果執行指定為 EXISTS 運算子右側運算元的 SELECT 陳述式會傳回一列或多列,則 EXISTS 運算子會評估為 1。如果執行 SELECT 不會傳回任何列,則 EXISTS 運算子會評估為 0。

SELECT 陳述式(如果有的話)傳回的每一列中的欄位數和傳回的特定值不會影響 EXISTS 運算子的結果。尤其,包含 NULL 值的列與不包含 NULL 值的列的處理方式沒有不同。

11. 子查詢表達式

用括號括起來的 SELECT 陳述式是一個子查詢。所有類型的 SELECT 陳述式,包括聚合和複合 SELECT 查詢(帶有 UNION 或 EXCEPT 等關鍵字的查詢)都允許作為純量子查詢。子查詢表達式的值是括起來的 SELECT 陳述式結果的第一列。如果括起來的 SELECT 陳述式沒有傳回任何列,則子查詢表達式的值為 NULL。

傳回單一欄位的子查詢是純量子查詢,幾乎可以在任何地方使用。傳回兩個或多個欄位的子查詢是列值子查詢,只能用作比較運算子的運算元,或用作 UPDATE SET 子句中欄位名稱列表大小相同的子句值。

12. 相關子查詢

用作純量子查詢或 IN、NOT IN 或 EXISTS 表達式右側運算元的 SELECT 陳述式可能包含對外部查詢中欄位的參考。這樣的子查詢稱為相關子查詢。每次需要其結果時,都會重新評估相關子查詢。非相關子查詢只會評估一次,並根據需要重複使用結果。

13. CAST 表達式

格式為「CAST(expr AS type-name)」的 CAST 表達式用於將 expr 的值轉換為由 type-name 指定的不同 儲存類別。CAST 轉換類似於將 欄位親和性 套用至值時發生的轉換,不同之處在於使用 CAST 運算子時,即使轉換會造成損失且不可逆,轉換也會進行,而欄位親和性僅在變更不會造成損失且可逆的情況下才會變更值的資料類型。

如果 expr 的值為 NULL,則 CAST 表達式的結果也為 NULL。否則,結果的儲存類別是透過將 用於判斷欄位親和性的規則套用至 type-name 來決定的。

type-name 的親和性轉換處理
將值轉換為沒有親和性的 類型名稱 會導致該值被轉換為 BLOB。轉換為 BLOB 的過程包含先將值轉換為資料庫連線 編碼 的 TEXT,然後將產生的位元組序列解讀為 BLOB,而不是 TEXT。
TEXT要將 BLOB 值轉換為 TEXT,組成 BLOB 的位元組序列會被解讀為使用資料庫編碼編碼的文字。

將 INTEGER 或 REAL 值轉換為 TEXT 會呈現該值,如同透過 sqlite3_snprintf() 進行轉換,但產生的 TEXT 使用資料庫連線的 編碼

REAL將 BLOB 值轉換為 REAL 時,該值會先轉換為 TEXT。

將 TEXT 值轉換為 REAL 時,會從 TEXT 值中擷取可解讀為實數的最長可能前綴,並忽略其餘部分。從 TEXT 轉換為 REAL 時,TEXT 值中的任何前導空格都會被忽略。如果沒有可解讀為實數的前綴,則轉換結果為 0.0。

INTEGER將 BLOB 值轉換為 INTEGER 時,該值會先轉換為 TEXT。

將 TEXT 值轉換為 INTEGER 時,會從 TEXT 值中擷取可解讀為整數的最長可能前綴,並忽略其餘部分。從 TEXT 轉換為 INTEGER 時,TEXT 值中的任何前導空格都會被忽略。如果沒有可解讀為整數的前綴,則轉換結果為 0。如果前綴整數大於 +9223372036854775807,則轉換結果正好是 +9223372036854775807。同樣地,如果前綴整數小於 -9223372036854775808,則轉換結果正好是 -9223372036854775808。

轉換為 INTEGER 時,如果文字看起來像帶有指數的浮點值,則指數將被忽略,因為它不是整數前綴的一部分。例如,「CAST('123e+5' AS INTEGER)」的結果是 123,而不是 12300000。

CAST 運算子只理解十進位整數 — 十六進位整數 的轉換會在十六進位整數字串的「0x」前綴中的「x」處停止,因此 CAST 的結果始終為零。

將 REAL 值轉換為 INTEGER 會產生介於 REAL 值和零之間,且最接近 REAL 值的整數。如果 REAL 大於可能的最大帶符號整數 (+9223372036854775807),則結果為最大的可能帶符號整數;如果 REAL 小於可能的最小帶符號整數 (-9223372036854775808),則結果為最小的可能帶符號整數。

在 SQLite 3.8.2 版 (2013-12-06) 之前,將大於 +9223372036854775807.0 的 REAL 值轉換為整數會產生最小的負整數 -9223372036854775808。這種行為旨在模擬 x86/x64 硬體在進行等效轉換時的行為。

NUMERIC將 TEXT 或 BLOB 值轉換為 NUMERIC 會產生 INTEGER 或 REAL 結果。如果輸入文字看起來像整數(沒有小數點或指數),且值小到足以容納在 64 位元帶符號整數中,則結果將為 INTEGER。如果輸入文字看起來像浮點數(有小數點和/或指數),且文字描述的值可以在 IEEE 754 64 位元浮點數和 51 位元帶符號整數之間無損地來回轉換,則結果為 INTEGER。(在上一句中,指定了 51 位元整數,因為它比 IEEE 754 64 位元浮點數的尾數長度少一位元,因此為文字到浮點數的轉換操作提供了一位元的邊界。)任何描述超出 64 位元帶符號整數範圍的值的文字輸入都會產生 REAL 結果。

將 REAL 或 INTEGER 值轉換為 NUMERIC 是一個空操作,即使實數值可以無損地轉換為整數也是如此。

請注意,將任何非 BLOB 值轉換為 BLOB 的結果,以及將任何 BLOB 值轉換為非 BLOB 值的結果,可能會根據資料庫編碼是 UTF-8、UTF-16be 或 UTF-16le 而有所不同。

14. 布林運算式 (Boolean Expressions)

SQL 語言在幾個情況下會評估運算式並將結果轉換為布林值(真或假)。這些情況包括:

為了將 SQL 運算式的結果轉換為布林值,SQLite 首先會將結果轉換為數值 (NUMERIC),方式與CAST 運算式相同。數值零(整數值 0 或實數值 0.0)被視為假。NULL 值仍然是 NULL。所有其他值都被視為真。

例如,值 NULL、0.0、0、'english' 和 '0' 都被視為假。值 1、1.0、0.1、-0.1 和 '1english' 則被視為真。

從 SQLite 3.23.0 (2018-04-02) 開始,SQLite 識別識別符號 "TRUE" 和 "FALSE" 為布林字面值,前提是這些識別符號尚未用於其他含義。如果已經存在名為 TRUE 或 FALSE 的欄位、表格或其他物件,則為了向後相容性,TRUE 和 FALSE 識別符號會引用這些其他物件,而不是布林值。

布林識別符號 TRUE 和 FALSE 通常只是整數值 1 和 0 的別名。但是,如果 TRUE 或 FALSE 出現在 IS 運算子的右側,則 IS 運算子會將左側運算元評估為布林值並返回適當的答案。

15. 函式 (Functions)

SQLite 支援許多簡單聚合視窗 SQL 函式。為了便於說明,簡單函式會進一步細分為核心函式日期時間函式數學函式JSON 函式。應用程式可以使用sqlite3_create_function()介面新增以 C/C++ 編寫的新函式。

上面的主要運算式泡泡圖顯示了所有函式呼叫的單一語法。但这仅仅是为了简化表达式泡泡图。实际上,每种类型的函数都有略微不同的语法,如下所示。主表达式泡泡图中显示的函数调用语法是此处显示的三种语法的并集

簡單函式呼叫 (simple-function-invocation)

simple-func ( expr ) , *

聚合函式呼叫 (aggregate-function-invocation)

aggregate-func ( DISTINCT expr ) filter-clause , * ORDER BY ordering-term ,

視窗函式呼叫 (window-function-invocation)

window-func ( expr ) filter-clause OVER window-name window-defn , *

OVER 子句是視窗函式所需的,其他情況下則禁止使用。DISTINCT 關鍵字和 ORDER BY 子句僅允許在聚合函式中使用。FILTER 子句不能出現在簡單函式上。

只要兩種形式的函式的引數數量不同,就可以讓聚合函式與簡單函式同名。例如,具有一個引數的max()函式是聚合函式,而具有兩個或多個引數的max()函式是簡單函式。聚合函式通常也可以用作視窗函式。

本頁面最後修改時間:世界協調時間 2024-06-02 10:08:16