'From Squeak3.8 of ''5 May 2005'' [latest update: #6665] on 31 August 2006 at 2:03:38 am'!
"Change Set:		TimeForSpeed
Date:			31 August 2006
Author:			Göran Krampe

Ok, so we were trying to figure out why our logger in Gjallar spent so much time in creating DateAndTime etc... after looking, tweaking, profiling and thinking I came up with this SpeedPack of changes.

I might have broken some subtleties (I don't think I have though) but the tests are still green. Glad for any indepth review.

And? Well, the speed differences are quite huge. :)

Lots of it comes from better Duration instance creation methods and from not going back and forth too much between nanos and seconds etc.

To see the difference, print this before and after, it tests some simple stuff and finally runs almost all (removed a few with Delays in them) Chronology tests 10 times:

| suite |
Smalltalk garbageCollect.
result _ {
	[10000 timesRepeat: [Date today]] timeToRun.
	[10000 timesRepeat: [DateAndTime now]] timeToRun.
	[10000 timesRepeat: [DateAndTime year: 2000 day: 120]] timeToRun.
	[10000 timesRepeat: [Duration weeks: 1]] timeToRun.
	[10000 timesRepeat: [Duration milliSeconds: 1]] timeToRun.
	[10000 timesRepeat: [(Duration seconds: 10 nanoSeconds: 1) ticks]] timeToRun.
} asOrderedCollection.
suite _ TestSuite new name: 'Chronology tests'.
(SystemOrganization superclassOrder: #'Kernel-Chronology-Tests') remove: StopwatchTest; 
	do: [:c | c addToSuiteFromSelectors: suite ].
suite tests remove: (suite tests detect: [:e | e selector = #testMonotonicity ]).
result add: [10 timesRepeat: [suite run]] timeToRun.
result
"!


!DateAndTime methodsFor: 'squeak protocol' stamp: 'gk 8/31/2006 00:55'!
asDuration
	"Answer the duration since midnight."

	^ Duration seconds: seconds nanoSeconds: nanos
! !

!DateAndTime methodsFor: 'private' stamp: 'gk 8/30/2006 22:59'!
normalize: i ticks: ticks base: base

	| tick div quo rem |
	tick := ticks at: i.
	div := tick digitDiv: base neg: tick negative.
	quo := (div at: 1) normalize.
	rem := (div at: 2) normalize.
	rem < 0 ifTrue: [ quo := quo - 1. rem := base + rem ].
	ticks at: (i-1) put: ((ticks at: i-1) + quo).
	ticks at: i put: rem
! !

!DateAndTime methodsFor: 'private' stamp: 'gk 8/30/2006 23:01'!
ticks: ticks offset: utcOffset
	"ticks is {julianDayNumber. secondCount. nanoSeconds}"

	self normalize: 3 ticks: ticks base: NanosInSecond.
	self normalize: 2 ticks: ticks base: SecondsInDay.

	jdn	_ ticks at: 1.
	seconds	_ ticks at: 2.
	nanos := ticks at: 3.
	offset := utcOffset! !


!DateAndTime class methodsFor: 'ansi protocol' stamp: 'gk 8/31/2006 00:49'!
clockPrecision
	"One nanosecond precision"

	^ Duration seconds: 0 nanoSeconds: 1
! !

!DateAndTime class methodsFor: 'ansi protocol' stamp: 'gk 8/30/2006 23:01'!
now
	^ self basicNew 
		ticks: (Array with: SqueakEpoch with: Time totalSeconds with: 0)
		offset: self localTimeZone offset
! !

!DateAndTime class methodsFor: 'smalltalk-80' stamp: 'gk 8/31/2006 01:00'!
fromSeconds: seconds
	"Answer a DateAndTime since the Squeak epoch: 1 January 1901"

	^ self basicNew ticks: (Array with: SqueakEpoch with: seconds with: 0) offset: self localOffset
! !


!Duration methodsFor: 'ansi protocol' stamp: 'gk 8/30/2006 23:42'!
days
	"Answer the number of days the receiver represents."

	^ seconds quo: SecondsInDay
! !

!Duration methodsFor: 'private' stamp: 'gk 8/31/2006 01:33'!
seconds: secondCount nanoSeconds: nanoCount 
	"Private - only used by Duration class"

	seconds _ secondCount.
	nanos _ nanoCount rounded! !

!Duration methodsFor: 'private' stamp: 'gk 8/30/2006 23:41'!
ticks
	"Answer an array {days. seconds. nanoSeconds}. Used by DateAndTime and Time."

	| days |
	days _ self days.
	^ Array 
		with: days
		with: seconds - (days * SecondsInDay)
		with: nanos! !


!Duration class methodsFor: 'ansi protocol' stamp: 'gk 8/31/2006 01:09'!
days: days hours: hours minutes: minutes seconds: seconds

	^ self days: days hours: hours minutes: minutes seconds: seconds nanoSeconds: 0! !

!Duration class methodsFor: 'ansi protocol' stamp: 'gk 8/30/2006 23:18'!
days: days seconds: seconds

	^ self basicNew seconds: days * SecondsInDay + seconds nanoSeconds: 0
! !

!Duration class methodsFor: 'ansi protocol' stamp: 'gk 8/31/2006 01:34'!
seconds: seconds

	^ self seconds: seconds nanoSeconds: 0
! !

!Duration class methodsFor: 'ansi protocol' stamp: 'gk 8/31/2006 00:09'!
zero

	^ self basicNew seconds: 0 nanoSeconds: 0
! !

!Duration class methodsFor: 'squeak protocol' stamp: 'gk 8/31/2006 01:25'!
days: aNumber

	^ self seconds: aNumber * SecondsInDay nanoSeconds: 0! !

!Duration class methodsFor: 'squeak protocol' stamp: 'gk 8/31/2006 01:26'!
days: days hours: hours minutes: minutes seconds: seconds nanoSeconds: nanos	

 	^ self seconds: ((days * SecondsInDay) 
						+ (hours * SecondsInHour)
							+ (minutes * SecondsInMinute) 
								+ seconds)
		nanoSeconds: nanos
! !

!Duration class methodsFor: 'squeak protocol' stamp: 'gk 8/31/2006 01:07'!
fromString: aString

	^ self readFrom: (ReadStream on: aString)
! !

!Duration class methodsFor: 'squeak protocol' stamp: 'gk 8/31/2006 01:26'!
hours: aNumber

	^ self seconds: aNumber * SecondsInHour nanoSeconds: 0! !

!Duration class methodsFor: 'squeak protocol' stamp: 'gk 8/31/2006 01:35'!
milliSeconds: milliCount
	"Since seconds is 0 we can call the instance directly."

	^ self basicNew seconds: 0 nanoSeconds: milliCount * NanosInMillisecond! !

!Duration class methodsFor: 'squeak protocol' stamp: 'gk 8/31/2006 01:27'!
minutes: aNumber

	^ self seconds: aNumber * SecondsInMinute nanoSeconds: 0! !

!Duration class methodsFor: 'squeak protocol' stamp: 'gk 8/31/2006 01:30'!
nanoSeconds: nanos
	"This method is slow. If you have nanos less than 10^6 you should use #seconds:nanoSeconds: instead."

	| quo |
	quo _ nanos quo: NanosInSecond.
	^ self basicNew
		seconds: quo
		nanoSeconds: nanos - (quo * NanosInSecond)! !

!Duration class methodsFor: 'squeak protocol' stamp: 'gk 8/31/2006 01:32'!
seconds: seconds nanoSeconds: nanos

	^ self basicNew
		seconds: seconds truncated
		nanoSeconds: seconds fractionPart * NanosInSecond + nanos! !

!Duration class methodsFor: 'squeak protocol' stamp: 'gk 8/30/2006 23:20'!
weeks: aNumber

	^ self days: (aNumber * 7) seconds: 0
! !


!Time methodsFor: 'private' stamp: 'gk 8/31/2006 00:20'!
seconds: secondCount
	"Private - only used by Time class."

	seconds _ secondCount.
	nanos _ 0! !

!Time methodsFor: 'private' stamp: 'gk 8/31/2006 00:20'!
seconds: secondCount nanoSeconds: nanoCount 
	"Private - only used by Time class."

	seconds _ secondCount.
	nanos _ nanoCount! !

!Time methodsFor: 'private' stamp: 'gk 8/30/2006 23:28'!
ticks: anArray
	"ticks is an Array: { days. seconds. nanoSeconds }"

	seconds _ anArray at: 2.
	nanos _ anArray at: 3! !


!Time class methodsFor: 'squeak protocol' stamp: 'gk 8/31/2006 00:39'!
seconds: seconds
	"Answer a Time from midnight."

	^ self basicNew ticks: (Duration seconds: seconds) ticks! !

!Time class methodsFor: 'squeak protocol' stamp: 'gk 8/30/2006 23:29'!
seconds: seconds nanoSeconds: nanoCount
	"Answer a Time from midnight."

	^ self basicNew
		ticks: (Duration seconds: seconds nanoSeconds: nanoCount) ticks
! !


!TimeTest methodsFor: 'Tests' stamp: 'gk 8/31/2006 00:47'!
testSqueakInquiries
	| timewords totalseconds condensed corrected |
	self assert: 
		(self timeClass namesForTimes: #(2 10000023 10000026))
			= #('January, 1901' 'April, 1901, 4/26/1901, 5:47 pm' 'April, 1901, 4/26/1901, 5:47 pm').

	timewords := #(0.5 30 62 130 4000 10000 60000 86401) 
		collect: [ :ss | self timeClass humanWordsForSecondsAgo: ss ].
	self assert: 
		timewords = #('a second ago' '30 seconds ago' 'a minute ago' '2 minutes ago' 
			'an hour ago' '2 hours ago' '16 hours ago' 'yesterday').

	totalseconds :=  self timeClass totalSeconds.
	condensed := self timeClass condenseBunches: 
		(#(20 400 401  20000 20200 20300 40000 45000  200000 201000 202000) 
			collect: [:tt | totalseconds - tt]).
	corrected := condensed collect: [ :e | totalseconds - e ].
	self
		assert: (corrected includesAllOf: #(20 400 401 20000 40000 45000 200000)).
! !


!Week class methodsFor: 'squeak protocol' stamp: 'gk 8/30/2006 15:10'!
starting: aDateAndTime duration: aDuration
	"Override - the duration is always one week.
	 Week will start from the Week class>>startDay"

	| midnight delta adjusted |
	midnight _ aDateAndTime asDateAndTime midnight.
	delta _ ((midnight dayOfWeek + 7 - (DayNames indexOf: self startDay)) rem: 7) abs.
	adjusted _ midnight - (Duration days: delta seconds: 0).

	^ super starting: adjusted duration: (Duration weeks: 1).! !

