ローカライズされたカレンダーを操作するSQLiteを学びます。
パート1-英語版 、 パート2


最近、たまたまサイトトラフィックをiOSに分析するためにiOSアプリケーションを移植(より正確には書き換え)しました。 データはリレーショナルモデルに適合するため、SQLiteを使用することにしました。 私の知る限り、iOS用のリレーショナルDBMSは他にありません。
期間、期間...特定の日付範囲の週単位の効率を計算する時間です。
したがって、有用な作業と消費された作業の値、およびこの作業の完了日を含む表があります。 テーブルの構造は、SQLで次のように記述されています。
CREATE TABLE [ Usage ]
(
[FacetId] VARCHAR , -- ""
[ Value ] INTEGER , -- ""
[Visits ] INTEGER , -- ""
[ Date ] DATETIME --
);
* This source code was highlighted with Source Code Highlighter .
一部の日付範囲では、各週の効率を計算する必要があります。 OK、リクエストを書いた
SELECT SUM ( Value ) / SUM ( Visits ),
strftime( '%Y-%W' , Date ) AS week
FROM Usage
WHERE Date BETWEEN @startDate AND @endDate
GROUP BY week
ORDER BY week;
* This source code was highlighted with Source Code Highlighter .
ただし、何らかの理由で、結果は参照実装と一致しませんでした。 次のことが判明しました。 SQLiteは、週は月曜日から始まると考えています。 参照実装では、米国で慣例となっているように、日曜日が週の始まりであると見なされます。
sqlite> SELECT strftime( '%Y-%W' , '2011-01-02' );
2011-01 ## 2011-02
sqlite> SELECT strftime( '%Y-%W' , '2011-01-01' );
2011-01
* This source code was highlighted with Source Code Highlighter .
DBMSロケールを強制的に指定する方法が見つかりませんでした。 私は本当に美しいリクエストをいくつかに分割したくありませんでした。 さらに、sqlite3_create_functionを使用してSQLiteに関数を追加する機能を発見しました。
はい、好みと遊女で日付をフォーマットするための独自のオプションを書くことにしました。 strftimeとは、リクエストから渡されたロケールを考慮する能力が異なります。
このソリューションの利点は明らかです。
- SQL内にとどまります
- Objective-Cで追加のループを記述する必要はありません
- クエリの実行が潜在的に高速になります
- そして最も重要なこと-このソリューションは再利用のために設計されています
それでは始めましょう。 グレゴリオ暦に限定することにより、問題の記述を簡素化します。
SQLite拡張関数には、main()関数に似たシグネチャがあります。
void ObjcFormatAnsiDateUsingLocale( sqlite3_context* ctx_, int argc_,sqlite3_value** argv_ );
* This source code was highlighted with Source Code Highlighter .
違いは、戻りフラグがないことです。 代わりに、呼び出し元のデータベースコンテキストが渡されます。 このコンテキストは、結果またはエラーを返すために使用されます。
SQLクエリでは、関数はObjective-Cスタイルの日付形式、実際には日付とロケールを取ります。 このリクエストは、2011年1月2日土曜日が2011年の第2週に正しく関連付けられます。これはアメリカの地域で行われるはずです。
sqlite> SELECT ObjcFormatAnsiDateUsingLocale( 'YYYY-ww' , '2011-01-02' , 'en_US' );
2011-02
* This source code was highlighted with Source Code Highlighter .
したがって、4つのことを行う必要があります。
- クエリで使用できるように、関数をSQLiteに登録します。
- パラメーターをargv_からFoundation型に変換します。 私たちの場合、それぞれ[NSString、NSDate、NSString]になります。
- NSDateFormatterを使用して日付をフォーマットする
- 結果を返す
===============
0. SQLite関数を登録する
これはsqlite3_create_functionを使用して行われます。 www.sqlite.org/c3ref/create_function.html
sqlite3_create_function
(
db_, // HANDLE , sqlite3_open
"ObjcFormatAnsiDateUsingLocale" , //
3, // . SQLite
SQLITE_UTF8, // iOS
NULL,
&ObjcFormatAnsiDateUsingLocale, //
NULL, NULL // . .
);
* This source code was highlighted with Source Code Highlighter .
-
1.パラメーターの変換
SQLiteは、パラメーターの数が一致するかどうかを個別にチェックします。 ただし、念のためargcにチェックを残すことをお勧めします。
SQLite自体がパラメータリソースを解放するため、NSString-> initWithBytesNoCopy:length:encoding:freeWhenDoneコンストラクターを使用することをお勧めします。
-
2.日付のフォーマット
一見、すべてがシンプルです。
inputFormatter_.dateFormat = @"yyyy-MM-dd" ;
NSDate* date_ = [ inputFormatter_ dateFromString: strDate_ ];
targetFormatter_.dateFormat = format_;
return [ targetFormatter_ stringFromDate: date_ ];
* This source code was highlighted with Source Code Highlighter .
ただし、いくつかのニュアンスがあります。
- ご存知のように、NSLocaleクラスのインスタンスは、NSCalendarオブジェクトとNSDateFormatterの両方に含まれています。
「NSDateFormatter.calendar.locale == NSDateFormatter.locale」という条件が満たされていることが非常に重要です。
- inputFormatter_にはロケール「en_US_POSIX」が必要です
- SQLiteは、日付をANSI形式@ "yyyy-MM-dd"で保存します。 inputFormatter_に設定する必要があります
- NSDateFormatterの作成は非常に高価な操作です。 二度と電話しないように
-
3.結果を返す
これらの目的のために、sqlite3_result_text関数が使用されます。 SQLiteがFoundation Frameworkで割り当てられたリソースのコピーを作成できるように、SQLITE_TRANSIENTオプションを使用することが重要です。
===============
実際、それがすべてです。 計算が収束しました。
ソースコードはgithubプロジェクトページで利用可能です-dodikk / ESLocale
コードレビューとプルリクエストは大歓迎です。
私の機能が誰かに役立つことを願っています。
それから私は休暇を取る。