From 364a240a6217d4d1b6fd7c237a3cf9039cbad6ec Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Thu, 28 Sep 2017 02:09:47 +0200 Subject: [PATCH] Add date and time functions Closes #142 --- CHANGELOG.md | 2 + Documentation/Index.md | 13 +++ SQLite.xcodeproj/project.pbxproj | 18 +++ .../SQLite/Typed/DateAndTimeFunctions.swift | 106 ++++++++++++++++++ Sources/SQLite/Typed/Operators.swift | 17 ++- .../DateAndTimeFunctionTests.swift | 66 +++++++++++ Tests/SQLiteTests/OperatorsTests.swift | 31 +++++ 7 files changed, 249 insertions(+), 4 deletions(-) create mode 100644 Sources/SQLite/Typed/DateAndTimeFunctions.swift create mode 100644 Tests/SQLiteTests/DateAndTimeFunctionTests.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index bbb5d671..e36e40d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ 0.11.4 (xx-09-2017), [diff][diff-0.11.4] ======================================== +* Added Date and Time functions ([#142][]) * Preliminary Linux support ([#315][], [#681][]) * Add `RowIterator` for more safety ([#647][], [#726][]) * Make Row.get throw instead of crash ([#649][]) @@ -47,6 +48,7 @@ [diff-0.11.3]: https://github.com/stephencelis/SQLite.swift/compare/0.11.2...0.11.3 [diff-0.11.4]: https://github.com/stephencelis/SQLite.swift/compare/0.11.3...0.11.4 +[#142]: https://github.com/stephencelis/SQLit1e.swift/issues/142 [#315]: https://github.com/stephencelis/SQLit1e.swift/issues/315 [#481]: https://github.com/stephencelis/SQLit1e.swift/pull/481 [#532]: https://github.com/stephencelis/SQLit1e.swift/issues/532 diff --git a/Documentation/Index.md b/Documentation/Index.md index eb5cbc53..9a6d07ad 100644 --- a/Documentation/Index.md +++ b/Documentation/Index.md @@ -54,6 +54,7 @@ - [Other Operators](#other-operators) - [Core SQLite Functions](#core-sqlite-functions) - [Aggregate SQLite Functions](#aggregate-sqlite-functions) + - [Date and Time Functions](#date-and-time-functions) - [Custom SQL Functions](#custom-sql-functions) - [Custom Collations](#custom-collations) - [Full-text Search](#full-text-search) @@ -1430,6 +1431,18 @@ Many of SQLite’s [core functions](https://www.sqlite.org/lang_corefunc.html) h Most of SQLite’s [aggregate functions](https://www.sqlite.org/lang_aggfunc.html) have been surfaced in and type-audited for SQLite.swift. +## Date and Time functions + +SQLite's [date and time](https://www.sqlite.org/lang_datefunc.html) are available: + +```swift +DateFunctions.date("now") +// date('now') +Date().date +// date('2007-01-09T09:41:00.000') +Expression("date").date +// date("date") +``` ## Custom SQL Functions diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj index 3093b11d..cb776608 100644 --- a/SQLite.xcodeproj/project.pbxproj +++ b/SQLite.xcodeproj/project.pbxproj @@ -46,17 +46,22 @@ 03A65E951C6BB3030062603F /* TestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B161C3F127200AE3E12 /* TestHelpers.swift */; }; 03A65E971C6BB3210062603F /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 03A65E961C6BB3210062603F /* libsqlite3.tbd */; }; 19A1709C3E7A406E62293B2A /* Fixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17B93B48B5560E6E51791 /* Fixtures.swift */; }; + 19A17152E32A9585831E3FE0 /* DateAndTimeFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */; }; 19A1717B10CC941ACB5533D6 /* FTS5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1730E4390C775C25677D1 /* FTS5.swift */; }; 19A171967CC511C4F6F773C9 /* RowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A175C1F9CB3BBAB8FCEC7B /* RowTests.swift */; }; 19A171E6FA242F72A308C594 /* FTS5Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1721B8984686B9963B45D /* FTS5Tests.swift */; }; 19A171F12AB8B07F2FD7201A /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; }; 19A1720B67ED13E6150C6A3D /* RowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A175C1F9CB3BBAB8FCEC7B /* RowTests.swift */; }; 19A17254FBA7894891F7297B /* FTS5Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1721B8984686B9963B45D /* FTS5Tests.swift */; }; + 19A172EB202970561E5C4245 /* DateAndTimeFunctionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1729B75C33F9A0B9A89C1 /* DateAndTimeFunctionTests.swift */; }; + 19A173668D948AD4DF1F5352 /* DateAndTimeFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */; }; + 19A1737286A74F3CF7412906 /* DateAndTimeFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */; }; 19A17408007B182F884E3A53 /* Fixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17B93B48B5560E6E51791 /* Fixtures.swift */; }; 19A17490543609FCED53CACC /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1710E73A46D5AC721CDA9 /* Errors.swift */; }; 19A174D78559CD30679BCCCB /* FTS5Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1721B8984686B9963B45D /* FTS5Tests.swift */; }; 19A1750CEE9B05267995CF3D /* FTS5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1730E4390C775C25677D1 /* FTS5.swift */; }; 19A175DFF47B84757E547C62 /* fixtures in Resources */ = {isa = PBXBuildFile; fileRef = 19A17E2695737FAB5D6086E3 /* fixtures */; }; + 19A1769C1F3A7542BECF50FF /* DateAndTimeFunctionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1729B75C33F9A0B9A89C1 /* DateAndTimeFunctionTests.swift */; }; 19A177CC33F2E6A24AF90B02 /* CipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17399EA9E61235D5D77BF /* CipherTests.swift */; }; 19A178072B371489E6A1E839 /* FoundationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1794CC4D7827E997E32A7 /* FoundationTests.swift */; }; 19A17835FD5886FDC5A3228F /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; }; @@ -66,8 +71,10 @@ 19A179CCF9671E345E5A9811 /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; }; 19A179E76EA6207669B60C1B /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; }; 19A17C4B951CB054EE48AB1C /* CipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17399EA9E61235D5D77BF /* CipherTests.swift */; }; + 19A17C80076860CF7751A056 /* DateAndTimeFunctionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1729B75C33F9A0B9A89C1 /* DateAndTimeFunctionTests.swift */; }; 19A17DC282E36C4F41AA440B /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1710E73A46D5AC721CDA9 /* Errors.swift */; }; 19A17E04C4C0956715C5676A /* FoundationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1794CC4D7827E997E32A7 /* FoundationTests.swift */; }; + 19A17E29278A12BC4F542506 /* DateAndTimeFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */; }; 19A17EC0D68BA8C03288ADF7 /* FTS5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1730E4390C775C25677D1 /* FTS5.swift */; }; 19A17F3E1F7ACA33BD43E138 /* fixtures in Resources */ = {isa = PBXBuildFile; fileRef = 19A17E2695737FAB5D6086E3 /* fixtures */; }; 19A17F60B685636D1F83C2DD /* Fixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17B93B48B5560E6E51791 /* Fixtures.swift */; }; @@ -209,12 +216,14 @@ 03A65E961C6BB3210062603F /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk/usr/lib/libsqlite3.tbd; sourceTree = DEVELOPER_DIR; }; 19A1710E73A46D5AC721CDA9 /* Errors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = ""; }; 19A1721B8984686B9963B45D /* FTS5Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FTS5Tests.swift; sourceTree = ""; }; + 19A1729B75C33F9A0B9A89C1 /* DateAndTimeFunctionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateAndTimeFunctionTests.swift; sourceTree = ""; }; 19A1730E4390C775C25677D1 /* FTS5.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FTS5.swift; sourceTree = ""; }; 19A17399EA9E61235D5D77BF /* CipherTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CipherTests.swift; sourceTree = ""; }; 19A175C1F9CB3BBAB8FCEC7B /* RowTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RowTests.swift; sourceTree = ""; }; 19A178A39ACA9667A62663CC /* Cipher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cipher.swift; sourceTree = ""; }; 19A1794CC4D7827E997E32A7 /* FoundationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoundationTests.swift; sourceTree = ""; }; 19A17B93B48B5560E6E51791 /* Fixtures.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Fixtures.swift; sourceTree = ""; }; + 19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateAndTimeFunctions.swift; sourceTree = ""; }; 19A17E2695737FAB5D6086E3 /* fixtures */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = folder; path = fixtures; sourceTree = ""; }; 3D67B3E51DB2469200A4F4C6 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS3.0.sdk/usr/lib/libsqlite3.tbd; sourceTree = DEVELOPER_DIR; }; 49EB68C31F7B3CB400D89D40 /* Coding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coding.swift; sourceTree = ""; }; @@ -408,6 +417,7 @@ 19A17399EA9E61235D5D77BF /* CipherTests.swift */, 19A17B93B48B5560E6E51791 /* Fixtures.swift */, 19A175C1F9CB3BBAB8FCEC7B /* RowTests.swift */, + 19A1729B75C33F9A0B9A89C1 /* DateAndTimeFunctionTests.swift */, ); name = SQLiteTests; path = Tests/SQLiteTests; @@ -452,6 +462,7 @@ EE247B011C3F06E900AE3E12 /* Schema.swift */, EE247B021C3F06E900AE3E12 /* Setter.swift */, 49EB68C31F7B3CB400D89D40 /* Coding.swift */, + 19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */, ); path = Typed; sourceTree = ""; @@ -806,6 +817,7 @@ 19A17EC0D68BA8C03288ADF7 /* FTS5.swift in Sources */, 19A179E76EA6207669B60C1B /* Cipher.swift in Sources */, 19A17FF4A10B44D3937C8CAC /* Errors.swift in Sources */, + 19A1737286A74F3CF7412906 /* DateAndTimeFunctions.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -833,6 +845,7 @@ 19A179A0C45377CB09BB358C /* CipherTests.swift in Sources */, 19A17F60B685636D1F83C2DD /* Fixtures.swift in Sources */, 19A1785195182AF8731A8BDA /* RowTests.swift in Sources */, + 19A1769C1F3A7542BECF50FF /* DateAndTimeFunctionTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -862,6 +875,7 @@ 3D67B3E81DB246BA00A4F4C6 /* Connection.swift in Sources */, 19A179CCF9671E345E5A9811 /* Cipher.swift in Sources */, 19A17DC282E36C4F41AA440B /* Errors.swift in Sources */, + 19A173668D948AD4DF1F5352 /* DateAndTimeFunctions.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -891,6 +905,7 @@ 19A1717B10CC941ACB5533D6 /* FTS5.swift in Sources */, 19A171F12AB8B07F2FD7201A /* Cipher.swift in Sources */, 19A1792C0520D4E83C2EB075 /* Errors.swift in Sources */, + 19A17E29278A12BC4F542506 /* DateAndTimeFunctions.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -918,6 +933,7 @@ 19A177CC33F2E6A24AF90B02 /* CipherTests.swift in Sources */, 19A17408007B182F884E3A53 /* Fixtures.swift in Sources */, 19A1720B67ED13E6150C6A3D /* RowTests.swift in Sources */, + 19A17C80076860CF7751A056 /* DateAndTimeFunctionTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -947,6 +963,7 @@ 19A1750CEE9B05267995CF3D /* FTS5.swift in Sources */, 19A17835FD5886FDC5A3228F /* Cipher.swift in Sources */, 19A17490543609FCED53CACC /* Errors.swift in Sources */, + 19A17152E32A9585831E3FE0 /* DateAndTimeFunctions.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -974,6 +991,7 @@ 19A17C4B951CB054EE48AB1C /* CipherTests.swift in Sources */, 19A1709C3E7A406E62293B2A /* Fixtures.swift in Sources */, 19A171967CC511C4F6F773C9 /* RowTests.swift in Sources */, + 19A172EB202970561E5C4245 /* DateAndTimeFunctionTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Sources/SQLite/Typed/DateAndTimeFunctions.swift b/Sources/SQLite/Typed/DateAndTimeFunctions.swift new file mode 100644 index 00000000..0b9a497f --- /dev/null +++ b/Sources/SQLite/Typed/DateAndTimeFunctions.swift @@ -0,0 +1,106 @@ +// +// SQLite.swift +// https://github.com/stephencelis/SQLite.swift +// Copyright © 2014-2015 Stephen Celis. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import Foundation + +/// All five date and time functions take a time string as an argument. +/// The time string is followed by zero or more modifiers. +/// The strftime() function also takes a format string as its first argument. +/// +/// https://www.sqlite.org/lang_datefunc.html +public class DateFunctions { + /// The date() function returns the date in this format: YYYY-MM-DD. + public static func date(_ timestring: String, _ modifiers: String...) -> Expression { + return timefunction("date", timestring: timestring, modifiers: modifiers) + } + + /// The time() function returns the time as HH:MM:SS. + public static func time(_ timestring: String, _ modifiers: String...) -> Expression { + return timefunction("time", timestring: timestring, modifiers: modifiers) + } + + /// The datetime() function returns "YYYY-MM-DD HH:MM:SS". + public static func datetime(_ timestring: String, _ modifiers: String...) -> Expression { + return timefunction("datetime", timestring: timestring, modifiers: modifiers) + } + + /// The julianday() function returns the Julian day - + /// the number of days since noon in Greenwich on November 24, 4714 B.C. + public static func julianday(_ timestring: String, _ modifiers: String...) -> Expression { + return timefunction("julianday", timestring: timestring, modifiers: modifiers) + } + + /// The strftime() routine returns the date formatted according to the format string specified as the first argument. + public static func strftime(_ format: String, _ timestring: String, _ modifiers: String...) -> Expression { + if !modifiers.isEmpty { + let templates = [String](repeating: "?", count: modifiers.count).joined(separator: ", ") + return Expression("strftime(?, ?, \(templates))", [format, timestring] + modifiers) + } + return Expression("strftime(?, ?)", [format, timestring]) + } + + private static func timefunction(_ name: String, timestring: String, modifiers: [String]) -> Expression { + if !modifiers.isEmpty { + let templates = [String](repeating: "?", count: modifiers.count).joined(separator: ", ") + return Expression("\(name)(?, \(templates))", [timestring] + modifiers) + } + return Expression("\(name)(?)", [timestring]) + } +} + +extension Date { + public var date: Expression { + return DateFunctions.date(dateFormatter.string(from: self)) + } + + public var time: Expression { + return DateFunctions.time(dateFormatter.string(from: self)) + } + + public var datetime: Expression { + return DateFunctions.datetime(dateFormatter.string(from: self)) + } + + public var julianday: Expression { + return DateFunctions.julianday(dateFormatter.string(from: self)) + } +} + +extension Expression where UnderlyingType == Date { + public var date: Expression { + return Expression("date(\(template))", bindings) + } + + public var time: Expression { + return Expression("time(\(template))", bindings) + } + + public var datetime: Expression { + return Expression("datetime(\(template))", bindings) + } + + public var julianday: Expression { + return Expression("julianday(\(template))", bindings) + } +} diff --git a/Sources/SQLite/Typed/Operators.swift b/Sources/SQLite/Typed/Operators.swift index 01381bb8..5f95993f 100644 --- a/Sources/SQLite/Typed/Operators.swift +++ b/Sources/SQLite/Typed/Operators.swift @@ -474,11 +474,20 @@ public func <=(lhs: V, rhs: Expression) -> Expression wher return infix(lhs, rhs) } -public func ~=(lhs: ClosedRange, rhs: Expression) -> Expression where V.Datatype : Comparable { - return Expression("\(rhs.template) BETWEEN ? AND ?", rhs.bindings + [lhs.lowerBound as? Binding, lhs.upperBound as? Binding]) +public func ~=(lhs: ClosedRange, rhs: Expression) -> Expression where V.Datatype : Comparable & Value { + return Expression("\(rhs.template) BETWEEN ? AND ?", rhs.bindings + [lhs.lowerBound.datatypeValue, lhs.upperBound.datatypeValue]) } -public func ~=(lhs: ClosedRange, rhs: Expression) -> Expression where V.Datatype : Comparable { - return Expression("\(rhs.template) BETWEEN ? AND ?", rhs.bindings + [lhs.lowerBound as? Binding, lhs.upperBound as? Binding]) + +public func ~=(lhs: ClosedRange, rhs: Expression) -> Expression where V.Datatype : Comparable & Value { + return Expression("\(rhs.template) BETWEEN ? AND ?", rhs.bindings + [lhs.lowerBound.datatypeValue, lhs.upperBound.datatypeValue]) +} + +public func ~=(lhs: Range, rhs: Expression) -> Expression where V.Datatype : Comparable & Value { + return Expression("\(rhs.template) >= ? AND \(rhs.template) < ?", rhs.bindings + [lhs.lowerBound.datatypeValue] + rhs.bindings + [lhs.upperBound.datatypeValue]) +} + +public func ~=(lhs: Range, rhs: Expression) -> Expression where V.Datatype : Comparable & Value { + return Expression("\(rhs.template) >= ? AND \(rhs.template) < ?", rhs.bindings + [lhs.lowerBound.datatypeValue] + rhs.bindings + [lhs.upperBound.datatypeValue]) } // MARK: - diff --git a/Tests/SQLiteTests/DateAndTimeFunctionTests.swift b/Tests/SQLiteTests/DateAndTimeFunctionTests.swift new file mode 100644 index 00000000..628b5910 --- /dev/null +++ b/Tests/SQLiteTests/DateAndTimeFunctionTests.swift @@ -0,0 +1,66 @@ +import XCTest +@testable import SQLite + +class DateAndTimeFunctionsTests : XCTestCase { + + func test_date() { + AssertSQL("date('now')", DateFunctions.date("now")) + AssertSQL("date('now', 'localtime')", DateFunctions.date("now", "localtime")) + } + + func test_time() { + AssertSQL("time('now')", DateFunctions.time("now")) + AssertSQL("time('now', 'localtime')", DateFunctions.time("now", "localtime")) + } + + func test_datetime() { + AssertSQL("datetime('now')", DateFunctions.datetime("now")) + AssertSQL("datetime('now', 'localtime')", DateFunctions.datetime("now", "localtime")) + } + + func test_julianday() { + AssertSQL("julianday('now')", DateFunctions.julianday("now")) + AssertSQL("julianday('now', 'localtime')", DateFunctions.julianday("now", "localtime")) + } + + func test_strftime() { + AssertSQL("strftime('%Y-%m-%d', 'now')", DateFunctions.strftime("%Y-%m-%d", "now")) + AssertSQL("strftime('%Y-%m-%d', 'now', 'localtime')", DateFunctions.strftime("%Y-%m-%d", "now", "localtime")) + } +} + +class DateExtensionTests : XCTestCase { + func test_time() { + AssertSQL("time('1970-01-01T00:00:00.000')", Date(timeIntervalSince1970: 0).time) + } + + func test_date() { + AssertSQL("date('1970-01-01T00:00:00.000')", Date(timeIntervalSince1970: 0).date) + } + + func test_datetime() { + AssertSQL("datetime('1970-01-01T00:00:00.000')", Date(timeIntervalSince1970: 0).datetime) + } + + func test_julianday() { + AssertSQL("julianday('1970-01-01T00:00:00.000')", Date(timeIntervalSince1970: 0).julianday) + } +} + +class DateExpressionTests : XCTestCase { + func test_date() { + AssertSQL("date(\"date\")", date.date) + } + + func test_time() { + AssertSQL("time(\"date\")", date.time) + } + + func test_datetime() { + AssertSQL("datetime(\"date\")", date.datetime) + } + + func test_julianday() { + AssertSQL("julianday(\"date\")", date.julianday) + } +} diff --git a/Tests/SQLiteTests/OperatorsTests.swift b/Tests/SQLiteTests/OperatorsTests.swift index f0e585cd..08e679c1 100644 --- a/Tests/SQLiteTests/OperatorsTests.swift +++ b/Tests/SQLiteTests/OperatorsTests.swift @@ -255,6 +255,11 @@ class OperatorsTests : XCTestCase { AssertSQL("\"doubleOptional\" BETWEEN 1.2 AND 4.5", 1.2...4.5 ~= doubleOptional) } + func test_patternMatchingOperator_withComparableRange_buildsBooleanExpression() { + AssertSQL("\"double\" >= 1.2 AND \"double\" < 4.5", 1.2..<4.5 ~= double) + AssertSQL("\"doubleOptional\" >= 1.2 AND \"doubleOptional\" < 4.5", 1.2..<4.5 ~= doubleOptional) + } + func test_patternMatchingOperator_withomparableClosedRangeString_buildsBetweenBooleanExpression() { AssertSQL("\"string\" BETWEEN 'a' AND 'b'", "a"..."b" ~= string) AssertSQL("\"stringOptional\" BETWEEN 'a' AND 'b'", "a"..."b" ~= stringOptional) @@ -293,4 +298,30 @@ class OperatorsTests : XCTestCase { AssertSQL("((1 = 1) AND ((1 = 1) OR (1 = 1)))", n == n && (n == n || n == n)) } + func test_dateExpressionLessGreater() { + let begin = Date(timeIntervalSince1970: 0) + AssertSQL("(\"date\" < '1970-01-01T00:00:00.000')", date < begin) + AssertSQL("(\"date\" > '1970-01-01T00:00:00.000')", date > begin) + AssertSQL("(\"date\" >= '1970-01-01T00:00:00.000')", date >= begin) + AssertSQL("(\"date\" <= '1970-01-01T00:00:00.000')", date <= begin) + } + + func test_dateExpressionRange() { + let begin = Date(timeIntervalSince1970: 0) + let end = Date(timeIntervalSince1970: 5000) + AssertSQL( + "\"date\" >= '1970-01-01T00:00:00.000' AND \"date\" < '1970-01-01T01:23:20.000'", + (begin..