time: fix calculate_date_from_offset (#14399)

master
David 'Epper' Marshall 2022-05-15 03:55:24 -04:00 committed by GitHub
parent b50f7fdc71
commit c28051020a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 58 additions and 65 deletions

View File

@ -184,3 +184,17 @@ fn test_parse_rfc3339() {
assert expected == output assert expected == output
} }
} }
fn test_ad_second_to_parse_result_in_2001() ? {
now_tm := time.parse('2001-01-01 04:00:00')?
future_tm := now_tm.add_seconds(60)
assert future_tm.str() == '2001-01-01 04:01:00'
assert now_tm.unix < future_tm.unix
}
fn test_ad_second_to_parse_result_pre_2001() ? {
now_tm := time.parse('2000-01-01 04:00:00')?
future_tm := now_tm.add_seconds(60)
assert future_tm.str() == '2000-01-01 04:01:00'
assert now_tm.unix < future_tm.unix
}

View File

@ -16,9 +16,10 @@ pub const (
seconds_per_hour = 60 * seconds_per_minute seconds_per_hour = 60 * seconds_per_minute
seconds_per_day = 24 * seconds_per_hour seconds_per_day = 24 * seconds_per_hour
seconds_per_week = 7 * seconds_per_day seconds_per_week = 7 * seconds_per_day
days_per_400_years = 365 * 400 + 97 days_per_400_years = days_in_year * 400 + 97
days_per_100_years = 365 * 100 + 24 days_per_100_years = days_in_year * 100 + 24
days_per_4_years = 365 * 4 + 1 days_per_4_years = days_in_year * 4 + 1
days_in_year = 365
days_before = [ days_before = [
0, 0,
31, 31,
@ -179,13 +180,13 @@ pub fn (t Time) relative() string {
} }
return '$prefix$d days$suffix' return '$prefix$d days$suffix'
} }
if secs < time.seconds_per_hour * 24 * 365 { if secs < time.seconds_per_hour * 24 * time.days_in_year {
if prefix == 'in ' { if prefix == 'in ' {
return 'on $t.md()' return 'on $t.md()'
} }
return 'last $t.md()' return 'last $t.md()'
} }
y := secs / time.seconds_per_hour / 24 / 365 y := secs / time.seconds_per_hour / 24 / time.days_in_year
if y == 1 { if y == 1 {
return '${prefix}1 year$suffix' return '${prefix}1 year$suffix'
} }
@ -234,14 +235,14 @@ pub fn (t Time) relative_short() string {
} }
return '$prefix${h}h$suffix' return '$prefix${h}h$suffix'
} }
if secs < time.seconds_per_hour * 24 * 365 { if secs < time.seconds_per_hour * 24 * time.days_in_year {
d := secs / time.seconds_per_hour / 24 d := secs / time.seconds_per_hour / 24
if d == 1 { if d == 1 {
return '${prefix}1d$suffix' return '${prefix}1d$suffix'
} }
return '$prefix${d}d$suffix' return '$prefix${d}d$suffix'
} }
y := secs / time.seconds_per_hour / 24 / 365 y := secs / time.seconds_per_hour / 24 / time.days_in_year
if y == 1 { if y == 1 {
return '${prefix}1y$suffix' return '${prefix}1y$suffix'
} }

View File

@ -5,13 +5,13 @@ fn test_add_to_day_in_the_previous_century() ? {
aa := a.add_days(180) aa := a.add_days(180)
dump(a.debug()) dump(a.debug())
dump(aa.debug()) dump(aa.debug())
assert aa.ymmdd() == '1900-06-29' assert aa.ymmdd() == '1900-06-30'
} }
fn test_add_to_day_in_the_past() ? { fn test_add_to_day_in_the_past() ? {
a := time.parse_iso8601('1990-03-01')? a := time.parse_iso8601('1990-03-01')?
aa := a.add_days(180) aa := a.add_days(180)
assert aa.ymmdd() == '1990-08-27' assert aa.ymmdd() == '1990-08-28'
} }
fn test_add_to_day_in_the_recent_past() ? { fn test_add_to_day_in_the_recent_past() ? {

View File

@ -267,3 +267,9 @@ fn test_recursive_local_call() {
fn test_strftime() { fn test_strftime() {
assert '1980 July 11' == time_to_test.strftime('%Y %B %d') assert '1980 July 11' == time_to_test.strftime('%Y %B %d')
} }
fn test_add_seconds_to_time() {
now_tm := time.now()
future_tm := now_tm.add_seconds(60)
assert now_tm.unix < future_tm.unix
}

View File

@ -48,67 +48,39 @@ pub fn unix2(abs i64, microsecond int) Time {
fn calculate_date_from_offset(day_offset_ i64) (int, int, int) { fn calculate_date_from_offset(day_offset_ i64) (int, int, int) {
mut day_offset := day_offset_ mut day_offset := day_offset_
// Move offset to year 2001 as it's the start of a new 400-year cycle
// Code below this rely on the fact that the day_offset is lined up with the 400-year cycle // source: http://howardhinnant.github.io/date_algorithms.html#civil_from_days
// 1970-2000 (inclusive) has 31 years (8 of which are leap years)
mut year := 2001 // shift from 1970-01-01 to 0000-03-01
day_offset -= 31 * 365 + 8 day_offset += 719468 // int(days_per_400_years * 1970 / 400 - (28+31))
// Account for 400 year cycle
year += int(day_offset / days_per_400_years) * 400 mut era := 0
day_offset %= days_per_400_years if day_offset >= 0 {
// Account for 100 year cycle era = int(day_offset / days_per_400_years)
if day_offset == days_per_100_years * 4 {
year += 300
day_offset -= days_per_100_years * 3
} else { } else {
year += int(day_offset / days_per_100_years) * 100 era = int((day_offset - days_per_400_years - 1) / days_per_400_years)
day_offset %= days_per_100_years
} }
// Account for 4 year cycle // doe(day of era) [0, 146096]
if day_offset == days_per_4_years * 25 { doe := day_offset - era * days_per_400_years
year += 96 // yoe(year of era) [0, 399]
day_offset -= days_per_4_years * 24 yoe := (doe - doe / (days_per_4_years - 1) + doe / days_per_100_years - doe / (days_per_400_years - 1)) / days_in_year
// year number
mut y := int(yoe + era * 400)
// doy (day of year), with year beginning Mar 1 [0, 365]
doy := doe - (days_in_year * yoe + yoe / 4 - yoe / 100)
mp := (5 * doy + 2) / 153
d := int(doy - (153 * mp + 2) / 5 + 1)
mut m := int(mp)
if mp < 10 {
m += 3
} else { } else {
year += int(day_offset / days_per_4_years) * 4 m -= 9
day_offset %= days_per_4_years
} }
// Account for every year if m <= 2 {
if day_offset == 365 * 4 { y += 1
year += 3
day_offset -= 365 * 3
} else {
year += int(day_offset / 365)
day_offset %= 365
} }
if day_offset < 0 { return y, m, d
year--
if is_leap_year(year) {
day_offset += 366
} else {
day_offset += 365
}
}
if is_leap_year(year) {
if day_offset > 31 + 29 - 1 {
// After leap day; pretend it wasn't there.
day_offset--
} else if day_offset == 31 + 29 - 1 {
// Leap day.
return year, 2, 29
}
}
mut estimated_month := day_offset / 31
for day_offset >= days_before[estimated_month + 1] {
estimated_month++
}
for day_offset < days_before[estimated_month] {
if estimated_month == 0 {
break
}
estimated_month--
}
day_offset -= days_before[estimated_month]
return year, int(estimated_month + 1), int(day_offset + 1)
} }
fn calculate_time_from_offset(second_offset_ i64) (int, int, int) { fn calculate_time_from_offset(second_offset_ i64) (int, int, int) {