INT 21h AH=2Dh sets the time of day from the same registers INT 21h AH=2Ch returns it in: the hour in CH, the minute in CL, the second in DH and hundredths in DL, all binary. DOS turns the time into timer ticks and sets the BIOS tick count; DOS 3.3 and later also set the CMOS real-time clock of an AT, so the time survives a reboot.

Values out of range are refused: AL = FFh comes back and the clock is untouched. The runnable example relies on exactly that, so it changes nothing:

prints: AL = FFh
MOV CH, 25 ; hour 25: there is none
MOV CL, 0
MOV DX, 0 ; 0 seconds, 0 hundredths
MOV AH, 2Dh ; DOS: set the time
INT 21h ; AL = FFh: refused, the clock is untouched
PUSH AX
MOV DX, OFFSET MSG1
MOV AH, 09h ; DOS: print the string at DS:DX
INT 21h
POP AX
CALL HEX2
MOV DL, "h"
MOV AH, 02h ; DOS: print the character in DL
INT 21h
MSG1 DB "AL = $"
HEX2 PROC NEAR ; print AL as two hex digits
PUSH AX
MOV CL, 4
SHR AL, CL ; the high digit first
CALL NIB1
POP AX
AND AL, 0Fh ; then the low one
CALL NIB1
RET
HEX2 ENDP
NIB1 PROC NEAR ; print AL (0-15) as one hex digit
ADD AL, "0"
CMP AL, "9"
JBE NIB2
ADD AL, 7 ; 10-15 become "A"-"F"
NIB2:
MOV DL, AL
MOV AH, 02h
INT 21h
RET
NIB1 ENDP

A real setting looks like this, with a check of AL afterwards:

Not runnable here: it would really change the clock of your DOS box.
MOV CH, 12 ; 12:30:00.00
MOV CL, 30
MOV DH, 0
MOV DL, 0
MOV AH, 2Dh ; DOS: set the time
INT 21h
CMP AL, 0 ; 00h: set; FFh: refused
JNE BADTIME1

Pitfalls

Binary, not BCD: 12:30 is CH = 12 (0Ch) and CL = 30 (1Eh), whereas the BIOS clock service INT 1Ah AH=03h wants 12h and 30h. The tick count has a resolution of 55 ms, so the hundredths read back a moment later need not match those set. The date is separate (INT 21h AH=2Bh): setting the time does not touch it, and a time set just before midnight rolls the date over normally when midnight comes. In DOSBox-X a successful set writes the CMOS clock and the tick count, and switches off the "synchronize time" option (following the host's clock) if it was on.