diff --git a/examples/random_ips.v b/examples/random_ips.v index 7fb694541a..fa6d003398 100644 --- a/examples/random_ips.v +++ b/examples/random_ips.v @@ -2,7 +2,7 @@ import rand import time fn main() { - rand.seed(time.now().uni) + rand.seed(time.now().unix) for _ in 0..10 { println('${rand.next(255)}.${rand.next(255)}.${rand.next(255)}.${rand.next(255)}') diff --git a/examples/tetris/tetris.v b/examples/tetris/tetris.v index 5f6aac33a5..61b3f8e580 100644 --- a/examples/tetris/tetris.v +++ b/examples/tetris/tetris.v @@ -173,7 +173,7 @@ fn main() { fn (g mut Game) init_game() { g.parse_tetros() - rand.seed(time.now().uni) + rand.seed(time.now().unix) g.generate_tetro() g.field = [] // TODO: g.field = [][]int // Generate the field, fill it with 0's, add -1's on each edge diff --git a/examples/vcasino/VCasino.v b/examples/vcasino/VCasino.v index 2dbd62f53a..c5f5c9fac7 100644 --- a/examples/vcasino/VCasino.v +++ b/examples/vcasino/VCasino.v @@ -89,7 +89,7 @@ fn get_bet(money int) int { fn run_wheel(bet_nbr int, _bet int) int { mut bet := _bet - rand.seed(time.now().uni) + rand.seed(time.now().unix) winning_nbr := rand.next(50) print('Roulette Wheel spinning... and stops on the number $winning_nbr which is a ') if winning_nbr % 2 == 1 { diff --git a/vlib/bitfield/bitfield_test.v b/vlib/bitfield/bitfield_test.v index 8d86e6b64e..6977b1d38c 100644 --- a/vlib/bitfield/bitfield_test.v +++ b/vlib/bitfield/bitfield_test.v @@ -19,7 +19,7 @@ fn test_bf_set_clear_toggle_get() { } fn test_bf_and_not_or_xor() { - rand.seed(time.now().uni) + rand.seed(time.now().unix) len := 80 mut input1 := bitfield.new(len) mut input2 := bitfield.new(len) @@ -46,7 +46,7 @@ fn test_bf_and_not_or_xor() { } fn test_clone_cmp() { - rand.seed(time.now().uni) + rand.seed(time.now().unix) len := 80 mut input := bitfield.new(len) for i := 0; i < len; i++ { @@ -60,7 +60,7 @@ fn test_clone_cmp() { } fn test_slice_join() { - rand.seed(time.now().uni) + rand.seed(time.now().unix) len := 80 mut input := bitfield.new(len) for i := 0; i < len; i++ { @@ -83,7 +83,7 @@ fn test_slice_join() { } fn test_popcount() { - rand.seed(time.now().uni) + rand.seed(time.now().unix) len := 80 mut count0 := 0 mut input := bitfield.new(len) @@ -98,7 +98,7 @@ fn test_popcount() { } fn test_hamming() { - rand.seed(time.now().uni) + rand.seed(time.now().unix) len := 80 mut count := 0 mut input1 := bitfield.new(len) @@ -138,7 +138,7 @@ fn test_bf_from_bytes() { } fn test_bf_from_string() { - rand.seed(time.now().uni) + rand.seed(time.now().unix) len := 80 mut input := '' for i := 0; i < len; i++ { @@ -160,7 +160,7 @@ fn test_bf_from_string() { } fn test_bf_bf2str() { - rand.seed(time.now().uni) + rand.seed(time.now().unix) len := 80 mut input := bitfield.new(len) for i := 0; i < len; i++ { @@ -188,7 +188,7 @@ fn test_bf_bf2str() { } fn test_bf_setall() { - rand.seed(time.now().uni) + rand.seed(time.now().unix) len := 80 mut input := bitfield.new(len) input.setall() @@ -202,7 +202,7 @@ fn test_bf_setall() { } fn test_bf_clearall() { - rand.seed(time.now().uni) + rand.seed(time.now().unix) len := 80 mut input := bitfield.new(len) for i := 0; i < len; i++ { @@ -221,7 +221,7 @@ fn test_bf_clearall() { } fn test_bf_reverse() { - rand.seed(time.now().uni) + rand.seed(time.now().unix) len := 80 mut input := bitfield.new(len) for i := 0; i < len; i++ { @@ -241,7 +241,7 @@ fn test_bf_reverse() { } fn test_bf_resize() { - rand.seed(time.now().uni) + rand.seed(time.now().unix) len := 80 mut input := bitfield.new(rand.next(len) + 1) for i := 0; i < 100; i++ { @@ -259,7 +259,7 @@ fn test_bf_pos() { * all haystacks here contain exactly one instanse of needle, * so search should return non-negative-values **/ - rand.seed(time.now().uni) + rand.seed(time.now().unix) len := 80 mut result := 1 for i := 1; i < len; i++ { // needle size diff --git a/vlib/sdl/examples/tvintris/tvintris.v b/vlib/sdl/examples/tvintris/tvintris.v index cca201a6c4..595d218a42 100644 --- a/vlib/sdl/examples/tvintris/tvintris.v +++ b/vlib/sdl/examples/tvintris/tvintris.v @@ -292,7 +292,7 @@ fn main() { game.sdl.jids[1] = -1 game.sdl.set_sdl_context(WinWidth, WinHeight, Title) game.font = C.TTF_OpenFont(FontName.str, TextSize) - seed := time.now().uni + seed := time.now().unix mut game2 := &Game{ font: 0 } game2.sdl = game.sdl game2.font = game.font diff --git a/vlib/time/time.v b/vlib/time/time.v index f8dbdf88bd..1f4bd71a54 100644 --- a/vlib/time/time.v +++ b/vlib/time/time.v @@ -32,7 +32,7 @@ pub: hour int minute int second int - uni int // TODO it's safe to use "unix" now + unix int } pub enum FormatTime { @@ -92,92 +92,11 @@ pub fn now() Time { } pub fn random() Time { - now_unix := now().uni + now_unix := now().unix rand_unix := rand.next(now_unix) return time.unix(rand_unix) } -// Based on Go's time package. -// Copyright 2009 The Go Authors. -pub fn unix(abs int) Time { - // Split into time and day. - mut d := abs / seconds_per_day - // Account for 400 year cycles. - mut n := d / days_per_400_years - mut y := 400 * n - d -= days_per_400_years * n - // Cut off 100-year cycles. - // The last cycle has one extra leap year, so on the last day - // of that year, day / days_per_100_years will be 4 instead of 3. - // Cut it back down to 3 by subtracting n>>2. - n = d / days_per_100_years - n -= n>>2 - y += 100 * n - d -= days_per_100_years * n - // Cut off 4-year cycles. - // The last cycle has a missing leap year, which does not - // affect the computation. - n = d / days_per_4_years - y += 4 * n - d -= days_per_4_years * n - // Cut off years within a 4-year cycle. - // The last year is a leap year, so on the last day of that year, - // day / 365 will be 4 instead of 3. Cut it back down to 3 - // by subtracting n>>2. - n = d / 365 - n -= n>>2 - y += n - d -= 365 * n - yday := d - mut day := yday - year := abs / int(3.154e+7) + 1970 // int(i64(y) + absolute_zero_year) - hour := (abs % seconds_per_day) / seconds_per_hour - minute := (abs % seconds_per_hour) / seconds_per_minute - second := (abs % seconds_per_minute) - if is_leap_year(year) { - // Leap year - if day > 31 + 29 - 1 { - // After leap day; pretend it wasn't there. - day-- - } - else if day == 31 + 29 - 1 { - // Leap day. - day = 29 - return Time{ - year: year - month: 2 - day: day - hour: hour - minute: minute - second: second - } - } - } - // Estimate month on assumption that every month has 31 days. - // The estimate may be too low by at most one month, so adjust. - mut month := day / 31 - mut begin := 0 - end := (days_before[month + 1]) - if day >= end { - month++ - begin = end - } - else { - begin = (days_before[month]) - } - month++ // because January is 1 - day = day - begin + 1 - return Time{ - year: year - month: month - day: day - hour: hour - minute: minute - second: second - uni: abs - } -} - pub fn convert_ctime(t tm) Time { return Time{ year: t.tm_year + 1900 @@ -186,7 +105,7 @@ pub fn convert_ctime(t tm) Time { hour: t.tm_hour minute: t.tm_min second: t.tm_sec - uni: C.mktime(&t) + unix: C.mktime(&t) } } @@ -337,15 +256,28 @@ pub fn parse_iso(s string) Time { } pub fn new_time(t Time) Time { + return Time{ + year: t.year, + month: t.month, + day: t.day, + hour: t.hour, + minute: t.minute, + second: t.second, + unix: t.calc_unix() + } + + //TODO: Use the syntax below when it works with reserved keywords like `unix` + /* return { t | - uni:t.calc_unix() + unix:t.calc_unix() } + */ } pub fn (t &Time) calc_unix() int { - if t.uni != 0 { - return t.uni + if t.unix != 0 { + return t.unix } tt := C.tm{ tm_sec: t.second @@ -360,11 +292,11 @@ pub fn (t &Time) calc_unix() int { // TODO add(d time.Duration) pub fn (t Time) add_seconds(seconds int) Time { - return unix(t.uni + seconds) + return unix(t.unix + seconds) } pub fn (t Time) add_days(days int) Time { - return unix(t.uni + days * 3600 * 24) + return unix(t.unix + days * 3600 * 24) } // TODO use time.Duration instead of seconds @@ -374,7 +306,7 @@ fn since(t Time) int { pub fn (t Time) relative() string { now := time.now() - secs := now.uni - t.uni + secs := now.unix - t.unix if secs <= 30 { // right now or in the future // TODO handle time in the future diff --git a/vlib/time/time_test.v b/vlib/time/time_test.v index 3f2b44a89e..46d639d5f6 100644 --- a/vlib/time/time_test.v +++ b/vlib/time/time_test.v @@ -46,6 +46,38 @@ fn test_unix() { assert t.hour == 2 assert t.minute == 14 assert t.second == 59 + + t2 := time.unix(1078058096) + assert t2.year == 2004 + assert t2.month == 2 + assert t2.day == 29 + assert t2.hour == 12 + assert t2.minute == 34 + assert t2.second == 56 + + t3 := time.unix(1070236799) + assert t3.year == 2003 + assert t3.month == 11 + assert t3.day == 30 + assert t3.hour == 23 + assert t3.minute == 59 + assert t3.second == 59 + + t4 := time.unix(1577783439) + assert t4.year == 2019 + assert t4.month == 12 + assert t4.day == 31 + assert t4.hour == 9 + assert t4.minute == 10 + assert t4.second == 39 + + t5 := time.unix(-1824922433) + assert t5.year == 1912 + assert t5.month == 3 + assert t5.day == 4 + assert t5.hour == 5 + assert t5.minute == 6 + assert t5.second == 7 } fn test_format_ss() { @@ -63,7 +95,7 @@ fn test_smonth() { t := time.Time { year: 1980, month: month_num, day: 1, - hour: 0, minute: 0, second: 0, uni: 0 + hour: 0, minute: 0, second: 0, unix: 0 } assert t.smonth() == name @@ -105,7 +137,7 @@ fn test_day_of_week() { // 2 Dec 2019 is Monday t := time.Time { year: 2019, month: 12, day: 2 + i, - hour: 0, minute: 0, second: 0, uni: 0 + hour: 0, minute: 0, second: 0, unix: 0 } assert day_of_week == t.day_of_week() @@ -119,7 +151,7 @@ fn test_weekday_str() { // 2 Dec 2019 is Monday t := time.Time { year: 2019, month: 12, day: 2 + i, - hour: 0, minute: 0, second: 0, uni: 0 + hour: 0, minute: 0, second: 0, unix: 0 } assert t.weekday_str() == name diff --git a/vlib/time/time_unix.v b/vlib/time/time_unix.v new file mode 100644 index 0000000000..40e9213174 --- /dev/null +++ b/vlib/time/time_unix.v @@ -0,0 +1,113 @@ +// Copyright (c) 2019 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module time + +pub fn unix(abs int) Time { + // Split into day and time + mut day_offset := abs / seconds_per_day + if abs % seconds_per_day < 0 { + // Compensate for round towards zero on integers as we want floored instead + day_offset -= 1 + } + year, month, day := calculate_date_from_offset(day_offset) + hr, min, sec := calculate_time_from_offset(abs % seconds_per_day) + return Time{ + year: year + month: month + day: day + hour: hr + minute: min + second: sec + unix: abs + } +} + +[inline] +fn calculate_date_from_offset(day_offset_ int) (int, int, int) { + 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 + // 1970-2000 (inclusive) has 31 years (8 of which are leap years) + mut year := 2001 + day_offset -= 31*365 + 8 + + // Account for 400 year cycle + year += (day_offset / days_per_400_years) * 400 + day_offset %= days_per_400_years + + // Account for 100 year cycle + if day_offset == days_per_100_years * 4 { + year += 300 + day_offset -= days_per_100_years * 3 + } else { + year += (day_offset / days_per_100_years) * 100 + day_offset %= days_per_100_years + } + + // Account for 4 year cycle + if day_offset == days_per_4_years * 25 { + year += 96 + day_offset -= days_per_4_years * 24 + } else { + year += (day_offset / days_per_4_years) * 4 + day_offset %= days_per_4_years + } + + // Account for every year + if day_offset == 365 * 4 { + year += 3 + day_offset -= 365 * 3 + } else { + year += (day_offset / 365) + day_offset %= 365 + } + + if day_offset < 0 { + year -= 1 + 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] { + estimated_month-- + } + + day_offset -= days_before[estimated_month] + + return year, estimated_month+1, day_offset+1 +} + +[inline] +fn calculate_time_from_offset(second_offset_ int) (int, int, int) { + mut second_offset := second_offset_ + if second_offset < 0 { + second_offset += seconds_per_day + } + + hour := second_offset / seconds_per_hour + second_offset %= seconds_per_hour + + min := second_offset / seconds_per_minute + second_offset %= seconds_per_minute + + return hour, min, second_offset +}