DOS keeps one timestamp per file, the date and time of the last write, in two 16-bit words of the directory entry. INT 21h AH=57h reads them (AL = 0) or replaces them (AL = 1) through an open handle; unlike INT 21h AH=43h it does not take a name.
The packed format
| Time (CX) bits | Hold |
|---|---|
| 15-11 | hours, 0 to 23 |
| 10-5 | minutes, 0 to 59 |
| 4-0 | seconds ÷ 2, 0 to 29 |
| Date (DX) bits | Hold |
|---|---|
| 15-9 | year − 1980, 0 to 127 (1980 to 2107) |
| 8-5 | month, 1 to 12 |
| 4-0 | day, 1 to 31 |
So the time word is hours × 2048 + minutes × 32 + seconds ÷ 2, and the date word (year − 1980) × 512 + month × 32 + day. Seconds have a resolution of two: an odd second cannot be stored. The same two words appear in the record that find first / next fill in (INT 21h AH=4Eh: time at offset 16h, date at 18h). Unpacking is shifts and masks: SHR by 9 for the year, by 5 and AND 0Fh for the month, AND 1Fh for the day.
MOV AH, 3Ch ; DOS: create TEST.TXTXOR CX, CX ; CX = 0: normal attributesMOV DX, OFFSET FNAMEINT 21hCALL CHECK ; stop if CF = 1MOV BX, AX ; BX = the handleMOV AX, 5701h ; DOS: set the date and timeMOV CX, 63C0h ; 12:30:00 = 12 x 2048 + 30 x 32 + 0MOV DX, 1ECFh ; 1995-06-15 = 15 x 512 + 6 x 32 + 15INT 21hCALL CHECKMOV AH, 3Eh ; close: the stamp is written nowINT 21hMOV AX, 3D00h ; open the file againMOV DX, OFFSET FNAMEINT 21hCALL CHECKMOV BX, AXMOV AX, 5700h ; DOS: get the date and timeINT 21hCALL CHECKMOV SI, DX ; SI = dateMOV DI, CX ; DI = timeMOV AH, 3Eh ; close the handleINT 21hMOV AH, 41h ; delete TEST.TXTMOV DX, OFFSET FNAMEINT 21hMOV DX, OFFSET M1MOV AH, 09h ; DOS: print the string at DS:DXINT 21hMOV AX, DICALL PHEXMOV DX, OFFSET M2MOV AH, 09h ; DOS: print the string at DS:DXINT 21hMOV AX, SICALL PHEXMOV DX, OFFSET CRLFMOV AH, 09h ; DOS: print the string at DS:DXINT 21hMOV AX, SI ; year: bits 15-9, + 1980MOV CL, 9SHR AX, CLADD AX, 1980CALL PDECMOV DL, "-"MOV AH, 02h ; DOS: print DLINT 21hMOV AX, SI ; month: bits 8-5MOV CL, 5SHR AX, CLAND AX, 0FhCALL P2MOV DL, "-"MOV AH, 02h ; DOS: print DLINT 21hMOV AX, SI ; day: bits 4-0AND AX, 1FhCALL P2MOV DL, " "MOV AH, 02h ; DOS: print DLINT 21hMOV AX, DI ; hours: bits 15-11MOV CL, 11SHR AX, CLCALL P2MOV DL, ":"MOV AH, 02h ; DOS: print DLINT 21hMOV AX, DI ; minutes: bits 10-5MOV CL, 5SHR AX, CLAND AX, 3FhCALL P2MOV DL, ":"MOV AH, 02h ; DOS: print DLINT 21hMOV AX, DI ; seconds: bits 4-0, times 2AND AX, 1FhSHL AX, 1CALL P2MOV DX, OFFSET CRLFMOV AH, 09h ; DOS: print the string at DS:DXINT 21hFNAME DB "TEST.TXT", 0M1 DB "packed: time $"M2 DB " date $"CRLF DB 13, 10, "$"CHECK PROC NEAR ; CF = 1: print the error code in AL and exitJNC CHECK1PUSH AXMOV DX, OFFSET FAILMSGMOV AH, 09hINT 21hPOP AXCALL PHEXBMOV AX, 4C01h ; exit with code 1INT 21hCHECK1:RETCHECK ENDPFAILMSG DB "failed: error $"PHEXB PROC NEAR ; print AL as two hex digitsPUSH CXPUSH DXMOV DH, AL ; keep the byteMOV CL, 4SHR AL, CL ; the high digit firstMOV CX, 2PHEXB1:AND AL, 0FhADD AL, "0"CMP AL, "9"JBE PHEXB2ADD AL, 7 ; 10..15 -> "A".."F"PHEXB2:MOV DL, ALMOV AH, 02h ; DOS: print DLINT 21hMOV AL, DH ; then the low digitLOOP PHEXB1POP DXPOP CXRETPHEXB ENDPPHEX PROC NEAR ; print AX as four hex digitsPUSH AXMOV AL, AHCALL PHEXBPOP AXCALL PHEXBRETPHEX ENDPPDEC PROC NEAR ; print AX in decimalPUSH BXPUSH CXPUSH DXMOV BX, 10XOR CX, CX ; CX = digits pushedPDEC1:XOR DX, DXDIV BX ; AX = AX / 10, DX = the last digitPUSH DXINC CXOR AX, AXJNZ PDEC1PDEC2:POP DX ; the digits come back first to lastADD DL, "0"MOV AH, 02hINT 21hLOOP PDEC2POP DXPOP CXPOP BXRETPDEC ENDPP2 PROC NEAR ; print AX (0..99) as two digitsMOV DL, 10DIV DL ; AL = tens, AH = onesADD AX, 3030h ; both to charactersPUSH AXMOV DL, ALMOV AH, 02hINT 21hPOP AXMOV DL, AHMOV AH, 02hINT 21hRETP2 ENDP
When a set stamp sticks
DOS keeps the new stamp with the open file and writes it to the directory when the file is closed. A write through the handle after the set replaces it with the current date and time again, so set the stamp last, after all writes. Archivers and copy programs use this to give files their original dates.
In DOSBox-X, on a host folder, the stamp becomes the host file's modification time when the file is closed, and a get always reads the host file. A value just set therefore shows up only after closing and reopening, which is what the example does.