Fix gmtime() handling of times very near the limits of time_t.

The UTC time may be several hours before or after local time, and therefore the UTC time/date may be slightly outside the limits of what can be represented as a local time/date. This is now handled correctly.

This also more generally fixes handling of negative seconds/minutes/hours, which is also applicable to mktime().
This commit is contained in:
Stephen Heumann 2022-12-30 17:28:16 -06:00
parent 32c5fd94a1
commit 3b0c1c2149
1 changed files with 62 additions and 50 deletions

112
time.asm
View File

@ -227,9 +227,13 @@ mk1 inx
* *
* factor - compute the seconds since 13 Nov 1969 from date * factor - compute the seconds since 13 Nov 1969 from date
* *
* factor_second32 - alt entry point taking second as a
* signed 32-bit input value
*
* Inputs: * Inputs:
* year,month,day,hour,minute,second - time to convert * year,month,day,hour,minute,second - time to convert
* (each treated as a signed 16-bit value) * (each treated as a signed 16-bit value, with the
* exception of second when using factor_second32)
* *
* Outputs: * Outputs:
* count - seconds since 13 Nov 1969 (signed 64-bit value) * count - seconds since 13 Nov 1969 (signed 64-bit value)
@ -244,12 +248,18 @@ factor private
; ;
; sign-extend time components to 4 bytes ; sign-extend time components to 4 bytes
; ;
stz year+2
lda year stz second+2
lda second
bpl lb0 bpl lb0
dec second+2
factor_second32 entry
lb0 stz year+2
lda year
bpl lb0a
dec year+2 dec year+2
lb0 stz month+2 lb0a stz month+2
lb0a stz day+2 stz day+2
lda day lda day
bpl lb0b bpl lb0b
dec day+2 dec day+2
@ -259,12 +269,8 @@ lb0b stz hour+2
dec hour+2 dec hour+2
lb0c stz minute+2 lb0c stz minute+2
lda minute lda minute
bpl lb0d
dec minute+2
lb0d stz second+2
lda second
bpl lb0e bpl lb0e
dec second+2 dec minute+2
; ;
; adjust for out-of-range month values ; adjust for out-of-range month values
; ;
@ -347,17 +353,24 @@ lb4 mul4 hour,#3600,t1 add in hours*3600
; ;
add_t1_to_count anop add_t1_to_count anop
clc clc
lda count lda t1
adc t1 adc count
sta count sta count
lda count+2 lda t1+2
adc t1+2 tax
adc count+2
sta count+2 sta count+2
bcc ret lda #0
inc count+4 txy
bne ret bpl ad1
inc count+6 dec a
ret rts ad1 tay
adc count+4
sta count+4
tya
adc count+6
sta count+6
rts
end end
**************************************************************** ****************************************************************
@ -397,14 +410,13 @@ t equ 6
phx push time_t value to convert phx push time_t value to convert
phy phy
pha check if time tool is active pha make space for status return/TZ prefs
_tiStatus
pla
bcs lb2
beq lb2
pha make space for TZ preferences record
pha pha
_tiStatus check if time tool is active
bcs no_tz
lda 1,s
beq no_tz
pea 1 get one record element only (TZ offset) pea 1 get one record element only (TZ offset)
tsc get time zone preference tsc get time zone preference
@ -413,15 +425,7 @@ t equ 6
pha pha
_tiGetTimePrefs _tiGetTimePrefs
pla pla
bcs lb1 bcs no_tz
sec adjust for time zone (standard time)
lda 5,s
sbc 1,s
sta 5,s
lda 7,s
sbc 3,s
sta 7,s
pha determine if it's daylight savings pha determine if it's daylight savings
ph2 #$5E ph2 #$5E
@ -429,20 +433,22 @@ t equ 6
pla pla
lsr a lsr a
lsr a lsr a
bcs lb1 bcs doit
; clc ; clc
lda #-60*60 adjust for DST (+1 hour) if needed lda #60*60 adjust for DST (+1 hour) if needed
adc 5,s adc 1,s
sta 5,s sta 1,s
lda #$ffff lda #0
adc 7,s adc 3,s
sta 7,s sta 3,s
bra doit
lb1 pla remove time zone offset from stack no_tz lda #0
pla sta 1,s
sta 3,s
lb2 jsl ~gmlocaltime use common gmtime/localtime code doit jsl ~gmlocaltime use common gmtime/localtime code
rtl rtl
end end
@ -490,6 +496,8 @@ lb1 plb
pha push tm_isdst value pha push tm_isdst value
phx push time_t value to convert phx push time_t value to convert
phy phy
pea 0 no time zone offset
pea 0
jsl ~gmlocaltime use common gmtime/localtime code jsl ~gmlocaltime use common gmtime/localtime code
rtl rtl
end end
@ -499,6 +507,7 @@ lb1 plb
* ~gmlocaltime - common code for gmtime and localtime * ~gmlocaltime - common code for gmtime and localtime
* *
* Inputs: * Inputs:
* tz_offset - offset of local time from desired time zone
* t - time_t value (# of seconds since 13 Nov 1969) * t - time_t value (# of seconds since 13 Nov 1969)
* isdst - value for tm_isdst flag * isdst - value for tm_isdst flag
* *
@ -510,7 +519,7 @@ lb1 plb
~gmlocaltime private ~gmlocaltime private
using TimeCommon using TimeCommon
csubroutine (4:t,2:isdst),0 csubroutine (4:tz_offset,4:t,2:isdst),0
phb phb
phk phk
plb plb
@ -522,9 +531,12 @@ lb1 plb
stz month stz month
stz hour stz hour
stz minute stz minute
stz second lda tz_offset
sta second
lda tz_offset+2
sta second+2
lb1 inc year lb1 inc year
jsr factor jsr factor_second32
lda count+4 lda count+4
bne lb1b bne lb1b
lda count+2 lda count+2
@ -535,7 +547,7 @@ lb1 inc year
lb1a ble lb1 lb1a ble lb1
lb1b dec year lb1b dec year
lb2 inc month find the month lb2 inc month find the month
jsr factor jsr factor_second32
lda count+4 lda count+4
bmi lb2 bmi lb2
bne lb2b bne lb2b
@ -546,7 +558,7 @@ lb2 inc month find the month
cmp t cmp t
lb2a ble lb2 lb2a ble lb2
lb2b dec month lb2b dec month
jsr factor recompute the factor jsr factor_second32 recompute the factor
lda year set the year lda year set the year
sta tm_year sta tm_year
lda month set the month lda month set the month