Sujet : Re: Parsing timestamps?
De : the.beez.speaks (at) *nospam* gmail.com (Hans Bezemer)
Groupes : comp.lang.forthDate : 16. Oct 2024, 17:29:37
Autres entêtes
Organisation : KPN B.V.
Message-ID : <nnd$5457a6ac$67808f49@6a3890933e051687>
References : 1 2
User-Agent : Mozilla Thunderbird
On 09-10-2024 17:27, alaa wrote:
Hi,
here is another way, which was fun:
\
https://www.novabbs.com/devel/article-flat.php?id=28040&group=comp.lang.forth#28040
VOCABULARY timestamps-parser ALSO timestamps-parser DEFINITIONS
\ Idea: parse backward
VARIABLE a VARIABLE n
: >an n ! a ! ; : /an 0 a ! 0 n ! ;
: ch ( -- c ) a @ n @ + 1- C@ ;
: more? ( -- t/f ) n @ 0<> ;
: ch+ ( -- ) more? NOT ABORT" exceeded string" -1 n +! ;
: colon ( -- ) more? IF ch ':' <> ABORT" Expected colon!" ch+ THEN ;
: d? ( c -- t/f ) '0' '9' 1+ WITHIN ;
: digit? ( c -- t/f ) d? NOT ABORT" Expected a digit!" ;
: d ( -- ) ch digit? ch 48 - ch+ ;
: [d] ( -- n ) ch d? IF ch 48 - ch+ ELSE 0 THEN ;
: hr ( -- n ) [d] [d] 10 * + ; : min ( -- n ) [d] [d] 10 * + ; : sec (
-- n ) d [d] 10 * + ;
: parse ( a n -- s m h ) >an sec colon min colon hr /an ;
: .hr ( n -- ) ?DUP IF . ." hr " THEN ;
: .min ( n -- ) ?DUP IF . ." min " THEN ;
: .sec ( n -- ) ?DUP IF . ." sec " THEN ;
: .ts ( s m h -- ) .hr .min .sec ;
\ Examples
: e1 S" 1:2:3" ; CR e1 type e1 parse .s .ts
: e2 S" 02:03" ; CR CR e2 type e2 parse .s .ts
: e3 S" 03" ; CR CR e3 type e3 parse .s .ts
: e4 S" 23:59:59" ; CR CR e4 type e4 parse .s .ts
: e5 S" 0:00:03" ; CR CR e5 type e5 parse .s .ts
: e6 S" " ; CR CR e6 type e6 parse .s .ts \ will fail since we expect at
least one digit with 'd' word, we can use '[d]' for optional digit
\ another stupid idea/hack to try: count ':', replace them with BL,
Evaluate string, push missing fields as zeros
Still another way, not complying to this specific implementation:
---8<---
3600 12 * constant (12hours) \ a constant holding 12 hours seconds
: h:m:s>secs ( a1 n1 -- n2)
['] is-type defer@ >r [: is-digit 0= ;] is is-type
0 >r 3600 >r -leading -trailing \ clean up string
begin \ split off all the components
dup \ if a null string, skip loop
while \ if not, convert it to a number
split> number error? dup >r \ if a number, scale it, add to result
if drop else r> swap r> tuck * r> + >r 60 / >r >r dup if chop then then r>
until r> drop \ drop scaling factor
dup if \ if remainder, unchop, clean string up
1+ swap char- swap -leading 2dup s" PM" compare
if \ is it in the afternoon?
s" AM" compare if r> else r> dup (12hours) >= if (12hours) - then then
else \ is it in the morning?
2drop r> dup (12hours) < if (12hours) + then
then \ then adjust accordingly
else \ if null string, discard it
2drop r> \ and just retrieve result
then r> is is-type
;
---8<---
Basically, it takes any non-digit as a delimiter, parses hours, minutes and seconds and then checks for any AM or PM remaining, making adjustments when required. It returns seconds. Those may easily be combined with other epoch information or split into any required format.
Or be a component in this beauty:
: stamp>iso8601 ( a1 n1 -- a2 n2)
dup if
bl split trim 2swap trim 2>r s>date 2r> h:m:s>secs s>smh .datetime0
then
;
- Split the timestamp at the space;
- Convert the string to a date (d m y);
- Convert the other string to seconds;
- Convert the seconds to time (s m h);
- Push out a 8601 string.
Hans Bezemer