!
!
!
category: 'ANSI'
classmethod: Character
codePoint: anInteger

	^self withValue: anInteger.
%
category: 'ANSI'
method: Character
codePoint

	^self asciiValue.
%
category: 'Queries'
method: Class
allSubclasses

	^ClassOrganizer new allSubclassesOf: self.
%
category: 'Queries'
method: Class
subclasses

	^ClassOrganizer new subclassesOf: self.
%
category: 'Accessing'
method: PositionableStream
nextLine

| result cr lf char |
result := itsCollection species new.
cr := Character cr.
lf := Character lf.
[self atEnd not _and: [(char := self peek) ~~ cr _and: [char ~~ lf]]] whileTrue: [
  result add: self next
].
self atEnd not ifTrue: [
  self next.
  (self atEnd not _and: [char == cr _and: [self peek == lf]]) ifTrue: [
    self next.
  ].
].

^result
%
category: 'Accessing'
method: PositionableStream
peekFor: anObject

self peek = anObject ifTrue: [
  self next.
  ^true.
].
^false.
%
category: 'Enumerating'
method: Collection
anySatisfy: aBlock

	self do: [:each | 
		(aBlock value: each) ifTrue: [^true].
	].
	^false.
%
category: 'Enumerating'
method: Collection
allSatisfy: aBlock

	self do: [:each | 
		(aBlock value: each) ifFalse: [^false].
	].
	^true.
%
category: 'cache'
classmethod: ObsoleteTimeZone
cache

	^TimeZones.
%
category: 'Instance Creation'
classmethod: DateTime
now

"Creates and returns an instance of the receiver representing the current
 time."

^ self now: (ObsoleteTimeZone current).
%
expectvalue %String
run
(Globals includesKey: #DateAndTimeANSI) ifFalse: [
  ^	Magnitude 
	  _newKernelSubclass: 'DateAndTimeANSI'
	  instVarNames: #( seconds offset)
	  classVars: #()
	  classInstVars: #()
	  poolDictionaries: #[]
	  inDictionary: Globals
	  constraints: #[]
	  instancesInvariant: false
	  isModifiable: true
	  reservedOop: nil.
] ifTrue:[
  ^ 'class exists' 
]   
%
expectvalue %String
run
(Globals includesKey: #DateAndTime) ifFalse: [
  ^	DateAndTimeANSI 
	  _newKernelSubclass: 'DateAndTime'
	  instVarNames: #()
	  classVars: #()
	  classInstVars: #()
	  poolDictionaries: #[]
	  inDictionary: Globals
	  constraints: #[]
	  instancesInvariant: false
	  isModifiable: true
	  reservedOop: nil.
] ifTrue:[
  ^ 'class exists'
]  
%
expectvalue %String
run
(Globals includesKey: #Duration) ifFalse: [
  ^	Magnitude 
	  _newKernelSubclass: 'Duration'
	  instVarNames: #( seconds)
	  classVars: #()
	  classInstVars: #()
	  poolDictionaries: #[]
	  inDictionary: Globals
	  constraints: #[]
	  instancesInvariant: false
	  isModifiable: true
	  reservedOop: nil.
] ifTrue:[
  ^ 'class exists'
]  
%
expectvalue %String
run
(Globals includesKey: #TimeZoneInfo) ifFalse: [
  ^  Object 
	  _newKernelSubclass: 'TimeZoneInfo'
	  instVarNames: #( transitions leapSeconds stream
	                    types charcnt)
	  classVars: #()
	  classInstVars: #( default cache)  "VM dependency, 
		default must be first class instvar, see OC_TIMEZONE2007_CIV_DEFAULT in opalcls.ht "
	  poolDictionaries: #[]
	  inDictionary: Globals
	  constraints: #[]
	  instancesInvariant: false
	  isModifiable: true
	  reservedOop: nil.
] ifTrue:[
  ^ 'class exists'
]  
%
expectvalue %String
run
(Globals includesKey: #TimeZoneTransition) ifFalse: [
  ^	Object 
	  _newKernelSubclass: 'TimeZoneTransition'
	  instVarNames: #( transitionTime offsetFromUTC isDST
	                    abbreviation)
	  classVars: #()
	  classInstVars: #()
	  poolDictionaries: #[]
	  inDictionary: Globals
	  constraints: #[]
	  instancesInvariant: false
	  isModifiable: true
	  reservedOop: nil.
] ifTrue:[
  ^ 'class exists'
]  
%
expectvalue %String
run
"see bom.gs for new image creation"
| res |
TimeZone == ObsoleteTimeZone ifTrue: [
    ObsoleteTimeZone changeNameTo: #ObsoleteTimeZone.
    Globals at: #'TimeZone' put: nil.
    res := TimeZoneInfo 
	  _newKernelSubclass: 'TimeZone'
	  instVarNames: #( standardPrintString dstPrintString dstStartTimeList
	                    dstEndTimeList)
	  classVars: #()
	  classInstVars: #()  "VM dependency, must not define any new civs"
	  poolDictionaries: #[]
	  inDictionary: Globals
	  constraints: #[]
	  instancesInvariant: false
	  isModifiable: true
	  reservedOop: nil.
     ObsoleteTimeZone classHistory newVersion: TimeZone.
     TimeZone classHistory: ObsoleteTimeZone classHistory.
] ifFalse:[
  res := 'TimeZone == ObsoleteTimeZone    was false '
].  
^ res 
%
run
DateAndTimeANSI immediateInvariant.
DateAndTime immediateInvariant.
Duration immediateInvariant.
TimeZoneInfo immediateInvariant.
TimeZoneTransition immediateInvariant.
TimeZone immediateInvariant.
true.
%

! Remove existing behavior from DateAndTimeANSI
run
DateAndTimeANSI removeAllMethods.
DateAndTimeANSI class removeAllMethods.
true.
%
! ------------------- Class methods for DateAndTimeANSI
category: 'For Documentation Installation only'
classmethod: DateAndTimeANSI
installDocumentation

| doc txt |
doc := GsClassDocumentation newForClass: self.

txt := (GsDocText new) details:
'This protocol describes the behavior that is common to date 
time objects. Date time objects represent individual points 
in Coordinated Universal Time (UTC) as represented in an
implementation defined local time.

The exact properties of local times are unspecified. Local 
times may differ in their offset from UTC. A given local time 
may have different offsets from UTC at different points in time.

All dates and times in the UTC local time are in the Gregorian 
calendar. Date times prior to the adoption of the Gregorian 
calendar are given in the retrospective astronomical Gregorian 
calendar. The year 1 B.C. is astronomical Gregorian year 0. 
The year 2 B.C. is astronomical Gregorian year-1. The year 1 A.D. 
is astronomical Gregorian year 1. The offset of the UTC local 
time is zero.'.
doc documentClassWith: txt.

self description: doc.
%
category: 'DateAndTime factory'
classmethod: DateAndTimeANSI
clockPrecision
	"Answer a <Duration> such that after that period of time passes, 
	#now is guaranteed to give a different result.
	Ideally implementations should answer the least such duration."

	^Duration seconds: 1.
%
category: 'DateAndTime factory'
classmethod: DateAndTimeANSI
now
	"Synopsis
	Answer a <DateAndTime> representing the current date and time.
	Definition: <DateAndTime factory>
	Answer a <DateAndTime> representing the current date and time in the local time specified by the
	implementation.
	Return Values
	<DateAndTime> unspecified"

	^super new
		initializeAsNow;
		yourself.
%
category: 'DateAndTime factory'
classmethod: DateAndTimeANSI
year: year day: dayOfYear hour: hour minute: minute second: second
	"Synopsis
	Answer a <DateAndTime> which is the second second of the minute minute of the hour hour of
	the day dayOfYear of the year year of the astronomical Gregorian calendar in local time.
	Definition: <DateAndTime factory>
	Answer the least <DateAndTime> which is the second second of the minute minute of the hour
	hour of the day dayOfYear of the year year of the astronomical Gregorian calendar in the local
	time specified by the implementation. The second must be a <number> greater than or equal to 0
	and strictly less than 60. The minute must be an <integer> between 0 and 59 inclusive. The hour
	must be an <integer> between 0 and 23 inclusive. The day must be an <integer> between 1 and
	366 inclusive. An implementation may not impose any limits on the year other than those imposed
	on <integer> constants.
	It is possible that the time specified does not exist in the local time specified by the implementation.
	If there is a time change such that the local time is set forward and the time specified is in the
	interregnum, then that time does not exist in the local time. For example if at 02:00 in California on
	April 26, 1997 there is a time change that sets local time forward one hour, then the local time
	02:30 in California does not exist. Conversely if there is a time change that sets the local time back
	there are times which are ambiguous. For example if instead of setting the local time forward from
	02:00 to 03:00 it is set back to 01:00 the the local time 01:30 in California is ambiguious. The result
	is the least <DateAndTime> that conforms to the given parameters.
	It is worth noting that the year 1 B.C. is year 0 in the astronomical Gregorian calendar. Similarly the
	year 2 B.C. is year -1 in the astronomical Gregorian calendar and so on. The year 1 A.D. is year 1
	in the astronomical Gregorian calendar.
	Parameters
	year <integer> unspecified
	dayOfYear <integer> unspecified
	hour <integer> unspecified
	minute <integer> unspecified
	second <number> unspecified
	Return Values
	<DateAndTime> new
	Errors
	month is not between 1 and 12 inclusive.
	dayOfYear greater than the number of days in the year year of the astronomical Gregorian
	calendar.
	hour is not between 0 and 23 inclusive.
	minute is not between 0 and 59 inclusive.
	second is not greater than or equal to 0 and strictly less than 60.
	the time specified does not exist."

	^self 
		year: year 
		day: dayOfYear 
		hour: hour 
		minute: minute 
		second: second 
		offset: nil.
%
category: 'DateAndTime factory'
classmethod: DateAndTimeANSI
year: year day: dayOfYear hour: hour minute: minute second: second offset: offset
	"Synopsis
	Answer a <DateAndTime> which is the second second of the minute minute of the hour hour of
	the day dayOfYear of the year year of the astronomical Gregorian calendar offset from UTC by
	offset.
	Definition: <DateAndTime factory>
	Answer the least <DateAndTime> which is the second second of the minute minute of the hour
	hour of the day dayOfYear of the year year of the astronomical Gregorian calendar in the local
	time of the local time specified by the implementation. The second must be a <number> greater
	than or equal to 0 and strictly less than 60. The minute must be an <integer> between 0 and 59
	inclusive. The hour must be an <integer> between 0 and 23 inclusive. The day must be an
	<integer> between 1 and 366 inclusive. An implementation may not impose any limits on the year
	other than those imposed on <integer> constants.
	It is possible that the time specified does not exist in the local time defined by the implementation. If
	there is a time change such that the local time is set forward and the time specified is in the
	interregnum, then that time does not exist in the local time. For example if at 02:00 in California on
	April 26, 1997 there is a time change that sets local time forward one hour, then the local time
	02:30 in California does not exist. Conversely if there is a time change that sets the local time back
	there are times which are ambiguous. For example if instead of setting the local time forward from
	02:00 to 03:00 it is set back to 01:00 the the local time 01:30 in California is ambiguious. The result
	is the least <DateAndTime> that conforms to the given parameters.
	Parameters
	year <integer> unspecified
	dayOfYear <integer> unspecified
	hour <integer> unspecified
	minute <integer> unspecified
	second <number> unspecified
	offset <Duration> unspecified
	Return Values
	<DateAndTime> new
	Errors
	month is not between 1 and 12 inclusive.
	dayOfYear greater than the number of days in the year year of the astronomical Gregorian
	calendar.
	hour is not between 0 and 23 inclusive.
	minute is not between 0 and 59 inclusive.
	second is not greater than or equal to 0 and strictly less than the number of seconds in the minute
	specified."

	| isLeapYear daysInYear x days seconds |
	isLeapYear := year \\ 4 == 0 _and: [year \\ 100 ~~ 0 _or: [year \\ 400 == 0]].
	daysInYear := isLeapYear ifTrue: [366] ifFalse: [365].
	(1 <= dayOfYear and: [dayOfYear <= daysInYear]) ifFalse: [self error: 'Invalid date'].
	(0 <= second and: [second < 60]) ifFalse: [self error: 'Invalid time'].
	(0 <= minute and: [minute < 60]) ifFalse: [self error: 'Invalid time'].
	(0 <= hour and: [hour < 24]) ifFalse: [self error: 'Invalid time'].
	x := year - 1901.
	days := x * 365
		+ (x // 4)
		- (x // 100)
		+ (x + 300 "1901 - 1601" // 400)
		+ dayOfYear
		- 36526.		"set base to 2001-01-01"
	seconds := days * 24 + hour * 60 + minute * 60 + second.
	^self 
		secondsLocal: seconds
		offset: offset.
%
category: 'DateAndTime factory'
classmethod: DateAndTimeANSI
year: year month: month day: dayOfMonth hour: hour minute: minute second: second
"Synopsis
Answer a <DateAndTime> which is the second second of the minute minute of the hour hour of
the day dayOfMonth of the month month of the year year of the astronomical Gregorian calendar
in local time.
Definition: <DateAndTime factory>
Answer the least <DateAndTime> which is the second second of the minute minute of the hour
hour of the day dayOfMonth of the month month of the year year of the astronomical Gregorian
calendar in the local time specified by the implementation. The second must be a <number>
greater than or equal to 0 and strictly less than 60. The minute must be an <integer> between 0
and 59 inclusive. The hour must be an <integer> between 0 and 23 inclusive. The day must be an
<integer> between 1 and 31 inclusive. The month must be an <integer> between 1 and 12
inclusive. An implementation may not impose any limits on the year other than those imposed on
<integer> constants.
It is possible that the time specified does not exist in the local time defined by the implementation. If
there is a time change such that the local time is set forward and the time specified is in the
interregnum, then that time does not exist in the local time. For example if at 02:00 in California on
April 26, 1997 there is a time change that sets local time forward one hour, then the local time
02:30 in California does not exist. Conversely if there is a time change that sets the locale time
back there are times which are ambiguous. For example if instead of setting the local time forward
from 02:00 to 03:00 it is set back to 01:00 the the local time 01:30 in California is ambiguious. The
result is the least <DateAndTime> that conforms to the given parameters.
Parameters
year <integer> unspecified
month <integer> unspecified
dayOfMonth <integer> unspecified
hour <integer> unspecified
minute <integer> unspecified
second <number> unspecified
Return Values
<DateAndTime> new
Errors
month is not between 1 and 12 inclusive.
dayOfMonth greater than the number of days in the month month of year year of the
astronomical Gregorian calendar.
hour is not between 0 and 23 inclusive.
minute is not between 0 and 59 inclusive.
second is not greater than or equal to 0 and strictly less than 60.
the time specified does not exist."

	^self
		year: year 
		month: month 
		day: dayOfMonth 
		hour: hour 
		minute: minute 
		second: second 
		offset: nil.
%
category: 'DateAndTime factory'
classmethod: DateAndTimeANSI
year: year month: month day: dayOfMonth hour: hour minute: minute second: second offset: offset
	"Synopsis
	Answer a <DateAndTime> which is the second second of the minute minute of the hour hour of
	the day dayOfMonth of the month month of the year year of the astronomical Gregorian calendar
	offset from UTC by offset.
	Definition: <DateAndTime factory>
	Answer the least <DateAndTime> which is the second second of the minute minute of the hour
	hour of the day dayOfMonth of the month month of the year year of the astronomical Gregorian
	calendar offset from UTC by offset. The second must be a <number> greater than or equal to 0
	and strictly less than 60. The minute must be an <integer> between 0 and 59 inclusive. The hour
	must be an <integer> between 0 and 23 inclusive. The day must be an <integer> between 1 and
	31 inclusive. The month must be an <integer> between 1 and 12 inclusive. An implementation may
	not impose any limits on the year other than those imposed on <integer> constants.
	It is possible that the time specified does not exist in the local time defined by the implementation. If
	there is a time change such that the local time is set forward and the time specified is in the
	interregnum, then that time does not exist in the local time. For example if at 02:00 in California on
	April 26, 1997 there is a time change that sets local time forward one hour, then the local time
	02:30 in California does not exist. Conversely if there is a time change that sets the local time back
	there are times which are ambiguous. For example if instead of setting the local time forward from
	02:00 to 03:00 it is set back to 01:00 the the local time 01:30 in California is ambiguious. The result
	is the least <DateAndTime> that conforms to the given parameters.
	Parameters
	year <integer> unspecified
	month <integer> unspecified
	dayOfMonth <integer> unspecified
	hour <integer> unspecified
	minute <integer> unspecified
	second <number> unspecified
	offset <Duration> unspecified
	Return Values
	<DateAndTime> new
	Errors
	month is not between 1 and 12 inclusive.
	dayOfMonth greater than the number of days in the month month of year year of the
	astronomical Gregorian calendar.
	hour is not between 0 and 23 inclusive.
	minute is not between 0 and 59 inclusive.
	second is not greater than or equal to 0 and strictly less than 60."

	| isLeapYear dayOfYear daysPerMonth daysInMonth |
	(1 <= month and: [month <= 12]) ifFalse: [self error: 'Invalid date'].
	isLeapYear := year \\ 4 == 0 _and: [year \\ 100 ~~ 0 _or: [year \\ 400 == 0]].
	daysPerMonth := isLeapYear
		ifTrue: [#(31 29 31 30 31 30 31 31 30 31 30 31)]
		ifFalse: [#(31 28 31 30 31 30 31 31 30 31 30 31)].
	daysInMonth := daysPerMonth at: month.
	(1 <= dayOfMonth and: [dayOfMonth <= daysInMonth]) ifFalse: [self error: 'Invalid date'].
	dayOfYear := (#(0 31 59 90 120 151 181 212 243 273 304 334) at: month) + dayOfMonth.
	(month > 2 _and: [isLeapYear]) ifTrue: [dayOfYear := dayOfYear + 1].
	^self
		year: year 
		day: dayOfYear 
		hour: hour 
		minute: minute 
		second: second 
		offset: offset.
%
category: 'other'
classmethod: DateAndTimeANSI
fromString: aString
	"YYYY-MM-DDTHH:MM:SS+HH:MM"

	^self
		year:   (aString copyFrom:  1 to:  4) asNumber
		month:  (aString copyFrom:  6 to:  7) asNumber
		day:    (aString copyFrom:  9 to: 10) asNumber
		hour:   (aString copyFrom: 12 to: 13) asNumber
		minute: (aString copyFrom: 15 to: 16) asNumber
		second: (aString copyFrom: 18 to: 19) asNumber
		offset: (Duration
			days:    0 
			hours:   (aString copyFrom: 21 to: 22) asNumber * ((aString at: 20) = $+ ifTrue: [1] ifFalse: [-1])
			minutes: (aString copyFrom: 24 to: 25) asNumber
			seconds: 0)
%
category: 'other'
classmethod: DateAndTimeANSI
posixSeconds: aNumber offset: aDuration

	^super new
		posixSeconds: aNumber;
		offset: aDuration;
		yourself.
%
category: 'other'
classmethod: DateAndTimeANSI
secondsLocal: aNumber offset: aDuration

	^super new
		_secondsLocal: aNumber offset: aDuration;
		yourself.
%
category: 'other'
classmethod: DateAndTimeANSI
secondsSince2001

	self subclassResponsibility.
%
category: 'other'
classmethod: DateAndTimeANSI
secondsUTC: aNumber offset: aDuration

	^super new
		_secondsUTC: aNumber offset: aDuration;
		yourself.
%
category: 'instance creation'
classmethod: DateAndTimeANSI
new

"Disallowed"

self shouldNotImplement: #new
%

! ------------------- Instance methods for DateAndTimeANSI
category: 'comparing'
method: DateAndTimeANSI
hash

	^seconds hash.
%
category: 'DateAndTime'
method: DateAndTimeANSI
+ operand
	"Answer a <DateAndTime> that represents the UTC time that is operand after the receiver and
	whose local time is the same as the receiver's. If operand is less than <Duration factory> #zero,
	the result is the <DateAndTime> that is that is the absolute value of operand before the receiver."

	^self class 
		secondsUTC: self asSeconds + operand asSeconds
		offset: self offset.
%
category: 'DateAndTime'
method: DateAndTimeANSI
- operand
	"If operand is a <DateAndTime>, answer a <Duration> whose value is the period of time between
	the operand and the receiver. If operand is a <DateAndTime> prior to the receiver then the result
	is a <Duration> less than <Duration factory> #zero.
	If operand is a <Duration>, answer a new <DateAndTime> which represents the UTC time that is
	operand before the receiver and whose local time is the same as the receiver's. If operand is a
	duration less than <Duration factory> #zero then the result is a <DateAndTime> that is the
	absolute value of operand after the receiver."

	^(operand isKindOf: Duration) ifTrue: [
		self class 
			secondsUTC: self asSeconds - operand asSeconds
			offset: self offset.
	] ifFalse: [
		Duration seconds: self asSeconds - operand asSeconds.
	].
%
category: 'DateAndTime'
method: DateAndTimeANSI
< aDateAndTime
	"Synopsis
	Answer true if the receiver is less than operand. Answer false otherwise.
	Definition: <magnitude>
	Answer true if the receiver is less than operand with respect to the ordering defined for them.
	Answer false otherwise.
	It is erroneous if the receiver and operand are not comparable.
	The semantics of the natural ordering must be defined by refinement, which may also restrict the
	type of operand.
	Refinement: <DateAndTime>
	Answer true if the UTC time represented by operand follows the UTC time represented by the
	receiver. Answer false otherwise.
	If the offsets of the receiver and operand are the same then their order is determined by their
	lexical order in the sequence #year, #month, #day, #hour24, #minute, #second. If their
	offsets differ then result is the same as if receiver asUTC < operand asUTC were evaluated."

	^self asSeconds < aDateAndTime asSeconds.
%
category: 'DateAndTime'
method: DateAndTimeANSI
= aMagnitude
	"Synopsis
	Object equivalence test.
	Definition: <Object>
	This message tests whether the receiver and the comparand are equivalent objects at the time the
	message is processed. Return true if the receiver is equivalent to comparand. Otherwise return
	false.
	The meaning of 'equivalent' cannot be precisely defined but the intent is that two objects are
	considered equivalent if they can be used interchangeably. Conforming protocols may choose to
	more precisely define the meaning of 'equivalent.'
	The value of
	receiver = comparand
	is true if and only if the value of
	comparand = receiver
	would also be true. If the value of
	receiver = comparand
	is true then the receiver and comparand must have equivalent hash values. Thus if
	receiver = comparand
	is true then
	receiver hash = comparand hash
	must also be true.
	The equivalence of objects need not be temporally invariant. Two independent invocations of #=
	with the same receiver and operand objects may not always yield the same results. Note that a
	collection that uses #= to discriminate objects may only reliably store objects whose hash values
	do not change while the objects are contained in the collection.
	Refinement: <DateAndTime>
	Answer true if the comparand conforms to <DateAndTime> and if it represents the
	same UTC time as the receiver. Answer false otherwise. The local times of the receiver and
	operand are ignored."

	^(aMagnitude isKindOf: DateAndTime) _and: [self asSeconds = aMagnitude asSeconds].
%
category: 'DateAndTime'
method: DateAndTimeANSI
asLocal
	"Synopsis
	Answer a <DateAndTime> that represents the same UTC time as the receiver but in the local time
	specified by the implementation.
	Definition: <DateAndTime>
	Answer a <DateAndTime> that represents the same UTC time as the receiver but in the local time
	specified by the implementation.
	Return Values
	<DateAndTime> unspecified"

	^self class 
		secondsUTC: seconds
		offset: nil.
%
category: 'DateAndTime'
method: DateAndTimeANSI
asUTC
	"Synopsis
	Answer a <DateAndTime> that represents the same absolute time as the receiver but in the local
	time UTC.
	Definition: <DateAndTime>
	Answer a <DateAndTime> that represents the same absolute time as the receiver but in the local
	time UTC. The exact meaning of UTC local time is specified by the implementation. The UTC local
	time must use the Gregorian calendar. <DateAndTimes> representing UTC times prior to the
	adoption of the Gregorian calendar must use the retrospective astronomical Gregorian calendar. It
	is an invariant that
	<DateAndTime> asUTC offset = Duration zero.
	Return Values
	<DateAndTime> unspecified"

	^self class
		secondsLocal: seconds
		offset: Duration zero.

%
category: 'DateAndTime'
method: DateAndTimeANSI
dayOfMonth
	"Synopsis
	Answer the number of the day in the month in the local time of the receiver which includes the
	receiver.
	Definition: <DateAndTime>
	Answer an <integer> between 1 and 31 inclusive representing the number of the day in the month,
	in the local time of the receiver, which includes the receiver.
	Return Values
	<integer>unspecified"

	^self asParts at: 4.
%
category: 'DateAndTime'
method: DateAndTimeANSI
dayOfWeek
	"Synopsis
	Answer the number of the day in the week, in the local time of the receiver, which includes the
	receiver.
	Definition: <DateAndTime>
	Answer an <integer> between 1 and 7 inclusive representing the number of the day in the week, in
	the local time of the receiver, which includes the receiver. Sunday is 1, Monday is 2, and so on.
	Return Values
	<integer>unspecified"

	^seconds // 60 // 60 // 24 + 1 \\ 7 + 1  "1 January 2001 was a Monday"
%
category: 'DateAndTime'
method: DateAndTimeANSI
dayOfWeekAbbreviation
	"Synopsis
	Answer the abbreviation of the name, in the local time of the receiver, of the day of the week which
	includes the receiver.
	Definition: <DateAndTime>
	Answer an <readableString> which is the abbreviation of the name, in the local time of the
	receiver, of the day of the week which includes the receiver.
	Return Values
	<readableString> unspecified"

	^self dayOfWeekName copyFrom: 1 to: 3.
%
category: 'DateAndTime'
method: DateAndTimeANSI
dayOfWeekName
	"Synopsis
	Answer the name, in the local time of the receiver, of the day of the week which includes the
	receiver.
	Definition: <DateAndTime>
	Answer an <readableString> which is the name, in the local time of the receiver, of the day of the
	week which includes the receiver.
	Return Values
	<readableString> unspecified"

	^#('Sunday' 'Monday' 'Tuesday' 'Wednesday' 'Thursday' 'Friday' 'Saturday') at: self dayOfWeek.
%
category: 'DateAndTime'
method: DateAndTimeANSI
dayOfYear
	"Synopsis
	Answer the number of the day in the year, in the local time of the receiver, which includes the
	receiver.
	Definition: <DateAndTime>
	Answer an <integer> between 1 and 366 inclusive representing the number of the day in the year,
	in the local time of the receiver, which includes the receiver.
	Return Values
	<integer>unspecified"

	^self asParts at: 2.
%
category: 'DateAndTime'
method: DateAndTimeANSI
hour
	"Synopsis
	Answer the number of the hour in the day, in the local time of the receiver, which includes the
	receiver.
	Definition: <DateAndTime>
	Answer an <integer> between 0 and 23 inclusive representing the number of the hour in the day, in
	the local time of the receiver, which includes the receiver. It is implementation defined whether a
	given local time uses the 12-hour clock or the 24-hour clock, except that the UTC local time must
	use the 24-hour clock.
	Return Values
	<integer>unspecified"

	^seconds + offset // 60 // 60 \\ 24.
%
category: 'DateAndTime'
method: DateAndTimeANSI
hour12

	"Synopsis
	Answer the hour in the day in the 12-hour clock of the local time of the receiver.
	Definition: <DateAndTime>
	Answer an <integer> between 1 and 12 inclusive representing the hour in the day in the 12-hour
	clock of the local time of the receiver.
	Return Values
	<integer>unspecified"

	^self hour - 1 \\ 12 + 1.
%
category: 'DateAndTime'
method: DateAndTimeANSI
hour24
	"Synopsis
	Answer the hour in the day in the 24-hour clock of the local time of the receiver.
	Definition: <DateAndTime>
	Answer an <integer> between 0 and 23 inclusive representing the hour in the day in the 24-hour
	clock of the local time of the receiver.
	Return Values
	<integer>unspecified"

	^self hour.
%
category: 'DateAndTime'
method: DateAndTimeANSI
isLeapYear
	"Synopsis
	Test for leap year.
	Definition: <DateAndTime>
	Answer true if the year, which includes the receiver, in the local time of the receiver is a leap year,
	false otherwise.
	Two <DateAndTime> objects that are equal can give different results for #isLeapYear. Equality
	depends on their UTC time whereas #isLeapYear depends on their local time.
	Return Values
	<boolean> unspecified"

	| year |
	year := self year.
	^year \\ 4 == 0 _and: [year \\ 100 ~~ 0 _or: [year \\ 400 == 0]].
%
category: 'DateAndTime'
method: DateAndTimeANSI
meridianAbbreviation
	"Synopsis
	Answer the abbreviation, in the local time of the receiver, of the name of the half of the day, which
	includes the receiver.
	Definition: <DateAndTime>
	Answer a <readableString> that is the abbreviation, in the local time of the receiver, of the name of
	the half of the day, which includes the receiver.
	Return Values
	<readableString> unspecified"

	^self hour < 12
		ifTrue: ['AM']
		ifFalse: ['PM'].
%
category: 'DateAndTime'
method: DateAndTimeANSI
minute
	"Synopsis
	Answer the minute of the hour in the local time of the receiver.
	Definition: <DateAndTime>
	Answer an <integer> between 0 and 59 inclusive representing the minute of hour in the local time
	of the receiver.
	Return Values
	<integer>unspecified"

	^seconds + offset // 60 \\ 60.
%
category: 'DateAndTime'
method: DateAndTimeANSI
month
	"Synopsis
	Answer the number of the month in the year, in the local time of the receiver, which includes the
	receiver.
	Definition: <DateAndTime>
	Answer an <integer> between 1 and 12 inclusive representing the number of the month in the year,
	in the local time of the receiver, which includes the receiver.
	Return Values
	<integer>unspecified"

	^self asParts at: 3.
%
category: 'DateAndTime'
method: DateAndTimeANSI
monthAbbreviation
	"Synopsis
	Answer the abbreviation of the name of the month, in the local time of the receiver, which includes
	the receiver.
	Definition: <DateAndTime>
	Answer a <readableString> that is the abbreviation of the name of the month, in the local time of
	the receiver, which includes the receiver.
	Return Values
	<readableString> unspecified"

	^self monthName copyFrom: 1 to: 3.
%
category: 'DateAndTime'
method: DateAndTimeANSI
monthName
	"Synopsis
	Answer the name of the month, in the local time of the receiver, which includes the receiver.
	Definition: <DateAndTime>
	Answer a <readableString> that is the name of the month, in the local time of the receiver, which
	includes the receiver.
	Return Values
	<readableString> unspecified"

	^#(
		'January' 'February' 'March'
		'April' 'May' 'June'
		'July' 'August' 'September'
		'October' 'November' 'December'
	) at: self month.
%
category: 'DateAndTime'
method: DateAndTimeANSI
offset
	"Synopsis
	Answer the difference between the local time of the receiver and UTC at the time of the receiver.
	Definition: <DateAndTime>
	Answer a <Duration> representing the difference between the local time of the receiver and UTC at
	the time of the receiver.
	Return Values
	<Duration> unspecified"

	^Duration seconds: offset.
%
category: 'DateAndTime'
method: DateAndTimeANSI
offset: aDuration
	"Synopsis
	Answer a <DateAndTime> equivalent to the receiver but with its local time being offset from UTC
	by offset.
	Definition: <DateAndTime>
	Answer a <DateAndTime> equivalent to the receiver but with its local time being offset from UTC
	by offset. The impact of this on any other local time property is unspecified.
	Implementations may define a limit to the range of offset, but it must be at least
	-12:00:00 to 12:00:00 inclusive.
	It is an invariant that if x is a <Duration> in range then
	(<DateAndTime> offset: x) offset = x
	Parameters
	offset <Duration> unspecified
	Return Values
	<DateAndTime> unspecified
	Errors
	offset out of range"

	offset := aDuration asSeconds.
%
category: 'DateAndTime'
method: DateAndTimeANSI
printOn: aStream 
	"Synopsis
	Return a string that describes the receiver.
	Definition: <Object>
	A string consisting of a sequence of characters that describe the receiver are returned as the
	result.
	The exact sequence of characters that describe an object are implementation defined.
	Refinement: <DateAndTime>
	The returned string will represent the UTC time of the receiver offset from UTC by the offset of the
	receiver. All dates are in the astronomical Gregorian calendar. The result will be formatted as
	-YYYY-MM-DDThh:mm:ss.s+ZZ:zz:z where
	- is the <Character> $- if the year is less than 0 otherwise it is the <Character> that is
	returned from the message #space sent to the standard global Character
	[but see VAST which makes a case that the space should be excluded],
	YYYY is the year left zero filled to four places,
	- is the <Character> $-,
	MM is the month of the year left zero filled to two places,
	- is the <Character> $-,
	DD is the day of the month left zero filled to two places,
	T is the <Character> $T,
	hh is the hour in the 24-hour clock left zero filled to two places,
	: is the <Character> $:,
	mm is the minute left zero filled to two places,
	: is the <Character> $:,
	ss is the second left zero filled to two places,
	. is the <Character> $. and is present only if the fraction of a second is non-zero,
	s is the fraction of a second and is present only if non-zero,
	+ is the <Character> $+ if the offset is greater than or equal to <Duration factory> #zero
	and the <Character> $- if it is less,
	ZZ is the hours of the offset left zero filled to two places, and
	: is the <Character> $:,
	zz is the minutes of the offset left zero filled to two places,
	: is the <Character> $: and is present only if the seconds of the offset is non-zero,
	z is the seconds of the offset including any fractional part and is present only if nonzero.
	This format is based on ISO 8601 sections 5.3.3 and 5.4.1.
	Example: 8:33:14.321 PM EST January 5, 1200 B.C.
	-1199-01-05T20:33:14.321-05:00
	Example: 12 midnight UTC January 1, 2001 A.D.
	 2001-01-01T00:00:00+00:00
	Return Values
	<readableString> unspecified

	-YYYY-MM-DDThh:mm:ss.s+ZZ:zz:z"

	| array x |
	array := self asParts.
	x := array at: 1.
	x < 0 ifTrue: [aStream nextPut: $-].
	(x := x abs) < 1000 ifTrue: [aStream nextPut: $0].
	x < 100 ifTrue: [aStream nextPut: $0].
	x < 10 ifTrue: [aStream nextPut: $0].
	x printOn: aStream.
	aStream nextPut: $-.
	(x := array at: 3) < 10 ifTrue: [aStream nextPut: $0].
	x printOn: aStream.
	aStream nextPut: $-.
	(x := array at: 4) < 10 ifTrue: [aStream nextPut: $0].
	x printOn: aStream.
	aStream nextPut: $T.
	(x := array at: 5) < 10 ifTrue: [aStream nextPut: $0].
	x printOn: aStream.
	aStream nextPut: $:.
	(x := array at: 6) < 10 ifTrue: [aStream nextPut: $0].
	x printOn: aStream.
	aStream nextPut: $:.
	(x := array at: 7) < 10 ifTrue: [aStream nextPut: $0].
	x rounded printOn: aStream.
	self offset printOnHoursMinutes: aStream.
%
category: 'DateAndTime'
method: DateAndTimeANSI
printString
	"Synopsis
	Return a string that describes the receiver.
	Definition: <Object>
	A string consisting of a sequence of characters that describe the receiver are returned as the
	result.
	The exact sequence of characters that describe an object are implementation defined.
	Refinement: <DateAndTime>
	The returned string will represent the UTC time of the receiver offset from UTC by the offset of the
	receiver. All dates are in the astronomical Gregorian calendar. The result will be formatted as
	-YYYY-MM-DDThh:mm:ss.s+ZZ:zz:z where
	- is the <Character> $- if the year is less than 0 otherwise it is the <Character> that is
	returned from the message #space sent to the standard global Character
	[but see VAST which makes a case that the space should be excluded],
	YYYY is the year left zero filled to four places,
	- is the <Character> $-,
	MM is the month of the year left zero filled to two places,
	- is the <Character> $-,
	DD is the day of the month left zero filled to two places,
	T is the <Character> $T,
	hh is the hour in the 24-hour clock left zero filled to two places,
	: is the <Character> $:,
	mm is the minute left zero filled to two places,
	: is the <Character> $:,
	ss is the second left zero filled to two places,
	. is the <Character> $. and is present only if the fraction of a second is non-zero,
	s is the fraction of a second and is present only if non-zero,
	+ is the <Character> $+ if the offset is greater than or equal to <Duration factory> #zero
	and the <Character> $- if it is less,
	ZZ is the hours of the offset left zero filled to two places, and
	: is the <Character> $:,
	zz is the minutes of the offset left zero filled to two places,
	: is the <Character> $: and is present only if the seconds of the offset is non-zero,
	z is the seconds of the offset including any fractional part and is present only if nonzero.
	This format is based on ISO 8601 sections 5.3.3 and 5.4.1.
	Example: 8:33:14.321 PM EST January 5, 1200 B.C.
	-1199-01-05T20:33:14.321-05:00
	Example: 12 midnight UTC January 1, 2001 A.D.
	 2001-01-01T00:00:00+00:00
	Return Values
	<readableString> unspecified"

	^super printString.

%
category: 'DateAndTime'
method: DateAndTimeANSI
second
	"Synopsis
	Answer the second of the minute of the local time of the receiver.
	Definition: <DateAndTime>
	Answer a <number> greater than or equal to 0 and strictly less than 60 representing the second of
	the minute of the local time of the receiver.
	Return Values
	<number> unspecified"

	^seconds + offset \\ 60.
%
category: 'DateAndTime'
method: DateAndTimeANSI
timeZoneAbbreviation
	"Synopsis
	Answer the abbreviation of the name, in the local time of the receiver, of the time zone of the
	receiver.
	Definition: <DateAndTime>
	Answer a <readableString> that is the abbreviation of the name, in the local time of the receiver, of
	the time zone of the receiver.
	Return Values
	<readableString> unspecified"

	offset = 0 ifTrue: [^'UTC'].
	self error: 'not supported'.
%
category: 'DateAndTime'
method: DateAndTimeANSI
timeZoneName
	"Synopsis
	Answer the name in the local time of the receiver of the time zone of the receiver.
	Definition: <DateAndTime>
	Answer a <readableString> that is the name in the local time of the receiver of the time zone of the
	receiver.
	Return Values
	<readableString> unspecified"

	offset = 0 ifTrue: [^'Coordinated Universal Time'].
	self error: 'not supported'.
%
category: 'DateAndTime'
method: DateAndTimeANSI
year
	"Synopsis
	Answer the number of the year in the local time of the receiver which includes the receiver.
	Definition: <DateAndTime>
	Answer an <integer> the number of the year which includes the receiver.
	Return Values
	<integer>unspecified"

	^self asParts at: 1.
%
category: 'initialize-release'
method: DateAndTimeANSI
initializeAsNow

	seconds := self class secondsSince2001.
	offset := (TimeZoneInfo default transitionAtUTC: self) offsetFromUTC.
%
category: 'other'
method: DateAndTimeANSI
asDays

	| x |
	x := seconds.	"seconds from base"
	x := x // 60.	"minutes from base"
	x := x // 60.	"hours from base"
	x := x // 24.	"days from base"
	^x.
%
category: 'other'
method: DateAndTimeANSI
asParts
	"#(year dayOfYear monthIndex dayOfMonth hour minute second)"

	| x y array year isLeapYear numDaysArr |
	array := Array new: 7.
	x := seconds + offset.	"seconds from base"
	array at: 7 put: x \\ 60.
	x := x // 60.	"minutes from base"
	array at: 6 put: x \\ 60.
	x := x // 60.	"hours from base"
	array at: 5 put: x \\ 24.

	x := x // 24.	"days from base"
	year := 2001.

	y := x // 146097.
	year := y * 400 + year.
	x := x - (y * 146097).		"days since beginning of 400-year cycle"

	y := x // 36524 min: 3.
	year := y * 100 + year.
	x := x - (y * 36524).		"days since beginning of 100-year cycle"

	y := x // 1461 min: 96.
	year := y * 4 + year.
	x := x - (y * 1461).			"days since beginning of 4-year cycle"

	y := x // 365 min: 3.
	year := y + year.
	x := x - (y * 365) + 1.		"days since beginning of year"

	array 
		at: 1 put: year;
		at: 2 put: x;
		yourself.
	x <= 31 ifTrue: [
		^array
			at: 3 put: 1;
			at: 4 put: x;
			yourself.
	].
	x <= 59 ifTrue: [
		^array
			at: 3 put: 2;
			at: 4 put: x - 31;
			yourself.
	].
	isLeapYear := year \\ 4 == 0 _and: [year \\ 100 ~~ 0 _or: [year \\ 400 == 0]].

	isLeapYear ifTrue: [
		x == 60 ifTrue: [
			^array
				at: 3 put: 2;
				at: 4 put: 29;
				yourself.
		].
		x := x - 1.
	].
	array at: 3 put: 3.
	x := x - 59.

	numDaysArr := #(31 30 31 30 31 31 30 31 30 31)  .
	1 to: numDaysArr size do:[:j | | each |
		each := numDaysArr at: j .
		x <= each ifTrue: [
			^array
				at: 4 put: x;
				yourself.
		].
		array at: 3 put: (array at: 3) + 1.
		x := x - each.
	].
	self error: 'invalid date'.
%
category: 'other'
method: DateAndTimeANSI
asPosixSeconds

	^seconds + 978307200.
%
category: 'other'
method: DateAndTimeANSI
asSeconds

	^seconds.
%
category: 'other'
method: DateAndTimeANSI
posixSeconds: anInteger

	seconds := anInteger - 978307200.
%
category: 'other'
method: DateAndTimeANSI
_secondsLocal: aNumber offset: anObject

	seconds := aNumber.
	anObject == nil ifTrue: [
		offset := TimeZoneInfo default offsetAtLocal: self.
	] ifFalse: [
		offset  := anObject asSeconds.
	].
	seconds := seconds - offset.
%
category: 'other'
method: DateAndTimeANSI
_secondsUTC: aNumber offset: anObject

	seconds := aNumber.
	anObject == nil ifTrue: [
		offset := TimeZoneInfo default offsetAtUTC: self.
	] ifFalse: [
		offset  := anObject asSeconds.
	].
%

! Remove existing behavior from DateAndTime
run
DateAndTime removeAllMethods.
DateAndTime class removeAllMethods.
true.
%
! ------------------- Class methods for DateAndTime
category: 'For Documentation Installation only'
classmethod: DateAndTime
installDocumentation

| doc txt |
doc := GsClassDocumentation newForClass: self.

txt := (GsDocText new) details:
'This is the GemStone/S override to access System data.

This protocol describes the behavior that is common to date 
time objects. Date time objects represent individual points 
in Coordinated Universal Time (UTC) as represented in an
implementation defined local time.

The exact properties of local times are unspecified. Local 
times may differ in their offset from UTC. A given local time 
may have different offsets from UTC at different points in time.

All dates and times in the UTC local time are in the Gregorian 
calendar. Date times prior to the adoption of the Gregorian 
calendar are given in the retrospective astronomical Gregorian 
calendar. The year 1 B.C. is astronomical Gregorian year 0. 
The year 2 B.C. is astronomical Gregorian year-1. The year 1 A.D. 
is astronomical Gregorian year 1. The offset of the UTC local 
time is zero.'.
doc documentClassWith: txt.

self description: doc.
%
category: 'Overrides'
classmethod: DateAndTime
clockPrecision
	"Answer a <Duration> such that after that period of time passes, 
	#now is guaranteed to give a different result.
	Ideally implementations should answer the least such duration."

	| is64bit seconds |
	is64bit := 536870911 < SmallInteger maximumValue.
	seconds := is64bit
		ifTrue:  [1e-6]
		ifFalse: [1e-3].
	^Duration seconds: seconds.
%
category: 'Overrides'
classmethod: DateAndTime
secondsSince2001

	"^DateTime now asSecondsGmt - 3155760000."
	^System _timeGmtFloat - 978307200.
%
! ------------------- Instance methods for DateAndTime

! Remove existing behavior from Duration
run
Duration removeAllMethods.
Duration class removeAllMethods.
true.
%
! ------------------- Class methods for Duration
category: 'For Documentation Installation only'
classmethod: Duration
installDocumentation

| doc txt |
doc := GsClassDocumentation newForClass: self.

txt := (GsDocText new) details:
'Represents a length of time (from ANSI).'.
doc documentClassWith: txt.

self description: doc.
%
category: 'Duration Factory'
classmethod: Duration
days: days hours: hours minutes: minutes seconds: seconds
	"Synopsis
	Answer a <Duration> of the number of days, hours, minutes, and seconds.
	Definition: <Duration factory>
	Answer a <Duration> of the number of days, hours, minutes, and seconds. If any of the operands
	are negative, the result is smaller by that number of days, hours, minutes, or seconds as
	appropriate.
	Parameters
	days <integer> unspecified
	hours <integer> unspecified
	minutes <integer> unspecified
	seconds <number> unspecified
	Return Values
	<Duration> new"

	^self seconds: days * 24 + hours * 60 + minutes * 60 + seconds.
%
category: 'Duration Factory'
classmethod: Duration
zero
	"Synopsis
	Answer a <Duration> of zero length.
	Definition: <Duration factory>
	Answer a <Duration> representing a length of no time.
	Return Values
	<Duration> unspecified"

	^self seconds: 0.
%
category: 'other'
classmethod: Duration
seconds: seconds
	"Synopsis
	Answer a <Duration> which is seconds in length
	Definition: <Duration factory>
	If seconds is negative, answer a <Duration> that is abs (seconds) less than <Duration factory>
	#zero.
	Parameters
	seconds <number> unspecified
	Return Values
	<Duration> new"

	^self new
		_seconds: seconds;
		yourself.
%
! ------------------- Instance methods for Duration
category: 'Duration'
method: Duration
* operand
	"Synopsis
	Answer the result of multiplying the receiver by operand.
	Definition: <Duration>
	Answer a <Duration> that is the result of multiplying the receiver by operand.
	Parameters
	operand <number> unspecified
	Return Values
	<Duration> new"

	^self class seconds: seconds * operand.
%
category: 'Duration'
method: Duration
+ operand
	"Synopsis
	Answer the result of adding operand to the receiver.
	Definition: <Duration>
	Answer a <Duration> whose value is the result of adding the receiver and operand.
	Parameters
	operand <Duration> unspecified
	Return Values
	<Duration> new"

	^self class seconds: seconds + operand asSeconds.
%
category: 'Duration'
method: Duration
- operand
	"Synopsis
	Answer the result of subtracting the operand from the receiver.
	Definition: <Duration>
	Answer a <Duration> whose value is the result of subtracting operand from the receiver.
	Parameters
	operand <Duration> unspecified
	Return Values
	<Duration> new"

	^self class seconds: seconds - operand asSeconds.
%
category: 'Duration'
method: Duration
/ operand
	"Synopsis
	Answer the result of dividing the receiver by operand.
	Definition: <Duration>
	If operand is a <number> answer a new <Duration> whose value is the result of dividing the
	receiver by operand. If operand equals zero the ZeroDivide exception is signaled.
	If operand is a <Duration> answer a <number> whose value is the result of dividing the receiver
	by operand. If operand is <Duration factory> #zero the ZeroDivide exception is signaled.
	Parameters
	operand<number> | <Duration> unspecified
	Return Values
	<Duration> unspecified
	<number> unspecified"

	^(operand isKindOf: Duration)
		ifTrue: [seconds / operand asSeconds]
		ifFalse: [Duration seconds: seconds / operand].
%
category: 'Duration'
method: Duration
< operand
	"Synopsis
	Answer true if the receiver is less than operand. Answer false otherwise.
	Definition: <magnitude>
	Answer true if the receiver is less than operand with respect to the ordering defined for them.
	Answer false otherwise.
	It is erroneous if the receiver and operand are not comparable.
	The semantics of the natural ordering must be defined by refinement, which may also restrict the
	type of operand.
	Refinement: <Duration>
	Answer true if operand represents a <Duration> that is larger than the receiver. Answer false
	otherwise.
	Parameters
	operand <Duration> unspecified
	Return Values
	<boolean> unspecified"

	^seconds < operand asSeconds.
%
category: 'Duration'
method: Duration
= comparand
	"Synopsis
	Object equivalence test.
	Definition: <Object>
	This message tests whether the receiver and the comparand are equivalent objects at the time the
	message is processed. Return true if the receiver is equivalent to comparand. Otherwise return
	false.
	The meaning of 'equivalent' cannot be precisely defined but the intent is that two objects are
	considered equivalent if they can be used interchangeably. Conforming protocols may choose to
	more precisely define the meaning of 'equivalent'.
	The value of
	receiver = comparand
	is true if and only if the value of
	comparand = receiver
	would also be true. If the value of
	receiver = comparand
	is true then the receiver and comparand must have equivalent hash values. Or more formally:
	receiver = comparand
	receiver hash = comparand hash
	The equivalence of objects need not be temporally invariant. Two independent invocations of #=
	with the same receiver and operand objects may not always yield the same results. Note that a
	collection that uses #= to discriminate objects may only reliably store objects whose hash values
	do not change while the objects are contained in the collection.
	Refinement: <Duration>
	Answer true if the comparand is a <Duration> representing the same length of time as the
	receiver. Answer false otherwise.
	Parameters
	comparand <Object> uncaptured
	Return Values
	<boolean> unspecified"

	^(comparand isKindOf: Duration) _and: [seconds = comparand asSeconds].
%
category: 'Duration'
method: Duration
abs
	"Synopsis
	Answer the absolute value of the receiver.
	Definition: <Duration>
	If the receiver is greater than or equal to <Duration Factory> #zero answer a <Duration> which is
	equal to the receiver. Otherwise answer a <Duration> which has the same magnitude as the
	receiver but the opposite sign.
	Return Values
	<Duration> unspecified"

	^0 <= seconds
		ifTrue: [self]
		ifFalse: [self class seconds: seconds negated].
%
category: 'Duration'
method: Duration
asSeconds
	"Synopsis
	Answer the total number of seconds in the length of time represented by the receiver.
	Definition: <Duration>
	Answer the total number of seconds in the length of time represented by the receiver including any
	fractional part of a second. If the receiver is less than <Duration factory> #zero then the result will
	be less than 0.
	Return Values
	<number> unspecified"

	^seconds.
%
category: 'Duration'
method: Duration
days
	"Synopsis
	Answer the number of complete days in the receiver.
	Definition: <Duration>
	Answer the number of complete days in the receiver. If the receiver is less than <Duration factory>
	#zero then the result will be less than or equal to 0.
	Return Values
	<integer> unspecified"

	^seconds abs // 60 // 60 // 24 * seconds sign.
%
category: 'Duration'
method: Duration
hours
	"Synopsis
	Answer the number of complete hours in the receiver.
	Definition: <Duration>
	Answer an <integer> between -23 and 23 inclusive that represents the number of complete hours
	in the receiver, after the number of complete days has been removed. If the receiver is less than
	<Duration factory> #zero then the result will be less than or equal to 0.
	Return Values
	<integer>unspecified"

	^seconds abs // 60 // 60 \\ 24 * seconds sign.
%
category: 'Duration'
method: Duration
minutes
	"Synopsis
	Answer the number of complete minutes in the receiver.
	Definition: <time>
	Answer an <integer> between -59 and 59 inclusive that represents the number of complete
	minutes in the receiver, after the number of complete days and hours have been removed. If the
	receiver is less than <Duration factory> #zero then the result will be less than or equal to 0.
	Return Values
	<integer>unspecified"

	^seconds abs // 60 \\ 60 * seconds sign.
%
category: 'Duration'
method: Duration
negated
	"Synopsis
	Answer the negation of the receiver.
	Definition: <Duration>
	Answer a <Duration> which is of the same magnitude but opposite sign as the receiver.
	Return Values
	<Duration> unspecified"

	^self class seconds: seconds negated.
%
category: 'Duration'
method: Duration
negative
	"Synopsis
	Answer true if the receiver is less than <Duration factory> #zero.
	Definition: <Duration>
	Answer true if the receiver is less than <Duration factory> #zero, false otherwise.
	Return Values
	<boolean> unspecified"

	^seconds < 0.
%
category: 'Duration'
method: Duration
positive
	"Synopsis
	Answer true if the receiver is greater than or equal to <Duration factory> #zero.
	Definition: <Duration>
	Answer true if the receiver is greater than or equal to the <Duration factory> #zero, false
	otherwise.
	Return Values
	<boolean> unspecified"

	^0 <= seconds.
%
category: 'Duration'
method: Duration
printOn: aStream
	"Synopsis
	Return a string that describes the receiver.
	Definition: <Object>
	A string consisting of a sequence of characters that describe the receiver are returned as the
	result.
	The exact sequence of characters that describe an object is implementation defined.
	Refinement: <Duration>
	Answer a description of the receiver that is formatted as
	[-]D:HH:MM:SS[.S] where
	- is a minus sign if the receiver represents a length of time going from the future into
	the past,
	D is the number of complete days with leading zeros to fill one place,
	HH is the number of complete hours with leading zeros to fill two places,
	MM is the number of complete minutes with leading zeros to fill two places,
	SS is. the number of complete seconds with leading zeros to fill two places, and
	.S is the fractional part of the number of seconds, if any.
	Return Values
	<readableString> unspecified"

	seconds negative ifTrue: [aStream nextPut: $-].
	self days abs printOn: aStream.
	aStream nextPut: $:.
	self hours abs < 10 ifTrue: [aStream nextPut: $0].
	self hours abs printOn: aStream.
	aStream nextPut: $:.
	self minutes abs < 10 ifTrue: [aStream nextPut: $0].
	self minutes abs printOn: aStream.
	aStream nextPut: $:.
	self seconds asInteger abs < 10 ifTrue: [aStream nextPut: $0].
	self seconds asInteger abs printOn: aStream.
	(self seconds isKindOf: Integer) ifFalse: [
		| fraction |
		0 < (fraction := self seconds abs * 10 \\ 10) ifTrue: [
			aStream nextPut: $..
			(fraction + 0.5) truncated printOn: aStream.
		].
	].
%
category: 'Duration'
method: Duration
seconds
	"Synopsis
	Answer the number of seconds in the receiver.
	Definition: <Duration>
	Answer a <number> strictly greater than -60 and strictly less than 60 that represents the number of
	seconds in the receiver, after the complete days, hours, and minutes have been removed. If the
	receiver is less than <Duration factory> #zero then the result will be less than or equal to 0.
	Return Values
	<number> unspecified"

	^seconds abs \\ 60 * seconds sign.
%
category: 'other'
method: Duration
hash

	^seconds hash.
%
category: 'other'
method: Duration
printOnHoursMinutes: aStream

	aStream nextPut: (seconds negative ifTrue: [$-] ifFalse: [$+]).
	self hours abs < 10 ifTrue: [aStream nextPut: $0].
	self hours abs printOn: aStream.
	aStream nextPut: $:.
	self minutes abs < 10 ifTrue: [aStream nextPut: $0].
	self minutes abs printOn: aStream.
%
category: 'other'
method: Duration
_seconds: aNumber

	seconds := aNumber.
%

! Remove existing behavior from TimeZoneInfo
run
TimeZoneInfo removeAllMethods.
TimeZoneInfo class removeAllMethods.
true.
%
! ------------------- Class methods for TimeZoneInfo
category: 'For Documentation Installation only'
classmethod: TimeZoneInfo
installDocumentation

| doc txt |
doc := GsClassDocumentation newForClass: self.

txt := (GsDocText new) details:
'This is the abstract superclass that contains a dialect-independent
implementation of the TimeZone behavior. It reads a binary file
generated by the Olsen ZoneInfo database.'.
doc documentClassWith: txt.

self description: doc.
%
category: 'cache'
classmethod: TimeZoneInfo
cache

	^cache.
%
category: 'cache'
classmethod: TimeZoneInfo
cacheAt: aString

	cache == nil ifTrue: [cache := KeyValueDictionary new].
	^cache 
		at: aString
		ifAbsent: [nil].
%
category: 'cache'
classmethod: TimeZoneInfo
cacheAt: aString put: aTimeZoneInfo

	cache == nil ifTrue: [cache := KeyValueDictionary new].
	^cache 
		at: aString
		put: aTimeZoneInfo.
%
category: 'instance creation'
classmethod: TimeZoneInfo
fromStream: aStream

	^super new
		initialize: aStream;
		yourself.
%
category: 'instance creation'
classmethod: TimeZoneInfo
new

"Disallowed.  To create a new TimeZone, use another instance creation method."

self shouldNotImplement: #new
%
category: 'instance creation'
classmethod: TimeZoneInfo
new: anInteger

"Disallowed.  To create a new TimeZone, use another instance creation method."

self shouldNotImplement: #new:
%
category: 'other'
classmethod: TimeZoneInfo
sampleLosAngelesA

	^self fromStream: (ReadStream on: self sampleLosAngelesDataA).
%
category: 'other'
classmethod: TimeZoneInfo
sampleLosAngelesB

	^self fromStream: (ReadStream on: self sampleLosAngelesDataB).
%
category: 'other'
classmethod: TimeZoneInfo
sampleTokyo

	^self fromStream: (ReadStream on: self sampleTokyoData).
%
category: 'other'
classmethod: TimeZoneInfo
sampleLosAngelesDataA
	"without leap seconds and without 2007 fix"

^#( 84 90 105 102 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 4 0 0 0 4 0 0 0 0 0 0 0 185 0 0 0
 4 0 0 0 16 158 166 72 160 159 187 21 144 160 134 42 160 161 154 247
 144 203 137 26 160 210 35 244 112 210 97 38 16 214 254 116 32 216 128 173
 144 218 254 209 160 219 192 144 16 220 222 179 160 221 169 172 144 222 190 149
 160 223 137 142 144 224 158 119 160 225 105 112 144 226 126 89 160 227 73 82
 144 228 94 59 160 229 41 52 144 230 71 88 32 231 18 81 16 232 39 58
 32 232 242 51 16 234 7 28 32 234 210 21 16 235 230 254 32 236 177 247
 16 237 198 224 32 238 145 217 16 239 175 252 160 240 113 187 16 241 143 222
 160 242 127 193 144 243 111 192 160 244 95 163 144 245 79 162 160 246 63 133
 144 247 47 132 160 248 40 162 16 249 15 102 160 250 8 132 16 250 248 131
 32 251 232 102 16 252 216 101 32 253 200 72 16 254 184 71 32 255 168 42
 16 0 152 41 32 1 136 12 16 2 120 11 32 3 113 40 144 4 97 39
 160 5 81 10 144 6 65 9 160 7 48 236 144 7 141 67 160 9 16 206
 144 9 173 191 32 10 240 176 144 11 224 175 160 12 217 205 16 13 192 145
 160 14 185 175 16 15 169 174 32 16 153 145 16 17 137 144 32 18 121 115
 16 19 105 114 32 20 89 85 16 21 73 84 32 22 57 55 16 23 41 54
 32 24 34 83 144 25 9 24 32 26 2 53 144 26 242 52 160 27 226 23
 144 28 210 22 160 29 193 249 144 30 177 248 160 31 161 219 144 32 118 43
 32 33 129 189 144 34 86 13 32 35 106 218 16 36 53 239 32 37 74 188
 16 38 21 209 32 39 42 158 16 39 254 237 160 41 10 128 16 41 222 207
 160 42 234 98 16 43 190 177 160 44 211 126 144 45 158 147 160 46 179 96
 144 47 126 117 160 48 147 66 144 49 103 146 32 50 115 36 144 51 71 116
 32 52 83 6 144 53 39 86 32 54 50 232 144 55 7 56 32 56 28 5
 16 56 231 26 32 57 251 231 16 58 198 252 32 59 219 201 16 60 176 24
 160 61 187 171 16 62 143 250 160 63 155 141 16 64 111 220 160 65 132 169
 144 66 79 190 160 67 100 139 144 68 47 160 160 69 68 109 144 70 15 130
 160 71 36 79 144 71 248 159 32 73 4 49 144 73 216 129 32 74 228 19
 144 75 184 99 32 76 205 48 16 77 152 69 32 78 173 18 16 79 120 39
 32 80 140 244 16 81 97 67 160 82 108 214 16 83 65 37 160 84 76 184
 16 85 33 7 160 86 44 154 16 87 0 233 160 88 21 182 144 88 224 203
 160 89 245 152 144 90 192 173 160 91 213 122 144 92 169 202 32 93 181 92
 144 94 137 172 32 95 149 62 144 96 105 142 32 97 126 91 16 98 73 112
 32 99 94 61 16 100 41 82 32 101 62 31 16 102 18 110 160 103 30 1
 16 103 242 80 160 104 253 227 16 105 210 50 160 106 221 197 16 107 178 20
 160 108 198 225 144 109 145 246 160 110 166 195 144 111 113 216 160 112 134 165
 144 113 90 245 32 114 102 135 144 115 58 215 32 116 70 105 144 117 26 185
 32 118 47 134 16 118 250 155 32 120 15 104 16 120 218 125 32 121 239 74
 16 122 186 95 32 123 207 44 16 124 163 123 160 125 175 14 16 126 131 93
 160 127 142 240 16 0 1 0 1 2 3 1 0 1 0 1 0 1 0 1
 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
 0 1 0 1 0 1 0 1 0 1 255 255 157 144 1 0 255 255 143 128
 0 4 255 255 157 144 1 8 255 255 157 144 1 12 80 68 84 0 80 83
 84 0 80 87 84 0 80 80 84 0 0 0 0 1 0 0 0 1 ).
%
category: 'other'
classmethod: TimeZoneInfo
sampleLosAngelesDataB
	"without leap seconds and with 2007 fix"

^#( 84 90 105 102 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 4 0 0 0 4 0 0 0 0 0 0 0 185 0 0 0
 4 0 0 0 16 158 166 72 160 159 187 21 144 160 134 42 160 161 154 247
 144 203 137 26 160 210 35 244 112 210 97 38 16 214 254 116 32 216 128 173
 144 218 254 209 160 219 192 144 16 220 222 179 160 221 169 172 144 222 190 149
 160 223 137 142 144 224 158 119 160 225 105 112 144 226 126 89 160 227 73 82
 144 228 94 59 160 229 41 52 144 230 71 88 32 231 18 81 16 232 39 58
 32 232 242 51 16 234 7 28 32 234 210 21 16 235 230 254 32 236 177 247
 16 237 198 224 32 238 145 217 16 239 175 252 160 240 113 187 16 241 143 222
 160 242 127 193 144 243 111 192 160 244 95 163 144 245 79 162 160 246 63 133
 144 247 47 132 160 248 40 162 16 249 15 102 160 250 8 132 16 250 248 131
 32 251 232 102 16 252 216 101 32 253 200 72 16 254 184 71 32 255 168 42
 16 0 152 41 32 1 136 12 16 2 120 11 32 3 113 40 144 4 97 39
 160 5 81 10 144 6 65 9 160 7 48 236 144 7 141 67 160 9 16 206
 144 9 173 191 32 10 240 176 144 11 224 175 160 12 217 205 16 13 192 145
 160 14 185 175 16 15 169 174 32 16 153 145 16 17 137 144 32 18 121 115
 16 19 105 114 32 20 89 85 16 21 73 84 32 22 57 55 16 23 41 54
 32 24 34 83 144 25 9 24 32 26 2 53 144 26 242 52 160 27 226 23
 144 28 210 22 160 29 193 249 144 30 177 248 160 31 161 219 144 32 118 43
 32 33 129 189 144 34 86 13 32 35 106 218 16 36 53 239 32 37 74 188
 16 38 21 209 32 39 42 158 16 39 254 237 160 41 10 128 16 41 222 207
 160 42 234 98 16 43 190 177 160 44 211 126 144 45 158 147 160 46 179 96
 144 47 126 117 160 48 147 66 144 49 103 146 32 50 115 36 144 51 71 116
 32 52 83 6 144 53 39 86 32 54 50 232 144 55 7 56 32 56 28 5
 16 56 231 26 32 57 251 231 16 58 198 252 32 59 219 201 16 60 176 24
 160 61 187 171 16 62 143 250 160 63 155 141 16 64 111 220 160 65 132 169
 144 66 79 190 160 67 100 139 144 68 47 160 160 69 68 109 144 69 243 211
 32 71 45 138 16 71 211 181 32 73 13 108 16 73 179 151 32 74 237 78
 16 75 156 179 160 76 214 106 144 77 124 149 160 78 182 76 144 79 92 119
 160 80 150 46 144 81 60 89 160 82 118 16 144 83 28 59 160 84 85 242
 144 84 252 29 160 86 53 212 144 86 229 58 32 88 30 241 16 88 197 28
 32 89 254 211 16 90 164 254 32 91 222 181 16 92 132 224 32 93 190 151
 16 94 100 194 32 95 158 121 16 96 77 222 160 97 135 149 144 98 45 192
 160 99 103 119 144 100 13 162 160 101 71 89 144 101 237 132 160 103 39 59
 144 103 205 102 160 105 7 29 144 105 173 72 160 106 230 255 144 107 150 101
 32 108 208 28 16 109 118 71 32 110 175 254 16 111 86 41 32 112 143 224
 16 113 54 11 32 114 111 194 16 115 21 237 32 116 79 164 16 116 255 9
 160 118 56 192 144 118 222 235 160 120 24 162 144 120 190 205 160 121 248 132
 144 122 158 175 160 123 216 102 144 124 126 145 160 125 184 72 144 126 94 115
 160 127 152 42 144 0 1 0 1 2 3 1 0 1 0 1 0 1 0 1
 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
 0 1 0 1 0 1 0 1 0 1 255 255 157 144 1 0 255 255 143 128
 0 4 255 255 157 144 1 8 255 255 157 144 1 12 80 68 84 0 80 83
 84 0 80 87 84 0 80 80 84 0 0 0 0 1 0 0 0 1 ).
%
category: 'other'
classmethod: TimeZoneInfo
sampleTokyoData
	"without leap seconds and without 2007 fix"

^#( 84 90 105 102 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 2 0 0 0 2 0 0 0 0 0 0 0 1 0 0 0
 2 0 0 0 8 195 206 133 112 1 0 0 126 144 0 0 0 0 126 144
 0 4 67 74 84 0 74 83 84 0 0 0 0 0 )
%
category: 'samples'
classmethod: TimeZoneInfo
buildSample
"
	See #sampleLosAngelesDataB for an example of what this gives.
	(TimeZoneInfo buildSample).
"

	| file data stream i |
	file := GsFile 
		openOnServer: '/usr/share/lib/zoneinfo/America/Los_Angeles' 
		mode: 'rb'.
	[
		data := file contents.
	] ensure: [
		file close.
	].
	stream := WriteStream on: String new.
	stream nextPutAll: '#('.
	i := 1.
	data do: [:each | 
		stream space.
		each codePoint printOn: stream.
		(i := i + 1) \\ 20 == 0 ifTrue: [stream cr].
	].
	stream nextPutAll: ' ).'.
	^stream contents.
%
category: 'singleton'
classmethod: TimeZoneInfo
default

	default == nil ifTrue: [
		default := self sampleLosAngelesB.
	].
	^default.
%
category: 'singleton'
classmethod: TimeZoneInfo
default: aTimeZoneInfo

	default := aTimeZoneInfo.
%
! ------------------- Instance methods for TimeZoneInfo
category: 'instance creation'
method: TimeZoneInfo
initialize: aStream

	stream := aStream.
	self 
		readHeader;
		readTransitions;
		readTypes;
		readAbbreviations;
		updateTransitions;
		readLeapSeconds;
		yourself.
	stream  := nil.
	types   := nil.
	charcnt := nil.
%
category: 'instance creation'
method: TimeZoneInfo
nextByte

	| object |
	object := stream next.
	(object isKindOf: Integer) ifTrue: [^object].
	(object isKindOf: Character) ifTrue: [^object codePoint].
	self error: 'invalid file format'.
%
category: 'instance creation'
method: TimeZoneInfo
nextSignedInt

	| b0 b1 b2 w h |
	b0 := self nextByte.
	b1 := self nextByte.
	b2 := self nextByte.
	w :=  self nextByte.

	"Minimize LargeInteger arithmetic"
	h := ((b0 bitAnd: 16r7F) - (b0 bitAnd: 16r80) bitShift: 8) + b1.
	b2 == 0 ifFalse: [w := (b2 bitShift: 8) + w].
	h == 0 ifFalse: [w := (h bitShift: 16) + w].
	^w
%
category: 'instance creation'
method: TimeZoneInfo
nextUnsignedInt

	^(self nextByte * 16r1000000) +
		(self nextByte * 16r10000) +
		(self nextByte *   16r100) +
		self nextByte.
%
category: 'instance creation'
method: TimeZoneInfo
readAbbreviations

	| list index dict |
	list := (stream next: charcnt) collect: [:each | 
		(each isKindOf: Integer) 
			ifTrue: [Character codePoint: each]
			ifFalse: [each]
	].
	list := (String withAll: list) subStrings: (Character codePoint: 0).
	dict := Dictionary new.
	index := 1.
	list do: [:each | 
		dict 
			at: index
			put: each.
		index := index + each size + 1.
	].
	1 to: types size do: [:i | 
		| array offset |
		array := types at: i.
		offset := array at: 3.
		array at: 3 put: (dict at: offset ifAbsent: ['']).
	].
%
category: 'instance creation'
method: TimeZoneInfo
readHeader

	'TZif' do: [:each | 
		self nextByte = each codePoint ifFalse: [self error: 'Invalid file format'].
	].
	stream next: 16. "unused"
	stream next:  4. "ttisgmtcnt"
	stream next:  4. "ttisstdcnt"
	leapSeconds := Array new: self nextUnsignedInt.
	transitions := (Array new: self nextUnsignedInt) collect: [:ignore | TimeZoneTransition new].
	types       := Array new: self nextUnsignedInt.
	charcnt     := self nextUnsignedInt.
%
category: 'instance creation'
method: TimeZoneInfo
readLeapSeconds

	leapSeconds := leapSeconds collect: [:each | 
		Array
			with: self nextSignedInt
			with: self nextSignedInt.	
	].
%
category: 'instance creation'
method: TimeZoneInfo
readTransitions

	transitions do: [:each | each transitionTime: self nextSignedInt].
	transitions do: [:each | each localTimeTypeID: self nextByte + 1].
%
category: 'instance creation'
method: TimeZoneInfo
readTypes

	types := types collect: [:ignore | 
		Array
			with: self nextSignedInt	"UTC offset"
			with: self nextByte == 1		"is DST"
			with: self nextByte + 1.	"abbreviation"
	].
%
category: 'instance creation'
method: TimeZoneInfo
tzfile_h
	"from tzfile.h in ftp://elsie.nci.nih.gov/pub/tzcode2006n.tar.gz on 2006-10-30"

^'
#define	TZ_MAGIC	"TZif"

struct tzhead {
	char	tzh_magic[4];			/* TZ_MAGIC */
	char	tzh_version[1];		/* ''\0'' or ''2'' as of 2005 */
	char	tzh_reserved[15];		/* reserved--must be zero */
	char	tzh_ttisgmtcnt[4];	/* coded number of trans. time flags */
	char	tzh_ttisstdcnt[4];	/* coded number of trans. time flags */
	char	tzh_leapcnt[4];		/* coded number of leap seconds */
	char	tzh_timecnt[4];		/* coded number of transition times */
	char	tzh_typecnt[4];		/* coded number of local time types */
	char	tzh_charcnt[4];		/* coded number of abbr. chars */
};

/*
** . . .followed by. . .
**
**	tzh_timecnt (char [4])s		coded transition times a la time(2)
**	tzh_timecnt (unsigned char)s	types of local time starting at above
**	tzh_typecnt repetitions of
**		one (char [4])		coded UTC offset in seconds
**		one (unsigned char)	used to set tm_isdst
**		one (unsigned char)	that''s an abbreviation list index
**	tzh_charcnt (char)s		''\0''-terminated zone abbreviations
**	tzh_leapcnt repetitions of
**		one (char [4])		coded leap second transition times
**		one (char [4])		total correction after above
**	tzh_ttisstdcnt (char)s		indexed by type; if TRUE, transition
**					time is standard time, if FALSE,
**					transition time is wall clock time
**					if absent, transition times are
**					assumed to be wall clock time
**	tzh_ttisgmtcnt (char)s		indexed by type; if TRUE, transition
**					time is UTC, if FALSE,
**					transition time is local time
**					if absent, transition times are
**					assumed to be local time
*/'
%
category: 'instance creation'
method: TimeZoneInfo
updateTransitions

	transitions do: [:each | each typeList: types].
%
category: 'public'
method: TimeZoneInfo
detectLastTransition: aBlock

	transitions reverseDo: [:each | 
		(aBlock value: each) ifTrue: [^each].
	].
	^nil.
%
category: 'public'
method: TimeZoneInfo
offsetAtLocal: aDateAndTime

	| transition |
	transition := self transitionAtLocal: aDateAndTime.
	transition isNil ifTrue: [
		transition := transitions 
			detect: [:each | each isDST not]
			ifNone: [^0].
	].
	^transition offsetFromUTC.
%
category: 'public'
method: TimeZoneInfo
offsetAtUTC:   aDateAndTime

	| transition |
	transition := self transitionAtUTC: aDateAndTime.
	transition isNil ifTrue: [
		transition := transitions 
			detect: [:each | each isDST not]
			ifNone: [^0].
	].
	^transition offsetFromUTC.
%
category: 'public'
method: TimeZoneInfo
transitionAtLocal: aDateAndTime

	| seconds |
	seconds := aDateAndTime asPosixSeconds.
	^self detectLastTransition: [:each | each transitionTimeLocal <= seconds].
%
category: 'public'
method: TimeZoneInfo
transitionAtUTC:   aDateAndTime

	| seconds |
	seconds := aDateAndTime asPosixSeconds.
	^self detectLastTransition: [:each | each transitionTimeUTC   <= seconds].
%

! Remove existing behavior from TimeZoneTransition
run
TimeZoneTransition removeAllMethods.
TimeZoneTransition class removeAllMethods.
true.
%
! ------------------- Class methods for TimeZoneTransition
! ------------------- Instance methods for TimeZoneTransition
category: 'For Documentation Installation only'
classmethod: TimeZoneTransition
installDocumentation

| doc txt |
doc := GsClassDocumentation newForClass: self.

txt := (GsDocText new) details:
'Instances of this class represent records in a ZoneInfo database.
It describes characteristics of a transition to/from DST.'.
doc documentClassWith: txt.

self description: doc.
%
category: 'other'
method: TimeZoneTransition
= aTimeZoneTransition

	^(aTimeZoneTransition isKindOf: TimeZoneTransition)
		_and: [self transitionTimeUTC	= aTimeZoneTransition transitionTimeUTC
		_and: [self offsetFromUTC 		= aTimeZoneTransition offsetFromUTC
		_and: [self isDST 					= aTimeZoneTransition isDST
		_and: [self abbreviation 		= aTimeZoneTransition abbreviation]]]].
%
category: 'other'
method: TimeZoneTransition
hash

	^transitionTime hash.
%
category: 'other'
method: TimeZoneTransition
abbreviation

	^abbreviation.
%
category: 'other'
method: TimeZoneTransition
asDateAndTimeUTC

	^DateAndTime 
		posixSeconds: transitionTime
		offset: Duration zero.
%
category: 'other'
method: TimeZoneTransition
isDST

	^isDST.
%
category: 'other'
method: TimeZoneTransition
localTimeTypeID: anInteger

	offsetFromUTC := anInteger.
%
category: 'other'
method: TimeZoneTransition
offsetFromUTC

	^offsetFromUTC.
%
category: 'other'
method: TimeZoneTransition
printOn: aStream

	| dt duration |
	duration := Duration seconds: offsetFromUTC.
	dt := DateAndTime 
		posixSeconds: transitionTime
		offset: duration.
	dt printOn: aStream.
	aStream space.
	duration printOnHoursMinutes: aStream.
	aStream space.
	abbreviation printOn: aStream.
%
category: 'other'
method: TimeZoneTransition
transitionTime: anInteger

	transitionTime := anInteger.
%
category: 'other'
method: TimeZoneTransition
transitionTimeLocal

	^transitionTime + offsetFromUTC.
%
category: 'other'
method: TimeZoneTransition
transitionTimeUTC

	^transitionTime.
%
category: 'other'
method: TimeZoneTransition
typeList: anArray

	| data |
	data := anArray at: offsetFromUTC.
	offsetFromUTC := data at: 1.
	isDST := data at: 2.
	abbreviation := data at: 3.
%

! Remove existing behavior from TimeZone
run
TimeZone removeAllMethods.
TimeZone class removeAllMethods.
true.
%
! ------------------- Class methods for TimeZone
category: 'For Documentation Installation only'
classmethod: TimeZone
installDocumentation

| doc txt |
doc := GsClassDocumentation newForClass: self.

txt := (GsDocText new) details:
'This provides GemStone/S overrides and extensions to the
ANSI TimeZone behavior.'.
doc documentClassWith: txt.

self description: doc.
%
category: 'cache'
classmethod: TimeZone
for: aPlace

"Returns a TimeZoneInfo object for the specified place if it has been defined
 and stored in the class. Returns nil if it is not defined. 
 E.g. TimeZoneInfo for: #'America/Los_Angeles'."

^self cacheAt: aPlace.
%
category: 'cache'
classmethod: TimeZone
for: aPlace put: aTimeZone

"Stores aTimeZone as the TimeZoneInfo object identified with a particular
 place. A single TimeZoneInfo can be associated with any number of places.
 E.g. TimeZoneInfo for: #'America/Los_Angeles' put: aTimeZone; 
 TimeZoneInfo for: #'Europe/Berlin' put: aTimeZone. 
 Returns aTimeZone."

self cacheAt: aPlace put: aTimeZone.
^aTimeZone.
%
category: 'instance creation'
classmethod: TimeZone
timeDifferenceHrs: hours dstHrs: dstHrs atTimeHrs: startTimeHrs 
fromDayNum: startDay toDayNum: endDay on: nameOfDay beginning: startYear
stdPrintString: stdString dstPrintString: dstString

| oldTZ |
oldTZ := ObsoleteTimeZone
	timeDifferenceHrs: hours 
	dstHrs: dstHrs  
	atTimeHrs: startTimeHrs 
	fromDayNum: startDay  
	toDayNum: endDay  
	on: nameOfDay  
	beginning: startYear
	stdPrintString: stdString  
	dstPrintString: dstString.
^self fromObsolete: oldTZ.
%
category: 'instance creation'
classmethod: TimeZone
timeDifferenceMin: minutes dstMin: dstMins atTimeMin: startTimeMins 
fromDayNum: startDay toDayNum: endDay on: nameOfDay beginning: startYear
stdPrintString: stdString dstPrintString: dstString

| oldTZ |
oldTZ := ObsoleteTimeZone
	timeDifferenceMin: minutes 
	dstMin: dstMins 
	atTimeMin: startTimeMins 
	fromDayNum: startDay 
	toDayNum: endDay 
	on: nameOfDay 
	beginning: startYear
	stdPrintString: stdString 
	dstPrintString: dstString.
^self fromObsolete: oldTZ.
%
category: 'other'
classmethod: TimeZone
migrateNew

"Override default migrateNew behavior with #_basicNew because
we disallow #new (which is called by Behavior>>migrateNew)."

^ self _basicNew
%
category: 'singleton'
classmethod: TimeZone
current

"Returns the current session's current TimeZoneInfo. E.g. TimeZoneInfo current."

^ System _sessionStateAt: 17.
%
category: 'singleton'
classmethod: TimeZone
default

	^default.
%
category: 'singleton'
classmethod: TimeZone
default: aTimeZone

"Makes the specified time zone the default time zone. Returns aTimeZone.
 Must be SystemUser to do so."

aTimeZone _validateClass: TimeZone.
System myUserProfile userId = 'SystemUser' ifFalse:[
  self error:'instance only modifiable by SystemUser'.
  self _uncontinuableError .
].
super default: aTimeZone.
ObsoleteTimeZone default: aTimeZone.  "work-around for #36178"
^aTimeZone.
%
category: 'instance creation'
classmethod: TimeZone
fromGciPath: pathString

	^self fromGsFile: (GsFile open: pathString mode: 'rb' onClient: true).
%
category: 'instance creation'
classmethod: TimeZone
fromGemPath: pathString

	^self fromGsFile: (GsFile open: pathString mode: 'rb' onClient: false).
%
category: 'instance creation'
classmethod: TimeZone
fromGsFile: aGsFile

	| instance |
	[
		instance := self fromStream: aGsFile.
	] ensure: [
		aGsFile close.
	].
	^instance.
%
category: 'instance creation'
classmethod: TimeZone
fromLinux

	^TimeZone fromGemPath: '/etc/localtime'.
%
category: 'instance creation'
classmethod: TimeZone
fromObsolete: anObsoleteTimeZone
"
TimeZone fromObsolete: ObsoleteTimeZone default.
"

	^self basicNew
		initializeFromObsolete: anObsoleteTimeZone;
		yourself.
%
category: 'instance creation'
classmethod: TimeZone
fromPath: aString

	| block |
	block := [:prefix | 
		| path |
		path := prefix , aString.
		(GsFile existsOnServer: path) == true ifTrue: [
			^self fromGemPath: path.
		].
		(GsFile exists: path) == true ifTrue: [
			^self fromGciPath: path.
		].
	].
	block 
		value: '';	"full path"
		value: '/usr/share/lib/zoneinfo/';	"Solaris"
		value: '/usr/share/zoneinfo/';		"Linux"
		value: '$GEMSTONE/pub/timezone/etc/zoneinfo/';	"GemStone on linux/unix"
		value: '%GEMSTONE%\pub\timezone\etc\zoneinfo\'; "GemStone on Windows"
		yourself.
	^nil.
%
category: 'instance creation'
classmethod: TimeZone
fromOS

	| osName |
	osName := System gemVersionReport at: #osName.
	osName = 'Linux' ifTrue: [^self fromLinux  ].
	osName = 'SunOS' ifTrue: [^self fromSolaris].
	^nil.
%
category: 'instance creation'
classmethod: TimeZone
fromSolaris

	^TimeZone fromGemPath: '/usr/share/lib/zoneinfo/' , (System gemEnvironmentVariable: 'TZ').
%
! ------------------- Instance methods for TimeZone
category: 'internal'
method: TimeZone
initialize: aStream

	| transition |
	super initialize: aStream.
	transition := self detectLastTransition: [:each | each isDST].
	dstPrintString := transition isNil
		ifTrue:  ['']
		ifFalse: [transition abbreviation].
	transition := self detectLastTransition: [:each | each isDST not].
	standardPrintString := transition isNil
		ifTrue:  ['']
		ifFalse: [transition abbreviation].
	self initializeCache.
%
category: 'internal'
method: TimeZone
initializeCache

	dstStartTimeList := IntegerKeyValueDictionary new.
	dstEndTimeList := IntegerKeyValueDictionary new.
	self populateCacheFor: (1950 to: 2050).
%
category: 'internal'
method: TimeZone
initializeFromObsolete: anObsoleteTimeZone

	| base startArray endArray old new |
	base := (DateTime newWithYear: 1970 dayOfYear: 1 seconds: 0) asSeconds.
	transitions := OrderedCollection new.
	standardPrintString := anObsoleteTimeZone standardPrintString.
	dstPrintString := anObsoleteTimeZone dstPrintString.
	startArray := Array with: (Array
		with: anObsoleteTimeZone secondsFromGmt + anObsoleteTimeZone secondsForDst
		with: true
		with: dstPrintString).
	endArray := Array with: (Array
		with: anObsoleteTimeZone secondsFromGmt
		with: false
		with: standardPrintString).
	anObsoleteTimeZone secondsForDst = 0 ifTrue: [
		| year transition |
		year := (anObsoleteTimeZone yearStartDst max: 1900) printString.
		transition := (DateTime fromStringGmt: '01/01/' ,  year , ' 00:00:00' ) asSecondsGmt - base.
		transition := TimeZoneTransition new
			localTimeTypeID: 1;
			transitionTime: transition;
			typeList: endArray;
			yourself.
		transitions add: transition.
		self initializeCache.
		^self.
	].
	anObsoleteTimeZone yearStartDst to: 2030 do: [:year | 
		| start end |
		start := (anObsoleteTimeZone startOfDstFor: year) asSecondsGmt - base.
		end   := (anObsoleteTimeZone endOfDstFor:   year) asSecondsGmt - base.
		start := TimeZoneTransition new
			localTimeTypeID: 1;
			transitionTime: start;
			typeList: startArray;
			yourself.
		end := TimeZoneTransition new
			localTimeTypeID: 1;
			transitionTime: end;
			typeList: endArray;
			yourself.
		start transitionTimeUTC < end transitionTimeUTC ifTrue: [
			transitions add: start; add: end.
		] ifFalse: [
			transitions add: end; add: start.
		].
	].
	transitions := transitions asArray.
	self initializeCache.

	old := anObsoleteTimeZone.
	new := self.
	anObsoleteTimeZone yearStartDst to: 2030 do: [:year | 
		| oldStart oldEnd newStart newEnd |
		oldStart := old startOfDstFor: year.
		newStart := new startOfDstFor: year.
		oldEnd   := old endOfDstFor:   year.
		newEnd   := new endOfDstFor:   year.
		oldStart = newStart ifFalse: [self error: 'start date calculation error'].
		oldEnd   = newEnd   ifFalse: [self error: 'end date calculation error'].
	].
%
category: 'legacy protocol'
method: TimeZone
dateTimeClass

"Returns the class of DateTime objects that are to be created by the
 various methods in this class."

^ DateTime
%
category: 'legacy protocol'
method: TimeZone
dstPrintString

	^dstPrintString.
%
category: 'legacy protocol'
method: TimeZone
dstPrintString: aString

"Sets the dstPrintString instance variable. Returns the receiver."

dstPrintString := aString.
^ self
%
category: 'legacy protocol'
method: TimeZone
endOfDstFor: aYear

	^dstEndTimeList 
		at: aYear 
		ifAbsent: [self endOfDstForA: aYear].
%
category: 'legacy protocol'
method: TimeZone
endOfDstForA: aYear

	| dt transition next |
	dt := DateAndTime year: aYear + 1 day: 1 hour: 0 minute: 0 second: 0.
	[
		next := self transitionAtLocal: dt.
		next = transition ifTrue: [^nil].
		transition := next.
		dt := transition asDateAndTimeUTC asLocal.
		dt year ~~ aYear ifTrue: [^nil].
		transition isDST.
	] whileTrue: [].
	dt := dt asUTC - (Duration seconds: 3601).
	dt := DateTime
		newGmtWithYear: dt year 
		month: dt month
		day: dt dayOfMonth
		hours: dt hour
		minutes: dt minute
		seconds: dt second
		timeZone: self.
	^dt addSeconds: 3601.
%
category: 'internal'
method: TimeZone
populateCacheFor: anInterval

	anInterval do: [:year | 
		dstStartTimeList 
			at: year 
			put: (self startOfDstFor: year).
		dstEndTimeList 
			at: year 
			put: (self endOfDstFor: year).
	].
%
category: 'legacy protocol'
method: TimeZone
shouldWriteInstVar: instVarName
 
"Returns whether the given instance variable should be written out."

"exclude the ditionaries"

instVarName == #dstStartTimeList ifTrue:[ ^ false ].
instVarName == #dstEndTimeList ifTrue:[ ^ false ].
^ true
%
category: 'legacy protocol'
method: TimeZone
standardPrintString

	standardPrintString == nil ifTrue: [
		standardPrintString := (self detectLastTransition: [:each | each isDST not]) abbreviation.
	].
	^standardPrintString.
%
category: 'legacy protocol'
method: TimeZone
standardPrintString: aString

"Sets the standardPrintString instance variable. Returns the receiver."

standardPrintString := aString.
^ self
%
category: 'legacy protocol'
method: TimeZone
startOfDstFor: aYear

	^dstStartTimeList 
		at: aYear 
		ifAbsent: [self startOfDstForA: aYear].
%
category: 'legacy protocol'
method: TimeZone
startOfDstForA: aYear

	| dt transition next |
	dt := DateAndTime year: aYear + 1 day: 1 hour: 0 minute: 0 second: 0.
	[
		(next := self transitionAtUTC: dt) isNil ifTrue: [^nil].
		next = transition ifTrue: [^nil].
		transition := next.
		dt := transition asDateAndTimeUTC.
		dt year ~~ aYear ifTrue: [^nil].
		dt := dt - (Duration seconds: 3601).
		transition isDST not.
	] whileTrue: [
	].
	dt := DateTime
		newGmtWithYear: dt year 
		month: dt month 
		day: dt dayOfMonth 
		hours: dt hour
		minutes: dt minute 
		seconds: dt second
		timeZone: self.
	^dt addSeconds: 3601.
%
category: 'obsolete accessors'
method: TimeZone
dayEndDst

	| transition dt |
	transition := self detectLastTransition: [:each | each isDST not].
	dt := transition asDateAndTimeUTC asLocal.
	^dt dayOfYear.
%
category: 'obsolete accessors'
method: TimeZone
dayStartDst

	| transition dt |
	transition := self detectLastTransition: [:each | each isDST].
	dt := transition asDateAndTimeUTC asLocal.
	dt := dt + (Duration seconds: self secondsForDst negated).
	^dt dayOfYear.
%
category: 'obsolete accessors'
method: TimeZone
dstEndTimeList

"Returns the dstEndTimeList instance variable." 

^ dstEndTimeList 
%
category: 'obsolete accessors'
method: TimeZone
dstStartTimeList

"Returns the dstStartTimeList instance variable." 

^ dstStartTimeList 
%
category: 'obsolete accessors'
method: TimeZone
secondsForDst

	| isDST isNotDST |
	isDST := self detectLastTransition: [:each | each isDST].
	isDST isNil ifTrue: [^0].
	isNotDST := self detectLastTransition: [:each | each isDST not].
	isNotDST isNil ifTrue: [^0].
	^isDST offsetFromUTC - isNotDST offsetFromUTC.
%
category: 'obsolete accessors'
method: TimeZone
secondsFromGmt

	| transition |
	transition := self detectLastTransition: [:each | each isDST not].
	^transition isNil
		ifTrue: [0]
		ifFalse: [transition offsetFromUTC].
%
category: 'obsolete accessors'
method: TimeZone
timeStartDst

	| transition dt |
	transition := self detectLastTransition: [:each | each isDST].
	dt := transition asDateAndTimeUTC - (Duration seconds: 1).
	dt := DateAndTime 
		secondsUTC: dt asSeconds
		offset: (Duration seconds: (self offsetAtUTC: dt)).
	^dt hour * 60 + dt minute * 60 + dt second + 1.
%
category: 'obsolete accessors'
method: TimeZone
weekDayStartDst

	| transition year dateTime date |
	transition := self detectLastTransition: [:each | each isDST].
	year := transition asDateAndTimeUTC year.
	dateTime := self startOfDstFor: year.
	dateTime := dateTime addSeconds: self secondsForDst negated.
	date := dateTime asDateIn: self.
	^date weekDayName asSymbol.
%
category: 'obsolete accessors'
method: TimeZone
yearStartDst

	| transition |
	transition := transitions 
		detect: [:each | each isDST]
		ifNone: [self error: 'DST is not observed in this TimeZone (see bugnote for 36401)!'].
	^transition asDateAndTimeUTC year.
%
category: 'singleton'
method: TimeZone
installAsCurrentTimeZone

"Sets the receiver as the current session's current Timezone. Returns the
 receiver."

System _sessionStateAt: 17 put: self.
^ self.
%
category: 'accessors'
method: TimeZone
transitions

	^transitions.
%
category: 'accessors'
method: TimeZone
leapSeconds

	^leapSeconds.
%
category: 'accessors'
method: TimeZone
= aTimeZone

	^(aTimeZone isKindOf: TimeZone)
		_and: [self transitions = aTimeZone transitions
		_and: [self leapSeconds = aTimeZone leapSeconds
		_and: [self standardPrintString = aTimeZone standardPrintString 
		_and: [self dstPrintString = aTimeZone dstPrintString]]]].
%
category: 'accessors'
method: TimeZone
hash

	^transitions first hash + standardPrintString hash.
%

category: 'Instance Creation'
classmethod: DateTime
now

"Creates and returns an instance of the receiver representing the current
 time."

^ self now: (TimeZone current).
%
run
TimeZone default == nil ifTrue: [
	| old new |
	ObsoleteTimeZone cache keysAndValuesDo: [:key :value |
		old := value.
		old class == ObsoleteTimeZone ifTrue: [
			new := TimeZone fromObsolete: old.
			old become: new.
		].
		TimeZone 
			for: key
			put: old.
	].
	old := ObsoleteTimeZone default.
	old class == ObsoleteTimeZone ifTrue: [
		new := TimeZone fromObsolete: old.
		old become: new.
	].
	TimeZone default: old.
].
true.
%
