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

removeallmethods Date
removeallclassmethods Date

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

| doc txt |

doc := GsClassDocumentation newForClass: self.

txt := (GsDocText new) details:
'An instance of Date describes a date after December 31, 1900.

 You can convert a Date to a String (using Formatting instance methods), and
 you can convert a String to a Date (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, which expresses the
 day and month (in that order) as digits.

 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 Date.

 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).' .
doc documentClassWith: txt.

txt := (GsDocText new) details:
'A SmallInteger that represents the year. Years beyond one million are not
 allowed.' .
doc documentInstVar: #year with: txt.

txt := (GsDocText new) details:
'A SmallInteger between 1 and 366 inclusive that represents the day of the
 year.'.
doc documentInstVar: #dayOfYear with: txt.

txt := (GsDocText new) details:
'A SymbolDictionary.  Each key is a Symbol representing one of the native
 languages supported by GemStone, and each value is an Array of Strings, the
 names of the days of the week in the corresponding language.'.
doc documentClassVar: #WeekDayNames with: txt.

txt := (GsDocText new) details:
'A SymbolDictionary.  Each key is a Symbol representing one of the native
 languages supported by GemStone, and each value is an Array of Strings, the
 names of the months of the year in the corresponding language.'.
doc documentClassVar: #MonthNames with: txt.

self description: doc.
%

category: 'Instance Creation'
classmethod: Date
new

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

self shouldNotImplement: #new
%

category: 'Instance Creation'
classmethod: Date
migrateNew

"Override default migrateNew behavior with #_basicNew."

^ self _basicNew
%

category: 'Instance Creation'
classmethod: Date
new: anInteger

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

self shouldNotImplement: #new:
%

category: 'Instance Creation'
classmethod: Date
newDay: dayInt month: monthString year: yearInt

"Creates and returns an instance of the receiver from the specified values.
 Generates an error if any of the values are out of range."

 | monthInt |

 monthInt := self _getMonthFrom: monthString .
 (monthInt < 1 _or: [ monthInt > 12 ]) ifTrue:[ 
   Date _error: #rtErrBadFormat args: #[ #[dayInt, monthString, yearInt] ].
   ].
^ self newDay: dayInt monthNumber: monthInt year: yearInt .
%

category: 'Instance Creation'
classmethod: Date
newDay: day monthNumber: month year: year

"Creates and returns an instance of the receiver from the specified values.
 Generates an error if any of the values are out of range."

<primitive: 316>

day _validateClass: SmallInteger .
month _validateClass: SmallInteger .
year _validateClass: SmallInteger .

^ self _primitiveFailed: #newDay:monthNumber:year:
%

category: 'Instance Creation'
classmethod: Date
newDay: day year: year

"Creates and returns an instance of the receiver from the specified values.
 Generates an error if any of the values are out of range."

^ self newDay: day monthNumber: 1 year: year.
%

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

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

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

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

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

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

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

"Creates and returns an instance of the receiver from the String aString.
 The String expresses the date 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 Date 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)."

| s result |

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

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

"Creates and returns an instance of the receiver by reading a String from
 aStream.  The String expresses the date 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 Date 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)."

| dayInt monthInt yearInt dateDelim result parseField parseBlocks |

"This block returns a string up from the input stream up to the specified
 delimiter.  If also allows an end-of-file 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:[ Date _error: #rtErrBadFormat args: #[aStream] ].
     ]
  ifFalse:[ aStream next "skip over delimiter" ].
  str
  ].

parseBlocks := Array new: 3 .
parseBlocks at: 1 put: "parse day"
  [ :delim | | nextField |
    nextField := parseField value: delim value: (delim isEquivalent: $ ).
    dayInt := Integer fromCompleteString: nextField
  ].
parseBlocks at: 2 put: "parse month"
  [ :delim | | nextField |
    nextField := parseField value: delim value: (delim isEquivalent: $ ).
    (nextField = '' )
    ifTrue:
      [ Date _error: #rtErrBadFormat args: #[aStream]].
    (anArray at: 5) == 1
    ifTrue:
      [ monthInt := Integer fromCompleteString: nextField ]
    ifFalse:
      [ monthInt := self _getMonthFrom: nextField ].
    (monthInt < 1) | (monthInt > 12)
    ifTrue:
      [ Date _error: #rtErrBadFormat args: #[aStream] ]
  ].
parseBlocks at: 3 put: "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 + ((Date today year) // 100 * 100) ]
      ]
  ].

self _checkReadStream: aStream forClass: CharacterCollection.

Date _checkFormat: anArray.

dateDelim := anArray at: 4.

"parse the date, with day, month, year in the specified format order"
(parseBlocks at:( anArray at: 1)) value: dateDelim . 
(parseBlocks at:( anArray at: 2)) value: dateDelim .
(parseBlocks at:( anArray at: 3)) value: $  . 

result := self newDay: dayInt monthNumber: monthInt year: yearInt .
^ result
%

category: 'Instance Creation'
classmethod: Date
today

"Creates and returns an instance of the receiver from the system calendar
 on the machine that is running the Gem process, which is assumed to
 represent the current date."

<primitive: 315>
^ self _primitiveFailed: #today
%

category: 'Instance Creation'
classmethod: Date
_getMonthFrom: aCharCollection

"Private.  Returns the SmallInteger that corresponds to the month of the year
 that matches aCharCollection.

 The argument may include any number of characters, but must exactly match a
 legal month name (or the initial portion of that month name).  If the argument
 matches more than one month, the first month matched is used.  The search
 begins with January."

| whichMonth monthArray argSize matchMonth |

monthArray := MonthNames value.
matchMonth:= [:monthStr | | strSize match i |
   i:= 1.
   match:= false.
   strSize:= monthStr size.
   [ ((i <= argSize) & (i <= strSize)) _and:
     [match:= (aCharCollection at: i) isEquivalent: (monthStr at: i)]]
   whileTrue: [
      i:= i + 1.
   ].
   match
].

   argSize:= aCharCollection size.
   whichMonth:= 1.
   [ (whichMonth <= 12) _and:
     [(matchMonth value: (monthArray at: whichMonth)) not]
   ]
   whileTrue:
      [whichMonth := whichMonth + 1].

   (whichMonth <= 12)
      ifTrue: [ ^whichMonth].
   ^ 0
%

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

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

| v |

anArray _validateClass: Array.
(anArray size < 6)
  ifTrue:[ Date _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:[ Date _error: #rtErrBadFormatSpec args: #[anArray] ].

(anArray at: 4) _validateClass: AbstractCharacter.

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

((anArray at: 6) == 1 _or: [(anArray at: 6) == 2])
  ifFalse:[ Date _error: #rtErrBadFormatSpec args: #[anArray] ].
%

category: 'Accessing'
method: Date
at: anIndex put: aValue

"Disallowed.  You may not change the value of a Date."

self shouldNotImplement: #at:put:
%

category: 'Backward Compatibility'
method: Date
dayOfMonth

"Returns a SmallInteger that gives the day of the month described by the
 receiver."

^ self day.
%

! 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: Date
dayOfWeek

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

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

category: 'Accessing'
method: Date
julianDay

"Returns the Julian Day of the receiver, a SmallInteger that gives the number of
 days since January 1, 4713 B.C., as defined in Communications of the ACM,
 algorithm #199."

< primitive: 46 >

^ self _primitiveFailed: #julianDay
%

category: 'Accessing'
method: Date
dayOfYear

"Returns a SmallInteger that gives the day of the year described by the
 receiver."

^ dayOfYear
%

category: 'Accessing'
method: Date
daysInMonth

"Returns a SmallInteger that gives the number of days in the month
 described by the receiver."

^ self _daysInMonth: self monthOfYear

%

category: 'Accessing'
method: Date
_daysInMonth: month

"Returns a SmallInteger that gives the number of days in the month
 specified by the Integer month."

((month == 1) _or: [(month == 3) _or: [(month == 5) _or: [(month == 7) _or:
   [(month == 8) _or: [(month == 10) _or: [(month == 12)]]]]]])
   ifTrue: [^ 31].
((month == 4) _or: [(month == 6) _or: [(month == 9) _or: [(month == 11)]]])
   ifTrue: [^ 30].
(self leap)
   ifTrue: [^ 29].
^ 28
%

category: 'Accessing'
method: Date
daysInYear

"Returns a SmallInteger that gives the number of days in the year
 described by the receiver."

(self leap) ifTrue: [^ 366].
^ 365
%

category: 'Accessing'
method: Date
leap

"Returns true if the receiver describes a leap year and false if it does not."

| yr |
  "a year is a leap year if: (it is evenly divisible by 4 and it is not a
   century year) or (it is a century year and evenly divisible by 400)"

yr := (self _yearMonthDay) at: 1.
((yr \\ 100) == 0)
   ifTrue: [^ ((yr \\ 400) == 0)].
^ ((yr \\ 4) == 0)
%

category: 'Accessing'
method: Date
monthName

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

^ MonthNames value at: self monthOfYear
%

category: 'Backward Compatibility'
method: Date
monthOfYear

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

^ self month.
%

category: 'Accessing'
method: Date
size: anInteger

"Disallowed.  You may not change the size of a Date."

self shouldNotImplement: #size:
%

category: 'Accessing'
method: Date
weekDayName

"Returns a String that gives the name of the day of the week described by the
 receiver, in the user's native language."

^ WeekDayNames value at: (self dayOfWeek)
%

category: 'Accessing'
method: Date
year

"Returns a SmallInteger that gives the year described by the receiver."

^ year
%

category: 'Accessing'
method: Date
_yearMonthDay

"Private.  Returns a three-element Array of SmallIntegers containing the year,
 the index of the month, and the day of the month described by the receiver."

< primitive: 239 >

^ self _primitiveFailed: #_yearMonthDay
%

category: 'Formatting'
method: Date
printOn: aStream

"Puts a displayable representation of the receiver on aStream."

aStream nextPutAll: self asString .
%

category: 'Formatting'
method: Date
asString

"Returns a String that expresses the receiver in the default format
 (DD/MM/YYYY)."

| t result |

t := self _yearMonthDay.
result := (t at: 3) _digitsAsString .
result addAll: '/';
  addAll: (t at: 2) _digitsAsString;
  addAll: '/';
  addAll: (t at: 1) _digitsAsString.
^ result
%

category: 'Formatting'
method: Date
asStringUsingFormat: anArray

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

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

| t dateSeparator monthName aString day yearNumber |

t := self _yearMonthDay.

Date _checkFormat: anArray.

dateSeparator := (anArray at: 4) 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 := Date nameOfMonth: (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"

^ aString
%

category: 'Formatting'
method: Date
_monthAbbrev: anIndex

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

|theMonth itsAbbrev|

theMonth := Date nameOfMonth: anIndex.  "get its full name"
itsAbbrev := String new.
1 to: 3 do: "take the first three letters"
   [:aChar | itsAbbrev := itsAbbrev , (theMonth at: aChar)].
^ itsAbbrev
%

category: 'General Inquiries'
classmethod: Date
nameOfMonth: anIndex

"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."

^ (MonthNames value) at: anIndex.
%

category: 'Converting'
method: Date
asDays

"Returns an Integer that represents the receiver in units of days since
 January 1, 1901."

| numYears numDays |

numYears := year - 1901.
numDays := (numYears * 365) + (numYears // 4) + 
           ((numYears + 300) // 400) - (numYears // 100) + dayOfYear - 1.
^ numDays.
%

category: 'Comparing'
method: Date
< aDate

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

| argYear |

argYear := aDate year.
(year == argYear)
  ifTrue: [ ^ dayOfYear < aDate dayOfYear ]
  ifFalse: [ ^ year < argYear ].
%

category: 'Comparing'
method: Date
= aDate

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

(self == aDate) ifTrue: [ ^ true ].
(aDate isKindOf: self class) ifFalse: [ ^false ].
^ (year == aDate year) _and: [dayOfYear == aDate dayOfYear]
%

category: 'Comparing'
method: Date
hash

"Returns an Integer hash code for the receiver."

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

category: 'Comparing'
method: Date
> aDate

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

| argYear |

argYear := aDate year.
(year == argYear)
  ifTrue: [ ^ dayOfYear > aDate dayOfYear ]
  ifFalse: [^  year > argYear ].
%

category: 'Arithmetic'
method: Date
addDays: anInteger

"Returns a Date that describes a date anInteger days later than that
 of the receiver."

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

category: 'Arithmetic'
method: Date
subtractDays: anInteger

"Returns a Date that describes a date anInteger days earlier than that
 of the receiver."

^ self class newDay: dayOfYear - anInteger year: year 
%

category: 'Arithmetic'
method: Date
subtractDate: aDate

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

^ (self asDays - aDate asDays) abs
%

category: 'Formatting'
method: Date
USDateFormat

"Returns a String that expresses the date of the receiver. The date is in 
 United States format, month first (MM/DD/YY)."

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

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

"Creates and returns an active instance of the receiver from the passive form
 of the object."

| inst yr dy |

yr := passiveObj readObject .
dy := passiveObj readObject .
inst := self newDay: dy year: yr .
passiveObj hasRead: inst.
^inst.
%

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

"Writes the passive form of the receiver into passiveObj."

passiveObj writeClass: self class.
year writeTo: passiveObj.
dayOfYear writeTo: passiveObj.
passiveObj space
%

category: 'Accessing'
classmethod: Date
isLeap: year

"Returns true if year is a leap year; false otherwise." 

"a year is a leap year if: (it is evenly divisible by 4 and it is not a
 century year) or (it is a century year and evenly divisible by 400)"

((year \\ 100) == 0)
   ifTrue: [^ ((year \\ 400) == 0)].
^ ((year \\ 4) == 0)
%

category: 'Inquiries'
classmethod: Date
dayOfWeek: dayName

"Returns a SmallInteger that gives the numeric index of the day of the week
 described by dayName. The index is a number between 1 and 7 inclusive,
 where 1 signifies Sunday. dayName must be the full symbolic name of the
 day. Raises an error if dayName is not a valid week day name." 

 "Example: Date dayOfWeek: 'Tuesday'."

| aDay |
aDay := dayName asString asLowercase.
aDay at: 1 put: ((aDay at: 1) asUppercase).
^ WeekDayNames value indexOf: aDay 
    ifAbsent: [ dayName _error: #rtErrInvalidArgument 
    args: #[ 'argument is not a week day name' ]].
%

category: 'Inquiries'
method: Date
previous: dayName

"Returns the previous date whose weekday name is dayName."

^ self subtractDays: self dayOfWeek - 
       (self class dayOfWeek: dayName) - 
       1 \\ 7 + 1.
%

category: 'Accessing'
method: Date
day

"Returns a SmallInteger that gives the day of the month described by the
 receiver."

^  (self _yearMonthDay) at: 3
%

category: 'Accessing'
method: Date
month

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

^ self monthIndex.
%

category: 'Accessing'
method: Date
monthIndex

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

^ (self _yearMonthDay) at: 2
%

category: 'Arithmetic'
method: Date
addYears: anInteger

"Returns a Date that describes a date anInteger years later than that of the 
 receiver."

| yr month day newYear newDay newDate generatedDay |

yr := self year.
month := self month.
day := self day.

newYear := yr + anInteger.
newDate := self _class newDay: day monthNumber: month year: newYear.
generatedDay := newDate day.
(generatedDay ~= day)
  ifTrue: [
    newDay := newDate _daysInMonth: month.
    newDate := self _class newDay: newDay monthNumber: month year: newYear
    ].
^ newDate.
%

category: 'Arithmetic'
method: Date
subtractYears: anInteger

"Returns a Date that describes a date anInteger years earlier than that of the 
 receiver."

^ self addYears: (anInteger negated).
%

category: 'Arithmetic'
method: Date
addMonths: anInteger

"Returns a Date that describes a date 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."

| yr month day newYear newMonth newDay newDate generatedDay |

yr := self year.
month := self month.
day := self day.

newMonth := month + anInteger.
newYear := yr + ((newMonth - 1) // 12).
newMonth := (newMonth - 1) \\ 12 + 1.
newDate := self _class newDay: day monthNumber: newMonth year: newYear.
generatedDay := newDate day.
(generatedDay ~= day)
  ifTrue: [
    newDay := newDate _daysInMonth: newMonth.
    newDate := self _class newDay: newDay monthNumber: newMonth year: newYear
    ].
^ newDate.
%

category: 'Arithmetic'
method: Date
subtractMonths: anInteger

"Returns a Date that describes a date 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).
%

! deleted convertTo51, was primitive 275, primitive 275 reused in GsMethod

category: 'Converting'
method: Date
_asDaysFrom1970
"Convert the receiver to a SmallInteger representing the number of days since 
January 1, 1970."

<primitive: 540>
^ self _primitiveFailed: #_asDaysFrom1970
%

category: 'Instance Creation'
classmethod: Date
_newFromDays1970: numDays 
"Return a new instance that represents the number of days given since January 1, 1970"

<primitive: 541>
numDays  _validateClass: SmallInteger .
^ self _primitiveFailed: #_newFromDays1970:
%
category: 'New Indexing Comparison'
method: Date
_classSortOrdinal

^ 50
%
