AbstractTime.st
branchjv
changeset 25423 bcfde4da086a
parent 23107 40173e082cbc
--- a/AbstractTime.st	Mon Oct 26 22:21:00 2020 +0000
+++ b/AbstractTime.st	Tue Jun 01 12:09:10 2021 +0100
@@ -254,15 +254,38 @@
     "Modified: 1.7.1996 / 15:20:10 / cg"
 !
 
+nowWithMicroseconds
+    "return an instance of myself representing this moment with at least microsecond precision."
+
+    |osTime millis micros|
+
+    osTime := OperatingSystem getOSTimeWithMicros.
+    millis := osTime at:1.
+    micros := osTime at:2.
+    ^ self basicNew fromOSTimeWithMilliseconds:millis additionalPicoseconds:(micros*(1000*1000)).
+
+    "
+     Timestamp now   
+     Time now
+     Timestamp nowWithMilliseconds
+     Timestamp nowWithMicroseconds
+    "
+
+    "Modified: 1.7.1996 / 15:20:10 / cg"
+!
+
 nowWithMilliseconds
-    "return an instance of myself representing this moment with millisecond precision."
+    "return an instance of myself representing this moment with at least millisecond precision."
 
     ^ self basicNew fromOSTimeWithMilliseconds:(OperatingSystem getOSTime)
 
     "
+     Time now
+     Time nowWithMilliseconds 
+
      Timestamp now   
-     Time now
-     Time nowWithMilliseconds
+     Timestamp nowWithMilliseconds
+     Timestamp nowWithMicroseconds
     "
 
     "Modified: 1.7.1996 / 15:20:10 / cg"
@@ -357,7 +380,6 @@
     ^ '%h:%m:%s'
 ! !
 
-
 !AbstractTime class methodsFor:'private-instance creation'!
 
 fromOSTime:osTime
@@ -398,7 +420,11 @@
 !
 
 microsecondClockValue
-    "return microseconds seconds of now"
+    "This returns a microsecond timer value.
+     The returned value is a 64bit value
+     (which is the number of microseconds since the system's boot time -
+      but you should not depend on that because it is system specific.
+     Only use for relative delta-times."
 
     ^ OperatingSystem getMicrosecondTime
 
@@ -418,6 +444,8 @@
      t2 := Time microsecondClockValue.
      t2 - t1 - overhead        
     "
+
+    "Modified (comment): / 17-05-2020 / 15:05:12 / cg"
 !
 
 millisecondClockValue
@@ -457,6 +485,8 @@
 !
 
 readFrom:aStringOrStream format:formatString language:languageString onError:exceptionalValue
+    "see format description in readFrom:format:language:onError:"
+
     ^ self subclassResponsibility
 !
 
@@ -586,6 +616,41 @@
     "
 ! !
 
+!AbstractTime methodsFor:'Compatibility-ST80'!
+
+hour
+    "return the hour (0..23).
+     ST-80 Timestamp compatibility 
+     (I'd prefer the name #hours, for Time compatibility)."
+
+    ^ self hours
+
+    "Created: 1.7.1996 / 15:14:50 / cg"
+    "Modified: 1.7.1996 / 15:15:32 / cg"
+!
+
+minute
+    "return the minute (0..59).
+     ST-80 Timestamp compatibility 
+     (I'd prefer the name #minutes, for Time compatibility)."
+
+    ^ self minutes
+
+    "Created: 1.7.1996 / 15:14:29 / cg"
+    "Modified: 1.7.1996 / 15:15:37 / cg"
+!
+
+second
+    "return the second (0..59).
+     ST-80 Timestamp compatibility 
+     (I'd prefer the name #seconds, for Time compatibility)."
+
+    ^ self seconds
+
+    "Created: 1.7.1996 / 15:14:19 / cg"
+    "Modified: 1.7.1996 / 15:15:49 / cg"
+! !
+
 !AbstractTime methodsFor:'abstract'!
 
 hours
@@ -645,6 +710,17 @@
     "
 !
 
+microseconds
+    "return the microseconds within the current second (0..999999)"
+
+    ^ (self milliseconds * 1000) + (self additionalPicoseconds // (1000*1000)).
+
+    "
+     Timestamp now microseconds   
+     Timestamp nowWithMicroseconds microseconds   
+    "
+!
+
 minuteInDay
     "return the minutes (0..59)"
 
@@ -656,6 +732,43 @@
     "
 !
 
+nanoseconds
+    "return the nanoseconds within the current second (0..999999)"
+
+    ^ (self milliseconds * 1000000) + (self additionalPicoseconds // (1000)).
+
+    "
+     Timestamp now nanoseconds   
+     Timestamp nowWithMicroseconds nanoseconds   
+    "
+!
+
+picoseconds
+    "return the picoseconds within the current second (0..999999999).
+     notice: that is NOT the total number of picoseconds,
+     but the fractional part (within the second) only. 
+     Use this only for printing.
+     Here, fall back and generate something based on the milliseconds"
+
+    ^ (self milliseconds * 1000 * 1000 * 1000) + (self additionalPicoseconds).
+
+    "
+     Timestamp now picoseconds
+     Timestamp nowWithMicroseconds picoseconds
+
+     (TimeDuration fromPicoseconds:100) picoseconds
+     (TimeDuration fromPicoseconds:100000) picoseconds
+     (TimeDuration fromPicoseconds:100000) nanoseconds
+     (TimeDuration fromPicoseconds:100000000) picoseconds
+     (TimeDuration fromPicoseconds:100000000) nanoseconds
+     (TimeDuration fromPicoseconds:100000000) microseconds
+     (TimeDuration fromPicoseconds:100000000000) picoseconds
+     (TimeDuration fromPicoseconds:100000000000) nanoseconds
+     (TimeDuration fromPicoseconds:100000000000) microseconds
+     (TimeDuration fromPicoseconds:100000000000) milliseconds
+    "
+!
+
 secondInDay
     "return the seconds (0..59)"
 
@@ -687,36 +800,56 @@
 !AbstractTime methodsFor:'arithmetic'!
 
 + aNumberOrTimeDuration
-    "Add aNumber numberOfSeconds or, if its a timeDuration, add its value"
+    "Add aNumber (numberOfSeconds) or, if it's a timeDuration, add it's value"
 
     ^ aNumberOrTimeDuration sumFromTimestamp:self.
 
-"/    ^ self speciesNew 
-"/        setMilliseconds:(self getMilliseconds + (aNumberOrTimeDuration * 1000) asInteger)
-
     "
      Timestamp now to:(Timestamp now + 30) by:2 do:[:time|
         Transcript showCR:time.
      ].
+     Timestamp now to:(Timestamp now + 30) by:2 seconds do:[:time|
+        Transcript showCR:time.
+     ].
+     (Timestamp now + 30 seconds) to:(Timestamp now) by:-2 seconds do:[:time|
+        Transcript showCR:time.
+     ].
+     (Timestamp now + 30 seconds) to:(Timestamp now) by:-2 do:[:time|
+        Transcript showCR:time.
+     ].
 
      (Timestamp now + 20)  -  Timestamp now  
      (Timestamp now + 0.5)  -  Timestamp now  
      (Timestamp now + (TimeDuration fromString:'1m 10s'))  -  Timestamp now  
      (Timestamp now + (10 seconds))  -  Timestamp now  
     "
+
+    "Modified (comment): / 13-02-2017 / 19:55:04 / cg"
 !
 
-- aTimeOrNumberOfSeconds
+- aTimeOrTimeDurationOrNumberOfSeconds
     "return the delta in seconds between 2 times or subtract a number of seconds."
 
-    ^ aTimeOrNumberOfSeconds differenceFromTimestamp:self.
+    "/ Q: for Time - x hours
+    "/ should we convert to timestamp i.e. caring for time-wrapping at midnight?
+    "/ no - for compatibility, we'll get the time-within-the day again
+    "/ if you need the real timestamp, a for it explicitly
+
+    ^ aTimeOrTimeDurationOrNumberOfSeconds differenceFromTimestamp:self.
 
     "
-     Timestamp now - 3600.  
-     Time now - 3600.   
-     (Time now addSeconds:10) - Time now   
-     (TimeDuration fromString:'1.5hr') - 3600.   
-     (TimeDuration fromString:'1.5hr') - (TimeDuration fromString:'1hr')   
+     Timestamp now - 3600.                          -> 2018-05-09 15:37:57.485
+     Timestamp now - 3600 seconds.                  -> 2018-05-09 15:38:04.665
+     (Timestamp now addSeconds:10) - Timestamp now  -> 10s
+
+     Time now - 3600.                               -> 03:38:23 PM
+     Time now - 3600 seconds.                       -> 03:38:35 PM
+     (Time now addSeconds:10) - Time now            -> 10s 
+     Time now - 1 hours.                            -> 11:04:02
+     Time now - 24 hours.                           -> 11:04:02
+
+     (TimeDuration fromString:'1.5hr') - 3600.                           -> 30m 
+     (TimeDuration fromString:'1.5hr') - (TimeDuration fromString:'1hr') -> 30m  
 
      |t1 t2|
 
@@ -725,6 +858,8 @@
      t2 := Timestamp now.
      Transcript showCR:('seconds passed: ' , (t2 - t1) printString).
     "
+
+    "Modified (comment): / 08-05-2019 / 12:49:59 / Claus Gittinger"
 !
 
 addDays:numberOfDays
@@ -763,11 +898,46 @@
     "
 !
 
+addMicroseconds:numberOfMicroSeconds
+    "return a new instance of myself, numberOfMicroSeconds afterwards."
+
+    ^ self speciesNew 
+        setMilliseconds:(self getMilliseconds + (numberOfMicroSeconds // 1000))
+        additionalPicoseconds:(self additionalPicoseconds 
+                                + ((numberOfMicroSeconds \\ 1000)*1000000) truncated)
+
+    "
+     |t1 t2|
+
+     t1 := Timestamp now.
+     Transcript showCR:t1.
+     t2 := t1 addMicroseconds:1000.
+     Transcript showCR:t2.
+     t2 := t1 addMicroseconds:1010.
+     Transcript showCR:t2.
+     Transcript showCR:(t2 - t1).
+     self halt.
+     Transcript showCR:(t2 - t1) asMicroseconds.
+    "
+
+    "
+     |t|
+
+     t := Time now.
+     Transcript showCR:t.
+     Transcript showCR:(t addMilliseconds:1000).
+    "
+
+    "Modified: / 22-05-2018 / 16:52:37 / Stefan Vogel"
+!
+
 addMilliseconds:numberOfMilliSeconds
     "return a new instance of myself, numberOfMilliSeconds afterwards."
 
     ^ self speciesNew 
-        setMilliseconds:(self getMilliseconds + numberOfMilliSeconds)
+        setMilliseconds:(self getMilliseconds + (numberOfMilliSeconds // 1 "this is inlined #truncated"))
+        additionalPicoseconds:(self additionalPicoseconds 
+                                + ((numberOfMilliSeconds \\ 1)*1000000000) truncated)
 
     "
      |t|
@@ -784,6 +954,8 @@
      Transcript showCR:t.
      Transcript showCR:(t addMilliseconds:1000).
     "
+
+    "Modified: / 22-05-2018 / 17:03:20 / Stefan Vogel"
 !
 
 addMinutes:numberOfMinutes
@@ -811,8 +983,7 @@
 addSeconds:numberOfSeconds
     "return a new instance of myself, numberOfSeconds afterwards."
 
-    ^ self speciesNew 
-        setMilliseconds:(self getMilliseconds + (numberOfSeconds * 1000))
+    ^ self addMilliseconds:(numberOfSeconds * 1000) 
 
     "
      |t|
@@ -876,6 +1047,8 @@
 !
 
 roundTo:aTimeDuration
+    "round the receiver to the next multiple of a TimeDuration"
+    
      ^ self class new setMilliseconds:(self getMilliseconds roundTo:aTimeDuration getMilliseconds)
 
     "
@@ -924,8 +1097,7 @@
 subtractMilliseconds:numberOfMilliSeconds
     "return a new instance of myself, numberOfMilliSeconds before."
 
-    ^ self speciesNew 
-        setMilliseconds:(self getMilliseconds - numberOfMilliSeconds)
+    ^ self addMilliseconds:numberOfMilliSeconds negated 
 
     "
      |t|
@@ -969,8 +1141,7 @@
 subtractSeconds:numberOfSeconds
     "return a new instance of myself, numberOfSeconds before."
 
-    ^ self speciesNew 
-        setMilliseconds:(self getMilliseconds - (numberOfSeconds * 1000))
+    ^ self addMilliseconds:(numberOfSeconds * -1000) 
 
     "
      |t|
@@ -1005,6 +1176,8 @@
 !
 
 truncateTo:aTimeDuration
+    "truncate the receiver to the next multiple of a TimeDuration"
+
      ^ self class new setMilliseconds:(self getMilliseconds truncateTo:aTimeDuration getMilliseconds)
 
     "
@@ -1016,20 +1189,149 @@
 
 !AbstractTime methodsFor:'comparing'!
 
-< aTime
+< aTimeOrTimeDurationOrNumberOfSeconds
     "return true if the receiver is before the argument"
 
-    ^ self getMilliseconds < aTime getMilliseconds
-
-    "Modified: 3.7.1996 / 13:10:17 / cg"
+    |myMilliseconds otherMilliseconds myPicos otherPicos|
+
+    myMilliseconds := self getMilliseconds.
+    myPicos := self additionalPicoseconds.
+
+    aTimeOrTimeDurationOrNumberOfSeconds isNumber ifTrue:[
+        "backward compatibility for old code, which expects (time1 - time2) to return seconds"    
+        otherMilliseconds := (aTimeOrTimeDurationOrNumberOfSeconds * 1000) asInteger.   "integer seconds"
+        otherPicos := 0.
+    ] ifFalse:[
+        otherMilliseconds := aTimeOrTimeDurationOrNumberOfSeconds getMilliseconds.
+        otherPicos := aTimeOrTimeDurationOrNumberOfSeconds additionalPicoseconds.
+    ].
+    ^ (myMilliseconds < otherMilliseconds)
+    or:[ myMilliseconds = otherMilliseconds and:[myPicos < otherPicos ]].
+
+    "
+        (Timestamp now + 10) - Timestamp now  < 10
+        (Timestamp now + 10) - Timestamp now  < 11
+    "
+!
+
+<= aTimeOrTimeDurationOrNumberOfSeconds
+    "return true if the receiver is before or the same time as the argument"
+
+    |myMilliseconds otherMilliseconds myPicos otherPicos|
+
+    myMilliseconds := self getMilliseconds.
+    myPicos := self additionalPicoseconds.
+
+    aTimeOrTimeDurationOrNumberOfSeconds isNumber ifTrue:[
+        "backward compatibility for old code, which expects (time1 - time2) to return seconds"    
+        otherMilliseconds := (aTimeOrTimeDurationOrNumberOfSeconds * 1000) asInteger.   "integer seconds"
+        otherPicos := 0.
+    ] ifFalse:[
+        otherMilliseconds := aTimeOrTimeDurationOrNumberOfSeconds getMilliseconds.
+        otherPicos := aTimeOrTimeDurationOrNumberOfSeconds additionalPicoseconds.
+    ].
+    ^ (myMilliseconds < otherMilliseconds)
+    or:[ myMilliseconds = otherMilliseconds and:[myPicos <= otherPicos ]].
+
+    "
+        (Timestamp now + 10) - Timestamp now  <= 10
+        (Timestamp now + 10) - Timestamp now  <= 11
+        (Timestamp now + 10) - Timestamp now  <= 9
+    "
 !
 
-> aTime
+= aTimeOrTimeDurationOrNumberOfSeconds
+    "return true if the receiver is before or the same time as the argument"
+
+    |myMilliseconds otherMilliseconds myPicos otherPicos|
+
+    myMilliseconds := self getMilliseconds.
+    myPicos := self additionalPicoseconds.
+
+    aTimeOrTimeDurationOrNumberOfSeconds isNumber ifTrue:[
+        "backward compatibility for old code, which expects (time1 - time2) to return seconds"    
+        otherMilliseconds := (aTimeOrTimeDurationOrNumberOfSeconds * 1000) asInteger.   "integer seconds"
+        otherPicos := 0.
+    ] ifFalse:[
+        self speciesForCompare = aTimeOrTimeDurationOrNumberOfSeconds speciesForCompare ifFalse:[   
+            ^ false        
+        ].
+        otherMilliseconds := aTimeOrTimeDurationOrNumberOfSeconds getMilliseconds.
+        otherPicos := aTimeOrTimeDurationOrNumberOfSeconds additionalPicoseconds.
+    ].
+    ^ (myMilliseconds = otherMilliseconds) and:[myPicos = otherPicos ].
+
+    "
+        (Timestamp now + 10) - Timestamp now  = 10
+        (Timestamp now + 10) - Timestamp now  = 9
+
+        (Timestamp now) = (Timestamp now + 10 - 10)
+        (Timestamp now) = (UtcTimestamp now)
+
+        (Time now + 10 seconds) - Time now  = 10
+        (Time now + 10) - Time now          = 10
+
+        (Time now + 10 seconds) > Time now 
+        (Time now + 10)         > Time now      
+
+        (Time now + 10 milliSeconds) > Time now 
+        (Time now + 0 milliseconds)  > Time now      
+    "
+!
+
+> aTimeOrTimeDurationOrNumberOfSeconds
     "return true if the receiver is after the argument"
 
-    ^ self getMilliseconds > aTime getMilliseconds
-
-    "Modified: 1.7.1996 / 15:24:38 / cg"
+    |myMilliseconds otherMilliseconds myPicos otherPicos|
+
+    myMilliseconds := self getMilliseconds.
+    myPicos := self additionalPicoseconds.
+
+    aTimeOrTimeDurationOrNumberOfSeconds isNumber ifTrue:[
+        "backward compatibility for old code, which expects (time1 - time2) to return seconds"    
+        otherMilliseconds := (aTimeOrTimeDurationOrNumberOfSeconds * 1000) asInteger.   "integer seconds"
+        otherPicos := 0.
+    ] ifFalse:[
+        otherMilliseconds := aTimeOrTimeDurationOrNumberOfSeconds getMilliseconds.
+        otherPicos := aTimeOrTimeDurationOrNumberOfSeconds additionalPicoseconds.
+    ].
+    ^ (myMilliseconds > otherMilliseconds)
+    or:[ myMilliseconds = otherMilliseconds and:[myPicos > otherPicos ]].
+
+    "
+        (Timestamp now + 10) - Timestamp now  > 10
+        (Timestamp now + 10) - Timestamp now  > 9
+    "
+!
+
+>= aTimeOrTimeDurationOrNumberOfSeconds
+    "return true if the receiver is after the argument or the same"
+
+    |myMilliseconds otherMilliseconds myPicos otherPicos|
+
+    myMilliseconds := self getMilliseconds.
+    myPicos := self additionalPicoseconds.
+
+    aTimeOrTimeDurationOrNumberOfSeconds isNumber ifTrue:[
+        "backward compatibility for old code, which expects (time1 - time2) to return seconds"    
+        otherMilliseconds := (aTimeOrTimeDurationOrNumberOfSeconds * 1000) asInteger.   "integer seconds"
+        otherPicos := 0.
+    ] ifFalse:[
+        otherMilliseconds := aTimeOrTimeDurationOrNumberOfSeconds getMilliseconds.
+        otherPicos := aTimeOrTimeDurationOrNumberOfSeconds additionalPicoseconds.
+    ].
+    ^ (myMilliseconds > otherMilliseconds)
+    or:[ myMilliseconds = otherMilliseconds and:[myPicos >= otherPicos ]].
+
+    "
+        (Timestamp now + 10) - Timestamp now  >= 11
+        (Timestamp now + 10) - Timestamp now  >= 10
+        (Timestamp now + 10) - Timestamp now  >= 9
+    "
+!
+
+hash
+    ^ self getMilliseconds
 ! !
 
 !AbstractTime methodsFor:'converting'!
@@ -1049,7 +1351,9 @@
 !
 
 asMilliseconds
-    "return the number of milliseconds elapsed since midnight"
+    "return the number of milliseconds elapsed since some starttime,
+     which is subclass specific (i.e. Time: since midnight; Timestamp: since the epoch).
+     Use this only to compute relative millisecond deltas."
 
     ^ self getMilliseconds
 
@@ -1065,9 +1369,9 @@
 
 asSeconds
     "get the seconds since some point of time in the past.
-     For Time instances, this is the number of seconds elapsed since midnight;
-     For TimeDurations, that is the duration in seconds;
-     For TimeStamps, that is the number of seconds since the epoch"
+     For Time instances, this is the number of seconds elapsed since midnight (truncated);
+     For TimeDurations, that is the duration in seconds (truncated);
+     For TimeStamps, that is the number of seconds since the epoch (truncated)"
 
     ^ self getSeconds
 
@@ -1076,6 +1380,26 @@
      Time now asSeconds
      (TimeDuration days:1) asSeconds
     "
+
+    "Modified (comment): / 24-05-2020 / 13:53:16 / cg"
+!
+
+asTZTimestamp
+    "raise an error: must be redefined in concrete subclass(es)"
+
+    ^ self subclassResponsibility
+!
+
+asTime
+    "return a Time object from the receiver."
+
+    ^ Time hours:(self hours) minutes:(self minutes) seconds:(self seconds) milliseconds:(self milliseconds)
+
+    "
+     (1 seconds + 0.01 seconds) asTime
+    "
+
+    "Created: / 17-07-2017 / 13:57:54 / cg"
 !
 
 asTimestamp
@@ -1093,18 +1417,11 @@
 !AbstractTime methodsFor:'double dispatching'!
 
 differenceFromTimestamp:aTimestamp
-    "/ the correct thing to do (and I will, in the future) is to
-    "/ return a TimeDuration:
-    "/
-    ^ TimeDuration fromMilliseconds:(aTimestamp getMilliseconds - self getMilliseconds).
-
-    "/ which is the same as: deltaFrom:aTimestamp
-    "/
-    "/ however, there might be old code around, which is not be prepared for
-    "/ getting a non-number (the seconds). Therefore, for the meantime,
-    "/ we return:
-
-    "/ ^ aTimestamp getSeconds - self getSeconds
+    "return the time difference as a timeDuration instance"
+
+    ^ self subclassResponsibility.
+
+    "Modified: / 27-07-2018 / 11:30:27 / Stefan Vogel"
 ! !
 
 !AbstractTime methodsFor:'printing & storing'!
@@ -1120,6 +1437,7 @@
         %m      minutes, 00..59                0-padded to length 2
         %s      seconds, 00..59                0-padded to length 2
         %i      milliseconds, 000..999         0-padded to length 3
+        %j      microseconds, 000000..999999   0-padded to length 6
 
      Timestamp only:
         %(day)   day, 00..31                    0-padded to length 2
@@ -1131,13 +1449,32 @@
         %M      minutes - unpadded
         %S      seconds - unpadded
         %I      milliseconds, unpadded
+        %J      microseconds, unpadded
+        %F      subsecond fraction, unpadded with as many post-digits as appropriate
+                (i.e. for .1 , .01 , .001 etc.)
 
         %t      seconds within hour  (unpadded)
         %T      seconds from midNight  (unpadded)
 
+        %(milli) milliseconds unpadded - alias for %I for convenience
         %(milli1) milliseconds, truncated to 1/10th of a second 0..9
         %(milli2) milliseconds, truncated to 1/100th of a second 00..99 0-padded to length 2
-        %(milli3) milliseconds, same as %i for convenience
+        %(milli3) milliseconds, padded to 3 (same as %i for convenience)
+        %(milliF1) milliseconds + 1 digits of micros
+        %(milliF2) milliseconds + 2 digits of micros
+        %(milliF3) milliseconds + 3 digits of micros
+        %(milliF4) milliseconds + 4 digits of micros
+
+        %(micro) microseconds unpadded - alias for %J for convenience
+        %(micro6) microseconds, padded to 6 (same as %j for convenience)
+
+        %(nano) nanoseconds unpadded
+        %(nano9) nanoseconds, padded to 9
+
+        %(pico) picoseconds unpadded
+        %(pico12) picoseconds, padded to 12
+
+        %(fract) fraction part - as many as needed alias for %F for convenience
 
      Timestamp only:
         %(Day)         - day - unpadded
@@ -1199,13 +1536,20 @@
 
     "
 
-    |time hours minutes seconds millis s t|
+    |time hours minutes seconds millis micros nanos picos 
+     millisZ3 picosZ9 fract012 fract s t|
+
+    aDictionary at:#'iso8601'         put:[ self printStringIso8601 ].
+    aDictionary at:#'iso8601_compact' put:[ self printStringIso8601Compressed ].
 
     time := self asTime.
     hours := time hours.
     minutes := time minutes.
     seconds := time seconds.
     millis := self milliseconds.
+    micros := self microseconds.
+    nanos := self nanoseconds.
+    picos := self picoseconds.
 
     aDictionary at:$H put:(s := hours printString).
     aDictionary at:$h put:(s leftPaddedTo:2 with:$0).
@@ -1217,22 +1561,70 @@
     aDictionary at:$s put:(s leftPaddedTo:2 with:$0).
 
     aDictionary at:$I put:(s := millis printString).
-    aDictionary at:$i put:(t := s leftPaddedTo:3 with:$0).
-    aDictionary at:#milli3 put:t.
+    aDictionary at:#milli put:s.
+    aDictionary at:$i put:(millisZ3 := s leftPaddedTo:3 with:$0).
+    aDictionary at:#milli3 put:millisZ3.
 
     aDictionary at:#milli1 put:((millis // 100) printString).
     aDictionary at:#milli2 put:((millis // 10) printStringLeftPaddedTo:2 with:$0).
+    aDictionary at:#milliF1 put:((micros asFixedPoint:1) / 1000).
+    aDictionary at:#milliF2 put:((micros asFixedPoint:2) / 1000).
+    aDictionary at:#milliF3 put:((micros asFixedPoint:3) / 1000).
+    aDictionary at:#milliF4 put:((nanos asFixedPoint:4) / 1000000).
+
+    aDictionary at:$J put:(s := micros printString).
+    aDictionary at:#micro put:s.
+    aDictionary at:$j put:(t := s leftPaddedTo:6 with:$0).
+    aDictionary at:#micro6 put:t.
+    aDictionary at:#microF1 put:((nanos asFixedPoint:1) / 1000).
+    aDictionary at:#microF2 put:((nanos asFixedPoint:2) / 1000).
+    aDictionary at:#microF3 put:((nanos asFixedPoint:3) / 1000).
+    aDictionary at:#microF4 put:((picos asFixedPoint:4) / 1000000).
+
+    aDictionary at:#nano put:(s := nanos printString).
+    aDictionary at:#nano9 put:(s leftPaddedTo:9 with:$0).
+
+    aDictionary at:#pico put:(s := picos printString).
+    aDictionary at:#pico12 put:(s leftPaddedTo:12 with:$0).
+
+    picosZ9 := self picoseconds printString leftPaddedTo:9 with:$0.
+    fract012 := millisZ3,picosZ9.
+    fract := fract012 copyTo:(fract012 findLast:[:ch | ch ~~ $0] ifNone:12).
+    aDictionary at:$F put:fract.
+    aDictionary at:#fract put:fract.
 
     aDictionary at:$t put:(seconds * minutes) printString.
     aDictionary at:$T put:(seconds * minutes * hours) printString.
 
-
     "
       |dict|
       dict := Dictionary new.
       Timestamp now addBasicPrintBindingsTo:dict language:#en.
       dict inspect
     "
+    "
+      Timestamp now printStringFormat:'%(milli)'        -- millis only  
+      Timestamp now printStringFormat:'%(milli3)'       -- millis padded  
+      Timestamp now printStringFormat:'%(micro6)'       -- micros padded  
+      Timestamp now printStringFormat:'%(fract)'        -- fraction part - as needed
+      Timestamp nowWithMicroseconds printStringFormat:'%(fract)'        -- fraction part - as needed
+
+      (TimeDuration fromMicroseconds:30) printStringFormat:'%(milliF1)ms'
+      (TimeDuration fromMicroseconds:30) printStringFormat:'%(milliF2)ms'
+      (TimeDuration fromMicroseconds:30) printStringFormat:'%(milliF3)ms'
+      (TimeDuration fromMicroseconds:30) printStringFormat:'%(milliF4)ms'
+      (TimeDuration fromMicroseconds:3) printStringFormat:'%(milliF4)ms'
+      (TimeDuration fromNanoseconds:300) printStringFormat:'%(milliF4)us'
+
+      (TimeDuration fromNanoseconds:30) printStringFormat:'%(microF1)us'
+      (TimeDuration fromNanoseconds:30) printStringFormat:'%(microF2)us'
+      (TimeDuration fromNanoseconds:30) printStringFormat:'%(microF3)us'
+      (TimeDuration fromNanoseconds:30) printStringFormat:'%(microF4)us'
+      (TimeDuration fromNanoseconds:3) printStringFormat:'%(microF4)us'
+      (TimeDuration fromPicoseconds:300) printStringFormat:'%(microF4)us'
+    "
+
+    "Modified (comment): / 14-01-2019 / 18:21:38 / Claus Gittinger"
 !
 
 addPrintBindingsTo:aDictionary
@@ -1271,6 +1663,7 @@
         %T      seconds from midNight  (unpadded)
 
         %(TZD)  timeZone delta of the receiver from UTC in the format +/-hh:mm
+        %(TZD822) timeZone delta of the receiver from UTC in the(RFC822-)format +/-hhmm
 
         %(milli1) milliseconds, truncated to 1/10th of a second 0..9
         %(milli2) milliseconds, truncated to 1/100th of a second 00..99 0-padded to length 2
@@ -1339,7 +1732,7 @@
 
     "
 
-    |usHours ampm s zone tzDelta|
+    |usHours ampm s zone tzDelta s0 s1 s2 s822|
 
     self addBasicPrintBindingsTo:aDictionary language:languageOrNil.
     zone := self timeZoneName.
@@ -1350,7 +1743,6 @@
     aDictionary at:$U put:usHours.
     aDictionary at:$u put:(usHours leftPaddedTo:2 with:$0).
 
-
     aDictionary at:$a put:ampm.
     aDictionary at:$A put:ampm asUppercase.
     aDictionary at:$z put:zone.
@@ -1358,12 +1750,17 @@
 
     tzDelta == 0 ifTrue:[
         s := 'Z'.
+        s822 := '+0000'
     ] ifFalse:[
-        s := tzDelta < 0 ifTrue:[ '-' ] ifFalse:[ '+' ].
+        s0 := tzDelta < 0 ifTrue:[ '-' ] ifFalse:[ '+' ].
         tzDelta := tzDelta abs.
-        s := s  , ((tzDelta // 60) printStringLeftPaddedTo:2 with:$0),
-            ':' , ((tzDelta \\ 60) printStringLeftPaddedTo:2 with:$0).
+        
+        s1 := ((tzDelta // 60) printStringLeftPaddedTo:2 with:$0).
+        s2 := ((tzDelta \\ 60) printStringLeftPaddedTo:2 with:$0).
+        s := s0, s1, ':', s2.
+        s822 := s0, s1, s2.
     ].
+    aDictionary at:#TZD822 put:s822.
     aDictionary at:#TZD put:s
 
 
@@ -1373,6 +1770,34 @@
       Timestamp now addPrintBindingsTo:dict language:#en.
       dict inspect
     "
+
+    "Modified (format): / 03-03-2020 / 17:49:57 / Stefan Vogel"
+!
+
+printIso8601CompressedOn:aStream
+    self subclassResponsibility
+
+    "
+     Timestamp now printStringIso8601       -> '2018-05-09T12:15:53.279'
+     Time now printStringIso8601            -> 'T16:27:08'
+     Date today printStringIso8601          -> '2018-05-09'
+
+     Timestamp now printStringIso8601Compressed       -> '20180525T120822.699'
+     Time now printStringIso8601Compressed            -> 'T120837'
+     Date today printStringIso8601Compressed          -> '20180525'
+    "
+
+    "Created: / 25-05-2018 / 12:08:07 / Claus Gittinger"
+!
+
+printIso8601FormatOn:aStream
+    self subclassResponsibility
+
+    "
+     Timestamp now printStringIso8601       -> '2018-05-09T12:15:53.279+02'
+     Time now printStringIso8601            -> 'T16:27:08'
+     Date today printStringIso8601          -> '2018-05-09'
+    "
 !
 
 printOn:aStream format:aFormatString
@@ -1400,6 +1825,17 @@
 
     |dict|
 
+"/ is this a good idea?    
+"/    aFormatString = '%h:%m:%s' ifTrue:[
+"/        "/ this is so common...
+"/        aStream nextPutAll:(self hours printStringLeftPaddedTo:2 with:$0).
+"/        aStream nextPutAll:':'.
+"/        aStream nextPutAll:(self minutes printStringLeftPaddedTo:2 with:$0).
+"/        aStream nextPutAll:':'.
+"/        aStream nextPutAll:(self seconds printStringLeftPaddedTo:2 with:$0).
+"/        ^ self 
+"/    ].
+    
     dict := IdentityDictionary new.
     self addPrintBindingsTo:dict language:languageString.
 
@@ -1415,7 +1851,8 @@
      Time now printOn:Transcript format:'minutes:%M seconds:%S'. Transcript cr.
     "
 
-    "Modified: 22.2.1996 / 16:58:30 / cg"
+    "Modified: / 22-02-1996 / 16:58:30 / cg"
+    "Modified: / 14-01-2019 / 18:22:12 / Claus Gittinger"
 !
 
 printStringFormat:aFormatString
@@ -1456,12 +1893,12 @@
 
     |s|
 
-    s := CharacterWriteStream on:(String new:20).
+    s := CharacterWriteStream new.
     self printOn:s format:aFormatString language:languageString.
     ^ s contents.
 
     "
-     Timestamp now printStringFormat:'%U:%m:%s %a  
+     Timestamp now printStringFormat:'%U:%m:%s %a'  
      Time now printStringFormat:'%U:%m:%s %a'   
 
      Time now printStringFormat:'%h:%m:%s'      
@@ -1484,10 +1921,75 @@
     "
 
     "Modified: 22.2.1996 / 16:58:30 / cg"
+!
+
+printStringIso8601
+    "return the Iso8601 representation of the receiver with local timezon information.
+     This format looks like:
+        1999-01-01T24:00:00
+     or, for zero hr:min:sec,
+        1999-01-01
+     Of course, a 24 hour clock is used."
+
+    ^ String streamContents:[:s | self printIso8601FormatOn:s]
+
+    "
+     Timestamp now printStringIso8601
+    "
+
+    "Modified (comment): / 25-05-2018 / 11:58:37 / Claus Gittinger"
+!
+
+printStringIso8601Compressed
+    "return the Iso8601 representation of the receiver with local timezon information.
+     This format looks like:
+        1999-01-01T24:00:00
+     or, for zero hr:min:sec,
+        1999-01-01
+     Of course, a 24 hour clock is used."
+
+    ^ String streamContents:[:s | self printIso8601CompressedOn:s]
+
+    "
+     Timestamp now printStringIso8601Compressed
+    "
+
+    "Created: / 25-05-2018 / 11:58:29 / Claus Gittinger"
+!
+
+printStringIso8601Format
+    "would like to make it obsolete, but it is:"
+    <resource: #EXPECCO_API>
+    "<resource: #obsolete>"
+
+    "return the Iso8601 representation of the receiver with local timezon information.
+     This format looks like:
+        1999-01-01T24:00:00
+     or, for zero hr:min:sec,
+        1999-01-01
+     Of course, a 24 hour clock is used."
+
+    "/ self obsoleteMethodWarning:'use printStringIso8601'.
+    ^ self printStringIso8601
+
+    "
+     Timestamp now printStringIso8601Format
+    "
 ! !
 
 !AbstractTime methodsFor:'private'!
 
+additionalPicoseconds
+    "return the additional picoseconds within the current second (0..999999999).
+     Here, assume that we have none"
+
+    ^ 0.
+
+    "
+     Timestamp now picoseconds
+    "
+!
+
 fromOSTime:osTime
     "set my time, from operatingSystems time parts"
 
@@ -1496,10 +1998,31 @@
     "Modified: 1.7.1996 / 15:09:44 / cg"
 !
 
+fromOSTimeWithMilliseconds:anUninterpretedOSTime
+    "strictly private: set the milliseconds from an OS time (since the epoch)"
+
+    self subclassResponsibility
+!
+
+getMicroseconds
+    "get the milliseconds since some point of time in the past.    
+     Since I am abstract (not knowing how the time is actually
+     represented), this must be done by a concrete class.
+     Also be aware that the returned value is concrete-class specific;
+     Time returns the micros since midnight, Timestamp since the epoch.
+     Use this only to compute relative time deltas.
+     Here is a fallback, which only returns milliseocnd precision values"
+
+    ^ self getMilliseconds * 1000
+!
+
 getMilliseconds
     "get the milliseconds since some point of time in the past.
      Since I am abstract (not knowing how the time is actually
-     represented), this must be done by a concrete class."
+     represented), this must be done by a concrete class.
+     Also be aware that the returned value is concrete-class specific;
+     Time returns the millis since midnight, Timestamp since the epoch.
+     Use this only to compute relative time deltas."
 
     ^ self subclassResponsibility
 
@@ -1507,11 +2030,13 @@
 !
 
 getSeconds
-    "get the seconds since some point of time in the past.
+    "get the (truncated) seconds since some point of time in the past.
      Since I am abstract (not knowing how the time is actually
      represented), this must be done by a concrete class."
 
     ^ self subclassResponsibility
+
+    "Modified (comment): / 21-09-2017 / 18:50:30 / cg"
 !
 
 setMilliseconds:millis
@@ -1524,6 +2049,12 @@
     "Created: 1.7.1996 / 14:17:00 / cg"
 !
 
+setMilliseconds:arg1 additionalPicoseconds:arg2
+    "raise an error: must be redefined in concrete subclass(es)"
+
+    ^ self subclassResponsibility
+!
+
 setSeconds:secs
     "set the seconds since some point of time in the past.
      Since I am abstract (not knowing how the time is actually