APLデータバインディングの構文


APLデータバインディングの構文

ドキュメントを表示するときにAlexaが評価する式を記述するには、データバインディングの構文を使用します。データバインディング式を使用して、コンポーネントプロパティをデータソースにバインドしたり、条件ロジックを記述してViewportの特性などの条件に応じてコンポーネントを表示または非表示にできます。

データバインディング式について

JSON文字列内でデータバインディング式を使用します。データバインディング式の形式は"${expression}"です。文字列内では、"${2}+${2} = ${2+2}"のように、任意の数の式を記述できます。

式は現在のデータバインディングコンテキスト内で評価されます。データバインディングコンテキストはブール値、数値、文字列、配列、オブジェクト、null値、および定義済みのリソースへの参照をサポートするグローバルディクショナリーです。

サポートされる値の型

識別子

識別子は、データバインディング変数を識別するために使用される名前です。識別子は、C識別子の命名規則である[a-zA-Z_][a-zA-Z0-9_]*に従う必要があります。つまり、識別子は大文字または小文字のASCII文字かアンダースコアで始まり、その後に0文字以上のASCII文字、数字、またはアンダースコアを続けます。

${data}
${_myWord23}
${__AnUgly26_letter__examplE}

文字列リテラル

文字列は一重または二重の引用符を使用して定義します。開始と終了の引用符はセットで使用する必要があります。引用符、キャリッジリターン、改行はエスケープ処理で表現できます。

	${"Double-quoted string"}
	${'Single-quoted string'}
	${"Inner quote: \" or '"}

式は文字列の中に入れ子にすることもできます。

	${"2たす2は${2+2}"}

数値

正の数、負の数、および浮動小数点数がサポートされます。指数表記には対応していません。すべての数値は内部で倍精度浮動小数点数として格納されます。

${1}
${-34.75}
${64000000000}

ブール値

truefalseのブール値がサポートされています。

	${true}
	${false}

null

null定数がサポートされています。

	${null}

配列

カンマで区切られた式のリストを角括弧で囲んで指定することで、データバインディング式の中で配列を定義します。

${["A", "B", "C"].length}     // 3に評価
${["on","off"][0]}            // "on"に評価
${[true,"true",1][0]}         // ブール値のtrueに評価

リソース

定義済みのリソースをデータバインディングコンテキストで表すには予約文字「@」を使用します。次に例を示します。

${@myBlue}
${@isLandscape ? @myWideValue : @myNarrowValue}

データバインディングで使用されるリソースはAPLパッケージで定義します。

絶対ディメンション

ディメンションサフィックスは、数値を絶対Viewportディメンションに変換します。有効なディメンションサフィックスは“dp”、“px”、“vh”、“vw”です。

${23 dp}     // 23画面非依存ピクセル
${10 px}     // 10ピクセル
${50 vw}     // Viewportの幅の50%
${100vh}     // Viewportの高さの100%

ディメンションサフィックスは、より複雑な式ではなく数値に付加する必要があります。たとえば、${(20*20) dp}は有効ではありませんが、${20*20dp}は有効です。

Truthyと強制

データバインディングの式には、さまざまな種類があります。これらの型は他の型に変換できます。次の表は、さまざまな変換の例を示しています(いずれの場合も、Viewport幅が512dpで320dpiとします)。

オブジェクト ブール値 数値 文字列 ディメンション
Null null false 0 "" transparent 0dp
ブール値 true true 1 "true" transparent 0dp
ブール値 false false 0 "false" transparent 0dp
数値 23 true 23 "23" #00000017 23dp
数値 0 false 0 "0" transparent 0dp
文字列 "私の犬" true 0 "私の犬" transparent 0dp
文字列 "" false 0 "" transparent 0dp
文字列 "-2.3" true -2.3 "-2.3" transparent -2.3dp
文字列 "red" true 0 "red" #ff0000ff 0dp
文字列 "50vw" true 50 "50vw" transparent 256dp
配列 [] true 0 "" transparent 0dp
マップ {} true 0 "" transparent 0dp
red true 0 "#ff0000ff" #ff0000ff 0dp
ディメンション 32px true 16 "16dp" transparent 16dp
ディメンション 0vh false 0 "0dp" transparent 0dp
ディメンション 23% true 0.23 "23%" transparent 23%
ディメンション 0% false 0 "0%" transparent 0%
ディメンション auto true 0 "auto" transparent auto
その他 ... true 0 "" transparent 0dp
             

ブール値の強制

truthy値とは、真偽を判定するコンテキストで評価したときにtrueと見なされる値です。false0""、ゼロの絶対ディメンションまたは相対ディメンション、nullを除いて、すべての値はtruthyです。

数値の強制

ブール値の「true」値は数値1に変換されます。文字列値はC++のstd::stodメソッドを使用して変換されます(ロケールの影響を受けます)。絶対ディメンションは絶対ディメンションのdpの数値に変換されます。相対ディメンションはパーセント値に変換されます(例:32% - > 0.32)。それ以外の場合はすべて0に変換されます。

文字列の強制

次の表の規則に従って、内部の型が文字列に変換されます。

オブジェクト 結果 説明
Null null '' null値は表示されません。
ブール値 true false 'true' 'false' ブール値のtrueとfalseは文字列として表示されます。
数値 -23 '-23' 整数では、小数点以下の桁は表示されません。
  1/3 '0.333333' 非整数では、小数点以下の桁が表示されます。
文字列 "My "dog" " 'My "dog" ' 文字列値
配列 [...] '' 配列は表示されません。
マップ {...} '' マップは表示されません。
red '#ff0000ff' 色は#rrggbbaa形式で表示されます。
ディメンション 23 dp '20dp' 絶対ディメンションは、サフィックス「dp」を付けて表示されます。
ディメンション 20 % '20%' パーセントディメンションは、サフィックス「%」を付けて表示されます。
ディメンション auto 'auto' 自動ディメンションは「auto」として表示されます。
その他 ${Math.min} '' 数学関数は表示されません。

非整数の具体的な形式は定義されていませんが、sprintf(buf, "%f", value)のC++規格に厳密に従ってください。ロケールに応じて変わる可能性があります。

色の強制

色値は、32ビットのRGBA値として内部的に格納されています。数値は32ビットの符号なし整数として扱われ、直接変換されます。文字列値は、色のデータ型の規則に従って解析されます。

絶対ディメンションの強制

数値は「dp」単位の測定値であると見なされ、絶対ディメンションに変換されます。文字列値は、ディメンションのデータ型の規則に従って解析されます。他のすべての値は0です。

相対ディメンションの強制

数値は割合と見なされ、直接変換されます。たとえば、0.5は50%に変換されます。文字列は、ディメンションのデータ型の規則に従って解析されます。他のすべての値は0です。

演算子

APLでは、算術、論理、比較、三項の演算子をサポートしています。

算術演算子

加算、減算、乗算、除算、剰余の標準的な算術演算子がサポートされています。

${1+2}  // 3
${1-2}  // -1
${1*2}  // 2
${1/2}  // 0.5
${1%2}  // 1.

加算と減算は、数値のペア、絶対ディメンション、相対ディメンションに対して機能します。数値が絶対ディメンションまたは相対ディメンションのいずれかと組み合わされると、その数値は適切なディメンションに強制変換されます。

加算演算子は、左オペランドか右オペランドが文字列の場合、文字列を連結する演算子としても使用できます。

${27+''}     // '27'
${1+' dog'}  // '1 dog'
${'have '+3} // 'have 3'

乗算演算子、除算演算子、剰余演算子は、数値のペアに対して機能します。一方のオペランドがディメンション(相対または絶対)で、もう一方のオペランドが数値の場合も、乗算は機能します。結果はディメンションとなります。第1オペランドがディメンション(相対または絶対)で、第2オペランドが数値の場合も、除算は機能します。結果はディメンションとなります。

剰余演算子はJavaScriptの場合と同じように機能します。つまり、次のようになります。

${10 % 3}  // 1
${-1 % 2}  // -1
${3 % -6}  // 3
${6.5 % 2} // 0.5

論理演算子

標準的なAND、OR、NOTの論理演算子がサポートされています。

${true || false}   // true
${true && false}   // false
${!true}           // false

&&は、第1オペランドがfalseと見なされる場合は第1オペランドを返し、そうでない場合は第2オペランドを返します。||演算子は、第1オペランドがtrueと見なされる場合は第1オペランドを返し、そうでない場合は第2オペランドを返します。

${7 && 2}    // 2
${null && 3} // null
${7 || 2}    // 7
${0 || -16}  // -16

比較演算子

比較演算子はブール値を返します。

${1 < 2}
${75 <= 100}
${3 > -1}
${4 >= 4}
${myNullValue == null}
${(2>1) == true}
${1 != 2}

比較演算子は配列やオブジェクトには適用しません。

比較演算子は、型の強制を行いません。たとえば、${1=='1'}はfalseを返します。次の表は、有効な比較の一覧です。他のすべての比較はfalseを返します。

比較タイプ <, >, <=, >= ==, != 備考
数値から数値 有効 有効  
数値から絶対ディメンション 有効 有効 この数値は、画面に依存しないピクセルディメンションとして扱われます。
数値から相対ディメンション 有効 有効 数値はパーセンテージとして扱われます。たとえば、0.4は40%を表します。
相対ディメンションから相対ディメンション 有効 有効 ディメンションはパーセンテージとして比較されます。
絶対ディメンションから絶対ディメンション 有効 有効 ディメンションは画面に依存しないピクセルに変換されます。
文字列から文字列 有効 有効  
ブーリアンからブーリアン False 有効  
色から色 False 有効  
NullからNull False 有効 ${null==null}は真です
AutoディメンションからAutoディメンション False 有効 2つのAutoディメンションが等しい(最終サイズが等しくない場合でも)
その他すべての組み合わせ False False  

APLの==演算子は、JavaScriptの===演算子に似ています。

Null合体

??演算子はnull合体演算子です。オペランドがnullでない場合は左オペランドを返し、そうでない場合は右オペランドを返します。null合体演算子は、連鎖させることができます。

${person.name ?? person.surname ?? 'Hey, you!'}

null合体演算子は、null以外であれば、左オペランドを返します。

${1==2 ?? 'Dog'}   // falseが返される
${1==2 || 'Dog'}   // 「Dog」が返される

三項演算子

三項条件演算子${a ? b : c}は左オペランドを評価します。trueまたはtruthy値と評価した場合は、中央のオペランドを返します。そうでない場合は、右オペランドを返します。

${person.rank > 8 ? 'General' : 'Private'}

配列とオブジェクトへのアクセス

配列

配列へのアクセスには[]演算子を使用します。この場合、オペランドは整数でなければなりません。配列は、配列の長さを返す.length演算子もサポートしています。配列の範囲外の要素にアクセスすると、nullを返します。

${myArray[4]}     // 配列の5番目の要素(0から始まるインデックス)
${myArray.length} // 配列の長さ
${myArray[-1])}   // 配列の最後の要素
${myArray[myArray.length]}  // nullが返される(範囲外)

負のインデックスを渡すと、配列を後ろから数えます。

${a[-1] == a[a.length - 1]}  // True

オブジェクト

オブジェクトはドット演算子と[]配列アクセス演算子での文字列値の使用をサポートします。

${myObject.name}    // myObjectの「name」プロパティ
${myObject['name']} // myObjectの「name」プロパティ

プロパティを定義していない場合は、nullが返されます。

nullに対してドットまたは[]演算子を呼び出すとnullを返します。

${myNullObject.address.zipcode}  // nullが返される

ドット演算子の右オペランドは、有効な識別子でなければなりません。

関数の呼び出し

データバインディングではビルトイン関数の一部をサポートしています。関数は次の形式を使用します。

functionName( arg1, arg2, … )

関数には引数は必要ありません。関数は単一の値を返します。以下は、さまざまな関数式の例です。

${Math.floor(1.1)}       // 1
${Math.ceil(1.2)}        // 2
${Math.round(1.2)}       // 1
${Math.min(1,2,3,4)}     // 1
${Math.max(1,2,3,4)}     // 4
${String.toUpperCase('Hello')}    // HELLO
${String.toLowerCase('Hello')}    // hello
${String.slice('Hello', 1, -1)}   // ell

使用可能な関数は、トップレベルのプロパティ別にグループ化されています。

配列関数

トップレベルのArrayプロパティは、配列を操作するための関数と定数のコレクションです。

列では、配列a = [101,102,103,104,105,106]を使用しています。

関数 説明
Array.indexOf(x,y) 配列xの要素yのインデックスです。配列xyが含まれない場合は、-1を返します。 ${Array.indexOf(a,102)} == 1
Array.range(start, end, [step])

startから開始し、stepごとに増加し、endの前に終了する数値要素の配列を返します。

  • stepは任意で、デフォルトは1です。
  • startは任意であり、デフォルトはゼロです。stepを省略しても、endだけは指定する必要があります。たとえば、Array.range(10)は、0~9の数値配列を返します。
  • Array.range(0)Array.range(10,3)Array.range(1,5,0)など、無効な引数を渡した場合は空白の配列を返します。

${Array.range(1,10,2)} == [1,3,5,7,9]

${Array.range(2,5)} == [2,3,4]

${Array.range(10)} == [0,1,2,3,4,5,6,7,8,9

Array.slice(array,start[,end])

インデックスstartから開始してインデックスendまで徐々に増加する(endは含まない)arrayの一部を返します。

  • endは任意であり、デフォルトは配列の長さです。
  • 負のstartend値は、配列の末尾から選択されます。
${Array.slice(a,3)} == [104,105,106]
${Array.slice(a,1,3)} == [102,103]
${Array.slice(a,-2)} == [105,106]

数学関数

トップレベルのMathプロパティは、数値を計算するための関数と定数のコレクションです。

関数 説明

Math.abs(x)

xの絶対値です。

${Math.abs(-2.3)} == 2.3

Math.acos(x)

xのアークコサインです。

${Math.acos(1)} == 0

Math.acosh(x)

xの双曲線アークコサインです。

${Math.acosh(1)} == 0

Math.asin(x)

xのアークサインです。

${Math.asin(0)} == 0

Math.asinh(x)

xの双曲線アークサインです。

${Math.asinh(2)} == 1.4436354751788103

Math.atan(x)

xのアークタンジェントです。

${Math.atan(1)} == 0.7853981633974483

Math.atanh(x)

xの双曲線アークタンジェントです。

${Math.atanh(0.5)} == 0.5493061443340548

Math.atan2(y,x)

y/xのアークタンジェントです。

${Math.atan2(1,0)} == 1.5707963267948966

Math.cbrt(x)

xの立法根です。

${Math.cbrt(8)} == 2

Math.ceil(x)

x以上の最小の整数です。

${Math.ceil(2.3)} == 3

Math.clamp(x,y,z)

y<xの場合はx、y>zの場合はz、それ以外の場合はyを返します。

${Math.clamp(1, 22.3,10)} == 10

Math.cos(x)

xのコサインです。

${Math.cos(0)} == 1

Math.cosh(x)

xの双曲線コサインです。

${Math.cosh(0)} == 1

Math.exp(x)

eのx乗です。eはオイラー定数です。

${Math.exp(1)} == 2.718281828459045

Math.exp2(x)

2のx乗です。

${Math.exp2(5)} == 32

Math.expm1(x)

eのx乗マイナス1です。

${Math.expm1(1)} == 1.718281828459045

Math.float(x)

xを浮動小数点数に変換します。末尾の「%」文字はパーセンテージを表します。

${Math.float('23.4')} == 23.4
${Math.float('23.4%')} == 0.234

Math.floor(x)

x以下の最大の整数です。

${Math.floor(2.3)} == 2

Math.hypot(x1,x2,…)

引数の2乗の和の平方根です。

${Math.hypot(3,4)} == 5

Math.int(x,[b=10])

xを整数に変換します。bは文字列変換に使用するオプションの基数です。0で自動検出するか、2~36で基数を定義します。

${Math.int('23.3')} == 23
${Math.int('20', 16)} == 32
${Math.int('0x20', 0)} == 32

Math.isFinite(x)

xが有限の場合はtrueを返します。xが無限か、非数(NaN)の場合は、falseを返します。

${Math.isFinite(1.0)} == true
${Math.isFinite(1/0)} == false
${Math.isFinite(0/0)} == false

Math.isInf(x)

xが無限の場合はtrueを返します。

${Math.isInf(1/0)} == true
${Math.isInf(-1/0)} == true
${Math.isInf(0/0)} == false
${Math.isInf(1.0)} == false

Math.isNaN(x)

xが非数(NaN)の場合はtrueを返します。

${Math.isNaN(0/0)} == true
${Math.isNaN(1/0)} == false
${Math.isNaN(1.0)} == false

Math.log(x)

xの自然対数です。

${Math.log(Math.E)} == 1

Math.log1p(x)

1+xの自然対数です。

${Math.log1p(1)} == 0.6931471805599453

Math.log10(x)

xの底を10とする対数です。

${Math.log10(100)} == 2

Math.log2(x)

xの底を2とする対数です。

${Math.log2(32)} == 5

Math.max(x1,x2,…)

最大の引数です。

${Math.max(2,3)} == 3

Math.min(x1,x2,…)

最小の引数です。

${Math.min(2,3)} == 2

Math.pow(x,y)

xのy乗です。

${Math.pow(3,4)} == 81

Math.random()

0と1の間の乱数です。

${Math.random()} == 0.7113654073137101(実際に返される乱数はこれとは異なります)

Math.round(x)

xに最も近い整数を返します。

${Math.round(2.3)} == 2

Math.sign(x)

xのサインです。-1、0、+1のいずれかになります。

${Math.sign(-43.1) == -1

Math.sin(x)

xのサインです。

${Math.sin(Math.PI/6)} == 0.5

Math.sinh(x)

xの双曲線サインです。

${Math.sinh(1)} == 1.1752011936438014

Math.sqrt(x)

xの平方根です。

${Math.sqrt(9)} == 3

Math.tan(x)

xのタンジェントです。

${Math.tan(Math.PI/4)} == 1

Math.tanh(x)

xの双曲線タンジェントです。

${Math.tanh(10)} == 0.9999999958776927

Math.trunc(x)

xの整数部です。

${Math.trunc(-1.2)} == -1

定数 説明
Math.E オイラーの定数(e 2.718281828459045
Math.LN2 2の自然対数 0.6931471805599453
Math.LN10 10の自然対数 2.302585092994046
Math.LOG2E eの底を2とする対数 1.4426950408889634
Math.LOG10E eの底を10とする対数 0.4342944819032518
Math.PI 円周率(π) 3.141592653589793
Math.SQRT1_2 0.5の平方根 0.7071067811865476
Math.SQRT2 2の平方根 1.4142135623730951

文字列関数

トップレベルのStringプロパティは、文字列を操作するための関数のコレクションです。

関数 説明
String.length(x) 文字列の長さを返します。 ${String.length('schön') == 5}
String.slice(x,y[,z]) インデックスyからインデックスzまでのxのサブセットを返します。zが省略された場合、文字列の残りが返されます。yが負の数の場合、文字列の最後から抽出されます。 ${String.slice('berry', 2, 4)} == 'rr' ${String.slice('berry', -2)} == 'ry'
String.toLowerCase(x) 文字列を小文字にします。 ${String.toLowerCase('bEn')} == 'ben'
String.toUpperCase(x) 文字列を大文字にします。 ${String.toUpperCase('bEn')} == 'BEN'

文字列のlength関数とslice関数は、バイトではなく、Unicodeのコードポイントで機能します。

時間関数

トップレベルのTimeプロパティは、ミリ秒単位の時間値から年、月、日、時、分、秒に変換する関数のコレクションです。この表の例では、バインドされた時間値T=1567786974710を想定しています。人間が読める言葉にすると、2019年9月6日金曜日の16:22:54と710ミリ秒です。

関数 説明
Time.year(x) 年です。 ${Time.year(T)} == 2019
Time.month(x) 月(0~11)です。 ${Time.month(T)} == 8(9月)
Time.date(x) 月内の日付(1~31)です。 ${Time.date(T)} == 6
Time.weekDay(x) 週内の曜日(0~6)です。 ${Time.weekDay(T)} == 5(金曜日)
Time.hours(x) 1日のうちの時間(0~23)です。 ${Time.hours(T)} == 16
Time.minutes(x) 1時間のうちの分(0~59)です。 ${Time.minutes(T)} == 22
Time.seconds(x) 1分のうちの秒数(0~59)です。 ${Time.seconds(T)} == 54
Time.milliseconds(x) ミリ秒(0~999)です。 ${Time.milliseconds(T)} == 710
Time.format(f, x) 表示形式を適用したテキスト文字列です。 ${Time.format('H:mm', T)} == "16:22"

シンプルなデジタル時計を作成するには、時間関数を使用して、TextコンポーネントとlocalTimeプロパティから24時間時計を作成できます。

{
  "type": "Text",
  "bind": {
    "name": "T",
    "value": "${localTime}"
  },
  "text": "${Time.hours(T)}:${Time.minutes(T)}"
}

この例では、localTimeの値をローカル変数Tにバインドして、テキスト式を少し単純にしています。この時計は、時間が午後4時04分であれば「16:4」と表示するため、希望どおりのものではないかもしれません。条件文を使用して時刻を確認し、必要に応じて先頭/末尾のゼロを含めることができます。

この例に示すように、条件文を使用して12時間時計を表示することもできます。これは時間と分の両方をローカル変数にバインドし、条件文を使用して「am」または「pm」を付けた12時間形式で時刻が出力されます。

{
  "type": "Text",
  "bind": [
    {
      "name": "h",
      "value": "${Time.hours(localTime)}"
    },
    {
      "name": "m",
      "value": "${Time.minutes(localTime)}"
    }
  ],
  "text": "${h >= 12 ? h - 12 : h}:${m < 10 ? '0' : ''}${m} ${h >= 12 ? 'pm' : 'am'}"
}

Time.format関数を使用してコードを単純化することもできます。この例では、時間に基づく条件文を使用して、最後に「am」または「pm」を追加します。

{
  "type": "Text",
  "bind": [
    {
      "name": "h",
      "value": "${Time.hours(localTime)}"
    }
  ],
  "text": "${Time.format('h:mm', localTime) + (h >= 12 ? ' pm' : ' am')}"
}

Time.format

format関数は、形式コードと時刻値を含む文字列引数を取ります。次の形式コードがサポートされています。

範囲 説明
YY 00~99 2桁で表した年です。
YYYY 1970~XXXX 4桁で表した年です。
M 1~12 月(1=1月)です。
MM 01~12 2桁で表した月(1=1月)です。
D 1~31 月内の日付です。
DD 01~31 2桁で表した月内の日付です。
DDD 0~N 任意の桁数で表した日です。
H 0~23 24時間表記の時間です。
HH 00~23 2桁で表した24時間表記の時間です。
HHH 0~N 任意の桁数で表した時間です。
h 1~12 12時間表記の時間です。
hh 01~12 2桁で表した12時間表記の時間です。
m 0~59 分です。
mm 00~59 2桁で表した分です。
mmm 0~N 任意の桁数で表した分です。
s 0~59 秒です。
ss 00~59 2桁で表した秒です。
sss 0~N 任意の桁数で表した秒です。
S 0~9 デシ秒です。
SS 00~99 センチ秒です。
SSS 000~999 ミリ秒です。

すべての形式コードは、数字のみを返すことに注意してください。

2019年9月6日16:22:54と710ミリ秒の時間値を使用して、形式設定関数の使用例をいくつか紹介します。

形式
"DD-MM-YYYY" 06-09-2019
"M/D/YY" 9/6/19
"DDD日" 18145日(エポックから)
"H:mm" 16:22
"hh:mm" 04:22
"H:mm:ss" 16:22:54

時刻の形式は、タイマーからの相対時間にも有効です。この例では、値7523194を使用します。これは2時間5分23.194秒に相当します。

形式
"mmm:ss.S" 125:23.1
"HHH:mm:ss.SS" 2:05:23.19
"sss.SSS" 7523.194

たとえば、ドキュメントがロードされてからのelapsedTimeを表示するには、次のようにします。

{
  "type": "Text",
  "text": "${Time.format('mmm:ss.S', elapsedTime)}"
}

データバインディングの文字列の変換

APLはJSONでシリアル化されているため、すべてのデータバインドの式はJSON文字列内で定義されます。

{
  "MY_EXPRESSION": "${....}"
}

引用符とデータバインディングの式の間にスペースがない場合、式の結果はデータバインディング評価の結果となります。次に例を示します。

"${true}"               -> ブール値のtrue
"${2+4}"                -> 数値6
"${0 <= 1 && 'three'}"  -> 文字列「three」

データバインディングの式の外側の文字列に余分なスペースがある場合、または2つのデータバインディングの式が並置されている場合、結果は文字列連結となります。

" ${true}"     -> 文字列「true」
"${2+4} "      -> 文字列「6」
"${2+1}${1+2}" -> 文字列「33」

このページは役に立ちましたか?

最終更新日: 2021 年 09 月 29 日