OK, here's what I believe is a correct macro for inserting the
current date and time as an HTTP Date header, portable across
different TECO environments as best I can make it. I tried to login
to the TOPS-10 machine at pdpplanet, but encountered difficulties.
Therefore, I have only tested this end-to-end on a Win32 environment
where I'm running TECOC. I started with DATE.TES in the TECOC
distribution and changed things around to try and make it as small as
possible when squished. Right now I'm at 1,109 bytes of squished
TECO. I could squeeze out 33 more bytes if I change the code that
builds up a string containing the days within a month by replacing
the 31 at I// commands with an insert using literal control characters
but I'm trying to keep my source file free of control characters, with
a small concession for ESC characters.
If others are willing to try this on their TECOs and tell me the
results, that would be great. The environments that are different
from my test environment are:
PDP-8, OS/8 Different date, time encoding
PDP-11, RT-11 Different date encoding
PDP-11, RSTS/E Different date, time encoding
TOPS-10 Different date, time encoding
TOPS-20 Different date, time encoding
<http://user.xmission.com/~legalize/vintage/http-date.zip>
If you're not in my time zone (Salt Lake City, -0600 from GMT), you
will want to change -0600 in the macro to the appropriate offset.
Some observations from doing this little sub exercise:
- nQq extracts the nth character code from the string portion of
Q-register q. Using another Q-register as the numeric argument to this
command confused the parser of my TECO, hence the use of an intermediate
Q-register by doing 'Q0QM U1'. It didn't uniformly confuse my TECO,
so there were some cases where I could use QqQq directly in some
expressions. This might cause a portability issue for other TECOs.
- I could make this macro much shorter if I hard-coded the date and
time decoding for a particular OS.
- I could make this macro much shorter if I hard-coded the time zone
offset from GMT, or even just lied and pretended my web server's local
time *was* GMT.
- Indexing the string %SunMonTueWedThuFriSatSun% and inserting 3 chars
was much less code than doing a comparison for each value.
- Extracting repeated code into a macro was most effective in reducing
squished size.
- The string portion of a Q-register can be viewed as an array of bytes;
I use this to build an array of days in each month. This reduced
the size of code computing day-in-a-year from day/month and day/month
from day-in-a-year. The array is also used to handle underflow and
overflow when applying the GMT offset.
- I made the labels reasonably small in order to squeeze out more
bytes, but since I don't use more than 96 labels, I could have reduced
them to a single character. I decided against this.
- SQU.TEC will recursively squish my macro definitions, but I kept
running into a problem with the day and month strings I was loading
into 1.str until I re-read the SQU.TES source and learned that if
I use % as the delimiter, then SQU will not treat these Q-register
string loads as macro definitions, but as literal text.
- Computed goto @O!tag0,tag1,tag2! made it easier to handle decoding
the different date/time formats in different environments.
- You can't insert or append character codes directly into a Q-register,
but you can insert character codes into the buffer and pull a portion
of the buffer into a Q-register.
- DATE.TES reports the wrong day of the week, probably because it uses
an algorithm that is no longer valid for years > 1999. I switched
to the Sakamoto, Lachman, Keith and Craver algorithm published in
Wikipedia. This also eliminated me having to compute day-within-year.
- n%q can be used to add n to Q-register q, avoiding the Qq + n Uq
phrase, but n%q leaves the result as a numeric argument to the next
command, so ESCs must be inserted to gobble these up.
- TECO numeric expressions have no operator precedence and are
evaluated strictly left to right. Sometimes this means that extra
parenthesis are necessary to get the right evaluation order and
other times parenthesis can be omitted and still retain the proper
evaluation order.
Squished output, made printable:
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
!HTTP-DATE.TEC![M[D[Y[H[N[S[0[1@^UY\0J31I$QY&3"=29I$|28I$'31I$30I$31I$30I$31
I$31I$30I$31I$30I$31I$0XM0K\@^US\U1Q1*3U13<Q1Q1I$1%1$>\@^U0/U0Q0-10"<I0$'Q0\/
^BU0-1EJ/256OD1,D8,DT$ODG$!D1!Q1-7"=Q0"<Q0&32767U064UY|0UY'(Q0/16384*32)+
(Q0&31)+1972%Y$MYQ0/32&31UDQ0/1024&15UM|-1EJ&255-4"=Q0-(Q0/1000*1000)UD
Q0/1000+1970UYMY0U01UM12<Q0QMU1QD-Q1U1Q1">Q1UD1%M$'1%0$>|ODG$'''OD$!D8!Q0&7
+2010UYMYQ0/8&31UDQ0/256&15UMOD$!DT!Q0-(Q0/31*31)+1UDQ0/32U0Q0-(Q0/12*12)+1
UMQ0/12+1964UYMYOD$!DG!Q0&31UDQ0/32&15UMQ0/512+1900UYMY!D!-1EJ/256
OT1,T8,TT$^H*2U0OTG$!T8!12UH00UN00USOO$!T1!-1EJ-4"=(24*60-^H)*60U0OTG$'^H*2U0
OTG$!TT!^H*60U0!TG!Q0/3600UHQH*3600U1(Q0-Q1)/60UNQ0-Q1-(QN*60)US!O!-600U0Q0
"<-Q0U1Q1-(Q1/100*100)%N$QN-59">1%H$-60%N$'Q1/100%H$QH-23">1%D$-24%H$QM-1QMU1
QD-Q1">1%M$1UDQM-12">1%Y$MY1UM'''|-Q0+(Q0/100*100)%N$QN"<-1%H$60%N$'-Q0/100
%H$QH"<-1%D$24%H$QD"=-1%M$QM"=-1%Y$MY12UM31UD|QM-1QMUD''''IDate:
$
^U1SunMonTueWedThuFriSat$QYU0QDU1QM-3"<Q0%1$-1%0$|Q0-2%1$'23*QM/9+Q1+4+
(Q0/4)-(Q0/100)+(Q0/400)U0Q0-(Q0/7*7)MSI, $QDM0I $
^U1JanFebMarAprMayJunJulAugSepOctNovDec$QM-1MSI $QY\I $QHM0I:$QNM0I:$QSM0
I GMT
$]1]0]S]N]H]Y]D]M$$
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Source file, made printable:
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
!HTTP-DATE.TEC!
! ------------------------------------------------------------------------- !
! Insert the current date and time as an HTTP Date: header !
! ------------------------------------------------------------------------- !
! The date and time varies by computer and OS, encoded in -1EJ: !
! -1EJ=256*m+n Computer (m) Operating System (n) !
! 0 PDP-11 0 RSX-11D !
! 1 RSX-11M !
! 2 RSX-11S !
! 3 IAS !
! 4 RSTS/E !
! 5 VAX/VMS !
! (compatibility mode) !
! 6 RSX-11M+ !
! 7 RT-11 !
! 1 PDP-8 0 OS/8 !
! 2 DEC-10 0 TOPS-10 !
! 3 DEC-20 0 TOPS-20 !
! 4 VAX-11 0 VAX/VMS !
! (native mode) !
! 100 Unix 0 Unix !
! 101 IBM PC 0 MS-DOS !
! 1 Win32, OS/2 !
! 102 Amiga 0 AmigaDOS 1.3 !
! ------------------------------------------------------------------------- !
[M [D [Y [H [N [S [0 [1 ! save used Q-reg's !
! M.num = month in year, 1-12 !
! M.str = days in months as characters adjusted for leap years !
! D.num = day in month, 1-31 !
! Y.num = 4-digit year !
! Y.str = macro to build M.str from Y.num !
! H.num = hour in day, 0-23 !
! N.num = minute in day, 0-59 !
! S.num = second in minute, 0-59 !
! S.str = macro to insert 3 chars from 1.str !
! 0.num = scratch value !
! 0.str = macro to insert 2-digit number with leading zero !
! 1.num = scratch value !
! 1.str = argument to S.str !
!
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
!
! Y.str = macro to build M.str = days in month as characters !
! using Y.num = current year !
@^UY\
0J ! Go to start of buffer !
31 at I// ! January !
QY&3 "= 29 @I// | 28 @I// ' ! February
!
31 @I// ! March !
30 @I// ! April !
31 @I// ! May !
30 @I// ! June !
31 @I// ! July !
31 @I// ! August !
30 @I// ! September !
31 @I// ! October !
30 @I// ! November !
31 @I// ! December !
0XM ! M.str = temporary string !
0K ! delete temporary string !
\
!
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
!
!
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
!
! S.str = macro to insert 3 chars from 1.str !
@^US\
U1 ! 1.num = 0-based word index !
Q1*3 U1 ! 1.num = char offset in 1.str !
3< ! for 3 chars... !
Q1Q1 @I"" ! insert one char !
1 %1$ ! 1.num++ !
! end
!
\
!
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
!
!
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
!
! 0.str = macro to insert two digit number, with leading zero !
@^U0/
U0 ! 0.num = arg !
Q0 - 10"< ! if 0.num < 10? !
@I"0" ! insert zero !
' ! end if !
Q0\ ! insert 0.num !
/
!
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
!
^B U0 ! 0.num = encoded date !
-1EJ/256 @O!D1,D8,DT! ! handle special date decodings !
@O!DG! ! handle general date decoding !
!D1!
Q1 - 7 "= ! RT-11? !
! ------------------------------------------------------------------------- !
! RT-11: ^B = ((((year-2003)*16+month)*32)+day)*32)+(year-1972)&31 !
! ------------------------------------------------------------------------- !
Q0 "< ! if high bit set? !
Q0&32767 U0 ! strip high bit !
64 UY ! Y.num = corresponding year !
| ! else !
0 UY ! Y.num = 0 !
' ! end if !
(Q0/16384*32) + (Q0&31) + 1972 %Y$ ! Y.num += remaining part of year !
MY ! M.str = days in months !
Q0/32 & 31 UD ! D.num = day !
Q0/1024 & 15 UM ! M.num = month !
| -1EJ & 255 - 4 "= ! if RSTS/E? !
! ------------------------------------------------------------------------- !
! RSTS/E: ^B = ((year-1970)*1000)+day within year !
! ------------------------------------------------------------------------- !
Q0 - (Q0/1000*1000) UD ! D.num = day in year !
Q0/1000 + 1970 UY ! Y.num = year !
MY ! M.str = days in months !
0U0 ! U.num = 0 !
1UM ! M.num = January !
12< ! for 12 months... !
Q0QM U1 ! 1.num = days in month 0.num !
QD - Q1 U1 ! 1.num = D.num - 1.num !
Q1 "> ! if D.num > days in month? !
Q1 UD ! D.num -= days !
1 %M$ ! M.num++ !
' ! end if !
1 %0$ ! 0.num++ !
! end
!
! D.num = day in month
!
! M.num = month in year !
| ! otherwise, !
@O!DG! ! general case !
' ! end if !
'
'
@O!D!
! ------------------------------------------------------------------------- !
! OS/8: ^B = (((month*32)+day)*8)+((year-1970)&7)+k !
! where k = 4096 if year>1977 !
! and k=0 otherwise !
! ------------------------------------------------------------------------- !
!D8!
Q0 & 7 + 2010 UY ! Y.num = year !
MY ! M.str = days in months !
Q0/8 & 31 UD ! D.num = day !
Q0/256 & 15 UM ! M.num = month !
@O!D!
! ------------------------------------------------------------------------- !
! TOPS-10, !
! TOPS-20: ^B = (((year-1964)*12+month-1)*31+day-1) !
! ------------------------------------------------------------------------- !
!DT!
Q0 - (Q0/31*31) + 1 UD ! D.num = day !
Q0/32 U0 ! 0.num /= 32 !
Q0 - (Q0/12*12) + 1 UM ! M.num = month !
Q0/12 + 1964 UY ! Y.num = year !
MY ! M.str = days in months !
@O!D!
! ------------------------------------------------------------------------- !
! RSX-11: ^B = ((year-1900)*16+month)*32+day !
! VAX/VMS: ^B = ((year-1900)*16+month)*32+day !
! Amiga: ^B = ((year-1900)*16+month)*32+day !
! Unix: ^B = ((year-1900)*16+month)*32+day !
! Win32: ^B = ((year-1900)*16+month)*32+day !
! OS/2: ^B = ((year-1900)*16+month)*32+day !
! MS-DOS: ^B = ((year-1900)*16+month)*32+day !
! ------------------------------------------------------------------------- !
!DG!
Q0 & 31 UD ! D.num = day !
Q0/32 & 15 UM ! M.num = month !
Q0/512 + 1900 UY ! Y.num = year !
MY ! M.str = days in months !
!D!
! Get HH:MM:SS in Q-reg's H,M,S !
-1EJ/256 @O!T1,T8,TT! ! handle special decodings !
^H*2 U0 ! 0.num = seconds since midnight !
@O!TG! ! handle general decoding !
! ------------------------------------------------------------------------- !
! OS/8: ^H = 0 !
! ------------------------------------------------------------------------- !
!T8!
12 UH ! H.num = 12 !
00 UN ! N.num = 0 !
00 US ! S.num = 0 !
@O!O! ! output time !
! ------------------------------------------------------------------------- !
! RSTS/E: ^H = minutes until midnight !
! ------------------------------------------------------------------------- !
!T1!
-1EJ -4 "= ! RSTS/E? !
(24*60 - ^H)*60 U0 ! 0.num = seconds since midnight !
@O!TG! ! handle general decoding !
'
^H*2 U0 ! 0.num = seconds since midnight !
@O!TG! ! do general case !
! ------------------------------------------------------------------------- !
! TOPS-10: ^H = 60ths of a second since midnight !
! (or 50ths of a second where 50 Hz power is used) !
! ------------------------------------------------------------------------- !
!TT!
^H*60 U0 ! 0.num = seconds since midnight !
! fall through to general case !
! ------------------------------------------------------------------------- !
! RT-11: ^H = (seconds since midnight)/2 !
! RSX-11: ^H = (seconds since midnight)/2 !
! VAX/VMS: ^H = (seconds since midnight)/2 !
! Amiga: ^H = (seconds since midnight)/2 !
! Unix: ^H = (seconds since midnight)/2 !
! Win32: ^H = (seconds since midnight)/2 !
! OS/2: ^H = (seconds since midnight)/2 !
! MS-DOS: ^H = (seconds since midnight)/2 !
! ------------------------------------------------------------------------- !
!TG!
Q0/3600 UH ! H.num = hours !
QH*3600 U1 ! 1.num = hours (in seconds) !
(Q0 - Q1)/60 UN ! N.num = minutes !
Q0 - Q1 - (QN*60) US ! S.num = seconds !
!O!
-600 U0 ! offset from GMT !
Q0 "< ! if offset < 0? !
-Q0 U1 ! add offset to local time !
Q1 - (Q1/100*100) %N$ ! N.num += minute offset !
QN - 59 "> ! if minutes overflowed? !
1 %H$ ! H.num++ !
-60 %N$ ! N.num -= 60 !
' ! end if !
Q1/100 %H$ ! H.num += hour offset !
QH - 23 "> ! if hours overflowed? !
1 %D$ ! D.num++ !
-24 %H$ ! H.num -= 24 !
QM-1QM U1 ! 1.num = days in month !
QD - Q1 "> ! if days overflowed? !
1 %M$ ! M.num++ !
1 UD ! D.num = 1 !
QM - 12 "> ! if month overflowed? !
1 %Y$ ! Y.num++ !
MY ! rebuild M.str !
1 UM ! M.num = 1 !
' ! end if !
' ! end if !
' ! end if !
| ! else !
! subtract offset from local time !
-Q0 + (Q0/100*100) %N$ ! N.num -= minute offset !
QN "< ! if minutes underflowed? !
-1 %H$ ! H.num-- !
60 %N$ ! N.num += 60 !
' ! end if !
-Q0/100 %H$ ! H.num -= hour offset !
QH "< ! if hours underflowed? !
-1 %D$ ! D.num-- !
24 %H$ ! H.num += 24 !
QD "= ! if days underflowed? !
-1 %M$ ! M.num-- !
QM "= ! if months underflowed? !
-1 %Y$ ! Y.num-- !
MY ! rebuild M.str !
12 UM ! M.num = 12 !
31 UD ! D.num = 31 !
| ! else !
QM-1QM UD ! D.num = last day of month !
' ! end if !
' ! end if !
' ! end if !
' ! end if !
@I"Date: " ! Insert Date: header !
! Insert DAY, DD Mon YYYY !
@^U1%SunMonTueWedThuFriSat%
QY U0 ! compute day within week from !
QD U1 ! methods of Sakamoto, Lachman, !
QM-3 "< ! Keith and Craver !
Q0 %1$
-1 %0$
|
Q0 - 2 %1$
'
23*QM/9 + Q1 + 4 + (Q0/4) - (Q0/100) + (Q0/400) U0
Q0 - (Q0/7*7)
MS ! Insert DAY !
@I", " ! Insert ,<SP>
!
QD M0 ! Insert DD !
@I" " ! Insert <SP>
!
@^U1%JanFebMarAprMayJunJulAugSepOctNovDec%
QM - 1 MS ! Insert name of month !
@I" " ! Insert <SP>
!
QY\ ! Insert YYYY !
@I" " ! Insert <SP>
!
! Insert HH:MM:SS !
QH M0 ! Insert HH !
@I":" ! Insert : !
QN M0 ! Insert MM !
@I":" ! Insert : !
QS M0 ! Insert SS !
@I" GMT
" ! Insert <SP>GMT<CR>
!
]1 ]0 ]S ]N ]H ]Y ]D ]M ! restore used Q-reg's !
$$
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
--
"The Direct3D Graphics Pipeline" free book
<http://tinyurl.com/d3d-pipeline>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
The Terminals Wiki <http://terminals.classiccmp.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>