The Inbox: Kernel-bf.700.mcz

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

The Inbox: Kernel-bf.700.mcz

commits-2
Bert Freudenberg uploaded a new version of Kernel to project The Inbox:
http://source.squeak.org/inbox/Kernel-bf.700.mcz

==================== Summary ====================

Name: Kernel-bf.700
Author: bf
Time: 28 June 2012, 7:34:28.085 pm
UUID: 019680d3-785f-43a5-bc2a-9fed7bade3bb
Ancestors: Kernel-fbs.699

Make DateAndTime use the Julian Calendar for dates earlier than 15 October 1582.

=============== Diff against Kernel-fbs.699 ===============

Item was changed:
  Magnitude subclass: #DateAndTime
  instanceVariableNames: 'seconds offset jdn nanos'
  classVariableNames: 'ClockProvider DaysSinceEpoch LastMilliSeconds LastTick LastTickSemaphore LocalTimeZone MilliSecondOffset OffsetsAreValid'
  poolDictionaries: 'ChronologyConstants'
  category: 'Kernel-Chronology'!
 
+ !DateAndTime commentStamp: 'bf 6/28/2012 19:09' prior: 0!
- !DateAndTime commentStamp: 'brp 5/13/2003 08:07' prior: 0!
  I represent a point in UTC time as defined by ISO 8601. I have zero duration.
 
+ In contrast to ISO 8601, which uses the Gregorian Proleptic Calendar exclusively, I use the Julian Proleptic Calendar for dates prior to 15 Oct 1582. However, for seriously dealing with multiple calendars, you should use a more thorough date package (e.g. Calendrica).
 
+ My implementation uses three SmallIntegers and a Duration:
- My implementation uses three SmallIntegers
-  and a Duration:
  jdn - julian day number.
  seconds - number of seconds since midnight.
  nanos - the number of nanoseconds since the second.
-
  offset - duration from UTC.
 
  The nanosecond attribute is almost always zero but it defined for full ISO compliance and is suitable for timestamping.
  !

Item was added:
+ ----- Method: DateAndTime class>>gregorianYear:month:day:hour:minute:second:nanoSecond:offset: (in category 'squeak protocol') -----
+ gregorianYear: year month: month day: day hour: hour minute: minute second: second nanoSecond: nanoCount offset: offset
+ "Return a DateAndTime"
+
+ | daysInMonth p q r s julianDayNumber |
+ daysInMonth := Month
+ daysInMonth: month
+ forYear: year.
+ day < 1 ifTrue: [self error: 'day may not be zero or negative'].
+ day > daysInMonth ifTrue: [self error: 'day is after month ends'].
+
+ p := (month - 14) quo: 12.
+ q := year + 4800 + p.
+ r := month - 2 - (12 * p).
+ s := (year + 4900 + p) quo: 100.
+
+ julianDayNumber :=
+   ( (1461 * q) quo: 4 ) +
+ ( (367 * r) quo: 12 ) -
+   ( (3 * s) quo: 4 ) +
+   ( day - 32075 ).
+
+ ^self basicNew
+ setJdn: julianDayNumber
+ seconds: hour * 60 + minute * 60 + second
+ nano: nanoCount
+ offset: offset;
+ yourself!

Item was added:
+ ----- Method: DateAndTime class>>julianYear:month:day:hour:minute:second:nanoSecond:offset: (in category 'squeak protocol') -----
+ julianYear: year month: month day: day hour: hour minute: minute second: second nanoSecond: nanoCount offset: offset
+ "Return a DateAndTime"
+
+ | days |
+ days :=
+ (365 * (year - 1)) + "years since epoch"
+ ((year - 1) // 4) + "leap days since epoch"
+ (((367 * month) - 362) // 12) + "30/31 day months since newyear"
+ day.
+ month > 2 ifTrue: [ "we counted this February as 30 days; correct to 29 or 28"
+ days := days - ((Year isJulianLeapYear: year) ifTrue: [1] ifFalse: [2])].
+ ^self basicNew
+ setJdn:  1721423 + days
+ seconds: hour * 60 + minute * 60 + second
+ nano: nanoCount
+ offset: offset;
+ yourself!

Item was changed:
  ----- Method: DateAndTime class>>year:month:day:hour:minute:second:nanoSecond:offset: (in category 'squeak protocol') -----
  year: year month: month day: day hour: hour minute: minute second: second nanoSecond: nanoCount offset: offset
+ "Return a DateAndTime. If date is after 15 Oct 1582, use Gregorian calendar, otherwise the Julian calendar"
- "Return a DateAndTime"
 
+ | monthIndex |
- | monthIndex daysInMonth p q r s julianDayNumber |
-
  monthIndex := month isInteger ifTrue: [month] ifFalse: [Month indexOfMonth: month].
+ ^(year > 1582 or: [year = 1582 and: [monthIndex > 10 or: [monthIndex = 10 and: [day >= 15]]]])
+ ifTrue: [self gregorianYear: year month: monthIndex day: day hour: hour minute: minute second: second nanoSecond: nanoCount offset: offset]
+ ifFalse: [self julianYear: year month: monthIndex day: day hour: hour minute: minute second: second nanoSecond: nanoCount offset: offset]
+ !
- daysInMonth := Month
- daysInMonth: monthIndex
- forYear: year.
- day < 1 ifTrue: [self error: 'day may not be zero or negative'].
- day > daysInMonth ifTrue: [self error: 'day is after month ends'].
-
- p := (monthIndex - 14) quo: 12.
- q := year + 4800 + p.
- r := monthIndex - 2 - (12 * p).
- s := (year + 4900 + p) quo: 100.
-
- julianDayNumber :=
-   ( (1461 * q) quo: 4 ) +
- ( (367 * r) quo: 12 ) -
-   ( (3 * s) quo: 4 ) +
-   ( day - 32075 ).
-
- ^self basicNew
- setJdn: julianDayNumber
- seconds: hour * 60 + minute * 60 + second
- nano: nanoCount
- offset: offset;
- yourself!

Item was changed:
  ----- Method: DateAndTime>>dayMonthYearDo: (in category 'squeak protocol') -----
  dayMonthYearDo: aBlock
+ "Evaluate the block with three arguments: day, month, year.
+ For dates since 15 Oct 1582 use Gregorian calendar, before that, the Julian calendar"
- "Evaluation the block with three arguments: day month, year."
 
+ ^jdn < 2299161
+ ifTrue: [self julianDayMonthYearDo: aBlock]
+ ifFalse: [self gregorianDayMonthYearDo: aBlock]!
- | l n i j dd mm yyyy |
- l := jdn + 68569.
- n := 4 * l // 146097.
- l := l - (146097 * n + 3 // 4).
- i := 4000 * (l + 1) // 1461001.
- l := l - (1461 * i // 4) + 31.
- j := 80 * l // 2447.
- dd := l - (2447 * j // 80).
- l := j // 11.
- mm := j + 2 - (12 * l).
- yyyy := 100 * (n - 49) + i + l.
-
- ^ aBlock
- value: dd
- value: mm
- value: yyyy!

Item was added:
+ ----- Method: DateAndTime>>gregorianDayMonthYearDo: (in category 'squeak protocol') -----
+ gregorianDayMonthYearDo: aBlock
+ "Evaluate the block with three arguments: day, month, year."
+
+ | l n i j dd mm yyyy |
+ l := jdn + 68569.
+ n := 4 * l // 146097.
+ l := l - (146097 * n + 3 // 4).
+ i := 4000 * (l + 1) // 1461001.
+ l := l - (1461 * i // 4) + 31.
+ j := 80 * l // 2447.
+ dd := l - (2447 * j // 80).
+ l := j // 11.
+ mm := j + 2 - (12 * l).
+ yyyy := 100 * (n - 49) + i + l.
+
+ ^ aBlock
+ value: dd
+ value: mm
+ value: yyyy!

Item was added:
+ ----- Method: DateAndTime>>julianDayMonthYearDo: (in category 'squeak protocol') -----
+ julianDayMonthYearDo: aBlock
+ "Evaluate the block with three arguments: day, month, year"
+
+ | totalDays day month year daysSinceNewYear fakeDaysSinceNewYear daysToFirstOfMonth fakeDaysToFirstOfMonth fakeAmount |
+ totalDays := jdn - 1721424. "days since epoch 1 January 1"
+ year := (4 * totalDays + 1464) // 1461.
+ daysSinceNewYear := totalDays
+ - (365 * (year - 1)) "years since epoch"
+ - ((year - 1) // 4) . "leap days since epoch"
+ "after Feb 28, make Feb look like it was a 30-day month"
+ fakeAmount := daysSinceNewYear <= 58
+ ifTrue: [0]
+ ifFalse: [(Year isJulianLeapYear: year) ifTrue: [1] ifFalse: [2]].
+ fakeDaysSinceNewYear := daysSinceNewYear + fakeAmount.
+ "month formulas assume 30/31 day months"
+ month := ((12 * fakeDaysSinceNewYear) + 373) // 367.
+ fakeDaysToFirstOfMonth := ((367 * month) - 362) // 12.
+ daysToFirstOfMonth := month > 2
+ ifTrue: [fakeDaysToFirstOfMonth - fakeAmount]
+ ifFalse: [fakeDaysToFirstOfMonth].
+ day := daysSinceNewYear - daysToFirstOfMonth + 1.
+ ^ aBlock
+ value: day
+ value: month
+ value: year!

Item was changed:
  ----- Method: Year class>>daysInYear: (in category 'smalltalk-80') -----
  daysInYear: yearInteger
 
+ ^ 365 + (self leapYear: yearInteger)
- ^ 365 + ((self isLeapYear: yearInteger) ifTrue: [1] ifFalse: [0]).
  !

Item was added:
+ ----- Method: Year class>>isGregorianLeapYear: (in category 'squeak protocol') -----
+ isGregorianLeapYear: aYearInteger
+ | adjustedYear |
+ adjustedYear := aYearInteger > 0
+ ifTrue: [aYearInteger]
+ ifFalse: [(aYearInteger + 1) negated].
+ "There was no year 0"
+ ^ ((adjustedYear \\ 4 ~= 0) or: [(adjustedYear \\ 100 = 0) and: [adjustedYear \\ 400 ~= 0]]) not!

Item was added:
+ ----- Method: Year class>>isJulianLeapYear: (in category 'squeak protocol') -----
+ isJulianLeapYear: aYearInteger
+ ^ aYearInteger \\ 4 = 0!

Item was changed:
  ----- Method: Year class>>isLeapYear: (in category 'squeak protocol') -----
  isLeapYear: aYearInteger
 
+ ^ aYearInteger >= 1582
+ ifTrue: [self isGregorianLeapYear: aYearInteger]
+ ifFalse: [self isJulianLeapYear: aYearInteger]
+ !
-
- | adjustedYear |
- adjustedYear := aYearInteger > 0
- ifTrue: [aYearInteger]
- ifFalse: [(aYearInteger + 1) negated].
-
- "There was no year 0"
- ^ ((adjustedYear \\ 4 ~= 0) or: [(adjustedYear \\ 100 = 0) and: [adjustedYear \\ 400 ~= 0]]) not!