--- a/Timestamp.st Wed Nov 05 17:49:21 2014 +0100
+++ b/Timestamp.st Wed Nov 05 21:27:34 2014 +0100
@@ -19,7 +19,8 @@
!
Object subclass:#TimestampBuilderAbstract
- instanceVariableNames:'year month day hour minute second millisecond isUtcTime'
+ instanceVariableNames:'year month day hour minute second millisecond isUtcTime
+ yearAlreadyRead'
classVariableNames:''
poolDictionaries:''
privateIn:Timestamp
@@ -360,8 +361,13 @@
basicReadFrom:aStream
"return a new Timestamp, reading a printed representation from aStream.
The string is interpreted as 24 hour format, as printed.
+
Notice, that this is not the storeString format and
is different from the format expected by readFrom:.
+ This is more or less a heuristic attempt to read any reasonable format.
+ If the input starts with an integer > 31, it is assumed to be a year and the rest
+ is read in iso8601 format.
+
KLUDGE:
us and non-us format have different ordering of day and month;
The format read here is (non-us) dd-mm-yyyy hh:mm:ss.iii
@@ -439,7 +445,8 @@
].
^ self year:year month:month day:day hour:hour minute:min second:sec millisecond:millis.
- "
+ "some add hoc formats:
+
Timestamp basicReadFrom:'20-2-1995 13:11:06' readStream
Timestamp basicReadFrom:'20-2-1995 13:11:06.' readStream
(Timestamp basicReadFrom:'10-9-1995 13:11:06' readStream) month
@@ -456,6 +463,8 @@
Timestamp basicReadFrom:'20-2-1995 24:01:00.100' readStream
Timestamp basicReadFrom:'20-2-1995 24:00:01.100' readStream
Timestamp basicReadFrom:'foo' readStream
+
+ any iso8601 format:.
Timestamp basicReadFrom:(Timestamp now printString readStream)
Timestamp basicReadFrom:'1995-10-20 24:00:00.000' readStream
Timestamp basicReadFrom:'1995-10-20 12:10:00.000' readStream
@@ -476,124 +485,149 @@
Missing minute, second and ms values are replaced with 0;
i.e. 1999T12 is the same as 1999-01-01T12:00:00.000.
Partial hours, minutes, seconds are allowed at the end,
- decimal separtors are both $. and $, .
+ decimal separators are both $. and $, .
Of course, a 24 hour clock is used.
On error, raise an exception.
- Please use this format for all external representations - its the standard."
-
- |str day month year hour min sec millis fraction isUtcTime peekChar|
-
- str := aStringOrStream readStream.
-
- month := day := 1.
- hour := millis := 0.
- isUtcTime := false.
-
- yearOrNil notNil ifTrue:[
- year := yearOrNil
- ] ifFalse:[
- year := Integer readFrom:str onError:nil.
- year isNil ifTrue:[ TimeConversionError raiseErrorString:' - bad year' ]
- ].
-
- str skipSeparators.
- str peek == $- ifTrue:[
- str next.
- "/ month follows.
- month := Integer readFrom:str.
- (month between:1 and:12) ifFalse:[ TimeConversionError raiseErrorString:' - bad month' ].
-
- str skipSeparators.
- str peek == $- ifTrue:[
- str next.
- "/ day follows.
- day := Integer readFrom:str.
- (day between:1 and:31) ifFalse:[ TimeConversionError raiseErrorString:' - bad day' ].
- ].
- ].
-
- str skipSeparators.
- str atEnd ifFalse:[
- "time follows"
-
- str peek == $T ifTrue:[
- "we treat the T as optional here"
- str next.
- str skipSeparators.
- ].
- hour := Integer readFrom:str onError:-1.
- (hour between:0 and:24) ifFalse:[ TimeConversionError raiseErrorString:' - bad hour' ].
- str skipSeparators.
- str peekOrNil == $: ifTrue:[
- str next.
- "/ minutes follow.
- min := Integer readFrom:str onError:-1.
- (min between:0 and:59) ifFalse:[ TimeConversionError raiseErrorString:' - bad minute' ].
- str skipSeparators.
- str peekOrNil == $: ifTrue:[
- str next.
- "/ seconds follow.
- sec := Integer readFrom:str onError:-1.
- (sec between:0 and:59) ifFalse:[ TimeConversionError raiseErrorString:' - bad seconds' ].
- str skipSeparators.
- ].
- ].
-
- peekChar := str peekOrNil.
- (peekChar == $. or:[peekChar == $,]) ifTrue:[
- str next.
- "/ decimals follow.
- fraction := Number readMantissaFrom:str radix:10.
- min isNil ifTrue:[
- min := 60 * fraction.
- fraction := min fractionPart.
- min := min truncated.
- ].
- (sec isNil and:[fraction ~= 0])ifTrue:[
- sec := 60 * fraction.
- fraction := sec fractionPart.
- sec := sec truncated.
- ].
- fraction ~= 0 ifTrue:[
- millis := (1000 * fraction) rounded. "/ mhmh - should it be truncated ?
- ].
- ].
-
- peekChar := str peekOrNil.
- peekChar notNil ifTrue:[
- peekChar == $Z ifTrue:[
- str next.
- isUtcTime := true.
- ]
-"/ Todo
-"/ ifFalse:[peekChar == $+ ifTrue:[
+ Please use this format for all external representations - it's the standard."
+
+ "/ changed to use the new reader
+ ^ TimestampISO8601Builder
+ read:aStringOrStream withClass:self
+ yearAlreadyReadAs:yearOrNil
+
+"/ |str day month dayInWeek week year hour min sec tmpDay millis fraction isUtcTime peekChar ch|
+"/
+"/ str := aStringOrStream readStream.
+"/
+"/ month := day := 1.
+"/ hour := millis := 0.
+"/ isUtcTime := false.
+"/
+"/ yearOrNil notNil ifTrue:[
+"/ year := yearOrNil
+"/ ] ifFalse:[
+"/ year := Integer readFrom:str onError:nil.
+"/ year isNil ifTrue:[ TimeConversionError raiseErrorString:' - bad year' ]
+"/ ].
+"/
+"/ str skipSeparators.
+"/ (((ch := str peek) == $-) or:[ch == $W]) ifTrue:[
+"/ (ch == $-) ifTrue:[ str next ].
+"/ str peek == $W ifTrue:[
+"/ str next.
+"/
+"/ "/ week follows
+"/ week := Integer readFrom:str onError:-1.
+"/ (week between:1 and:53) ifFalse:[ TimeConversionError raiseErrorString:' - bad week' ].
+"/
+"/ str skipSeparators.
+"/ str peek == $- ifTrue:[
+"/ str next.
+"/ "/ day follows.
+"/ dayInWeek := Integer readFrom:str onError:-1.
+"/ (dayInWeek between:1 and:7) ifFalse:[ TimeConversionError raiseErrorString:' - bad day in week' ].
+"/ ] ifFalse:[
+"/ dayInWeek := 1.
+"/ ].
+"/ tmpDay := Date newDayInWeek:dayInWeek week:week year:year.
+"/ day := tmpDay day.
+"/ month := tmpDay month.
+"/ year := tmpDay year.
+"/ ] ifFalse:[
+"/ "/ month follows.
+"/ month := Integer readFrom:str onError:-1.
+"/ (month between:1 and:12) ifFalse:[ TimeConversionError raiseErrorString:' - bad month' ].
+"/
+"/ str skipSeparators.
+"/ str peek == $- ifTrue:[
+"/ str next.
+"/ "/ day follows.
+"/ day := Integer readFrom:str onError:-1.
+"/ (day between:1 and:31) ifFalse:[ TimeConversionError raiseErrorString:' - bad day' ].
+"/ ].
+"/ ].
+"/ ].
+"/
+"/ str skipSeparators.
+"/ str atEnd ifFalse:[
+"/ "time follows"
+"/
+"/ str peek == $T ifTrue:[
+"/ "we treat the T as optional here"
+"/ str next.
+"/ str skipSeparators.
+"/ ].
+"/ hour := Integer readFrom:str onError:-1.
+"/ (hour between:0 and:24) ifFalse:[ TimeConversionError raiseErrorString:' - bad hour' ].
+"/ str skipSeparators.
+"/ str peekOrNil == $: ifTrue:[
+"/ str next.
+"/ "/ minutes follow.
+"/ min := Integer readFrom:str onError:-1.
+"/ (min between:0 and:59) ifFalse:[ TimeConversionError raiseErrorString:' - bad minute' ].
+"/ str skipSeparators.
+"/ str peekOrNil == $: ifTrue:[
+"/ str next.
+"/ "/ seconds follow.
+"/ sec := Integer readFrom:str onError:-1.
+"/ (sec between:0 and:59) ifFalse:[ TimeConversionError raiseErrorString:' - bad seconds' ].
+"/ str skipSeparators.
+"/ ].
+"/ ].
+"/
+"/ peekChar := str peekOrNil.
+"/ (peekChar == $. or:[peekChar == $,]) ifTrue:[
+"/ str next.
+"/ "/ decimals follow.
+"/ fraction := Number readMantissaFrom:str radix:10.
+"/ min isNil ifTrue:[
+"/ min := 60 * fraction.
+"/ fraction := min fractionPart.
+"/ min := min truncated.
+"/ ].
+"/ (sec isNil and:[fraction ~= 0])ifTrue:[
+"/ sec := 60 * fraction.
+"/ fraction := sec fractionPart.
+"/ sec := sec truncated.
+"/ ].
+"/ fraction ~= 0 ifTrue:[
+"/ millis := (1000 * fraction) rounded. "/ mhmh - should it be truncated ?
+"/ ].
+"/ ].
+"/
+"/ peekChar := str peekOrNil.
+"/ peekChar notNil ifTrue:[
+"/ peekChar == $Z ifTrue:[
"/ str next.
"/ isUtcTime := true.
-"/ ] ifFalse:[peekChar == $- ifTrue:[
-"/ str next.
-"/ isUtcTime := true.
-"/ ]]]
- ].
- ].
-
- min isNil ifTrue:[min := 0].
- sec isNil ifTrue:[sec := 0].
-
- "special check - only 24:00:00 is allowed;
- every time after that must wrap"
- hour == 24 ifTrue:[
- (min ~~ 0 or:[sec ~~ 0 or:[millis ~~ 0]]) ifTrue:[ TimeConversionError raiseErrorString:' - bad hour' ].
- ].
-
- isUtcTime ifTrue:[
- ^ self
- UTCYear:year month:month day:day
- hour:hour minute:min second:sec millisecond:millis.
- ] ifFalse:[
- ^ self
- year:year month:month day:day
- hour:hour minute:min second:sec millisecond:millis.
- ]
+"/ ] ifFalse:[
+"/ ((peekChar == $+) or:[peekChar == $- ]) ifTrue:[
+"/ str next.
+"/ self halt.
+"/ isUtcTime := true.
+"/ ]
+"/ ]
+"/ ].
+"/ ].
+"/
+"/ min isNil ifTrue:[min := 0].
+"/ sec isNil ifTrue:[sec := 0].
+"/
+"/ "special check - only 24:00:00 is allowed;
+"/ every time after that must wrap"
+"/ hour == 24 ifTrue:[
+"/ (min ~~ 0 or:[sec ~~ 0 or:[millis ~~ 0]]) ifTrue:[ TimeConversionError raiseErrorString:' - bad hour' ].
+"/ ].
+"/
+"/ isUtcTime ifTrue:[
+"/ ^ self
+"/ UTCYear:year month:month day:day
+"/ hour:hour minute:min second:sec millisecond:millis.
+"/ ] ifFalse:[
+"/ ^ self
+"/ year:year month:month day:day
+"/ hour:hour minute:min second:sec millisecond:millis.
+"/ ]
"
Timestamp readIso8601FormatFrom:'1995-02-20T13:11:06.123'
@@ -608,6 +642,13 @@
Timestamp readIso8601FormatFrom:'1995T13.333333'
Timestamp readIso8601FormatFrom:'1995'
+ Timestamp readIso8601FormatFrom:'2014W40' -> 29.sep.2014
+ Timestamp readIso8601FormatFrom:'2014W44-4' -> 30.oct.2014
+ Timestamp readIso8601FormatFrom:'2014W1' -> 30.dec.2013 !!!!!! (this week starts in the previous year)
+ Timestamp readIso8601FormatFrom:'2014W1-1' -> same 30.dec.2013 !!!!!! (this week starts in the previous year)
+ Timestamp readIso8601FormatFrom:'2014W1-2' -> 31.dec.2013 !!!!!! (this week starts in the previous year)
+ Timestamp readIso8601FormatFrom:'2014W1-3' -> 1.jan.2014 !!!!!! (this week starts in the previous year)
+
Timestamp readIso8601FormatFrom:'1995-02-20 13:11:06'
Timestamp readIso8601FormatFrom:'1995-02-20 13:11:06Z'
@@ -637,9 +678,9 @@
readFrom:aStringOrStream format:formatString language:languageOrNil onError:exceptionalValue
"return a new Timestamp, reading a printed representation from aStream using a formatString.
The formatString is similar to the one used when printing.
- On error, exceptionalValue is returned. If exceptionalValue is a one-arg block, an error message is
- passed as argument.
- format:
+ On error, exceptionalValue is returned.
+ If exceptionalValue is a one-arg block, an error message is passed as argument.
+ Format:
%h hours, 00..23 (i.e. european) 0-padded to length 2
%u hours, 00..12 (i.e. us) 0-padded to length 2
%m minutes, 00..59 0-padded to length 2
@@ -664,9 +705,11 @@
%Y1900 - year, last 2 digits only, map to 1900..1999
%Y2000 - year, last 2 digits only, map to 2000..2099
- an optional length after the % gives a field length;
+ an optional length after the % gives a field length;
i.e. %2h%2m%2s parses 123557 as 12:35:37
-"
+
+ Please consider using a standard format, such as iso8601.
+ "
|day month year
hour minute second millisecond
@@ -815,7 +858,10 @@
The string is interpreted as 24 hour format, as printed.
Notice, that this is not the storeString format and
is different from the format expected by readFrom:.
- The format read here is dd-mm-yyyy hh:mm:ss.iii"
+ The format read here is either dd-mm-yyyy hh:mm:ss.iii
+ or iso8601 (if the first integer is >31).
+
+ Please consider using a standard format, such as iso8601."
|stream newTime|
@@ -857,9 +903,12 @@
This format is used for BER specification of the ASN.1 GeneralizedTime as defined in X.208 Sec. 33,
so read this before changing anything here.
+ New applications should consider using a standard format, such as iso8601.
+
Notice, that this is not the storeString format and
is different from the format expected by readFrom:.
"
+
^ self
readGeneralizedFrom:aStringOrStream
short:false
@@ -881,6 +930,8 @@
This format is used for BER specification of the ASN.1 GeneralizedTime as defined in X.208 Sec. 33,
so read this before changing anything here.
+ New applications should consider using a standard format, such as iso8601.
+
Notice, that this is not the storeString format and
is different from the format expected by readFrom:.
"
@@ -939,6 +990,8 @@
UTCTime as defined in X.208 Sec. 33, so read this before changing anything here.
The short form is no longer recommended.
+ New applications should consider using a standard format, such as iso8601.
+
Notice, that this is not the storeString format and
is different from the format expected by readFrom:.
"
@@ -1056,13 +1109,31 @@
"Modified: / 22-08-2006 / 12:30:11 / cg"
!
-readISO8601From: stringOrStream
-
- ^ TimestampISO8601Builder read:stringOrStream withClass:self
+readISO8601From:aStringOrStream
+ "Please use this format for all external representations - it's the standard."
+
+ "using the new reader"
+
+ ^ TimestampISO8601Builder read:aStringOrStream withClass:self
"Created: / 16-06-2005 / 16:13:36 / masca"
!
+readISO8601From:aStringOrStream onError:exceptionValue
+ "Please use this format for all external representations - it's the standard."
+
+ "using the new reader"
+
+ |retVal|
+
+ ConversionError handle:[:ex |
+ retVal := exceptionValue value
+ ] do:[
+ retVal := TimestampISO8601Builder read:aStringOrStream withClass:self
+ ].
+ ^ retVal
+!
+
readIso8601FormatFrom:aStringOrStream
"return a new Timestamp, reading an iso8601 UTC representation from aStream.
Missing month/day values are replaced with 1; i.e. 1999T24:00
@@ -1071,10 +1142,14 @@
i.e. 1999T12 is the same as 1999-01-01T12:00:00.000.
Of course, a 24 hour clock is used.
On error, raise an exception.
- Please use this format for all external representations - its the standard."
-
- ^ self
- readIso8601FormatFrom:aStringOrStream yearAlreadyRead:nil
+
+ Please use this format for all external representations - it's the standard."
+
+ "changed to use the new reader"
+
+ ^ TimestampISO8601Builder read:aStringOrStream withClass:self
+"/ ^ self
+"/ readIso8601FormatFrom:aStringOrStream yearAlreadyRead:nil
"
Timestamp readIso8601FormatFrom:'1995-02-20T13:11:06'
@@ -1102,54 +1177,70 @@
i.e. 1999T12 is the same as 1999-01-01T12:00:00.000.
Of course, a 24 hour clock is used.
On error, raise an exception.
- Please use this format for all external representations - its the standard."
-
- ^ self
- readIso8601FormatFrom:aStringOrStream
- yearAlreadyRead:nil
- onError:exceptionValue
+
+ Please use this format for all external representations - it's the standard."
+
+ "/ changed to use the new reader
+ |retVal|
+
+ ConversionError handle:[:ex |
+ retVal := exceptionValue value
+ ] do:[
+ retVal := TimestampISO8601Builder read:aStringOrStream withClass:self
+ ].
+ ^ retVal
+
+"/ ^ self
+"/ readIso8601FormatFrom:aStringOrStream
+"/ yearAlreadyRead:nil
+"/ onError:exceptionValue
!
readRFC1123FormatFrom:rfc1123String onError:exceptionBlock
-"/ All HTTP/1.0 date/time stamps must be represented in Universal Time (UT),
-"/ also known as Greenwich Mean Time (GMT), without exception.
-"/ This is indicated in the first two formats by the inclusion of "GMT" as the three-letter abbreviation for time zone,
-"/ and should be assumed when reading the asctime format.
-"/
-"/ HTTP-date = rfc1123-date | rfc850-date | asctime-date
-"/
-"/ rfc1123-date = wkday "," SP date1 SP time SP "GMT"
-"/ rfc850-date = weekday "," SP date2 SP time SP "GMT"
-"/ asctime-date = wkday SP date3 SP time SP 4DIGIT
-"/
-"/ date1 = 2DIGIT SP month SP 4DIGIT
-"/ ; day month year (e.g., 02 Jun 1982)
-"/ date2 = 2DIGIT "-" month "-" 2DIGIT
-"/ ; day-month-year (e.g., 02-Jun-82)
-"/ date3 = month SP ( 2DIGIT | ( SP 1DIGIT ))
-"/ ; month day (e.g., Jun 2)
-"/
-"/ time = 2DIGIT ":" 2DIGIT ":" 2DIGIT
-"/ ; 00:00:00 - 23:59:59
-"/
-"/ wkday = "Mon" | "Tue" | "Wed"
-"/ | "Thu" | "Fri" | "Sat" | "Sun"
-"/
-"/ weekday = "Monday" | "Tuesday" | "Wednesday"
-"/ | "Thursday" | "Friday" | "Saturday" | "Sunday"
-"/
-"/ month = "Jan" | "Feb" | "Mar" | "Apr"
-"/ | "May" | "Jun" | "Jul" | "Aug"
-"/ | "Sep" | "Oct" | "Nov" | "Dec"
-"/
-"/ Mon, 17 Aug 2009 11:11:15 GMT
-"/
-"/ however, occasionally, someone presents us with non-UTC strings which include a timezone;
-"/ thus, this also supports:
-"/ Mon, 17 Aug 2009 11:11:15 +xxxx
-"/ Mon, 17 Aug 2009 11:11:15 -xxxx
-"/ and:
-"/ Mon, 17 Aug 2009 11:11:15 PST
+ "please use this only for http-requests.
+ All other programs should use iso8601, which is the standard for times and dates.
+
+ All HTTP/1.0 date/time stamps must be represented in Universal Time (UT),
+ also known as Greenwich Mean Time (GMT), without exception.
+ This is indicated in the first two formats by the inclusion of
+ 'GMT' as the three-letter abbreviation for time zone,
+ and should be assumed when reading the asctime format.
+
+ HTTP-date = rfc1123-date | rfc850-date | asctime-date
+
+ rfc1123-date = wkday ',' SP date1 SP time SP 'GMT'
+ rfc850-date = weekday ',' SP date2 SP time SP 'GMT'
+ asctime-date = wkday SP date3 SP time SP 4DIGIT
+
+ date1 = 2DIGIT SP month SP 4DIGIT
+ ; day month year (e.g., 02 Jun 1982)
+ date2 = 2DIGIT '-' month '-' 2DIGIT
+ ; day-month-year (e.g., 02-Jun-82)
+ date3 = month SP ( 2DIGIT | ( SP 1DIGIT ))
+ ; month day (e.g., Jun 2)
+
+ time = 2DIGIT ':' 2DIGIT ':' 2DIGIT
+ ; 00:00:00 - 23:59:59
+
+ wkday = 'Mon' | 'Tue' | 'Wed'
+ | 'Thu' | 'Fri' | 'Sat' | 'Sun'
+
+ weekday = 'Monday' | 'Tuesday' | 'Wednesday'
+ | 'Thursday' | 'Friday' | 'Saturday' | 'Sunday'
+
+ month = 'Jan' | 'Feb' | 'Mar' | 'Apr'
+ | 'May' | 'Jun' | 'Jul' | 'Aug'
+ | 'Sep' | 'Oct' | 'Nov' | 'Dec'
+
+ Mon, 17 Aug 2009 11:11:15 GMT
+
+ however, occasionally, someone presents us with non-UTC strings which include a timezone;
+ thus, this also supports:
+ Mon, 17 Aug 2009 11:11:15 +xxxx
+ Mon, 17 Aug 2009 11:11:15 -xxxx
+ and:
+ Mon, 17 Aug 2009 11:11:15 PST
+ "
|parts indexModifier utcOffsetString utcOffset day year time monthName month|
@@ -2406,12 +2497,12 @@
minutes isZero ifFalse: [
minute := minute + minutes.
minute >= 60 ifTrue: [
- hours := hours + minute // 60.
- minute := minute \\ 60.
+ hours := hours + 1.
+ minute := minute - 60.
].
minute < 0 ifTrue: [
- hours := hours + minute // 60.
- minute := (minute \\ 60) negated
+ hours := hours - 1.
+ minute := minute + 60.
]
].
@@ -2466,14 +2557,16 @@
adjust the year. Both week and day are 1-based, the first week in a year is the one
with thursday (or the one containing 4.1.)."
+ |tmpDate|
+
"Check numbers. Year may be checked if it contains 53 weeks or 52 weeks only."
(dayInteger between: 1 and: 7) ifFalse: [self malformed: 'Bad weekday number: ' , dayInteger printString].
(weekInteger between: 1 and: 53) ifFalse: [self malformed: 'Bad week number: ' , weekInteger printString].
- self shouldImplement
-
- "Created: / 15-06-2005 / 11:29:42 / masca"
- "Modified: / 15-06-2005 / 16:42:33 / masca"
+ tmpDate := Date newDayInWeek:dayInteger week:weekInteger year:year.
+ day := tmpDate day.
+ month := tmpDate month.
+ year := tmpDate year.
!
isAllowedDay: anInteger
@@ -2517,6 +2610,13 @@
"Created: / 15-06-2005 / 15:39:24 / masca"
"Modified: / 30-06-2005 / 16:48:25 / masca"
+!
+
+yearAlreadyReadAs:yearArg
+ "support for readers which may have already preread the year"
+
+ year := yearArg.
+ yearAlreadyRead := true.
! !
!Timestamp::TimestampISO8601Builder class methodsFor:'documentation'!
@@ -2563,6 +2663,8 @@
Timestamp readISO8601From: (TimestampISO8601Builder print: Timestamp now)
UtcTimestamp readISO8601From: (TimestampISO8601Builder print: UtcTimestamp now)
Timestamp readISO8601From: (TimestampISO8601Builder print: UtcTimestamp now)
+
+ Timestamp readISO8601From:'fooBar' onError:[ Timestamp now ].
"
!
@@ -2570,14 +2672,6 @@
"Created: / 16-06-2005 / 16:28:38 / masca"
! !
-!Timestamp::TimestampISO8601Builder class methodsFor:'parsing'!
-
-read: stringOrStream withClass:timestampClass
- ^ self new read:stringOrStream withClass:timestampClass
-
- "Created: / 15-06-2005 / 17:52:03 / masca"
-! !
-
!Timestamp::TimestampISO8601Builder class methodsFor:'printing'!
print: aTimestamp
@@ -2645,6 +2739,24 @@
"Created: / 15-06-2005 / 17:54:17 / masca"
! !
+!Timestamp::TimestampISO8601Builder class methodsFor:'public parsing'!
+
+read: stringOrStream withClass:timestampClass
+ ^ self new read:stringOrStream withClass:timestampClass
+
+ "Created: / 15-06-2005 / 17:52:03 / masca"
+!
+
+read: stringOrStream withClass:timestampClass yearAlreadyReadAs:yearArg
+ "support for readers which may have already preread the year"
+
+ ^ self new
+ yearAlreadyReadAs:yearArg;
+ read:stringOrStream withClass:timestampClass
+
+ "Created: / 15-06-2005 / 17:52:03 / masca"
+! !
+
!Timestamp::TimestampISO8601Builder class methodsFor:'testing'!
test
@@ -2832,7 +2944,7 @@
"Modified: / 15-06-2005 / 15:54:29 / masca"
! !
-!Timestamp::TimestampISO8601Builder methodsFor:'processing'!
+!Timestamp::TimestampISO8601Builder methodsFor:'public processing'!
read:stringOrStream withClass:timestampClass
| peek |
@@ -2841,6 +2953,7 @@
month := day := 1.
hour := minute := second := millisecond := 0.
+ isUtcTime := false.
"Read the year. This will read and swallow up to four year digits."
self readYear.
@@ -2897,6 +3010,28 @@
!Timestamp::TimestampISO8601Builder methodsFor:'reading'!
+readFraction
+ "Read an arbitrary number of digits representing a fraction."
+
+ | 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
+!
+
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."
@@ -2959,25 +3094,70 @@
readTime
"Date read, don't mind it. Read only the time value."
- | peek |
+ | peek f |
hour := self nextDigits: 2.
(hour between: 0 and: 24) ifFalse: [self malformed: 'Bad hour: ' , hour printString].
peek := stream peekOrNil.
- peek = $:
- ifTrue: [stream next]
- ifFalse: [(peek notNil and: [peek isDigit]) ifFalse: [^self]].
-
- minute := self nextDigits: 2.
+ peek isNil ifTrue: [^self].
+ (peek == $:) ifTrue: [
+ "/ read minutes
+ stream next.
+ minute := self nextDigits: 2.
+ ] ifFalse: [
+ peek isDigit ifTrue: [
+ "/ read minutes
+ minute := self nextDigits: 2.
+ ] ifFalse:[
+ (peek == $. or:[peek == $,]) ifTrue:[
+ stream next.
+ minute := self readFraction * 60.
+ ] ifFalse:[
+ ^ self.
+ ].
+ ]
+ ].
+
+ minute isInteger ifFalse:[
+ f := minute.
+ minute := f truncated.
+ second := (f - minute) * 60.
+ second isInteger ifFalse:[
+ f := second.
+ second := f truncated.
+ millisecond := (f - second) * 1000.
+ millisecond := millisecond rounded.
+ ].
+ ].
(minute between: 0 and: 59) ifFalse: [self malformed: 'Bad minute: ' , minute printString].
peek := stream peekOrNil.
- peek = $:
- ifTrue: [stream next]
- ifFalse: [(peek notNil and: [peek isDigit]) ifFalse: [^self]].
-
- second := self nextDigits: 2.
+ peek isNil ifTrue: [^self].
+ (peek == $:) ifTrue: [
+ "/ read seconds
+ stream next.
+ second := self nextDigits: 2.
+ ] ifFalse: [
+ peek isDigit ifTrue: [
+ "/ read seconds
+ second := self nextDigits: 2.
+ ] ifFalse:[
+ (peek == $. or:[peek == $,]) ifTrue:[
+ stream next.
+ second := self readFraction * 60.
+ ] ifFalse:[
+ ^ self.
+ ].
+ ]
+ ].
+
+ second isInteger ifFalse:[
+ f := second.
+ second := f truncated.
+ millisecond := (f - second) * 1000.
+ millisecond := millisecond rounded.
+ ].
(second between: 0 and: 59) ifFalse: [
"Seconds are usually in this range, do a special check for leap seconds."
second <= 61
@@ -2990,7 +3170,7 @@
].
"Hour, minute and second read. Read appendices."
- stream peekOrNil = $.
+ ((peek := stream peekOrNil) == $. or:[peek == $,])
ifTrue: [
"Read dot. Skip it and read milliseconds."
stream next.
@@ -3083,7 +3263,8 @@
readWeekNumber
- | week day digit |
+ | week dayInWeek digit |
+
"Read week number. It is always two digits long."
week := self nextDigits: 2.
@@ -3093,14 +3274,16 @@
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."
- day := self nextDigit.
- day <= 0 ifTrue: [day := 1].
-
- self dateFromWeek: week andWeekday: day
+ dayInWeek := self nextDigit.
+ dayInWeek <= 0 ifTrue: [dayInWeek := 1].
+ dayInWeek > 7 ifTrue: [self malformed: 'Bad weekday number'].
+
+ self dateFromWeek: week andWeekday: dayInWeek
"Created: / 14-06-2005 / 12:06:47 / masca"
"Modified: / 15-06-2005 / 15:53:34 / masca"
@@ -3152,11 +3335,11 @@
!Timestamp class methodsFor:'documentation'!
version
- ^ '$Header: /cvs/stx/stx/libbasic/Timestamp.st,v 1.157 2014-10-18 11:17:48 cg Exp $'
+ ^ '$Header: /cvs/stx/stx/libbasic/Timestamp.st,v 1.158 2014-11-05 20:27:34 cg Exp $'
!
version_CVS
- ^ '$Header: /cvs/stx/stx/libbasic/Timestamp.st,v 1.157 2014-10-18 11:17:48 cg Exp $'
+ ^ '$Header: /cvs/stx/stx/libbasic/Timestamp.st,v 1.158 2014-11-05 20:27:34 cg Exp $'
! !