2009-09-05 74 views
1

我試圖將當天日期作爲字符串以24小時時間格式存儲在sqlite表中,而不管用戶的區域設置如何。因爲NSDate的榮譽用戶的區域設置,此代碼返回根據用戶的位置不同的結果:強制NSDate返回24小時時間而不管區域設置

NSLog(@"The date is %@", [NSDate date]); 

如果他們的區域設置爲英國,在24小時的時間是默認的,上述回報 「的日期是2009-09-05 11:17:35',而如果他們在美國,其中12小時時間是默認時間,則返回'日期爲2009-09-05 11:17:35 AM 」。

有沒有一種方法可以在將數據提交到數據庫之前自動檢測並將12小時時間轉換爲24小時時間?我使用SQLite持久對象,所以我需要提供日期作爲NSDate而不是NSString。

回答

1

在我以前的回答中,我認爲你遇到了SQLite持久對象的問題,但事實並非如此。我認爲你只是誤解了下面的代碼實際上是這樣做的:

NSLog(@"The date is %@", [NSDate date]); 

爲什麼你在不同的語言環境中獲得不同的日誌輸出的原因是因爲NSDate的,那是因爲NSDateFormatter的使用在引擎蓋下使用%@[NSDate date]的字符串表示形式插入到日誌字符串中。事實上,NSDate沒有「12小時」或「24小時」語言環境的概念 - 它只是一個時間點的表示。

當您將NSDate轉換爲字符串時(例如在NSLog語句中或作爲SQL查詢字符串的一部分)時,區域設置將起作用。當你想這樣做,你應該指定自己明確的格式化,像這樣:

NSDateFormatter *formatter = [[NSDateFormatter alloc] initWithDateFormat:@"yyyy-MM-dd HH:mm:ss" allowNaturalLanguage:NO]; 
NSLog(@"The date is %s", [formatter stringFromDate:[NSDate date]]); 
[formatter release]; 

使用上面的,你應該得到相同的字符串,無論用戶的區域設置。

至於你的SQL查詢,我正確地認爲你也使用%@插入日期到查詢字符串?如果是這樣,你應該這樣做,而不是:

NSString* criteriaTemplate = @"WHERE date(due) BETWEEN date(%d) AND date('now', 'localtime')"; 
NSString* criteria = [NSString stringWithFormat: criteriaTemplate, [myNSDate timeIntervalSince1970]]; 
NSArray* todayTasks = [Task findByCriteria:criteria]; 

希望這會有所幫助。

+0

謝謝,彌敦道 - 這幫助我更好地理解它。它引導我查看setDateFormat如何工作,這幫助我找到了我分別公佈的NSLocale覆蓋。非常感激。 – Nick 2009-09-06 12:32:46

0

您可以使用sqlite DATETIME類型,並實際存儲日期而不是字符串。您可以使用NSDateFormatter定義日期顯示給用戶的方式。

+0

非常感謝您的答覆,格雷厄姆。現在,我致力於使用SQLite持久對象,它將日期存儲爲字符串,否則日期時間將是一個不錯的選擇。 – Nick 2009-09-05 12:27:56

0

您可能會考慮swizzling調用[NSDate date]始終應用美國語言環境日期格式化程序。

+0

謝謝,亞歷克斯。我不知道這個 - 我會仔細看看。謝謝回覆。 – Nick 2009-09-05 12:30:38

+0

上述鏈接不再有效。有沒有這個描述? – 2017-01-13 20:13:25

0

這看起來像SQLite持久對象中的錯誤。該庫將採用您的NSDate實例並以格式@"yyyy-MM-dd HH:mm:ss.SSSS"的字符串將其序列化,這意味着時區信息實際上被拋出。當日期字符串轉換回NSDate時,時區被假定爲本地時區。這僅適用於所有NSDate實例都位於本地時區的情況。

定影這個的最簡單的方法是修補由SQLite的持久性對象定義的NSDate(SqlPersistence)類別中NSDate-SQLitePersistence.m序列化和deserialisation期間要包含在格式字符串(例如@"yyyy-MM-dd HH:mm:ss.SSSS Z")時區。但是,正確的解決方法是使用DATETIME列而不是基於char的列來序列化NSDate。

當您在UI中顯示日期時,您可以使用自己的NSDateFormatter來定義自己的字符串日期格式。

+0

感謝您的建議,內森 - 我實際上通過使用sqlite的'localtime'選項進行時間比較來解決時區問題,如下所示: todayTasks = [任務findByCriteria:@「WHERE date(due)BETWEEN'1980 -01-01'和日期('now','localtime')ORDER BY due「]; 當時間以24小時格式存儲時(例如2009-09-05 13:18:48.3970),這很有用,但是在使用SQLitePO並以12小時格式存儲時間時會中斷(例如2009-09-05 01:19:23 PM.1400),這讓我難倒了。 – Nick 2009-09-05 12:25:56

+0

SQLite持久對象將始終將您的NSDate屬性存儲爲24小時格式的字符串(這就是「HH」所使用的「yyyy-MM-dd HH:mm:ss.SSSS」日期格式 - 「hh」會12小時)。 要查詢,您應該使用datetime()而不是date()來包含時間組件。 datetime()方法理解紀元秒,所以你應該在查詢字符串中使用'[NSString stringWithFormat:@「datetime(%d)」,[yourNSDate timeIntervalSince1970]]'傳遞給-findByCriteria :. – 2009-09-06 09:09:18

+0

謝謝,彌敦道。非常感謝您的意見。我使用date(),因爲我不需要比較時間本身 - 僅僅是一天;無論如何,我認爲這是最簡單的方法。 我想你可能是正確的說明這是一個與sqlitepo的錯誤 - 我已經從我的iPhone抓取了數據庫文件,並且12小時時間沒有被轉換爲24小時時間。 12小時時間區域被存儲爲2009-09-06 01:09:11 PM.8550,而24小時時間正確寫爲2009-09-06 13:09:11.8550。 – Nick 2009-09-06 11:12:56

2

更新:最後,我選擇在使用NSLocale操作日期並存儲它之前覆蓋用戶的區域設置。所以,與其這樣:

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; 
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss.SSSS"]; 
NSString *formattedDateString = [dateFormatter stringFromDate:self];

我這樣做:

NSLocale *POSIXLocale = [[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"] autorelease]; 
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; 
[dateFormatter setLocale:POSIXLocale]; 
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss.SSSS"]; 

NSString *formattedDateString = [dateFormatter stringFromDate:self]; 

這有存放前應規範日期格式的效果。對於使用sqlitepo那些誰遇到同樣的問題,相關的代碼修改可以在NSDate的-SQLitePersistence.m被發現線33和46

感謝所有誰插話。

+0

@NickD:原來的代碼和你用*取代的代碼應該*按照Unicode TR35-4標準生成相同的字符串。看起來你可能遇到了一個bug - http://stackoverflow.com/questions/143075/nsdateformatter-am-i-doing-something-wrong-or-is-this-a-bug – 2009-09-06 13:24:02

+0

好吧,發現了。我搜索了Apple的iPhone開發者論壇,發現有人已經將該問題提交爲bug#7037950:https://devforums.apple.com/message/93981#93981 – Nick 2009-09-06 14:44:26