--- a/Timestamp.st Thu May 10 20:26:02 2018 +0200
+++ b/Timestamp.st Thu May 10 20:26:50 2018 +0200
@@ -24,7 +24,7 @@
Object subclass:#TimestampBuilderAbstract
instanceVariableNames:'year month day hour minute second millisecond isUtcTime
- hasTimezone yearAlreadyRead utcOffset'
+ hasTimezone yearAlreadyRead utcOffset picos'
classVariableNames:''
poolDictionaries:''
privateIn:Timestamp
@@ -114,6 +114,8 @@
Notice:
the picos are to be added to the millis, to get picos within the second.
this is ugly, but makes all the rest backward compatible.
+ Also, most timestamps only require/have millisecond resolution,
+ so the pico instvar is nil/0 and does not require an aditional largeInteger operation.
The typical OS-time resolution is in the milli- or microsecond range.
External logging hardware may generate timestamps in the micro- or nanosecond range.
@@ -163,6 +165,29 @@
"Modified: / 13.7.1999 / 12:42:30 / stefan"
!
+UTCYear:y month:m day:d hour:h minute:min second:s millisecond:millis additionalPicoseconds:picos
+ "return an instance of the receiver, given individual components,
+ interpreted in the UTC timezone."
+
+ ^ self basicNew
+ UTCyear:y month:m day:d hour:h minute:min second:s millisecond:millis
+ additionalPicoseconds:picos
+
+ "
+ Timestamp UTCYear:1970 month:1 day:1 hour:0 minute:0 second:0 millisecond:0
+ Timestamp UTCYear:1991 month:1 day:2 hour:12 minute:30 second:0 millisecond:0
+ Timestamp UTCYear:1991 month:1 day:2 hour:12 minute:30 second:0 millisecond:100
+ Timestamp UTCYear:1999 month:7 day:1 hour:1 minute:0 second:0 millisecond:0
+ Timestamp UTCYear:2000 month:1 day:1 hour:1 minute:0 second:0 millisecond:0
+
+ UtcTimestamp UTCYear:2000 month:1 day:1 hour:1 minute:0 second:0 millisecond:0
+ "
+
+ "Modified: / 1.7.1996 / 15:22:07 / cg"
+ "Created: / 13.7.1999 / 12:34:37 / stefan"
+ "Modified: / 13.7.1999 / 12:42:30 / stefan"
+!
+
decodeFromLiteralArray:anArray
"decode a Timestamp literalArray.
@@ -255,6 +280,78 @@
"Modified: / 13.7.1999 / 12:30:47 / stefan"
!
+fromDate:aDate hour:hour minute:minute second:second
+ "return an instance of the receiver, initialized from a time and a date object.
+ See also `Timestamp now' and other protocol inherited from my superclass."
+
+ ^ self fromDate:aDate hour:hour minute:minute second:second millisecond:0
+
+ "
+ Timestamp fromDate:(Date today) andTime:(Time now)
+ Timestamp fromDate:(Date today) hour:10 minute:5 second:30
+ "
+!
+
+fromDate:aDate hour:hour minute:minute second:second microsecond:micros
+ "return an instance of the receiver, initialized from a time and a date object.
+ See also `Timestamp now' and other protocol inherited from my superclass."
+
+ ^ (self
+ year:aDate year
+ month:aDate month
+ day:aDate day
+ hour:hour
+ minute:minute
+ second:second
+ millisecond:0
+ ) setMicrosecond:micros
+
+ "
+ Timestamp fromDate:(Date today) hour:10 minute:5 second:30 microsecond:123456
+ Timestamp fromDate:(Date today) hour:10 minute:5 second:30 microsecond:140
+ "
+!
+
+fromDate:aDate hour:hour minute:minute second:second millisecond:millis
+ "return an instance of the receiver, initialized from a time and a date object.
+ See also `Timestamp now' and other protocol inherited from my superclass."
+
+ ^ self
+ year:aDate year
+ month:aDate month
+ day:aDate day
+ hour:hour
+ minute:minute
+ second:second
+ millisecond:millis
+
+ "
+ Timestamp fromDate:(Date today) andTime:(Time now)
+ Timestamp fromDate:(Date today) andTime:(Time nowWithMilliseconds)
+ Timestamp fromDate:(Date today) hour:10 minute:5 second:30 millisecond:140
+ "
+!
+
+fromDate:aDate hour:hour minute:minute second:second nanosecond:nanos
+ "return an instance of the receiver, initialized from a time and a date object.
+ See also `Timestamp now' and other protocol inherited from my superclass."
+
+ ^ (self
+ year:aDate year
+ month:aDate month
+ day:aDate day
+ hour:hour
+ minute:minute
+ second:second
+ millisecond:0
+ ) setNanosecond:nanos
+
+ "
+ Timestamp fromDate:(Date today) hour:10 minute:5 second:30 microsecond:123456
+ Timestamp fromDate:(Date today) hour:10 minute:5 second:30 microsecond:140
+ "
+!
+
newDay:dayInYear year:year
"return a new Timestamp, given the year and the day-in-year (starting at 1).
Date protocol compatibility"
@@ -371,6 +468,18 @@
"Modified: / 13.7.1999 / 12:27:47 / stefan"
!
+year:y month:m day:d hour:h minute:min second:s microsecond:micros
+ "return an instance of the receiver, given individual components.
+ See also `Timestamp now' and other protocol inherited
+ from my superclass."
+
+ ^ (self year:y month:m day:d hour:h minute:min second:s) setMicrosecond:micros
+
+ "
+ Timestamp year:1991 month:1 day:2 hour:12 minute:30 second:0 microsecond:100
+ "
+!
+
year:y month:m day:d hour:h minute:min second:s millisecond:millis
"return an instance of the receiver, given individual components.
See also `Timestamp now' and other protocol inherited
@@ -389,6 +498,28 @@
"Modified: / 1.7.1996 / 15:22:07 / cg"
"Created: / 13.7.1999 / 12:28:44 / stefan"
"Modified: / 13.7.1999 / 12:37:57 / stefan"
+!
+
+year:y month:m day:d hour:h minute:min second:s millisecond:millis additionalPicoseconds:picos
+ "return an instance of the receiver, given individual components.
+ See also `Timestamp now' and other protocol inherited
+ from my superclass."
+
+ ^ self basicNew
+ year:y month:m day:d hour:h minute:min second:s
+ millisecond:millis additionalPicoseconds:picos
+
+ "
+ Timestamp year:1970 month:1 day:1 hour:0 minute:0 second:0
+ Timestamp year:1991 month:1 day:2 hour:12 minute:30 second:0
+ Timestamp year:1991 month:1 day:2 hour:12 minute:30 second:0 millisecond:100
+ Timestamp year:2000 month:7 day:1 hour:1 minute:0 second:0
+ UtcTimestamp year:2000 month:7 day:1 hour:1 minute:0 second:0
+ "
+
+ "Modified: / 1.7.1996 / 15:22:07 / cg"
+ "Created: / 13.7.1999 / 12:28:44 / stefan"
+ "Modified: / 13.7.1999 / 12:37:57 / stefan"
! !
@@ -489,17 +620,19 @@
newDay:day month:month year:year
<resource: #obsolete>
+
"return a new Timestamp, given the year, month and day (starting at 1).
- Date protocol compatibility"
+ Date protocol compatibility.
+ Obsolete: use year:month:day:."
^ self
- year:year
- month:month
- day:day
- hour:0
- minute:0
- second:0
- millisecond:0
+ year:year
+ month:month
+ day:day
+ hour:0
+ minute:0
+ second:0
+ millisecond:0
"
Timestamp newDay:1 month:1 year:1996
@@ -523,7 +656,8 @@
or (us-format, for Travis) mm/dd/yyyy hh:mm:ss.iii.
On error, raise an exception"
- |monthOrYear firstNumber secondNumber day month year hour min sec millis usFormat possibeMonthName ch utcOffsetOrNil count|
+ |monthOrYear firstNumber secondNumber day month year hour min sec
+ millis usFormat possibeMonthName ch utcOffsetOrNil count mantissa fraction picos ts|
count := 0.
monthOrYear := aStream throughAnyForWhich:[:ch | count := count+1. ch isDigit and:[count <= 4]].
@@ -560,6 +694,8 @@
[(ch := aStream peekOrNil) notNil and:[ch isDigit not]] whileTrue:[aStream next].
year := Integer readFrom:aStream onError:[ TimeConversionError raiseErrorString:' - bad year' ].
+ picos := 0.
+
aStream atEnd ifTrue:[
hour := min := sec := millis := 0.
] ifFalse:[
@@ -578,10 +714,19 @@
sec := Integer readFrom:aStream onError:-1.
(sec between:0 and:59) ifFalse:[ TimeConversionError raiseErrorString:' - bad second' ].
- aStream peek == $. ifTrue:[
+ (aStream peek == $. or:[aStream peek == $,]) ifTrue:[
aStream next.
- millis := Integer readFrom:aStream onError:0.
- millis >= 1000 ifTrue:[ TimeConversionError raiseErrorString:' - bad millisecond' ].
+ mantissa := Number readMantissaAndScaleFrom:aStream radix:10.
+ fraction := (mantissa at:2) / (10 raisedTo:(mantissa at:3)).
+ (mantissa at:3) > 3 ifTrue:[
+ picos := fraction * (1000 * 1000 * 1000 * 1000).
+ millis := picos // (1000 * 1000 * 1000).
+ picos := picos \\ (1000 * 1000 * 1000).
+ ] ifFalse:[
+ millis := fraction * 1000.
+ ].
+ "/ millis := Integer readFrom:aStream onError:0.
+ "/ millis >= 1000 ifTrue:[ TimeConversionError raiseErrorString:' - bad millisecond' ].
] ifFalse:[
millis := 0.
].
@@ -607,7 +752,9 @@
addSeconds:utcOffsetOrNil.
].
"/ a local timestamp
- ^ self year:year month:month day:day hour:hour minute:min second:sec millisecond:millis.
+ ts := self year:year month:month day:day hour:hour minute:min second:sec millisecond:millis.
+ picos ~~ 0 ifTrue:[ ts additionalPicoseconds:picos ].
+ ^ ts
"some ad hoc formats:
@@ -874,6 +1021,7 @@
%Y2000 - year, last 2 digits only, map to 2000..2099
%Y1950 - year, last 2 digits only, map to 1950..2049
%Y1980 - year, last 2 digits only, map to 1980..2079
+ %Y1970 - year, last 2 digits only, map to 1970..2069
an optional length after the % gives a field length;
i.e. %2h%2m%2s parses '123557' as 12:35:37
@@ -948,6 +1096,15 @@
year := year + 1900
]
+ ] ifFalse:[ ( format sameAs: 'Y1970' ) ifTrue:[
+ year := Integer readFrom:input onError:[ error value:'invalid year' ].
+ (year between:0 and: 99) ifFalse:[ error value:'invalid year' ].
+ (year between:0 and: 69) ifTrue:[
+ year := year + 2000
+ ] ifFalse:[
+ year := year + 1900
+ ]
+
] ifFalse:[ ( format sameAs: 'Y2000' ) ifTrue:[
year := Integer readFrom:input onError:[ error value:'invalid year' ].
(year between:0 and: 99) ifFalse:[ error value:'invalid year' ].
@@ -991,7 +1148,7 @@
] ifFalse:[
error value:'unhandled format:',format
- ]]]]]]]]]]]]]]]]]]
+ ]]]]]]]]]]]]]]]]]]]
].
hour := 0.
@@ -1884,6 +2041,145 @@
"Created: / 20-01-2011 / 12:28:46 / cg"
!
+exactMicroseconds
+ "return the exact microseconds within the stamp's second (0 .. 999.999...) as a fixedPoint number.
+ notice:
+ that is NOT the total number of microseconds,
+ but the fractional part (within the second) only.
+ A fixedPoint number holds the exact value, but prints itself rounded!!"
+
+ |millis microsFromMillis|
+
+ millis := (osTime \\ 1000).
+ microsFromMillis := millis * 1000.
+ additionalPicoseconds notNil ifTrue:[
+ ^ microsFromMillis + (FixedPoint numerator:additionalPicoseconds denominator:(1000*1000) scale:3)
+ ].
+ ^ microsFromMillis.
+
+ "
+ |ts|
+
+ ts := Timestamp nowWithMicroseconds.
+ Transcript showCR:ts.
+ Transcript showCR:ts microseconds.
+ Transcript showCR:ts exactMicroseconds.
+ Transcript showCR:ts nanoseconds.
+ Transcript showCR:ts picoseconds.
+ "
+!
+
+exactMilliseconds
+ "return the exact milliseconds within the stamp's second (0 .. 999.999...) as a fixedPoint number.
+ notice:
+ that is NOT the total number of microseconds,
+ but the fractional part (within the second) only.
+ A fixedPoint number holds the exact value, but prints itself rounded!!"
+
+ |millis|
+
+ millis := (osTime \\ 1000).
+ additionalPicoseconds notNil ifTrue:[
+ ^ millis + (FixedPoint numerator:additionalPicoseconds denominator:(1000*1000*1000) scale:3)
+ ].
+ ^ millis.
+
+ "
+ |ts|
+
+ ts := Timestamp nowWithMicroseconds.
+ Transcript showCR:ts.
+ Transcript showCR:ts milliseconds.
+ Transcript showCR:ts exactMilliseconds.
+ Transcript showCR:ts microseconds.
+ Transcript showCR:ts nanoseconds.
+ Transcript showCR:ts picoseconds.
+ "
+!
+
+exactMinutes
+ "return the exact minutes within the stamp's hour (00 .. 59.999...) as a fixedPoint number.
+ Notice:
+ that is NOT the total number of minutes,
+ but the fractional part (within the hour) only.
+ A fixedPoint number holds the exact value, but prints itself rounded!!"
+
+ |minutes additionalSeconds|
+
+ minutes := FixedPoint numerator:(osTime \\ (60*60*1000)) / 60 denominator:1000 scale:3.
+ additionalPicoseconds notNil ifTrue:[
+ additionalSeconds := (FixedPoint numerator:additionalPicoseconds denominator:(1000*1000*1000*1000) scale:3).
+ minutes := minutes + (additionalSeconds / 60).
+ ].
+ ^ minutes.
+
+ "
+ |ts|
+
+ ts := Timestamp nowWithMicroseconds.
+ Transcript showCR:ts.
+ Transcript showCR:ts minutes.
+ Transcript showCR:ts exactMinutes.
+ "
+!
+
+exactNanoseconds
+ "return the exact nanoseconds within the stamp's second (0 .. 999.999...).
+ notice:
+ that is NOT the total number of nanoseconds,
+ but the fractional part (within the second) only.
+ A fixedPoint number holds the exact value, but prints itself rounded!!"
+
+ |millis nanosFromMillis|
+
+ millis := (osTime \\ 1000).
+ nanosFromMillis := millis * 1000 * 1000.
+ additionalPicoseconds notNil ifTrue:[
+ ^ nanosFromMillis + (FixedPoint numerator:additionalPicoseconds denominator:(1000) scale:3)
+ ].
+ ^ nanosFromMillis.
+
+ "
+ |ts|
+
+ ts := Timestamp now + 100.3 nanoseconds.
+ Transcript showCR:ts.
+ Transcript showCR:ts milliseconds.
+ Transcript showCR:ts exactMilliseconds.
+ Transcript showCR:ts microseconds.
+ Transcript showCR:ts exactMicroseconds.
+ Transcript showCR:ts nanoseconds.
+ Transcript showCR:ts exactNanoseconds.
+ Transcript showCR:ts picoseconds.
+ "
+!
+
+exactSeconds
+ "return the exact seconds within the stamp's minute (00 .. 59.999...) as a fixedPoint number.
+ Notice:
+ that is NOT the total number of seconds,
+ but the fractional part (within the minute) only.
+ A fixedPoint number holds the exact value, but prints itself rounded!!"
+
+ |seconds additionalSeconds|
+
+ seconds := FixedPoint numerator:(osTime \\ (60*1000)) denominator:1000 scale:3.
+ additionalPicoseconds notNil ifTrue:[
+ additionalSeconds := (FixedPoint numerator:additionalPicoseconds denominator:(1000*1000*1000*1000) scale:3).
+ seconds := seconds + additionalSeconds
+ ].
+ ^ seconds.
+
+ "
+ |ts|
+
+ ts := Timestamp fromDate:(Date today) hour:10 minute:30 second:20 millisecond:300.
+ Transcript showCR:ts.
+ Transcript showCR:ts seconds.
+ Transcript showCR:ts exactSeconds.
+ "
+!
+
hours
"return the hours (0..23)"
@@ -1900,23 +2196,29 @@
!
microseconds
- "return the microseconds within the stamp's second (0..999999).
+ "return the truncated microseconds within the stamp's second (0..999999).
notice: that is NOT the total number of microseconds,
but the fractional part (within the second) only.
Use this only for printing."
- |microsFromMillis|
-
- microsFromMillis := (osTime \\ 1000) * 1000.
+ |millis microsFromMillis|
+
+ millis := (osTime \\ 1000).
+ microsFromMillis := millis * 1000.
additionalPicoseconds notNil ifTrue:[
^ microsFromMillis + (additionalPicoseconds // (1000*1000))
].
^ microsFromMillis.
"
- Timestamp now microseconds
+ -- (definitely millisecond resolution here)
+ Timestamp now
+ Timestamp now microseconds
+
+ -- (but some OS's only deliver millisecond resolution also here)
Timestamp nowWithMicroseconds microseconds
+
|t1 t2|
t1 := Timestamp nowWithMicroseconds microseconds.
t2 := Timestamp nowWithMicroseconds microseconds.
@@ -1933,7 +2235,7 @@
!
millisecond
- "return the millisecond within the stamp's second (0..999).
+ "return the truncated millisecond within the stamp's second (0..999).
ST-80 Timestamp compatibility (I'd prefer the name #milliseconds)."
^ self milliseconds
@@ -1943,7 +2245,7 @@
!
milliseconds
- "return the milliseconds within the stamp's second (0..999)"
+ "return the truncated milliseconds within the stamp's second (0..999)"
^ osTime \\ 1000.
@@ -1998,7 +2300,7 @@
!
nanoseconds
- "return the nanoseconds within the stamp's second (0..999999999).
+ "return the truncated nanoseconds within the stamp's second (0..999999999).
notice: that is NOT the total number of nanoseconds,
but the fractional part (within the second) only.
Use this only for printing."
@@ -2037,12 +2339,6 @@
^ osTime
!
-osTime:aTime
- "set the internal representation of the time"
-
- osTime := aTime.
-!
-
picoseconds
"return the picoseconds within the stamp's second (0..999999999999).
notice: that is NOT the total number of picoseconds,
@@ -2061,10 +2357,10 @@
!
seconds
- "return the seconds (0..59)"
+ "return the truncated seconds (0..59)"
(osTime between:MinOSTime and:MaxOSTime) ifFalse:[
- ^ self asTime seconds.
+ ^ self asTime seconds.
].
^ self timeInfo seconds
@@ -2627,6 +2923,14 @@
self setOSTimeFromUTCYear:y month:m day:d hour:h minute:min second:s millisecond:millis
!
+UTCyear:y month:m day:d hour:h minute:min second:s millisecond:millis additionalPicoseconds:picos
+ "private: ask the operating system to compute the internal osTime (based on the epoch),
+ given y,m,d and h,m,s in my time."
+
+ self setOSTimeFromUTCYear:y month:m day:d hour:h minute:min second:s millisecond:millis.
+ additionalPicoseconds := picos.
+!
+
setOSTimeFromUTCYear:y month:m day:d hour:h minute:min second:s millisecond:millis
"private: ask the operating system to compute the internal osTime (based on the epoch),
given y,m,d and h,m,s in local time"
@@ -2688,6 +2992,19 @@
self basicNew
year:2016 month:4 day:16 hour:17 minute:21 second:13 millisecond:726
"
+!
+
+year:y month:m day:d hour:h minute:min second:s millisecond:millis additionalPicoseconds:picos
+ "private: ask the operating system to compute the internal osTime (based on the epoch),
+ given y,m,d and h,m,s in my time."
+
+ self setOSTimeFromYear:y month:m day:d hour:h minute:min second:s millisecond:millis.
+ additionalPicoseconds := picos
+
+ "
+ self basicNew
+ year:2016 month:4 day:16 hour:17 minute:21 second:13 millisecond:726
+ "
! !
@@ -3231,7 +3548,7 @@
!
getMilliseconds
- "strictly private: return the milliseconds (since the epoch) in utc"
+ "strictly private: return the truncated milliseconds (since the epoch) in utc"
^ osTime
@@ -3246,6 +3563,38 @@
"Modified (comment): / 21-09-2017 / 18:50:23 / cg"
!
+osTime:aTime
+ "set the internal representation of the time"
+
+ osTime := aTime.
+!
+
+setMicrosecond:aNumber
+ "change the sub-second fractional part only (leaves everything above seconds unchanged)"
+
+ self
+ setMilliseconds:(self getMilliseconds // 1000) * 1000 "/ strip off any sub-second part
+ additionalPicoseconds:(aNumber * 1000 * 1000) rounded. "/ set picos
+
+ "
+ Timestamp now setMicrosecond:15 - 15 microseconds after the current second's start
+ Timestamp now setMicrosecond:0.1 - 100 nanoseconds after the current second's start
+ "
+!
+
+setMillisecond:aNumber
+ "change the sub-second fractional part only (leaves everything above seconds unchanged)"
+
+ self
+ setMilliseconds:(self getMilliseconds // 1000) * 1000 "/ strip off any sub-second part
+ additionalPicoseconds:(aNumber * 1000 * 1000 * 1000) rounded. "/ set picos
+
+ "
+ Timestamp now setMillisecond:15 - 15 milliseconds after the current second's start
+ Timestamp now setMillisecond:0.05 - 50 microseconds after the current second's start
+ "
+!
+
setMilliseconds:millis
"strictly private: set the milliseconds (since the epoch)"
@@ -3255,6 +3604,42 @@
"Created: 1.7.1996 / 14:34:24 / cg"
!
+setMilliseconds:millis additionalPicoseconds:picos
+ "strictly private: set the milliseconds (since the epoch) and additional picos"
+
+ |rest newMillis newPicos|
+
+ millis isInteger ifTrue:[
+ newMillis := millis.
+ newPicos := 0.
+ ] ifFalse:[
+ newMillis := millis truncated.
+ rest := millis - newMillis.
+ newPicos := (rest * 1000 * 1000 * 1000) rounded asInteger.
+ ].
+
+ picos ~~ 0 ifTrue:[
+ newPicos := newPicos + picos.
+ newMillis := newMillis + (newPicos // (1000*1000*1000)).
+ newPicos := newPicos \\ (1000*1000*1000).
+ ].
+ osTime := newMillis.
+ additionalPicoseconds := newPicos.
+!
+
+setNanosecond:aNumber
+ "change the sub-second fractional part only (leaves everything above seconds unchanged)"
+
+ self
+ setMilliseconds:(self getMilliseconds // 1000) * 1000 "/ strip off any sub-second part
+ additionalPicoseconds:(aNumber * 1000) rounded. "/ set picos
+
+ "
+ Timestamp now setNanosecond:15 - 15 nanoseconds after the current second's start
+ Timestamp now setNanosecond:0.1 - 10 picoseconds after the current second's start
+ "
+!
+
setSeconds:secs
"strictly private: set the seconds (since whatever)"
@@ -3330,6 +3715,35 @@
^ aVisitor visitTimestamp:self with:aParameter
! !
+!Timestamp::TimestampBuilderAbstract class methodsFor:'documentation'!
+
+documentation
+"
+ documentation to be added.
+
+ [author:]
+ cg
+
+ [instance variables:]
+
+ [class variables:]
+
+ [see also:]
+
+"
+!
+
+examples
+"
+
+ more examples to be added:
+ [exBegin]
+ ... add code fragment for
+ ... executable example here ...
+ [exEnd]
+"
+! !
+
!Timestamp::TimestampBuilderAbstract methodsFor:'error reporting'!
malformed:aString
@@ -3461,32 +3875,34 @@
Attention: an explicit utcOffset in the input string has already been added into the hh:mm values."
(timestampClass == UtcTimestamp) ifTrue:[
- ^ UtcTimestamp
- UTCYear: year month: month day: day
- hour: hour minute: minute second: second millisecond: millisecond
+ ^ UtcTimestamp
+ UTCYear:year month:month day:day
+ hour:hour minute:minute second:second millisecond:millisecond additionalPicoseconds:picos
].
(timestampClass == TZTimestamp) ifTrue:[
- "/ Attention: an explicit utcOffset in the input string has already been added into the hh:mm values."
- ^ ((TZTimestamp
- UTCYear: year month: month day: day
- hour: hour minute: minute second: second millisecond: millisecond) utcOffset:utcOffset)
+ "/ Attention: an explicit utcOffset in the input string has already been added into the hh:mm values."
+ ^ ((TZTimestamp
+ UTCYear:year month:month day:day
+ hour:hour minute:minute second:second millisecond:millisecond additionalPicoseconds:picos
+ ) utcOffset:utcOffset)
].
(isUtcTime or:[hasTimezone and:[utcOffset == 0]]) ifTrue:[
- ^ ((timestampClass == Timestamp) ifTrue:UtcTimestamp ifFalse:timestampClass)
- UTCYear: year month: month day: day
- hour: hour minute: minute second: second millisecond: millisecond
+ ^ ((timestampClass == Timestamp) ifTrue:UtcTimestamp ifFalse:timestampClass)
+ UTCYear:year month:month day:day
+ hour:hour minute:minute second:second millisecond:millisecond additionalPicoseconds:picos
].
hasTimezone ifTrue:[
- "/ Attention: an explicit utcOffset in the input string has already been added into the hh:mm values."
- ^ (((timestampClass == Timestamp) ifTrue:TZTimestamp ifFalse:timestampClass)
- UTCYear: year month: month day: day
- hour: hour minute: minute second: second millisecond: millisecond) utcOffset:utcOffset
+ "/ Attention: an explicit utcOffset in the input string has already been added into the hh:mm values."
+ ^ (((timestampClass == Timestamp) ifTrue:TZTimestamp ifFalse:timestampClass)
+ UTCYear:year month:month day:day
+ hour:hour minute:minute second:second millisecond:millisecond additionalPicoseconds:picos
+ ) utcOffset:utcOffset
].
"/ there was no timezone info, so make it a local timestamp again.
- ^ (timestampClass
- year: year month: month day: day
- hour: hour minute: minute second: second millisecond: millisecond)
+ ^ timestampClass
+ year:year month:month day:day
+ hour:hour minute:minute second:second millisecond:millisecond additionalPicoseconds:picos
!
yearAlreadyReadAs:yearArg
@@ -4009,7 +4425,7 @@
peek := stream peekOrNil.
peek ifNil: [
- "End of stream, only year has been read."
+ "End of stream, only date has been read."
^ self timestampWithClass:timestampClass].
(peek asUppercase == $T or: [peek == Character space])
@@ -4033,48 +4449,22 @@
"Read an arbitrary number of digits representing a fraction."
^ Fraction readDecimalFractionFrom:stream onError:[self malformed: 'Missing digits after fraction separator'].
-"/
-"/ | anyDigit digit factor fraction |
-"/
-"/ factor := (1 / 10).
-"/ fraction := 0.
-"/ anyDigit := false.
-"/
-"/ [
-"/ digit := self nextDigit.
-"/ digit >= 0
-"/ ] whileTrue: [
-"/ anyDigit := true.
-"/ fraction := digit * factor + fraction.
-"/ factor := (factor / 10)
-"/ ].
-"/
-"/ anyDigit ifFalse: [self malformed: 'Missing digits after fraction separator'].
-"/ ^ fraction
-"/
+
+ "
+ (Fraction readDecimalFractionFrom:'12345' readStream onError:nil)
+ "
!
readMilliseconds
- "Read an arbitrary number of digits representing milliseconds. As the timestamp can
- hold only integer amounts of milliseconds, don't mind the rest of the digits."
-
- millisecond := (self readFraction * 1000) asInteger
-"/ | digit factor |
-"/
-"/ factor := 100.
-"/
-"/ [
-"/ digit := self nextDigit.
-"/ digit >= 0
-"/ ] whileTrue: [
-"/ factor > 0 ifTrue: [
-"/ "Factor still > 0, did not read all three digits of mantissa."
-"/ millisecond := digit * factor + millisecond.
-"/ factor := (factor / 10) integerPart
-"/ ]
-"/ ].
-"/
-"/ factor = 100 ifTrue: [self malformed: 'No digits after millisecond separator']
+ "Read an arbitrary number of digits representing the fractional part
+ (used to be milliseconds, but now we can represent anything down to pico seconds"
+
+ |fraction ms|
+
+ fraction := self readFraction. "/ 0 .. 0.99999...
+ ms := (fraction * 1000). "/ 0 .. 999.999999
+ millisecond := (ms // 1). "/ 0 .. 999
+ picos := (ms \\ 1) * (1000 * 1000 * 1000).
"Created: / 15-06-2005 / 15:25:45 / masca"
!
@@ -4115,7 +4505,8 @@
!
readTime
- "Date read, don't mind it. Read only the time value."
+ "Date already read, don't mind it.
+ Read only the time value."
| peek f |
@@ -4214,8 +4605,8 @@
readTimezone
"Read time zone information. There are three possibilities of what can occur.
- If there is nothing more to read, the offset is unknown - this is treated as
- Zulu time as this may not be true."
+ If there is nothing more to read, the offset is unknown - this is treated as
+ Zulu time as this may not be true."
| peek tzOffset |
@@ -4224,7 +4615,7 @@
peek := peek asUppercase.
"If the time is in Zulu, don't modify the timestamp. This makes the machine
- run in Zulu time zone, maybe some corrections would be nice."
+ run in Zulu time zone, maybe some corrections would be nice."
peek == $Z ifTrue: [
"Time read, skip Zulu signature and exit."
isUtcTime := true.
@@ -4253,7 +4644,8 @@
readTimezoneOffset
"Read time zone offset as a number minutes. Generally, there should be hours only
- but as the format introduces minutes in offsets, we must accept them."
+ but as the format introduces minutes in offsets, we must accept them.
+ (actually: there are countries with half-hour offsets!!)"
| hours digit |
@@ -4262,12 +4654,12 @@
(hours between: 0 and: 12) ifFalse: [self malformed: 'Bad offset hour: ' , hours printString].
stream peekOrNil = $:
- ifTrue: [
- "Colon read, minutes must follow."
- stream next.
- digit := self nextDigits: 2.
- (digit between: 0 and: 59) ifFalse: [self malformed: 'Bad offset minute: ' , digit printString].
- ^Array with: hours with: digit].
+ ifTrue: [
+ "Colon read, minutes must follow."
+ stream next.
+ digit := self nextDigits: 2.
+ (digit between: 0 and: 59) ifFalse: [self malformed: 'Bad offset minute: ' , digit printString].
+ ^Array with: hours with: digit].
"Read next digit and check whether minutes follow. If not, return only with hours. If yes,
check boundaries."
@@ -4283,21 +4675,21 @@
!
readWeekNumber
+ "Read week number. It is always two digits long."
| week dayInWeek digit |
- "Read week number. It is always two digits long."
week := self nextDigits: 2.
stream peekOrNil = $-
- ifTrue: [
- "Got dash, day number must follow."
- stream next.
- digit := self nextDigit.
- digit < 0 ifTrue: [self malformed: 'Bad weekday number'].
- digit > 7 ifTrue: [self malformed: 'Bad weekday number'].
- self dateFromWeek: week andWeekday: digit.
- ^self].
+ ifTrue: [
+ "Got dash, day number must follow."
+ stream next.
+ digit := self nextDigit.
+ digit < 0 ifTrue: [self malformed: 'Bad weekday number'].
+ digit > 7 ifTrue: [self malformed: 'Bad weekday number'].
+ self dateFromWeek: week andWeekday: digit.
+ ^self].
"Read day number that follows the week. If the number is not given, consider it monday."
dayInWeek := self nextDigit.