I've finally got this to the point where I think it can be submitted. The previous version missed a case (sql between). To keep backward compatibility with earlier versions of UDBC SQLite3 I've sacrificed a bit of conformaty and made the representation: YYYY-MM-DD HH:MM:SS.ssssss (the difference being microseconds instead of millseconds). My limited interoperability testing (Python / Django) suggests this shouldn't be a problem. The tricky bit is that it affects four different packages: 1. UDBC-SQLite-Base (UDBCSQLite3Statement, PierceNg.49) 2. Glorp (UDBCSQLite3Platform, AlistairGrant.129) 3. Kernel (DateAndTime, TheIntegrator.2293) 4. Kernel-Tests (DateAndTimeTest, TheIntegrator.65) I assume that I can create a fogbugz issue and submit a single slice that will contain the changes to DateAndTime and DateAndTimeTest for Pharo 6. What's the best way to submit this for Pharo 5? The Glorp changes are dependent on the Kernel changes, and the UDBC-SQLite-Base changes are dependent on the Glorp changes. I guess that for Glorp, the best way is to include the Kernel changes as an extension for Pharo 5. How is a dependency such as that between UDBC-SQLite-Base and Glorp normally handled? Thanks! Alistair 'From Pharo5.0 of 16 April 2015 [Latest update: #50760] on 10 July 2016 at 10:29:14.682983 am'! !DateAndTime class methodsFor: 'input' stamp: 'AlistairGrant 6/25/2016 22:55'! readFrom: aStream defaultOffset: defaultOffset "Parse and return a new DateAndTime instance from stream, as a Date, an optional Time and an optional TimeZone offset. The time defaults to midnight, the timezone to defaultOffset" "self readFrom: '2013-03-04T23:47:52.876+01:00' readStream" | date time offset | date := Date readFrom: aStream. [ aStream atEnd or: [ '0123456789Z+-' includes: aStream peek ] ] whileFalse: [ aStream next ]. ('0123456789' includes: aStream peek) ifTrue: [ time := Time readFrom: aStream ] ifFalse: [ time := Time midnight ]. aStream skipSeparators. offset := self readTimezoneOffsetFrom: aStream default: defaultOffset. ^ self year: date year month: date monthIndex day: date dayOfMonth hour: time hour minute: time minute second: time second nanoSecond: time nanoSecond offset: offset! ! !DateAndTime class methodsFor: 'input' stamp: 'AlistairGrant 6/25/2016 22:55'! readFrom: aStream "Parse and return a new DateAndTime instance from stream, as a Date, an optional Time and an optional TimeZone offset. The time defaults to midnight, the timezone to the local offset" "self readFrom: '2013-03-04T23:47:52.876+01:00' readStream" ^self readFrom: aStream defaultOffset: self localOffset.! ! !DateAndTime class methodsFor: 'input' stamp: 'AlistairGrant 6/25/2016 22:47'! readTimezoneOffsetFrom: stream default: defaultOffset "Read and return an optional timezone offset in the form of [+|-]hh[[separator]mm[[separator]ss]] or Z from stream as a duration. If there is no offset, return the defaultOffset." | sign hour minute second | (stream peekFor: $Z) ifTrue: [ ^ Duration zero ]. hour := minute := second := 0. ^ ('+-' includes: stream peek) ifTrue: [ sign := stream next = $- ifTrue: [ -1 ] ifFalse: [ 1 ]. hour := self readTwoDigitIntegerFrom: stream. (self readOptionalSeparatorFrom: stream) ifNotNil: [ minute := self readTwoDigitIntegerFrom: stream. (self readOptionalSeparatorFrom: stream) ifNotNil: [ second := Integer readFrom: stream ] ]. Duration seconds: sign * ((hour * 3600) + (minute * 60) + second) ] ifFalse: [ defaultOffset ]! ! !DateAndTime class methodsFor: 'input' stamp: 'AlistairGrant 6/25/2016 22:47'! readTimezoneOffsetFrom: stream "Read and return an optional timezone offset in the form of [+|-]hh[[separator]mm[[separator]ss]] or Z from stream as a duration. If there is no offset, return the local offset." ^self readTimezoneOffsetFrom: stream default: self localOffset! ! !DateAndTimeTest methodsFor: 'tests - offset' stamp: 'AlistairGrant 7/3/2016 21:18'! testReadFromDefaultOffsetNotSpecified "self debug: #testReadFromDefaultOffsetSpecified" self assert: (DateAndTime readFrom: '2016-07-03T21:16:16.708241' readStream defaultOffset: Duration zero) offset printString equals: '0:00:00:00'. ! ! !DateAndTimeTest methodsFor: 'tests - offset' stamp: 'AlistairGrant 7/3/2016 21:17'! testReadFromDefaultOffsetSpecified "self debug: #testReadFromDefaultOffsetSpecified" self assert: (DateAndTime readFrom: '2016-07-03T21:16:16.708241+02:00' readStream defaultOffset: Duration zero) offset printString equals: '0:02:00:00'. ! ! 'From Pharo5.0 of 16 April 2015 [Latest update: #50760] on 10 July 2016 at 10:29:18.333218 am'! !UDBCSQLite3Platform methodsFor: 'conversion-times' stamp: 'AlistairGrant 7/3/2016 09:33'! toTimestamp: anObject for: aType "From DatabasePlatform." anObject isNil ifTrue: [^nil]. anObject class = Dialect timestampClass ifTrue: [^anObject asUTC]. anObject isInteger ifTrue: [^anObject]. ^anObject asDateAndTime asUTC! ! !UDBCSQLite3Platform methodsFor: 'conversion-times' stamp: 'AlistairGrant 7/3/2016 19:32'! printTimestamp: aTimestamp on: stream for: aType aTimestamp isNil ifTrue: [stream nextPutAll: 'NULL'. ^self]. stream nextPut: $'; nextPutAll: (self class datetimeToString: aTimestamp); nextPut: $'.! ! !UDBCSQLite3Platform class methodsFor: 'conversion' stamp: 'AlistairGrant 7/3/2016 19:30'! datetimeToString: aDateAndTime "Convert the supplied DateAndTime to something close to SQLite3's text timestamp format: YYYY-MM-DD HH:MM:SS.ssssss To keep backward compatibility with previous versions, microseconds are supplied instead of milliseconds (what SQLite3 officially supports). Most other applications seem to handle this OK." | string | aDateAndTime class = Dialect timestampClass ifFalse: [ ^aDateAndTime ]. string := UDBCSQLite3DateTimeString streamContents: [ :stream | | utc n len | utc := aDateAndTime asDateAndTime asUTC. utc printYMDOn: stream. stream nextPut: Character space. utc printHMSOn: stream. n := utc nanoSecond. n ~= 0 ifTrue: [ len := 9. [ n \\ 10 = 0 ] whileTrue: [ n := n / 10. len := len - 1 ]. stream nextPut: $.. n printOn: stream base: 10 length: len padded: true ] ]. ^string! ! !UDBCSQLite3Statement methodsFor: 'public - accessing' stamp: 'AlistairGrant 7/2/2016 22:54'! dateTimeAt: aColumn | utc | utc := DateAndTime readFrom: (self library stringFrom: handle at: aColumn) readStream defaultOffset: Duration zero. ^ utc offset: DateAndTime localOffset! ! !UDBCSQLite3Statement methodsFor: 'public - accessing' stamp: 'AlistairGrant 7/3/2016 19:42'! at: aColumn putDateTime: aDateTime "Put the supplied DateAndTime in column aColumn. SQLite3 supports three date/time formats: text, real (Julian day), integer (unix seconds). We use the ISO8601 text format: YYYY-MM-DD HH:MM:SS.SSS See https://www.sqlite.org/datatype3.html" | s | s := UDBCSQLite3Platform datetimeToString: aDateTime asDateAndTime. ^ self library with: handle at: aColumn putString: s! !