Skip to content

Commit c280510

Browse files
authored
time: fix calculate_date_from_offset (#14399)
1 parent b50f7fd commit c280510

5 files changed

Lines changed: 58 additions & 65 deletions

File tree

vlib/time/parse_test.v

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,3 +184,17 @@ fn test_parse_rfc3339() {
184184
assert expected == output
185185
}
186186
}
187+
188+
fn test_ad_second_to_parse_result_in_2001() ? {
189+
now_tm := time.parse('2001-01-01 04:00:00')?
190+
future_tm := now_tm.add_seconds(60)
191+
assert future_tm.str() == '2001-01-01 04:01:00'
192+
assert now_tm.unix < future_tm.unix
193+
}
194+
195+
fn test_ad_second_to_parse_result_pre_2001() ? {
196+
now_tm := time.parse('2000-01-01 04:00:00')?
197+
future_tm := now_tm.add_seconds(60)
198+
assert future_tm.str() == '2000-01-01 04:01:00'
199+
assert now_tm.unix < future_tm.unix
200+
}

vlib/time/time.v

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ pub const (
1616
seconds_per_hour = 60 * seconds_per_minute
1717
seconds_per_day = 24 * seconds_per_hour
1818
seconds_per_week = 7 * seconds_per_day
19-
days_per_400_years = 365 * 400 + 97
20-
days_per_100_years = 365 * 100 + 24
21-
days_per_4_years = 365 * 4 + 1
19+
days_per_400_years = days_in_year * 400 + 97
20+
days_per_100_years = days_in_year * 100 + 24
21+
days_per_4_years = days_in_year * 4 + 1
22+
days_in_year = 365
2223
days_before = [
2324
0,
2425
31,
@@ -179,13 +180,13 @@ pub fn (t Time) relative() string {
179180
}
180181
return '$prefix$d days$suffix'
181182
}
182-
if secs < time.seconds_per_hour * 24 * 365 {
183+
if secs < time.seconds_per_hour * 24 * time.days_in_year {
183184
if prefix == 'in ' {
184185
return 'on $t.md()'
185186
}
186187
return 'last $t.md()'
187188
}
188-
y := secs / time.seconds_per_hour / 24 / 365
189+
y := secs / time.seconds_per_hour / 24 / time.days_in_year
189190
if y == 1 {
190191
return '${prefix}1 year$suffix'
191192
}
@@ -234,14 +235,14 @@ pub fn (t Time) relative_short() string {
234235
}
235236
return '$prefix${h}h$suffix'
236237
}
237-
if secs < time.seconds_per_hour * 24 * 365 {
238+
if secs < time.seconds_per_hour * 24 * time.days_in_year {
238239
d := secs / time.seconds_per_hour / 24
239240
if d == 1 {
240241
return '${prefix}1d$suffix'
241242
}
242243
return '$prefix${d}d$suffix'
243244
}
244-
y := secs / time.seconds_per_hour / 24 / 365
245+
y := secs / time.seconds_per_hour / 24 / time.days_in_year
245246
if y == 1 {
246247
return '${prefix}1y$suffix'
247248
}

vlib/time/time_addition_test.v

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ fn test_add_to_day_in_the_previous_century() ? {
55
aa := a.add_days(180)
66
dump(a.debug())
77
dump(aa.debug())
8-
assert aa.ymmdd() == '1900-06-29'
8+
assert aa.ymmdd() == '1900-06-30'
99
}
1010

1111
fn test_add_to_day_in_the_past() ? {
1212
a := time.parse_iso8601('1990-03-01')?
1313
aa := a.add_days(180)
14-
assert aa.ymmdd() == '1990-08-27'
14+
assert aa.ymmdd() == '1990-08-28'
1515
}
1616

1717
fn test_add_to_day_in_the_recent_past() ? {

vlib/time/time_test.v

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,3 +267,9 @@ fn test_recursive_local_call() {
267267
fn test_strftime() {
268268
assert '1980 July 11' == time_to_test.strftime('%Y %B %d')
269269
}
270+
271+
fn test_add_seconds_to_time() {
272+
now_tm := time.now()
273+
future_tm := now_tm.add_seconds(60)
274+
assert now_tm.unix < future_tm.unix
275+
}

vlib/time/unix.v

Lines changed: 28 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -48,67 +48,39 @@ pub fn unix2(abs i64, microsecond int) Time {
4848

4949
fn calculate_date_from_offset(day_offset_ i64) (int, int, int) {
5050
mut day_offset := day_offset_
51-
// Move offset to year 2001 as it's the start of a new 400-year cycle
52-
// Code below this rely on the fact that the day_offset is lined up with the 400-year cycle
53-
// 1970-2000 (inclusive) has 31 years (8 of which are leap years)
54-
mut year := 2001
55-
day_offset -= 31 * 365 + 8
56-
// Account for 400 year cycle
57-
year += int(day_offset / days_per_400_years) * 400
58-
day_offset %= days_per_400_years
59-
// Account for 100 year cycle
60-
if day_offset == days_per_100_years * 4 {
61-
year += 300
62-
day_offset -= days_per_100_years * 3
63-
} else {
64-
year += int(day_offset / days_per_100_years) * 100
65-
day_offset %= days_per_100_years
66-
}
67-
// Account for 4 year cycle
68-
if day_offset == days_per_4_years * 25 {
69-
year += 96
70-
day_offset -= days_per_4_years * 24
51+
52+
// source: http://howardhinnant.github.io/date_algorithms.html#civil_from_days
53+
54+
// shift from 1970-01-01 to 0000-03-01
55+
day_offset += 719468 // int(days_per_400_years * 1970 / 400 - (28+31))
56+
57+
mut era := 0
58+
if day_offset >= 0 {
59+
era = int(day_offset / days_per_400_years)
7160
} else {
72-
year += int(day_offset / days_per_4_years) * 4
73-
day_offset %= days_per_4_years
61+
era = int((day_offset - days_per_400_years - 1) / days_per_400_years)
7462
}
75-
// Account for every year
76-
if day_offset == 365 * 4 {
77-
year += 3
78-
day_offset -= 365 * 3
63+
// doe(day of era) [0, 146096]
64+
doe := day_offset - era * days_per_400_years
65+
// yoe(year of era) [0, 399]
66+
yoe := (doe - doe / (days_per_4_years - 1) + doe / days_per_100_years - doe / (days_per_400_years - 1)) / days_in_year
67+
// year number
68+
mut y := int(yoe + era * 400)
69+
// doy (day of year), with year beginning Mar 1 [0, 365]
70+
doy := doe - (days_in_year * yoe + yoe / 4 - yoe / 100)
71+
72+
mp := (5 * doy + 2) / 153
73+
d := int(doy - (153 * mp + 2) / 5 + 1)
74+
mut m := int(mp)
75+
if mp < 10 {
76+
m += 3
7977
} else {
80-
year += int(day_offset / 365)
81-
day_offset %= 365
82-
}
83-
if day_offset < 0 {
84-
year--
85-
if is_leap_year(year) {
86-
day_offset += 366
87-
} else {
88-
day_offset += 365
89-
}
90-
}
91-
if is_leap_year(year) {
92-
if day_offset > 31 + 29 - 1 {
93-
// After leap day; pretend it wasn't there.
94-
day_offset--
95-
} else if day_offset == 31 + 29 - 1 {
96-
// Leap day.
97-
return year, 2, 29
98-
}
99-
}
100-
mut estimated_month := day_offset / 31
101-
for day_offset >= days_before[estimated_month + 1] {
102-
estimated_month++
78+
m -= 9
10379
}
104-
for day_offset < days_before[estimated_month] {
105-
if estimated_month == 0 {
106-
break
107-
}
108-
estimated_month--
80+
if m <= 2 {
81+
y += 1
10982
}
110-
day_offset -= days_before[estimated_month]
111-
return year, int(estimated_month + 1), int(day_offset + 1)
83+
return y, m, d
11284
}
11385

11486
fn calculate_time_from_offset(second_offset_ i64) (int, int, int) {

0 commit comments

Comments
 (0)