!=========================================================================
! Copyright (C) VMware, Inc. 1986-2011.  All Rights Reserved.
!
! $Id: obsdatetime50.gs,v 1.5 2008-01-09 22:50:12 stever Exp $
!
! Superclass Hierarchy:
!   ObsoleteDateTime50, Date, Magnitude, Object.
!
!=========================================================================

expectvalue %String
run
^ Date _newKernelSubclass: 'ObsoleteDateTime50'
        instVarNames: #( 'seconds' )
        classVars: #()
        classInstVars: #()
        poolDictionaries: #[]
        inDictionary: Globals
        constraints: #[ #[ #seconds, SmallInteger] ]
        instancesInvariant: false
        isModifiable: false
        reservedOop: 791 
%

removeallmethods ObsoleteDateTime50
removeallclassmethods ObsoleteDateTime50

category: 'For Documentation Installation only'
classmethod: ObsoleteDateTime50
installDocumentation

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

txt := (GsDocText new) details:
'An instance of ObsoleteDateTime50 describes a moment in time (with one-second resolution)
 on a date after December 31, 1900.

 The internal representation of a ObsoleteDateTime50 is based on Greenwich Mean Time.
 However, many methods express time in the local timezone.  ("Local" time is
 local to your Gem process.)  These methods automatically convert between
 timezones, but the internal representation remains in Greenwich Mean Time.
 Hence, you can interact with ObsoleteDateTime50 methods in a natural way, but ObsoleteDateTime50
 objects can be safely compared to each other no matter what time zone is used
 to express them.

 You can convert a ObsoleteDateTime50 to a String (using Formatting instance methods), and
 you can convert a String to a ObsoleteDateTime50 (using Instance Creation class methods).
 Such conversions require a specification to describe the format of the String.
 Some methods provide for the default format, DD/MM/YYYY HH:MM:SS, which
 expresses the day and month (in that order) as digits and uses a 24-hour clock.

 Explicit string-formatting specifications take the form of an Array, described
 in the following table.  A specification is incorrect if it is missing an
 element or if an element value is not one of the acceptable values listed in
 the table.

 String-formatting Specification Array for ObsoleteDateTime50.

 Element   Acceptable Value       Explanation

 1st,      Integers 1, 2,         Determines the position of the day (1),
 2nd, and  and 3, in any          month (2), and year (3).
 3rd       order

 4th       A Character literal    Separates year, month, and day.
           (such as a space, $ )

 5th       Integer                Determines the month format to be a number (1),
                                  three-letter abbreviation (2), or the entire
                                  name (3).

 6th       Integer                Determines the year format to be the entire
                                  number (1), or only the last two digits (2).

 7th       A Character literal    Separates hours, minutes, and seconds.
           (such as $: or $.)

 8th       true                   Include the time of day.

 8th       false                  Do not include the time of day.  Ignore elements
                                  7, 9, and 10.  Elements 9 and 10 are optional
                                  in the specification.

 9th       true                   Include seconds.

 9th       false                  Do not include seconds.

 10th      true                   Time is expressed in 12-hour format, with
                                  am or pm (such as 1:30:55 pm).  The space is
                                  required preceding the am or pm indicator.

 10th      false                  Time is expressed in 24-hour format
                                  (such as 13:30:55).' .
doc documentClassWith: txt.

txt := (GsDocText new) details:
'The number of seconds since midnight, Greenwich Mean Time.' .
doc documentInstVar: #seconds with: txt.

txt := (GsDocText new) details:
'Methods in this category are obsolete and are provided only for compatibility
 with earlier releases of GemStone.  They will be removed in a future release.'.
doc documentCategory: #'Backward Compatibility' with: txt.

self description: doc.
%

category: 'Instance Creation'
classmethod: ObsoleteDateTime50
newWithYear: yearInt month: monthInt day: dayInt hours: hourInt
  minutes: minuteInt seconds: secondInt

"Creates and returns an instance of the receiver from the specified values,
 which express local time.

 Generates an error if any of the values are out of range.  The argument
 yearInt must be a positive Integer between 1901 and 1,000,000 inclusive."

^ (self newGmtWithYear: yearInt month: monthInt day: dayInt hours: hourInt
    minutes: minuteInt seconds: secondInt) 
  addSeconds: Time gmtOffsetSeconds 
%

category: 'Instance Creation'
classmethod: ObsoleteDateTime50
newGmtWithYear: yearInt month: monthInt day: dayInt hours: hourInt
  minutes: minuteInt seconds: secondInt

"Creates and returns an instance of the receiver from the specified values,
 which express Greenwich Mean Time.

 Generates an error if any of the values are out of range.  The argument
 yearInt must be a positive Integer between 1901 and 1,000,000 inclusive."

| aDateTime |

aDateTime := self dateTimeClass newGmtWithYear: yearInt month: monthInt 
                  day: dayInt hours: hourInt minutes: minuteInt 
                  seconds: secondInt.

^ self newFromDateTime: aDateTime.
%

category: 'Instance Creation'
classmethod: ObsoleteDateTime50
newWithYear: year dayOfYear: dayCount seconds: seconds

"Creates and returns an instance of the receiver from the specified values,
 which express local time.

 Generates an error if any of the values are out of range.  The argument
 year must be a positive Integer between 1901 and 1,000,000 inclusive."

^ (self newGmtWithYear: year dayOfYear: dayCount seconds: seconds)
  addSeconds: Time gmtOffsetSeconds 
%

category: 'Instance Creation'
classmethod: ObsoleteDateTime50
newGmtWithYear: year dayOfYear: dayCount seconds: seconds

"Creates and returns an instance of the receiver from the specified values,
 which express Greenwich Mean Time.

 Generates an error if any of the values are out of range.  The argument
 year must be a positive Integer between 1901 and 1,000,000 inclusive."

| aDateTime |

aDateTime := self dateTimeClass newGmtWithYear: year dayOfYear: 
                  dayCount seconds: seconds.

^ self newFromDateTime: aDateTime.
%

category: 'Private'
classmethod: ObsoleteDateTime50
_newGmtWithYear: year dayOfYear: dayCount seconds: seconds

"Creates and returns an instance of the receiver from the specified values,
 which express Greenwich Mean Time.

 Generates an error if any of the values are out of range.  The argument
 year must be a positive Integer between 1901 and 1,000,000 inclusive."

| extraDays newSeconds newDayOfYear |

(seconds abs >= 86400)
  ifTrue: [
    extraDays := seconds // 86400.
    newDayOfYear := dayCount + extraDays.
    newSeconds := seconds \\ 86400.
    ]
  ifFalse: [
    newDayOfYear := dayCount.
    newSeconds := seconds.
    ].

^ self newGmtWithYear: year dayOfYear: newDayOfYear seconds: newSeconds
%

category: 'Instance Creation'
classmethod: ObsoleteDateTime50
_newGmtWithYear: yearInt month: monthInt day: dayInt seconds: secondInt

"Creates and returns an instance of the receiver from the specified values,
 which express Greenwich Mean Time.

 Generates an error if any of the values are out of range.  The argument
 year must be a positive Integer between 1901 and 1,000,000 inclusive."

| aDateTime |

aDateTime := self dateTimeClass _newGmtWithYear: yearInt month: monthInt 
                  day: dayInt seconds: secondInt.

^ self newFromDateTime: aDateTime.
%

category: 'Instance Creation'
classmethod: ObsoleteDateTime50
_newJulianDay: anInteger second: anotherInteger

"Creates and returns an instance of the receiver from the specified values,
 which express Greenwich Mean Time.  Generates an error if any of the values
 are out of range."

| aDateTime |

aDateTime := self dateTimeClass _newJulianDay: anInteger second: anotherInteger.
^ self newFromDateTime: aDateTime.
%

category: 'Instance Creation'
classmethod: ObsoleteDateTime50
fromString: aString

"Creates and returns an instance of the receiver from the String aString.
 The String expresses local time in the default format
 (DD/MM/YYYY HH:MM:SS).  Generates an error if the String does not conform to
 the format."

^ self fromString: aString usingFormat: #(1 2 3 $/ 1 1 $: true true false)
%

category: 'Instance Creation'
classmethod: ObsoleteDateTime50
fromStringGmt: aString

"Creates and returns an instance of the receiver from the String aString.
 The String expresses Greenwich Mean Time in the default format
 (DD/MM/YYYY HH:MM:SS).  Generates an error if the String does not conform to
 the format."

^ self fromStringGmt: aString usingFormat: #(1 2 3 $/ 1 1 $: true true false)
%

category: 'Instance Creation'
classmethod: ObsoleteDateTime50
fromStream: aStream

"Creates and returns an instance of the receiver by reading a String from
 aStream.  The String expresses local time in the default format
 (DD/MM/YYYY HH:MM:SS).  Generates an error if the String does not conform to
 the format."

^ self fromStream: aStream usingFormat: #(1 2 3 $/ 1 1 $: true true false)
%

category: 'Instance Creation'
classmethod: ObsoleteDateTime50
fromStreamGmt: aStream

"Creates and returns an instance of the receiver by reading a String from
 aStream.  The String expresses Greenwich Mean Time in the default format
 (DD/MM/YYYY HH:MM:SS).  Generates an error if the String does not conform to
 the format."

^ self fromStreamGmt: aStream usingFormat: #(1 2 3 $/ 1 1 $: true true false)
%

category: 'Instance Creation'
classmethod: ObsoleteDateTime50
fromString: aString usingFormat: anArray

"Creates and returns an instance of the receiver from the String aString.
 The String expresses local time in the format specified by anArray.
 The expression is terminated either by a space character or by the end of the
 String.  Generates an error if the String does not conform to the format,
 or if anArray contains an incorrect formatting specification.

 See the class documentation of ObsoleteDateTime50 for a complete description of the
 String-formatting specification Array.

 If the month format (5th element) indicates either an abbreviation (2) or an
 entire name (3), then this method tries to determine the month by decoding a
 character substring.  That substring may include any number of characters, but
 must exactly match a legal month name (or the initial portion of that month
 name).  If the substring matches more than one month, the first month matched
 is used (the search begins with January).

 If the specification indicates that seconds should not be included (9th
 element is false), and aString includes seconds, this method generates an
 error."

^ (self fromStringGmt: aString usingFormat: anArray)
    addSeconds: (Time gmtOffsetSeconds)
%

category: 'Instance Creation'
classmethod: ObsoleteDateTime50
fromStringGmt: aString usingFormat: anArray

"Creates and returns an instance of the receiver from the String aString.
 The String expresses Greenwich Mean Time in the format specified by anArray.
 The expression is terminated either by a space character or by the end of the
 String.  Generates an error if the String does not conform to the format,
 or if anArray contains an incorrect formatting specification.

 See the class documentation of ObsoleteDateTime50 for a complete description of the
 String-formatting specification Array.

 If the month format (5th element) indicates either an abbreviation (2) or an
 entire name (3), then this method tries to determine the month by decoding a
 character substring.  That substring may include any number of characters, but
 must exactly match a legal month name (or the initial portion of that month
 name).  If the substring matches more than one month, the first month matched
 is used (the search begins with January).

 If the specification indicates that seconds should not be included (9th
 element is false), and aString includes seconds, this method generates an
 error."

| s result |

s := ReadStream on: aString.
result := self fromStreamGmt: s usingFormat: anArray.
[ s atEnd ]
whileFalse:
  [ (s next isEquivalent:  $ )
    ifFalse:
      [ self _errIncorrectFormat: aString ]
  ].
^ result
%

category: 'Instance Creation'
classmethod: ObsoleteDateTime50
fromStream: aStream usingFormat: anArray

"Creates and returns an instance of the receiver by reading a String from
 aStream.  The String expresses local time in the format specified by
 anArray.  The expression is terminated either by a space character or by the
 end of the Stream.  Generates an error if the String does not conform to the
 format, or if anArray contains an incorrect formatting specification.

 See the class documentation of ObsoleteDateTime50 for a complete description of the
 String-formatting specification Array.

 If the month format (5th element) indicates either an abbreviation (2) or an
 entire name (3), then this method tries to determine the month by decoding a
 character substring.  That substring may include any number of characters, but
 must exactly match a legal month name (or the initial portion of that month
 name).  If the substring matches more than one month, the first month matched
 is used (the search begins with January).

 If the specification indicates that seconds should not be included (9th
 element is false), and aString includes seconds, this method generates an
 error."

^ (self fromStreamGmt: aStream usingFormat: anArray )
     addSeconds: Time gmtOffsetSeconds 
%

category: 'Instance Creation'
classmethod: ObsoleteDateTime50
fromStreamGmt: aStream usingFormat: anArray

"Creates and returns an instance of the receiver by reading a String from
 aStream.  The String expresses Greenwich Mean Time in the format specified by
 anArray.  The expression is terminated either by a space character or by the
 end of the Stream.  Generates an error if the String does not conform to the
 format, or if anArray contains an incorrect formatting specification.

 See the class documentation of ObsoleteDateTime50 for a complete description of the
 String-formatting specification Array.

 If the month format (5th element) indicates either an abbreviation (2) or an
 entire name (3), then this method tries to determine the month by decoding a
 character substring.  That substring may include any number of characters, but
 must exactly match a legal month name (or the initial portion of that month
 name).  If the substring matches more than one month, the first month matched
 is used (the search begins with January).

 If the specification indicates that seconds should not be included (9th
 element is false), and aString includes seconds, this method generates an
 error."

| dayInt monthInt yearInt hourInt minInt secInt timeDelim dateDelim ampm
  ampmPresent secondsPresent timePresent result parseDay
  blkNo delim parseMonth parseYear parseField |

"This block returns a string up from the input stream up to the specified
 delimiter.  If will also allow an eof if that parameter is set true.
 It then skips over the delimiter if it is found.
"
parseField := [ :delim :allowEof | | str |
                str := aStream contents class new.
                [ ((aStream peek isEquivalent: delim) not) & (aStream atEnd not) ]
                whileTrue:
                  [ str add: aStream next ].
                (aStream atEnd)
                ifTrue:
                  [ allowEof
                    ifFalse:
                      [ ObsoleteDateTime50 _error: #rtErrBadFormat args: #[aStream] ]
                  ]
                ifFalse:
                  [ aStream next "skip over delimiter" ].
                str
             ].

parseDay:= "parse day"
  [ :delim | | nextField |
    nextField := parseField value: delim value: (delim isEquivalent: $ ).
    dayInt := Integer fromCompleteString: nextField
  ].
parseMonth:= "parse month"
  [ :delim | | nextField |
    nextField := parseField value: delim value: (delim isEquivalent: $ ).
    (nextField =  '' )
    ifTrue:
      [ ObsoleteDateTime50 _error: #rtErrBadFormat args: #[aStream]].
    (anArray at: 5) = 1
    ifTrue:
      [ monthInt := Integer fromCompleteString: nextField ]
    ifFalse:
      [ monthInt := self _getMonthFrom: nextField ].
    (monthInt < 1) | (monthInt > 12)
    ifTrue:
      [ ObsoleteDateTime50 _error: #rtErrBadFormat args: #[aStream] ]
  ].
parseYear := "parse year"
  [ :delim | | nextField |
    nextField := parseField value: delim value: (delim isEquivalent: $ ).
    yearInt := Integer fromCompleteString: nextField.
    (anArray at: 6) = 2
    ifTrue:
      [ (yearInt > 99)
        ifFalse: [yearInt := yearInt + ((ObsoleteDateTime50 now yearGmt) // 100 * 100) ]
      ]
  ].

self _checkReadStream: aStream forClass: CharacterCollection.

ObsoleteDateTime50 _checkFormat: anArray.

dateDelim := anArray at: 4.
timeDelim := anArray at: 7.
timePresent := anArray at: 8.

"parse the date, with day, month, year in the specified format order"
delim:= Array new add: dateDelim; add: dateDelim; add: $ ; yourself .
1 to: 3 do: [:i | blkNo:= anArray at: i.
            (blkNo = 1) ifTrue: [parseDay value: (delim at: i)].
            (blkNo = 2) ifTrue: [parseMonth value: (delim at: i)].
            (blkNo = 3) ifTrue: [parseYear value: (delim at: i)]
      ].

timePresent ifTrue:[ "read the time" 
    secondsPresent := anArray at: 9.
    ampmPresent := anArray at: 10.
    hourInt := Integer fromCompleteString: (parseField value: timeDelim value: false).
    minInt := Integer fromCompleteString:
                     (parseField value: (secondsPresent
                                         ifTrue: [timeDelim]
                                         ifFalse: [$ ])
                                 value: (secondsPresent not & ampmPresent not)).
    secondsPresent ifTrue: [ 
      secInt := Integer fromCompleteString: (parseField value: $  value: ampmPresent not)]
    ifFalse:[ secInt := 0 ].

    ampmPresent ifTrue: [
        hourInt < 0 ifTrue: [
          ObsoleteDateTime50 _error: #rtErrBadFormat args: #[aStream]].
        hourInt > 12 ifTrue: [
          ObsoleteDateTime50 _error: #rtErrBadFormat args: #[aStream]].
	ampm := (String new add: (aStream next); add: aStream next; yourself).
        (ampm isEquivalent: 'PM') ifTrue: [
	  hourInt := hourInt + 12.
	  hourInt == 24 ifTrue: [
            hourInt := 12].
          ]
        ifFalse: [
	  (ampm isEquivalent: 'AM') ifFalse: [
            ObsoleteDateTime50 _error: #rtErrBadFormat args: #[aStream] ].
	  hourInt == 12 ifTrue: [
            hourInt := 0].
          ].
      ]
  ]
ifFalse:[ "ignore the time"
    hourInt := 0.
    minInt := 0.
    secInt := 0
  ].

result := self newGmtWithYear: yearInt
                month: monthInt
                day: dayInt
                hours: hourInt
                minutes: minInt
                seconds: secInt.

"This is an easy way to test that all of the values specified were in
 in range.  If any of them were not, the result will be different
 than what we specified."

(result asPartsGmt = #[yearInt, monthInt, dayInt, hourInt, minInt, secInt])
ifFalse:
  [ ObsoleteDateTime50 _error: #rtErrBadFormat args: #[aStream] ].

^ result
%

category: 'Instance Creation'
classmethod: ObsoleteDateTime50
now

"Creates and returns an instance of the receiver from the system calendar and
 clock on the machine that is running the Gem process, which are assumed to
 represent the current date and time of day expressed in Greenwich Mean Time."

| aDateTime |

aDateTime := self dateTimeClass now.
^ self newFromDateTime: aDateTime.
%

category: 'Instance Creation'
classmethod: ObsoleteDateTime50
_checkFormat: anArray

"Private.  Verifies that anArray is a valid string-formatting specification for
 the receiver.  Generates an error if it is not."

"anArray is a format array as documented in ObsoleteDateTime50>>asStringUsingFormat:"

| v doTime |

anArray _validateClass: Array.
(anArray size < 8 or:[ anArray size > 10])
  ifTrue:[ ObsoleteDateTime50 _error: #rtErrBadFormatSpec args: #[anArray] ].

"Check for a combination of the digits 1, 2, and 3"
((anArray at: 1) + (anArray at: 2) + (anArray at: 3) = 6 and:
        [(anArray at: 1) * (anArray at: 2) * (anArray at: 3) = 6])
  ifFalse:[ ObsoleteDateTime50 _error: #rtErrBadFormatSpec args: #[anArray] ].

(anArray at: 4) _validateClass: AbstractCharacter.

((v := anArray at: 5) = 1 or: [v = 2 or: [v = 3]])
  ifFalse:[ ObsoleteDateTime50 _error: #rtErrBadFormatSpec args: #[anArray] ].

((anArray at: 6) = 1 or: [(anArray at: 6) = 2])
  ifFalse:[ ObsoleteDateTime50 _error: #rtErrBadFormatSpec args: #[anArray] ].

(doTime := anArray at: 8) _validateClass: Boolean.
doTime ifTrue:[
  anArray size = 10 
    ifFalse:[ ObsoleteDateTime50 _error: #rtErrBadFormatSpec args: #[anArray] ].
  (anArray at: 7) _validateClass: AbstractCharacter.
  (anArray at: 9) _validateClass: Boolean.
  (anArray at: 10) _validateClass: Boolean.
  ]
%

category: 'Accessing'
method: ObsoleteDateTime50
leap

"Returns true if the receiver describes a leap year, expressed in local time,
 and false if it does not."

^ (self subtractSeconds: Time gmtOffsetSeconds) leapGmt
%

category: 'Accessing'
method: ObsoleteDateTime50
leapGmt

"Returns true if the receiver describes a leap year, expressed in Greenwich Mean
 Time, and false if it does not."

^ super leap 
%

category: 'Accessing'
method: ObsoleteDateTime50
daysInMonthGmt

"Returns a SmallInteger that gives the number of days in the month described by
 the receiver, expressed in Greenwich Mean Time."

^ self _daysInMonth: self monthOfYearGmt
%

category: 'Accessing'
method: ObsoleteDateTime50
monthOfYear

"Returns a SmallInteger that gives the numeric index of the month of the year
 described by the receiver, expressed in local time.  The index is a number
 between 1 and 12 inclusive, where 1 signifies January."

^ (self subtractSeconds: Time gmtOffsetSeconds) monthOfYearGmt
%

category: 'Accessing'
method: ObsoleteDateTime50
monthOfYearGmt

"Returns a SmallInteger that gives the numeric index of the month of the year
 described by the receiver, expressed in Greenwich Mean Time.  The index is a
 number between 1 and 12 inclusive, where 1 signifies January."

^ self _yearMonthDay at: 2
%

category: 'Accessing'
method: ObsoleteDateTime50
monthNameGmt

"Returns a String that gives the name of the month of the year described by the
 receiver, expressed in Greenwich Mean Time, in the user's native language."

^ MonthNames value at: self monthOfYearGmt
%

category: 'Accessing'
method: ObsoleteDateTime50
yearGmt

"Returns a SmallInteger that gives the year described by the receiver, expressed
 in Greenwich Mean Time."

^ year
%

category: 'Accessing'
method: ObsoleteDateTime50
year

"Returns a SmallInteger that gives the year described by the receiver,
 expressed in local time."

^  (self subtractSeconds: Time gmtOffsetSeconds) yearGmt 
%

category: 'Accessing'
method: ObsoleteDateTime50
dayOfMonth

"Returns a SmallInteger that gives the day of the month described by the
 receiver, expressed in local time."

^  ((self subtractSeconds: Time gmtOffsetSeconds) _yearMonthDayGmt) at: 3
%

category: 'Accessing'
method: ObsoleteDateTime50
dayOfMonthGmt

"Returns a SmallInteger that gives the day of the month described by the
 receiver, expressed in Greenwich Mean Time."

^  (self _yearMonthDayGmt) at: 3
%

category: 'Accessing'
method: ObsoleteDateTime50
dayOfWeek

"Returns a SmallInteger that gives the numeric index of the day of the week
 described by the receiver, expressed in local time.  The index is a
 number between 1 and 7 inclusive, where 1 signifies Sunday."

^ (self subtractSeconds: Time gmtOffsetSeconds) julianDay - 2299295 - 1 \\ 7 + 1

  "the julian day 2299298 is converted to the Gregorian"
  "date of March 1, 1583 by Communications of the ACM #199 algorithm"
  "was March 1, 1583 a Thursday?"
%

category: 'Accessing'
method: ObsoleteDateTime50
dayOfWeekGmt

"Returns a SmallInteger that gives the numeric index of the day of the week
 described by the receiver, expressed in Greenwich Mean Time.  The index is a
 number between 1 and 7 inclusive, where 1 signifies Sunday."

^ self julianDay - 2299295 - 1 \\ 7 + 1

  "the julian day 2299298 is converted to the Gregorian"
  "date of March 1, 1583 by Communications of the ACM #199 algorithm"
  "was March 1, 1583 a Thursday?"
%

category: 'Accessing'
method: ObsoleteDateTime50
dayOfYear

"Returns a SmallInteger that gives the day of the year described by the
 receiver, expressed in local time."

^ (self subtractSeconds: Time gmtOffsetSeconds) _dayOfYear

%
category: 'Accessing'
method: ObsoleteDateTime50
dayOfYearGmt

"Returns a SmallInteger that gives the day of the year described by the
 receiver, expressed in Greenwich Mean Time."

^ dayOfYear

%

category: 'Accessing'
method: ObsoleteDateTime50
hours

"Returns a SmallInteger (between zero and 23 inclusive) that gives the number of
 hours represented by the receiver since midnight, local time."

^ (seconds - Time gmtOffsetSeconds \\ 86400) // 3600
%

category: 'Accessing'
method: ObsoleteDateTime50
hoursGmt

"Returns a SmallInteger (between zero and 23 inclusive) that gives the number of
 hours represented by the receiver since midnight, Greenwich Mean Time."

^ seconds // 3600
%

! julianDay moved to Date

category: 'Backward Compatibility'
method: ObsoleteDateTime50
julianSecond

"Obsolete in GemStone 5.0."

"Returns a SmallInteger (between zero and 86399 inclusive) that gives the number
 of seconds represented by the receiver since midnight, local time."

^ (seconds - Time gmtOffsetSeconds + 86400) \\ 86400.
%

category: 'Accessing'
method: ObsoleteDateTime50
minutes

"Returns a SmallInteger (between zero and 59 inclusive) that gives the number of
 minutes represented by the receiver since the previous hour, local time."

^ ((seconds - Time gmtOffsetSeconds) \\ 3600) // 60
%

category: 'Accessing'
method: ObsoleteDateTime50
minutesGmt

"Returns a SmallInteger (between zero and 59 inclusive) that gives the number of
 minutes represented by the receiver since the previous hour, Greenwich Mean
 Time."

^ (seconds \\ 3600) // 60
%

category: 'Accessing'
method: ObsoleteDateTime50
secondsGmt

"Returns a SmallInteger (between zero and 59 inclusive) that gives the number of
 seconds represented by the receiver since the previous minute."

^ seconds \\ 60.
%

category: 'Accessing'
method: ObsoleteDateTime50
seconds

"Returns a SmallInteger (between zero and 59 inclusive) that gives the number of
 seconds represented by the receiver since the previous minute."

^ (seconds - Time gmtOffsetSeconds) \\ 60.
%

category: 'Accessing'
method: ObsoleteDateTime50
_yearMonthDayGmt

"Returns a three-element Array of SmallIntegers containing the year, index of
 the month, and the day of the month represented by the receiver, expressed in
 Greenwich Mean Time."

^ self asDateTime _yearMonthDayGmt.
%

category: 'Formatting'
method: ObsoleteDateTime50
asString

"Returns a String that expresses the receiver in local time
 in the default format (DD/MM/YYYY HH:MM:SS)."

|localDt|

localDt := self subtractSeconds: Time gmtOffsetSeconds .
^ localDt asStringGmt
%

category: 'Formatting'
method: ObsoleteDateTime50
asStringGmt

"Returns a String that expresses the receiver in Greenwich Mean Time
 in the default format (DD/MM/YYYY HH:MM:SS)."

| t result |

t := self _yearMonthDayGmt.
result := (t at: 3) _digitsAsString .
result addAll: '/';
  addAll: (t at: 2) _digitsAsString;
  addAll: '/';
  addAll: (t at: 1) _digitsAsString;
  addAll: ' ';
  addAll: (seconds // 3600) _digitsAsString;
  addAll: ':';
  addAll: (seconds \\ 3600 // 60) _digitsAsString;
  addAll: ':';
  addAll: (seconds \\ 60) _digitsAsString .
^ result
%

!  bug 6135 fixed
category: 'Formatting'
method: ObsoleteDateTime50
asStringUsingFormat: anArray

"Returns a String that expresses the receiver in local time
 in the format defined by anArray.  Generates an error if anArray
 contains an incorrect formatting specification.

 See the class documentation of ObsoleteDateTime50 for a complete description of the
 String-formatting specification Array."

| localDt |
localDt := self subtractSeconds: Time gmtOffsetSeconds .
^ localDt asStringGmtUsingFormat: anArray
%

category: 'Formatting'
method: ObsoleteDateTime50
asStringGmtUsingFormat: anArray

"Returns a String that expresses the receiver in Greenwich Mean Time
 in the format defined by anArray.  Generates an error if anArray
 contains an incorrect formatting specification.

 See the class documentation of ObsoleteDateTime50 for a complete description of the
 String-formatting specification Array."

|t dateSeparator timeSeparator monthName aString
 hour hourInt min sec day yearNumber |

t := self _yearMonthDayGmt.
ObsoleteDateTime50 _checkFormat: anArray.
dateSeparator := (anArray at: 4) asString.

timeSeparator := (anArray at: 7) asString.

((anArray at: 5) = 2) "get the month name according to the format"
   ifTrue: [monthName := self _monthAbbrev: (t at: 2)]
   ifFalse: [((anArray at: 5) = 3) "month as number is default"
      ifTrue: [monthName := self _monthName: (t at: 2)]
      ifFalse: [monthName := (t at: 2) _digitsAsString]].

((anArray at: 6) = 2)
   ifTrue: [yearNumber := ((t at: 1) \\ 100) _digitsAsString]
   ifFalse: [yearNumber := (t at: 1) asString].  "YYYY is default"

day := (t at:3) _digitsAsString.
((anArray at: 1) = 2) "month first"
   ifTrue: [aString := monthName + dateSeparator]
   ifFalse: [((anArray at: 1) = 3) "yearNumber first"
      ifTrue: [aString := yearNumber + dateSeparator]
      ifFalse: [aString := day + dateSeparator]].  "day first is default"

((anArray at: 2) = 1) "day second"
   ifTrue: [aString addAll: day; addAll: dateSeparator] "yearNumber second"
   ifFalse: [((anArray at: 2) = 3) "month second is default"
      ifTrue: [aString addAll: yearNumber; addAll: dateSeparator]
      ifFalse: [aString addAll: monthName; addAll: dateSeparator]].

((anArray at: 3) = 1) "day third"
   ifTrue: [aString addAll: day]
   ifFalse: [((anArray at: 3) = 2) "month third"
      ifTrue: [aString addAll: monthName]
      ifFalse: [aString addAll: yearNumber]].  "yearNumber third is default"

hourInt := seconds // 3600.
hour := hourInt _digitsAsString.
min := (seconds \\ 3600 // 60) _digitsAsString.
sec := (seconds \\ 60) _digitsAsString.

(anArray at: 8) ifTrue: [ "print the time"
  aString add: $ .
  (anArray at: 10) ifTrue: [ "12-hour format"
    (hourInt > 12) ifTrue: [
      aString addAll: (hourInt - 12) _digitsAsString;
              addAll: timeSeparator;
              addAll: min.

      (anArray at: 9) ifTrue: [
        aString addAll: timeSeparator;
                addAll: sec
        ].
      ]
    ifFalse: [
      aString addAll: (hourInt = 0 ifTrue: ['12'] ifFalse: [hour]);
              addAll: timeSeparator;
              addAll: min.

      (anArray at: 9) ifTrue: [
        aString addAll: timeSeparator;
                addAll: sec.
        ].
      ].

    aString addAll: (hourInt >= 12 ifTrue: [' pm'] ifFalse: [' am']).
    ]
  ifFalse: [
    aString addAll: hour;
            addAll: timeSeparator;
            addAll: min.

    (anArray at: 9) ifTrue: [
      aString addAll: timeSeparator;
              addAll: sec.
      ].
    ].
  ].

^ aString
%

category: 'Backward Compatibility'
method: ObsoleteDateTime50
_monthName: anIndex

"Private.  Returns a String that gives the name, in the user's native language,
 of the month of the year whose numeric index is anIndex.  The index is a number
 between 1 and 12 inclusive, where 1 signifies January."

"Use the nameOfMonth: method instead (inherited from Date)."

^ Date nameOfMonth: anIndex
%

category: 'Converting'
method: ObsoleteDateTime50
asPartsGmt

"Returns an Array of six SmallIntegers (year month day hours minutes seconds)
 that expresses the receiver in Greenwich Mean Time."

| result |

result := self _yearMonthDayGmt.  "year/month/day"
result addLast: (seconds // 3600).  "hours"
result addLast: (seconds \\ 3600) // 60.  "minutes"
result addLast: (seconds \\ 60).  "seconds"
^ result 
%

category: 'Converting'
method: ObsoleteDateTime50
asParts

"Returns an Array of six SmallIntegers (year month day hours minutes seconds)
 that expresses the receiver in local time."

^ ( self subtractSeconds: Time gmtOffsetSeconds) asPartsGmt  .
%

! fix 12105

category: 'Converting'
method: ObsoleteDateTime50
asSeconds

"Returns an Integer that represents the receiver in units of seconds since
 midnight January 1, 1901, Greenwich Mean Time."

^ ((self asDays) * 86400) + seconds.
%

category: 'Converting'
method: ObsoleteDateTime50
timeAsSeconds

"Returns a SmallInteger (between zero and 86399 inclusive) that gives the number
 of seconds represented by the receiver since midnight, Greenwich Mean Time."

^ seconds
%

category: 'Comparing'
method: ObsoleteDateTime50
< aObsoleteDateTime50

"Returns true if the receiver represents a moment in time before that of the
 argument, and false if it doesn't.  Generates an error if the argument is not
 a ObsoleteDateTime50."

| argYear argDayOfYear |

argYear := aObsoleteDateTime50 yearGmt.
(year < argYear) ifTrue: [ ^true ].
(year > argYear) ifTrue: [ ^false ]. 

"The years are the same"

argDayOfYear := aObsoleteDateTime50 dayOfYearGmt .
(dayOfYear < argDayOfYear) ifTrue: [ ^true ].
(dayOfYear > argDayOfYear) ifTrue: [ ^false ]. 

"The days are the same"

^ (seconds < aObsoleteDateTime50 timeAsSeconds).
%

category: 'Comparing'
method: ObsoleteDateTime50
= aObsoleteDateTime50

"Returns true if the receiver represents the same moment in time as that of the
 argument, and false if it doesn't."

(aObsoleteDateTime50 isKindOf: self _class) ifFalse: [ ^false ].
^ (year = aObsoleteDateTime50 yearGmt) 
  and: [(dayOfYear = aObsoleteDateTime50 dayOfYearGmt) 
    and: [seconds = aObsoleteDateTime50 timeAsSeconds]
    ]
%

category: 'Comparing'
method: ObsoleteDateTime50
hash

"Returns an Integer hash code for the receiver."

^ (((year hash) bitShift: -1) bitXor: (dayOfYear hash)) bitXor: (seconds hash).
%

category: 'Comparing'
method: ObsoleteDateTime50
> aObsoleteDateTime50

"Returns true if the receiver represents a moment in time after that of the
 argument, and false if it doesn't.  Generates an error if the argument is not
 a ObsoleteDateTime50."

| argYear argDayOfYear |

argYear := aObsoleteDateTime50 yearGmt.
(year > argYear) ifTrue: [ ^true ].
(year < argYear) ifTrue: [ ^false ]. 

"The years are the same"

argDayOfYear := aObsoleteDateTime50 dayOfYearGmt.
(dayOfYear > argDayOfYear) ifTrue: [ ^true ].
(dayOfYear < argDayOfYear) ifTrue: [ ^false ]. 

"The days are the same"

^ (seconds > aObsoleteDateTime50 timeAsSeconds).
%

category: 'Arithmetic'
method: ObsoleteDateTime50
addYears: anInteger

"Returns a ObsoleteDateTime50 that describes a moment in time anInteger years
 later than that of the receiver."

^ (self class) newGmtWithYear: (year + anInteger) dayOfYear: dayOfYear 
		   seconds: seconds.
%

category: 'Arithmetic'
method: ObsoleteDateTime50
subtractYears: anInteger

"Returns a ObsoleteDateTime50 that describes a moment in time anInteger years
 earlier than that of the receiver."

^ (self class) newGmtWithYear: (year - anInteger) dayOfYear: dayOfYear 
		seconds: seconds.
%

! fix bug 9360
category: 'Arithmetic'
method: ObsoleteDateTime50
addMonths: anInteger

"Returns a ObsoleteDateTime50 that describes a moment in time anInteger months
 later than that of the receiver.

 This method attempts to keep the day of the month the same.  If the
 new month has fewer days than the receiver's original month, then it
 truncates to the last day of the new month."

| t newMonth newYear |

t := self _yearMonthDayGmt.
newMonth := ((t at: 2) + anInteger) .
newYear := (t at: 1) .
(newMonth == 0) ifTrue:[ newYear := newYear - 1 ].
^ (self class) _newGmtWithYear: newYear
               month: newMonth
               day: (t at: 3)
               seconds: seconds
%

category: 'Arithmetic'
method: ObsoleteDateTime50
subtractMonths: anInteger

"Returns a ObsoleteDateTime50 that describes a moment in time anInteger months
 earlier than that of the receiver.

 This method attempts to keep the day of the month the same.  If the
 new month has fewer days than the receiver's original month, then it
 truncates to the last day of the new month."

^ self addMonths: (anInteger negated). 
%

category: 'Arithmetic'
method: ObsoleteDateTime50
addWeeks: anInteger

"Returns a ObsoleteDateTime50 that describes a moment in time anInteger weeks
 later than that of the receiver."

^ (self class) newGmtWithYear: year dayOfYear: (dayOfYear + (anInteger * 7)) 
		   seconds: seconds.
%

category: 'Arithmetic'
method: ObsoleteDateTime50
subtractWeeks: anInteger

"Returns a ObsoleteDateTime50 that describes a moment in time anInteger weeks
 earlier than that of the receiver."

^ (self class) newGmtWithYear: year dayOfYear: (dayOfYear - (anInteger * 7)) 
		seconds: seconds.
%

category: 'Arithmetic'
method: ObsoleteDateTime50
addDays: anInteger

"Returns a ObsoleteDateTime50 that describes a moment in time anInteger days
 later than that of the receiver."

^ (self class) newGmtWithYear: year dayOfYear: (dayOfYear + anInteger) 
		   seconds: seconds.
%

category: 'Arithmetic'
method: ObsoleteDateTime50
subtractDays: anInteger

"Returns a ObsoleteDateTime50 that describes a moment in time anInteger days
 earlier than that of the receiver."

^ (self class) newGmtWithYear: year dayOfYear: (dayOfYear - anInteger) 
		      seconds: seconds.
%

category: 'Arithmetic'
method: ObsoleteDateTime50
addHours: aNumber

"Returns a ObsoleteDateTime50 that describes a moment in time aNumber hours
 later than that of the receiver."

^ (self class) _newGmtWithYear: year dayOfYear: dayOfYear 
                  seconds: (seconds + (aNumber * 3600)) asInteger.
%

category: 'Arithmetic'
method: ObsoleteDateTime50
subtractHours: aNumber

"Returns a ObsoleteDateTime50 that describes a moment in time aNumber hours
 earlier than that of the receiver."

^ (self class) _newGmtWithYear: year dayOfYear: dayOfYear 
		  seconds: (seconds - (aNumber * 3600)) asInteger.
%

category: 'Arithmetic'
method: ObsoleteDateTime50
addMinutes: aNumber

"Returns a ObsoleteDateTime50 that describes a moment in time aNumber minutes
 later than that of the receiver."

^ (self class) _newGmtWithYear: year dayOfYear: dayOfYear 
           seconds: (seconds + (aNumber * 60)) asInteger .
%

category: 'Arithmetic'
method: ObsoleteDateTime50
subtractMinutes: aNumber

"Returns a ObsoleteDateTime50 that describes a moment in time aNumber minutes
 earlier than that of the receiver."

^ (self class) _newGmtWithYear: year dayOfYear: dayOfYear 
		seconds: (seconds - (aNumber * 60)) asInteger .
%

category: 'Arithmetic'
method: ObsoleteDateTime50
addSeconds: aNumber

"Returns a ObsoleteDateTime50 that describes a moment in time aNumber seconds
 later than that of the receiver."

^ (self class) _newGmtWithYear: year dayOfYear: dayOfYear 
		seconds: (seconds + aNumber) asInteger .
%

category: 'Arithmetic'
method: ObsoleteDateTime50
subtractSeconds: aNumber

"Returns a ObsoleteDateTime50 that describes a moment in time aNumber seconds
 earlier than that of the receiver."

^ (self class) _newGmtWithYear: year dayOfYear: dayOfYear 
		seconds: (seconds - aNumber) asInteger.
%

category: 'Arithmetic'
method: ObsoleteDateTime50
subtractDateGmt: aObsoleteDateTime50

"Returns a positive Integer that counts the number of times that midnight
 Greenwich Mean Time occurs between the times described by the receiver and
 aObsoleteDateTime50."

^ (self asDays - aObsoleteDateTime50 asDays) abs
%

category: 'Arithmetic'
method: ObsoleteDateTime50
subtractDate: aObsoleteDateTime50

"Returns a positive Integer that counts the number of times midnight local time
 occurs between the times described by the receiver and aObsoleteDateTime50."

| gmtOff |
gmtOff := Time gmtOffsetSeconds .
^ ((self subtractSeconds: gmtOff ) asDays - 
   (aObsoleteDateTime50 subtractSeconds: gmtOff ) asDays) abs
%

category: 'Arithmetic'
method: ObsoleteDateTime50
subtractTime: aObsoleteDateTime50

"Returns an Array of three positive Integers that count the hours, minutes, and
 seconds, respectively, between the times of day described by the receiver and
 aObsoleteDateTime50.

 The computation ignores the dates of both the receiver and aObsoleteDateTime50, and
 assumes that the receiver is the later time.  Hence, if the time of day in the
 receiver is less than the time of day in aObsoleteDateTime50, then the receiver's time of
 day is assumed to occur on the day following that of aObsoleteDateTime50."

| parts h m s |

parts := self asPartsGmt .
h := parts at: 4.
m := parts at: 5.
s := parts at: 6.

parts := aObsoleteDateTime50 asPartsGmt .
h < (parts at: 4) ifTrue:[ h := h + 24 ].
h := h - (parts at: 4).
m := m - (parts at: 5).
s := s - (parts at: 6).

s < 0 ifTrue: [
  s := s + 60.
  m := m - 1
].
s > 60 ifTrue: [
  s := s - 60.
  m := m + 1
].
m < 0 ifTrue: [
  m := m + 60.
  h := h - 1
].
m > 60 ifTrue: [
  m := m - 60.
  h := h + 1
].
^#[h, m, s]
%

category: 'Formatting'
method: ObsoleteDateTime50
US12HrFormat

"Returns a String that expresses the receiver in local time.  The date is in
 United States format (month first) and the time of day is based on the 12-hour
 clock (MM/DD/YY HH:MM:SS pm)."

^self asStringUsingFormat: #(2 1 3 $/ 1 2 $: true false true)
%

category: 'Formatting'
method: ObsoleteDateTime50
US24HrFormat

"Returns a String that expresses the receiver in local time.  The date is in
 United States format (month first) and the time of day is based on the 24-hour
 clock (MM/DD/YY HH:MM:SS)."

^self asStringUsingFormat: #(2 1 3 $/ 1 2 $: true false false)
%

category: 'Storing and Loading'
classmethod: ObsoleteDateTime50
loadFrom: passiveObj

"Creates and returns an active instance of the receiver from the passive form
 of the object, which expresses itself in Greenwich Mean Time."

| inst |

inst := self _newGmtWithYear: passiveObj readObject
               month: passiveObj readObject
               day: passiveObj readObject
               seconds: passiveObj readObject.
passiveObj version >= 500 ifFalse:[ "convert 4.1.3 local time to GMT"
  inst := inst addSeconds: Time gmtOffsetSeconds 
  ].
passiveObj hasRead: inst.
^inst.
%

category: 'Storing and Loading'
method: ObsoleteDateTime50
writeTo: passiveObj

"Writes the passive form of the receiver into passiveObj, expressed in
 Greenwich Mean Time."

passiveObj writeClass: self class.
self _yearMonthDayGmt do: [:each |  each writeTo: passiveObj].
seconds writeTo: passiveObj.
passiveObj space
%

category: 'Converting'
method: ObsoleteDateTime50
asObsoleteDateTime50

"Returns the receiver."

^ self.
%

category: 'Private'
classmethod: ObsoleteDateTime50
dateTimeClass

^ DateTime
%

category: 'Instance Creation'
classmethod: ObsoleteDateTime50
newFromDateTime: aDateTime

| anObsDateTime |

anObsDateTime := self basicNew.
anObsDateTime year: aDateTime yearGmt.
anObsDateTime dayOfYear: aDateTime dayOfYearGmt.
anObsDateTime seconds: aDateTime timeSinceMidnightGmt.
anObsDateTime immediateInvariant.
^ anObsDateTime.
%

category: 'Updating'
method: ObsoleteDateTime50
year: aYear

year := aYear.
^ self
%

category: 'Updating'
method: ObsoleteDateTime50
dayOfYear: aDay

dayOfYear := aDay.
^ self
%

category: 'Updating'
method: ObsoleteDateTime50
seconds: secs

seconds := secs.
^ self
%

category: 'Instance Creation'
method: ObsoleteDateTime50
asDateTime

| aDateTime |

aDateTime := self class dateTimeClass newGmtWithYear: (self yearGmt) 
                        dayOfYear: (self dayOfYearGmt) 
                        seconds: (self timeAsSeconds).
^ aDateTime.
%

! category: 'Repository Conversion'
! method: ObsoleteDateTime50
! convertTo51: aTimeZone
! 
! "Private. Convert to 5.1 DateTime"
! 
! <primitive: 331>
! self _primitiveFailed: #convertTo51
! %

category: 'Repository Conversion'
classmethod: ObsoleteDateTime50
_correspondingNewClass

"The class all instances of receiver are converted to during conversion."

(self == ObsoleteDateTime50)
  ifTrue: [ ^ DateTime ].

^ self.
%

