From f7df6d3700bac8181e2258a9df28a32153124c46 Mon Sep 17 00:00:00 2001 From: stuconnolly Date: Sat, 8 Sep 2012 10:45:57 +0000 Subject: Add native timezone type support. --- .../PostgresKit.xcodeproj/project.pbxproj | 16 +++++ .../Source/FLXPostgresConnectionPrivateAPI.h | 3 +- .../Source/FLXPostgresConnectionQueryPreparation.m | 2 +- Frameworks/PostgresKit/Source/FLXPostgresResult.m | 2 +- .../Source/FLXPostgresTypeDateTimeHandler.m | 48 ++++++++----- .../Source/FLXPostgresTypeNumberHandler.m | 2 +- Frameworks/PostgresKit/Source/FLXTimeTZ.h | 62 ++++++++++++++++ Frameworks/PostgresKit/Source/FLXTimeTZ.m | 84 ++++++++++++++++++++++ 8 files changed, 197 insertions(+), 22 deletions(-) create mode 100644 Frameworks/PostgresKit/Source/FLXTimeTZ.h create mode 100644 Frameworks/PostgresKit/Source/FLXTimeTZ.m (limited to 'Frameworks') diff --git a/Frameworks/PostgresKit/PostgresKit.xcodeproj/project.pbxproj b/Frameworks/PostgresKit/PostgresKit.xcodeproj/project.pbxproj index 9749dc39..fa760468 100644 --- a/Frameworks/PostgresKit/PostgresKit.xcodeproj/project.pbxproj +++ b/Frameworks/PostgresKit/PostgresKit.xcodeproj/project.pbxproj @@ -10,6 +10,8 @@ 170465CE15C2960F00DC5BE5 /* FLXPostgresTypeHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 170465CC15C2960F00DC5BE5 /* FLXPostgresTypeHandler.h */; settings = {ATTRIBUTES = (Public, ); }; }; 170465CF15C2960F00DC5BE5 /* FLXPostgresTypeHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 170465CD15C2960F00DC5BE5 /* FLXPostgresTypeHandler.m */; }; 1724C9B815F9ED8600AB2291 /* libpqtypes.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1724C9B715F9ED8600AB2291 /* libpqtypes.a */; }; + 1724CC9215FB4CC200AB2291 /* FLXTimeTZ.h in Headers */ = {isa = PBXBuildFile; fileRef = 1724CC9015FB4CC200AB2291 /* FLXTimeTZ.h */; }; + 1724CC9315FB4CC200AB2291 /* FLXTimeTZ.m in Sources */ = {isa = PBXBuildFile; fileRef = 1724CC9115FB4CC200AB2291 /* FLXTimeTZ.m */; }; 1731F02B15EE09E000D973EB /* FLXPostgresConnectionParameters.h in Headers */ = {isa = PBXBuildFile; fileRef = 1731F02915EE09E000D973EB /* FLXPostgresConnectionParameters.h */; }; 1731F02C15EE09E000D973EB /* FLXPostgresConnectionParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = 1731F02A15EE09E000D973EB /* FLXPostgresConnectionParameters.m */; }; 1731F10815F1A52B00D973EB /* FLXPostgresTypeDateTimeHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 1731F10615F1A52B00D973EB /* FLXPostgresTypeDateTimeHandler.h */; }; @@ -56,6 +58,8 @@ 170465CD15C2960F00DC5BE5 /* FLXPostgresTypeHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLXPostgresTypeHandler.m; sourceTree = ""; }; 1724C9B715F9ED8600AB2291 /* libpqtypes.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libpqtypes.a; sourceTree = ""; }; 1724CA3B15F9EE7300AB2291 /* libpqtypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libpqtypes.h; sourceTree = ""; }; + 1724CC9015FB4CC200AB2291 /* FLXTimeTZ.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLXTimeTZ.h; sourceTree = ""; }; + 1724CC9115FB4CC200AB2291 /* FLXTimeTZ.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLXTimeTZ.m; sourceTree = ""; }; 1731F02915EE09E000D973EB /* FLXPostgresConnectionParameters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLXPostgresConnectionParameters.h; sourceTree = ""; }; 1731F02A15EE09E000D973EB /* FLXPostgresConnectionParameters.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLXPostgresConnectionParameters.m; sourceTree = ""; }; 1731F10615F1A52B00D973EB /* FLXPostgresTypeDateTimeHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLXPostgresTypeDateTimeHandler.h; sourceTree = ""; }; @@ -192,6 +196,15 @@ path = lib; sourceTree = ""; }; + 1724CC8715FB4BF400AB2291 /* Domain */ = { + isa = PBXGroup; + children = ( + 1724CC9015FB4CC200AB2291 /* FLXTimeTZ.h */, + 1724CC9115FB4CC200AB2291 /* FLXTimeTZ.m */, + ); + name = Domain; + sourceTree = ""; + }; 1731F50415F369E400D973EB /* Query */ = { isa = PBXGroup; children = ( @@ -257,6 +270,7 @@ children = ( 173D4F5615BAD3030007F267 /* FLXPostgresTypes.h */, 173D4EA015BAB2A80007F267 /* FLXPostgresTypeHandlerProtocol.h */, + 1724CC8715FB4BF400AB2291 /* Domain */, 17F5B62215C7D1DA006DA689 /* Handlers */, ); name = Types; @@ -355,6 +369,7 @@ 1731F02B15EE09E000D973EB /* FLXPostgresConnectionParameters.h in Headers */, 1731F10815F1A52B00D973EB /* FLXPostgresTypeDateTimeHandler.h in Headers */, 1731F52E15F48BA500D973EB /* FLXPostgresError.h in Headers */, + 1724CC9215FB4CC200AB2291 /* FLXTimeTZ.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -425,6 +440,7 @@ 1731F02C15EE09E000D973EB /* FLXPostgresConnectionParameters.m in Sources */, 1731F10915F1A52B00D973EB /* FLXPostgresTypeDateTimeHandler.m in Sources */, 1731F52F15F48BA500D973EB /* FLXPostgresError.m in Sources */, + 1724CC9315FB4CC200AB2291 /* FLXTimeTZ.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Frameworks/PostgresKit/Source/FLXPostgresConnectionPrivateAPI.h b/Frameworks/PostgresKit/Source/FLXPostgresConnectionPrivateAPI.h index 53bcf4ee..8f0e6a6e 100644 --- a/Frameworks/PostgresKit/Source/FLXPostgresConnectionPrivateAPI.h +++ b/Frameworks/PostgresKit/Source/FLXPostgresConnectionPrivateAPI.h @@ -25,13 +25,12 @@ @interface FLXPostgresConnection (FLXPostgresConnectionPrivateAPI) - (PGconn *)postgresConnection; -- (void)_createConnectionParameters; @end @interface FLXPostgresConnection (FLXPostgresConnectionQueryPreparationPrivateAPI) -- (BOOL)_prepare:(FLXPostgresStatement *)statement num:(int)paramNum types:(FLXPostgresOid *)paramTypes; +- (BOOL)_prepare:(FLXPostgresStatement *)statement num:(NSInteger)paramNum types:(FLXPostgresOid *)paramTypes; @end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresConnectionQueryPreparation.m b/Frameworks/PostgresKit/Source/FLXPostgresConnectionQueryPreparation.m index 679237d1..deeefc1a 100644 --- a/Frameworks/PostgresKit/Source/FLXPostgresConnectionQueryPreparation.m +++ b/Frameworks/PostgresKit/Source/FLXPostgresConnectionQueryPreparation.m @@ -80,7 +80,7 @@ * * @return A BOOL indicating succes. Returns NO if there's no statement, statement name or current connection. */ -- (BOOL)_prepare:(FLXPostgresStatement *)statement num:(int)paramNum types:(FLXPostgresOid *)paramTypes +- (BOOL)_prepare:(FLXPostgresStatement *)statement num:(NSInteger)paramNum types:(FLXPostgresOid *)paramTypes { if (!statement || ![statement name] || ![self isConnected]) return NO; diff --git a/Frameworks/PostgresKit/Source/FLXPostgresResult.m b/Frameworks/PostgresKit/Source/FLXPostgresResult.m index 7136b819..303533bb 100644 --- a/Frameworks/PostgresKit/Source/FLXPostgresResult.m +++ b/Frameworks/PostgresKit/Source/FLXPostgresResult.m @@ -248,7 +248,7 @@ static NSString *FLXPostgresResultError = @"FLXPostgresResultError"; { PQclear(_result); - for (unsigned int i = 0; i < _numberOfFields; i++) [_fields[i] release]; + for (NSUInteger i = 0; i < _numberOfFields; i++) [_fields[i] release]; free(_fields); free(_typeHandlers); diff --git a/Frameworks/PostgresKit/Source/FLXPostgresTypeDateTimeHandler.m b/Frameworks/PostgresKit/Source/FLXPostgresTypeDateTimeHandler.m index 1494478c..c60c1eb9 100644 --- a/Frameworks/PostgresKit/Source/FLXPostgresTypeDateTimeHandler.m +++ b/Frameworks/PostgresKit/Source/FLXPostgresTypeDateTimeHandler.m @@ -25,6 +25,7 @@ #import "FLXPostgresConnectionParameters.h" #import "FLXPostgresConnection.h" #import "FLXPostgresConnectionTypeHandling.h" +#import "FLXTimeTZ.h" static FLXPostgresOid FLXPostgresTypeDateTimeTypes[] = { @@ -40,8 +41,8 @@ static FLXPostgresOid FLXPostgresTypeDateTimeTypes[] = @interface FLXPostgresTypeDateTimeHandler () - (NSDate *)_dateFromResult:(const PGresult *)result atRow:(NSUInteger)row column:(NSUInteger)column; -- (NSDate *)_timeFromResult:(const PGresult *)result atRow:(NSUInteger)row column:(NSUInteger)column; -- (NSDate *)_timestmpFromResult:(const PGresult *)result atRow:(NSUInteger)row column:(NSUInteger)column; +- (id)_timeFromResult:(const PGresult *)result atRow:(NSUInteger)row column:(NSUInteger)column type:(FLXPostgresOid)type; +- (id)_timestmpFromResult:(const PGresult *)result atRow:(NSUInteger)row column:(NSUInteger)column type:(FLXPostgresOid)type; - (NSDate *)_dateFromComponents:(NSDateComponents *)components; @@ -78,10 +79,10 @@ static FLXPostgresOid FLXPostgresTypeDateTimeTypes[] = case FLXPostgresOidTime: case FLXPostgresOidTimeTZ: case FLXPostgresOidAbsTime: - return [self _timeFromResult:result atRow:row column:column]; + return [self _timeFromResult:result atRow:row column:column type:type]; case FLXPostgresOidTimestamp: case FLXPostgresOidTimestampTZ: - return [self _timestmpFromResult:result atRow:row column:column]; + return [self _timestmpFromResult:result atRow:row column:column type:type]; default: return nil; } @@ -115,21 +116,24 @@ static FLXPostgresOid FLXPostgresTypeDateTimeTypes[] = } /** - * Returns an NSDate created from a time value. + * Returns a native object created from a time value. * * @note The date part should be ignored as it's set to a default value. * * @param result The result to extract the value from. * @param row The row to extract the value from. * @param column The column to extract the value from. + * @type type The type to be converted from (handles times and times with a time zone). * - * @return The NSDate representation. + * @return The object representation. */ -- (NSDate *)_timeFromResult:(const PGresult *)result atRow:(NSUInteger)row column:(NSUInteger)column +- (id)_timeFromResult:(const PGresult *)result atRow:(NSUInteger)row column:(NSUInteger)column type:(FLXPostgresOid)type { PGtime time; - PQgetf(result, row, "%time", column, &time); + BOOL hasTimeZone = type == FLXPostgresOidTimeTZ; + + PQgetf(result, row, hasTimeZone ? "%timetz" : "%time", column, &time); NSDateComponents *components = [[NSDateComponents alloc] init]; @@ -142,29 +146,39 @@ static FLXPostgresOid FLXPostgresTypeDateTimeTypes[] = [components setMinute:time.min]; [components setSecond:time.sec]; - // TODO: handle timezone - - return [self _dateFromComponents:components]; + NSDate *date = [self _dateFromComponents:components]; + + return hasTimeZone ? [FLXTimeTZ timeWithDate:date timeZoneGMTOffset:time.gmtoff] : date; } /** - * Returns an NSDate created from a timestamp value. + * Returns a native object created from a timestamp value. * * @param result The result to extract the value from. * @param row The row to extract the value from. * @param column The column to extract the value from. + * @type type The type to be converted from (handles timestamps and timestamps with a time zone). * - * @return The NSDate representation. + * @return The object representation. */ -- (NSDate *)_timestmpFromResult:(const PGresult *)result atRow:(NSUInteger)row column:(NSUInteger)column +- (id)_timestmpFromResult:(const PGresult *)result atRow:(NSUInteger)row column:(NSUInteger)column type:(FLXPostgresOid)type { PGtimestamp timestamp; - PQgetf(result, row, "%timestamp", column, ×tamp); + BOOL hasTimeZone = type == FLXPostgresOidTimestampTZ; + + PQgetf(result, row, hasTimeZone ? "%timstamptz" : "%timestamp", column, ×tamp); + + FLXTimeTZ *timestampTZ = nil; + NSDate *date = [NSDate dateWithTimeIntervalSince1970:timestamp.epoch]; - // TODO: handle timezone + if (hasTimeZone) { + timestampTZ = [FLXTimeTZ timeWithDate:date timeZoneGMTOffset:timestamp.time.gmtoff]; + + [timestampTZ setHasDate:YES]; + } - return [NSDate dateWithTimeIntervalSince1970:timestamp.epoch]; + return hasTimeZone ? timestampTZ : date; } /** diff --git a/Frameworks/PostgresKit/Source/FLXPostgresTypeNumberHandler.m b/Frameworks/PostgresKit/Source/FLXPostgresTypeNumberHandler.m index 2f0fc2ad..5ea07047 100644 --- a/Frameworks/PostgresKit/Source/FLXPostgresTypeNumberHandler.m +++ b/Frameworks/PostgresKit/Source/FLXPostgresTypeNumberHandler.m @@ -47,7 +47,7 @@ static FLXPostgresOid FLXPostgresTypeNumberTypes[] = @implementation FLXPostgresTypeNumberHandler #pragma mark - -#pragma mark Integer & Unsigned Integer +#pragma mark Integer - (NSNumber *)_integerObjectFromBytes:(const void *)bytes length:(NSUInteger)length { diff --git a/Frameworks/PostgresKit/Source/FLXTimeTZ.h b/Frameworks/PostgresKit/Source/FLXTimeTZ.h new file mode 100644 index 00000000..a24fc2cf --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXTimeTZ.h @@ -0,0 +1,62 @@ +// +// $Id$ +// +// FLXTimeTZ.h +// PostgresKit +// +// Created by Stuart Connolly (stuconnolly.com) on September 8, 2012. +// Copyright (c) 2012 Stuart Connolly. All rights reserved. +// +// 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. + +/** + * @class FLXTimeTZ FLXTimeTZ.h + * + * @author Stuart Connolly http://stuconnolly.com + * + * Simple wrapper to represet a time or timestamp with an associated time zone. + */ +@interface FLXTimeTZ : NSObject +{ + BOOL _hasDate; + + NSDate *_date; + NSTimeZone *_timeZone; +} + +@property (readwrite, assign) BOOL hasDate; + +/** + * @property date The date instance that holds the time. + */ +@property (readonly) NSDate *date; + +/** + * @property timeZone The time zone of the associated time. + */ +@property (readonly) NSTimeZone *timeZone; + ++ (FLXTimeTZ *)timeWithDate:(NSDate *)date timeZoneGMTOffset:(NSUInteger)offset; + +- (id)initWithDate:(NSDate *)date timeZoneGMTOffset:(NSUInteger)offset; + +@end diff --git a/Frameworks/PostgresKit/Source/FLXTimeTZ.m b/Frameworks/PostgresKit/Source/FLXTimeTZ.m new file mode 100644 index 00000000..48c97aa9 --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXTimeTZ.m @@ -0,0 +1,84 @@ +// +// $Id$ +// +// FLXTimeTZ.m +// PostgresKit +// +// Created by Stuart Connolly (stuconnolly.com) on September 8, 2012. +// Copyright (c) 2012 Stuart Connolly. All rights reserved. +// +// 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 "FLXTimeTZ.h" + +@implementation FLXTimeTZ + +@synthesize hasDate = _hasDate; +@synthesize date = _date; +@synthesize timeZone = _timeZone; + +- (id)init +{ + return [self initWithDate:[NSDate date] timeZoneGMTOffset:[[NSTimeZone systemTimeZone] secondsFromGMT]]; +} + ++ (FLXTimeTZ *)timeWithDate:(NSDate *)date timeZoneGMTOffset:(NSUInteger)offset +{ + return [[[FLXTimeTZ alloc] initWithDate:date timeZoneGMTOffset:offset] autorelease]; +} + +/** + * Initialise a FLXTimeTZ with the supplied date and GMT offset. + * + * @param date The date to use. + * @param offset The GMT offset in seconds that the associated time zone is. + * + * @return The initialised instance. + */ +- (id)initWithDate:(NSDate *)date timeZoneGMTOffset:(NSUInteger)offset +{ + if ((self = [super init])) { + _date = date; + _hasDate = NO; + _timeZone = [NSTimeZone timeZoneForSecondsFromGMT:offset]; + } + + return self; +} + +#pragma mark - + +- (NSString *)description +{ + NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; + + [formatter setDateStyle:_hasDate ? NSDateFormatterMediumStyle : NSDateFormatterNoStyle]; + [formatter setTimeStyle:NSDateFormatterMediumStyle]; + + NSString *output = [formatter stringFromDate:_date]; + + [formatter release]; + + return [NSString stringWithFormat:@"%@ %@", output, [_timeZone abbreviation]]; +} + +@end -- cgit v1.2.3