time: add microsecond precision to Time struct
							parent
							
								
									eec5cf1eb1
								
							
						
					
					
						commit
						9c8769503f
					
				| 
						 | 
				
			
			@ -20,6 +20,7 @@ fn C.sprintf(a ...voidptr) int
 | 
			
		|||
 | 
			
		||||
fn C.strlen(s byteptr) int
 | 
			
		||||
 | 
			
		||||
fn C.sscanf(byteptr, byteptr,...byteptr) int
 | 
			
		||||
 | 
			
		||||
fn C.isdigit(s byteptr) bool
 | 
			
		||||
// stdio.h
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -103,8 +103,6 @@ pub fn new(uri string) &Client {
 | 
			
		|||
	return ws
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn C.sscanf() int
 | 
			
		||||
 | 
			
		||||
fn (ws &Client) parse_uri() &Uri {
 | 
			
		||||
	u := urllib.parse(ws.uri) or {
 | 
			
		||||
		panic(err)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,40 @@
 | 
			
		|||
module time
 | 
			
		||||
 | 
			
		||||
// eq returns true if provided time is equal to time
 | 
			
		||||
pub fn (t1 Time) eq(t2 Time) bool {
 | 
			
		||||
	if t1.unix == t2.unix && t1.microsecond == t2.microsecond {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ne returns true if provided time is not equal to time
 | 
			
		||||
pub fn (t1 Time) ne(t2 Time) bool {
 | 
			
		||||
	return !t1.eq(t2)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// lt returns true if provided time is less than time
 | 
			
		||||
pub fn (t1 Time) lt(t2 Time) bool {
 | 
			
		||||
	if t1.unix < t2.unix || (t1.unix == t2.unix && t1.microsecond < t2.microsecond) {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// le returns true if provided time is less or equal to time
 | 
			
		||||
pub fn (t1 Time) le(t2 Time) bool {
 | 
			
		||||
	return t1.lt(t2) || t1.eq(t2)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// gt returns true if provided time is greater than time
 | 
			
		||||
pub fn (t1 Time) gt(t2 Time) bool {
 | 
			
		||||
	if t1.unix > t2.unix || (t1.unix == t2.unix && t1.microsecond > t2.microsecond) {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ge returns true if provided time is greater or equal to time
 | 
			
		||||
pub fn (t1 Time) ge(t2 Time) bool {
 | 
			
		||||
	return t1.gt(t2) || t1.eq(t2)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,409 @@
 | 
			
		|||
module time
 | 
			
		||||
 | 
			
		||||
fn assert_greater_time(ms int, t1 Time) {
 | 
			
		||||
	time.sleep_ms(ms)
 | 
			
		||||
	t2 := now()
 | 
			
		||||
	assert t2.gt(t1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Tests the now in all platform and the gt operator function with at least ms resolution
 | 
			
		||||
fn test_now_always_results_in_greater_time() {
 | 
			
		||||
	t1 := now()
 | 
			
		||||
	$if macos {
 | 
			
		||||
		assert_greater_time(1, t1)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	$if windows {
 | 
			
		||||
		// Lower resolution of time for windows
 | 
			
		||||
		assert_greater_time(15, t1)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	$if linux {
 | 
			
		||||
		assert_greater_time(1, t1)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	$if solaris {
 | 
			
		||||
		assert_greater_time(1, t1)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	// other platforms may have more accurate resolution,
 | 
			
		||||
	// but we do not know that ... so wait at least 1s:
 | 
			
		||||
	assert_greater_time(1001, t1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_time1_should_be_same_as_time2() {
 | 
			
		||||
 | 
			
		||||
	t1 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 3
 | 
			
		||||
		microsecond: 100
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	t2 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 3
 | 
			
		||||
		microsecond: 100
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	assert t1.eq(t2)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_time1_should_not_be_same_as_time2() {
 | 
			
		||||
 | 
			
		||||
	t1 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 3
 | 
			
		||||
		microsecond: 100
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Difference is one microsecond
 | 
			
		||||
	t2 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 3
 | 
			
		||||
		microsecond: 101
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	t3 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 3
 | 
			
		||||
		microsecond: 0
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Difference is one second
 | 
			
		||||
	t4 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 4
 | 
			
		||||
		microsecond: 0
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	assert t1.ne(t2)
 | 
			
		||||
	assert t3.ne(t4)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_time1_should_be_greater_than_time2() {
 | 
			
		||||
 | 
			
		||||
	t1 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 3
 | 
			
		||||
		microsecond: 102
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Difference is one microsecond
 | 
			
		||||
	t2 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 3
 | 
			
		||||
		microsecond: 101
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	t3 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 5
 | 
			
		||||
		microsecond: 0
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Difference is one second
 | 
			
		||||
	t4 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 4
 | 
			
		||||
		microsecond: 0
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	assert t1.gt(t2)
 | 
			
		||||
	assert t3.gt(t4)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
fn test_time2_should_be_less_than_time1() {
 | 
			
		||||
 | 
			
		||||
	t1 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 3
 | 
			
		||||
		microsecond: 102
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Difference is one microsecond
 | 
			
		||||
	t2 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 3
 | 
			
		||||
		microsecond: 101
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	t3 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 3
 | 
			
		||||
		microsecond: 0
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Difference is one second
 | 
			
		||||
	t4 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 2
 | 
			
		||||
		microsecond: 0
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	assert t2.lt(t1)
 | 
			
		||||
	assert t4.lt(t3)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_time1_should_be_greater_or_equal_to_time2_when_gt() {
 | 
			
		||||
 | 
			
		||||
	t1 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 3
 | 
			
		||||
		microsecond: 102
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Difference is one microsecond
 | 
			
		||||
	t2 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 3
 | 
			
		||||
		microsecond: 101
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	t3 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 5
 | 
			
		||||
		microsecond: 0
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Difference is one second
 | 
			
		||||
	t4 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 4
 | 
			
		||||
		microsecond: 0
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	assert t1.ge(t2)
 | 
			
		||||
	assert t3.ge(t4)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_time1_should_be_greater_or_equal_to_time2_when_eq() {
 | 
			
		||||
 | 
			
		||||
	t1 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 3
 | 
			
		||||
		microsecond: 100
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Difference is one microsecond
 | 
			
		||||
	t2 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 3
 | 
			
		||||
		microsecond: 100
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	t3 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 3
 | 
			
		||||
		microsecond: 0
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Difference is one second
 | 
			
		||||
	t4 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 3
 | 
			
		||||
		microsecond: 0
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	assert t1.ge(t2)
 | 
			
		||||
	assert t3.ge(t4)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_time1_should_be_less_or_equal_to_time2_when_lt() {
 | 
			
		||||
 | 
			
		||||
	t1 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 3
 | 
			
		||||
		microsecond: 100
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Difference is one microsecond
 | 
			
		||||
	t2 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 3
 | 
			
		||||
		microsecond: 101
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	t3 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 3
 | 
			
		||||
		microsecond: 0
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Difference is one second
 | 
			
		||||
	t4 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 4
 | 
			
		||||
		microsecond: 0
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	assert t1.le(t2)
 | 
			
		||||
	assert t3.le(t4)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_time1_should_be_less_or_equal_to_time2_when_eq() {
 | 
			
		||||
 | 
			
		||||
	t1 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 3
 | 
			
		||||
		microsecond: 100
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Difference is one microsecond
 | 
			
		||||
	t2 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 3
 | 
			
		||||
		microsecond: 100
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	t3 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 3
 | 
			
		||||
		microsecond: 0
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Difference is one second
 | 
			
		||||
	t4 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 3
 | 
			
		||||
		microsecond: 0
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	assert t1.le(t2)
 | 
			
		||||
	assert t3.le(t4)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_time2_copied_from_time1_should_be_equal() {
 | 
			
		||||
	t1 := new_time( Time {
 | 
			
		||||
		year: 2000
 | 
			
		||||
		month: 5
 | 
			
		||||
		day: 10
 | 
			
		||||
		hour: 22
 | 
			
		||||
		minute: 11
 | 
			
		||||
		second: 3
 | 
			
		||||
		microsecond: 100
 | 
			
		||||
	})
 | 
			
		||||
	t2 := new_time(t1)
 | 
			
		||||
 | 
			
		||||
	assert t2.eq(t1)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -47,3 +47,65 @@ pub fn parse_rfc2822(s string) ?Time {
 | 
			
		|||
 | 
			
		||||
	return parse(tos(tmstr, count))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// parse_rfc8601 parses rfc8601 time format yyyy-MM-ddTHH:mm:ss.dddddd+dd:dd as local time
 | 
			
		||||
// the fraction part is difference in milli seconds and the last part is offset
 | 
			
		||||
// from UTC time and can be both +/- HH:mm
 | 
			
		||||
// Remarks: Not all rfc8601 is supported only the 'yyyy-MM-ddTHH:mm:ss.dddddd+dd:dd'
 | 
			
		||||
pub fn parse_rfc8601(s string) ?Time {
 | 
			
		||||
 | 
			
		||||
	mut year 		:= 0
 | 
			
		||||
	mut month 		:= 0
 | 
			
		||||
	mut day 	  	:= 0
 | 
			
		||||
	mut hour 	  	:= 0
 | 
			
		||||
	mut minute 	  	:= 0
 | 
			
		||||
	mut second 	  	:= 0
 | 
			
		||||
	mut mic_second 	:= 0
 | 
			
		||||
	mut time_char 	:= `a`
 | 
			
		||||
	mut plus_min 	:= `a`
 | 
			
		||||
	mut offset_hour := 0
 | 
			
		||||
	mut offset_min  := 0
 | 
			
		||||
 | 
			
		||||
	count := C.sscanf(s.str, "%4d-%2d-%2d%c%2d:%2d:%2d.%6d%c%2d:%2d", &year, &month, &day,
 | 
			
		||||
													  &time_char, &hour, &minute,
 | 
			
		||||
													  &second, &mic_second, &plus_min,
 | 
			
		||||
													  &offset_hour, &offset_min)
 | 
			
		||||
 | 
			
		||||
	if count != 11 {
 | 
			
		||||
		return error('Invalid 8601 format')
 | 
			
		||||
	}
 | 
			
		||||
	if time_char != `T` && time_char != ` ` {
 | 
			
		||||
		return error('Invalid 8601 format, expected space or `T` as time separator')
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if plus_min != `+` && plus_min != `-` {
 | 
			
		||||
		return error('Invalid 8601 format, expected `+` or `-` as time separator' )
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	t := new_time(Time{
 | 
			
		||||
		year: year
 | 
			
		||||
		month: month
 | 
			
		||||
		day: day
 | 
			
		||||
		hour: hour
 | 
			
		||||
		minute: minute
 | 
			
		||||
		second: second
 | 
			
		||||
		microsecond: mic_second
 | 
			
		||||
	})
 | 
			
		||||
	mut unix_offset := int(0)
 | 
			
		||||
 | 
			
		||||
	if offset_hour > 0 {
 | 
			
		||||
		unix_offset += 3600*offset_hour
 | 
			
		||||
	}
 | 
			
		||||
	if offset_min > 0 {
 | 
			
		||||
		unix_offset += 60*offset_min
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if unix_offset != 0 {
 | 
			
		||||
		if plus_min == `+` {
 | 
			
		||||
			return unix2(int(t.unix) + unix_offset, t.microsecond)
 | 
			
		||||
		} else {
 | 
			
		||||
			return unix2(int(t.unix) - unix_offset, t.microsecond)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return t
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,7 +12,7 @@ fn test_parse() {
 | 
			
		|||
 | 
			
		||||
fn test_parse_invalid() {
 | 
			
		||||
	s := 'Invalid time string'
 | 
			
		||||
	t := time.parse(s) or {
 | 
			
		||||
	_ := time.parse(s) or {
 | 
			
		||||
		assert true
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -38,9 +38,37 @@ fn test_parse_rfc2822() {
 | 
			
		|||
 | 
			
		||||
fn test_parse_rfc2822_invalid() {
 | 
			
		||||
	s3 := 'Thu 12 Foo 2019 06:07:45 +0800'
 | 
			
		||||
	t3 := time.parse_rfc2822(s3) or {
 | 
			
		||||
	_ := time.parse_rfc2822(s3) or {
 | 
			
		||||
		assert true
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	assert false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_rfc8601_parse_utc() {
 | 
			
		||||
	ok_format := '2020-06-02T15:38:06.015959+00:00'
 | 
			
		||||
 | 
			
		||||
	t := time.parse_rfc8601(ok_format) or {panic(err)}
 | 
			
		||||
 | 
			
		||||
	assert t.year == 2020
 | 
			
		||||
	assert t.month == 6
 | 
			
		||||
	assert t.day == 2
 | 
			
		||||
	assert t.hour == 15
 | 
			
		||||
	assert t.minute == 38
 | 
			
		||||
	assert t.second == 6
 | 
			
		||||
	assert t.microsecond == 15959
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_rfc8601_parse_cest() {
 | 
			
		||||
	ok_format := '2020-06-02T15:38:06.015959+02:00'
 | 
			
		||||
 | 
			
		||||
	t := time.parse_rfc8601(ok_format) or {panic(err)}
 | 
			
		||||
 | 
			
		||||
	assert t.year == 2020
 | 
			
		||||
	assert t.month == 6
 | 
			
		||||
	assert t.day == 2
 | 
			
		||||
	assert t.hour == 17
 | 
			
		||||
	assert t.minute == 38
 | 
			
		||||
	assert t.second == 6
 | 
			
		||||
	assert t.microsecond == 15959
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,16 @@
 | 
			
		|||
// tests that use and test private functions
 | 
			
		||||
module time
 | 
			
		||||
 | 
			
		||||
// test the old behavor is same as new, the unix time should always be local time
 | 
			
		||||
fn test_new_is_same_as_old_for_all_platforms() {
 | 
			
		||||
	t := C.time(0)
 | 
			
		||||
	tm := C.localtime(&t)
 | 
			
		||||
 | 
			
		||||
	old_time := time.convert_ctime(tm, 0)
 | 
			
		||||
	new_time := time.now()
 | 
			
		||||
 | 
			
		||||
	diff := new_time.unix - old_time.unix
 | 
			
		||||
 | 
			
		||||
	// could in very rare cases be that the second changed between calls
 | 
			
		||||
	assert (diff >=0 && diff <=1) == true
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -45,6 +45,7 @@ pub:
 | 
			
		|||
	hour   			int
 | 
			
		||||
	minute 			int
 | 
			
		||||
	second 			int
 | 
			
		||||
	microsecond 	int
 | 
			
		||||
	unix   			u64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -86,9 +87,23 @@ fn C.time(t &C.time_t) C.time_t
 | 
			
		|||
 | 
			
		||||
// now returns current local time.
 | 
			
		||||
pub fn now() Time {
 | 
			
		||||
	$if macos {
 | 
			
		||||
		return darwin_now()
 | 
			
		||||
	}
 | 
			
		||||
	$if windows {
 | 
			
		||||
		return win_now()
 | 
			
		||||
	}
 | 
			
		||||
	$if solaris {
 | 
			
		||||
		return solaris_now()
 | 
			
		||||
	}
 | 
			
		||||
	$if linux {
 | 
			
		||||
		return linux_now()
 | 
			
		||||
	}
 | 
			
		||||
	// defaults to most common feature, the microsecond precision is not available
 | 
			
		||||
	// in this API call
 | 
			
		||||
	t := C.time(0)
 | 
			
		||||
	now := C.localtime(&t)
 | 
			
		||||
	return convert_ctime(now)
 | 
			
		||||
	return convert_ctime(now, 0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// smonth returns month name.
 | 
			
		||||
| 
						 | 
				
			
			@ -107,6 +122,7 @@ pub fn new_time(t Time) Time {
 | 
			
		|||
		minute: t.minute
 | 
			
		||||
		second: t.second
 | 
			
		||||
		unix: u64(t.unix_time())
 | 
			
		||||
		microsecond: t.microsecond
 | 
			
		||||
	}
 | 
			
		||||
	// TODO Use the syntax below when it works with reserved keywords like `unix`
 | 
			
		||||
	// return {
 | 
			
		||||
| 
						 | 
				
			
			@ -264,7 +280,7 @@ pub fn (t Time) str() string {
 | 
			
		|||
	return t.format_ss()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn convert_ctime(t C.tm) Time {
 | 
			
		||||
fn convert_ctime(t C.tm, microsecond int) Time {
 | 
			
		||||
	return Time{
 | 
			
		||||
		year: t.tm_year + 1900
 | 
			
		||||
		month: t.tm_mon + 1
 | 
			
		||||
| 
						 | 
				
			
			@ -272,6 +288,7 @@ fn convert_ctime(t C.tm) Time {
 | 
			
		|||
		hour: t.tm_hour
 | 
			
		||||
		minute: t.tm_min
 | 
			
		||||
		second: t.tm_sec
 | 
			
		||||
		microsecond: microsecond
 | 
			
		||||
		unix: u64(make_unix_time(t))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,6 +23,11 @@ struct InternalTimeBase {
 | 
			
		|||
	denom u32 = 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct C.timeval {
 | 
			
		||||
	tv_sec  u64
 | 
			
		||||
	tv_usec u64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn init_time_base() InternalTimeBase {
 | 
			
		||||
	tb := C.mach_timebase_info_data_t{}
 | 
			
		||||
	C.mach_timebase_info(&tb)
 | 
			
		||||
| 
						 | 
				
			
			@ -47,3 +52,29 @@ fn vpc_now_darwin() u64 {
 | 
			
		|||
	}
 | 
			
		||||
	return (tm - start_time) * time_base.numer / time_base.denom
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// darwin_now returns a better precision current time for Darwin based operating system
 | 
			
		||||
// this should be implemented with native system calls eventually
 | 
			
		||||
// but for now a bit tweaky. It uses the deprecated  gettimeofday clock to get
 | 
			
		||||
// the microseconds seconds part and normal local time to get correct local time
 | 
			
		||||
// if the time has shifted on a second level between calls it uses
 | 
			
		||||
// zero as microsecond. Not perfect but better that unix time only us a second
 | 
			
		||||
fn darwin_now() Time {
 | 
			
		||||
 | 
			
		||||
	// get the high precision time as UTC clock
 | 
			
		||||
	// and use the nanoseconds part
 | 
			
		||||
	tv := C.timeval{}
 | 
			
		||||
	C.gettimeofday(&tv, 0)
 | 
			
		||||
 | 
			
		||||
	t := C.time(0)
 | 
			
		||||
	tm := C.localtime(&t)
 | 
			
		||||
 | 
			
		||||
	// if the second part (very rare) is different
 | 
			
		||||
	// microseconds is set to zero since it passed the second
 | 
			
		||||
	// also avoid divide by zero if nsec is zero
 | 
			
		||||
	if int(t) != tv.tv_sec {
 | 
			
		||||
		return convert_ctime(tm, 0)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return convert_ctime(tm, int(tv.tv_usec))
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,3 +3,39 @@ module time
 | 
			
		|||
fn sys_mono_now_darwin() u64 {
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// dummy to compile with all compilers
 | 
			
		||||
pub fn darwin_now() Time {
 | 
			
		||||
	return Time{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// dummy to compile with all compilers
 | 
			
		||||
pub fn solaris_now() Time {
 | 
			
		||||
	return Time{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// linux_now returns the local time with high precision for most os:es
 | 
			
		||||
// this should be implemented with native system calls eventually
 | 
			
		||||
// but for now a bit tweaky. It uses the realtime clock to get
 | 
			
		||||
// the nano seconds part and normal local time to get correct local time
 | 
			
		||||
// if the time has shifted on a second level between calls it uses
 | 
			
		||||
// zero as microsecond. Not perfect but better that unix time only
 | 
			
		||||
fn linux_now() Time {
 | 
			
		||||
 | 
			
		||||
	// get the high precision time as UTC realtime clock
 | 
			
		||||
	// and use the nanoseconds part
 | 
			
		||||
	mut ts := C.timespec{}
 | 
			
		||||
	C.clock_gettime(C.CLOCK_REALTIME, &ts)
 | 
			
		||||
 | 
			
		||||
	t := C.time(0)
 | 
			
		||||
	tm := C.localtime(&t)
 | 
			
		||||
 | 
			
		||||
	// if the second part (very rare) is different
 | 
			
		||||
	// microseconds is set to zero since it passed the second
 | 
			
		||||
	// also avoid divide by zero if nsec is zero
 | 
			
		||||
	if int(t) != ts.tv_sec || ts.tv_nsec == 0 {
 | 
			
		||||
		return convert_ctime(tm, 0)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return convert_ctime(tm, int(ts.tv_nsec/1000))
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,8 @@
 | 
			
		|||
// that can be found in the LICENSE file.
 | 
			
		||||
module time
 | 
			
		||||
 | 
			
		||||
#include <time.h>
 | 
			
		||||
 | 
			
		||||
struct C.tm {
 | 
			
		||||
	tm_sec  int
 | 
			
		||||
	tm_min  int
 | 
			
		||||
| 
						 | 
				
			
			@ -47,3 +49,15 @@ fn vpc_now() u64 {
 | 
			
		|||
	C.clock_gettime(C.CLOCK_MONOTONIC, &ts)
 | 
			
		||||
	return u64(ts.tv_sec) * 1_000_000_000 + u64(ts.tv_nsec)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// dummy to compile with all compilers
 | 
			
		||||
pub fn win_now() Time {
 | 
			
		||||
	return Time{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// dummy to compile with all compilers
 | 
			
		||||
pub struct C.timeval {
 | 
			
		||||
	tv_sec  u64
 | 
			
		||||
	tv_usec u64
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,26 @@
 | 
			
		|||
module time
 | 
			
		||||
 | 
			
		||||
// dummy to compile with all compilers
 | 
			
		||||
pub fn linux_now() Time {
 | 
			
		||||
	return Time{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn darwin_now() Time {
 | 
			
		||||
	return Time{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn solaris_now() Time {
 | 
			
		||||
       // get the high precision time as UTC realtime clock
 | 
			
		||||
       // and use the nanoseconds part
 | 
			
		||||
       mut ts := C.timespec{}
 | 
			
		||||
       C.clock_gettime(C.CLOCK_REALTIME, &ts)
 | 
			
		||||
       t := C.time(0)
 | 
			
		||||
       tm := C.localtime(&t)
 | 
			
		||||
       // if the second part (very rare) is different
 | 
			
		||||
       // microseconds is set to zero since it passed the second
 | 
			
		||||
       // also avoid divide by zero if nsec is zero
 | 
			
		||||
       if int(t) != ts.tv_sec || ts.tv_nsec == 0 {
 | 
			
		||||
               return convert_ctime(tm, 0)
 | 
			
		||||
       }
 | 
			
		||||
       return convert_ctime(tm, int(ts.tv_nsec/1000))
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -149,3 +149,20 @@ fn test_add_days() {
 | 
			
		|||
fn test_str() {
 | 
			
		||||
	assert '1980-07-11 21:23:42' == time_to_test.str()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// not optimal test but will find obvious bugs
 | 
			
		||||
fn test_now() {
 | 
			
		||||
	now := time.now()
 | 
			
		||||
 | 
			
		||||
	// The year the test was built
 | 
			
		||||
	assert now.year 			>= 2020
 | 
			
		||||
	assert now.month 			> 0
 | 
			
		||||
	assert now.month 			<= 12
 | 
			
		||||
	assert now.minute 			>= 0
 | 
			
		||||
	assert now.minute 			< 60
 | 
			
		||||
	assert now.second 			>=0
 | 
			
		||||
	assert now.second 			< 60
 | 
			
		||||
	assert now.microsecond 		>= 0
 | 
			
		||||
	assert now.microsecond 		< 1000000
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,9 @@
 | 
			
		|||
// that can be found in the LICENSE file.
 | 
			
		||||
module time
 | 
			
		||||
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <sysinfoapi.h>
 | 
			
		||||
 | 
			
		||||
struct C.tm {
 | 
			
		||||
	tm_year int
 | 
			
		||||
	tm_mon  int
 | 
			
		||||
| 
						 | 
				
			
			@ -12,13 +15,37 @@ struct C.tm {
 | 
			
		|||
	tm_sec  int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct C._FILETIME
 | 
			
		||||
 | 
			
		||||
struct SystemTime {
 | 
			
		||||
  year 			u16
 | 
			
		||||
  month 		u16
 | 
			
		||||
  day_of_week 	u16
 | 
			
		||||
  day  			u16
 | 
			
		||||
  hour 			u16
 | 
			
		||||
  minute 		u16
 | 
			
		||||
  second 		u16
 | 
			
		||||
  millisecond 	u16
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn C.GetSystemTimeAsFileTime(lpSystemTimeAsFileTime C._FILETIME)
 | 
			
		||||
fn C.FileTimeToSystemTime()
 | 
			
		||||
fn C.SystemTimeToTzSpecificLocalTime()
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// start_time is needed on Darwin and Windows because of potential overflows
 | 
			
		||||
	start_time 		 	= init_win_time_start()
 | 
			
		||||
	freq_time  		 	= init_win_time_freq()
 | 
			
		||||
	start_local_time 	= local_as_unix_time()
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// in most systems, these are __quad_t, which is an i64
 | 
			
		||||
struct C.timespec {
 | 
			
		||||
	tv_sec  i64
 | 
			
		||||
	tv_nsec i64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
fn C._mkgmtime(&C.tm) time_t
 | 
			
		||||
 | 
			
		||||
fn C.QueryPerformanceCounter(&u64) C.BOOL
 | 
			
		||||
| 
						 | 
				
			
			@ -55,3 +82,73 @@ fn vpc_now() u64 {
 | 
			
		|||
	C.QueryPerformanceCounter(&tm)
 | 
			
		||||
	return tm
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// local_as_unix_time returns the current local time as unix time
 | 
			
		||||
fn local_as_unix_time() int {
 | 
			
		||||
	t := C.time(0)
 | 
			
		||||
	tm := C.localtime(&t)
 | 
			
		||||
 | 
			
		||||
	return make_unix_time(tm)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// win_now calculates current time using winapi to get higher resolution on windows
 | 
			
		||||
// GetSystemTimeAsFileTime is used. It can resolve time down to millisecond
 | 
			
		||||
// other more precice methods can be implemented in the future
 | 
			
		||||
fn win_now() Time {
 | 
			
		||||
 | 
			
		||||
	ft_utc := C._FILETIME{}
 | 
			
		||||
	C.GetSystemTimeAsFileTime(&ft_utc)
 | 
			
		||||
 | 
			
		||||
	st_utc := SystemTime{}
 | 
			
		||||
	C.FileTimeToSystemTime(&ft_utc, &st_utc)
 | 
			
		||||
 | 
			
		||||
	st_local := SystemTime{}
 | 
			
		||||
	C.SystemTimeToTzSpecificLocalTime(voidptr(0), &st_utc, &st_local)
 | 
			
		||||
 | 
			
		||||
	t := Time {
 | 
			
		||||
		year: st_local.year
 | 
			
		||||
		month: st_local.month
 | 
			
		||||
		day: st_local.day
 | 
			
		||||
		hour: st_local.hour
 | 
			
		||||
		minute: st_local.minute
 | 
			
		||||
		second: st_local.second
 | 
			
		||||
		microsecond: st_local.millisecond*1000
 | 
			
		||||
		unix: u64(st_local.unix_time())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return t
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// unix_time returns Unix time.
 | 
			
		||||
pub fn (st SystemTime) unix_time() int {
 | 
			
		||||
	tt := C.tm{
 | 
			
		||||
		tm_sec: st.second
 | 
			
		||||
		tm_min: st.minute
 | 
			
		||||
		tm_hour: st.hour
 | 
			
		||||
		tm_mday: st.day
 | 
			
		||||
		tm_mon: st.month - 1
 | 
			
		||||
		tm_year: st.year - 1900
 | 
			
		||||
	}
 | 
			
		||||
	return make_unix_time(tt)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// dummy to compile with all compilers
 | 
			
		||||
pub fn darwin_now() Time {
 | 
			
		||||
	return Time{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// dummy to compile with all compilers
 | 
			
		||||
pub fn linux_now() Time {
 | 
			
		||||
	return Time{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// dummy to compile with all compilers
 | 
			
		||||
pub fn solaris_now() Time {
 | 
			
		||||
	return Time{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// dummy to compile with all compilers
 | 
			
		||||
pub struct C.timeval {
 | 
			
		||||
	tv_sec  u64
 | 
			
		||||
	tv_usec u64
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,6 +24,29 @@ pub fn unix(abs int) Time {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// unix2 returns a time struct from Unix time and microsecond value
 | 
			
		||||
pub fn unix2(abs int, microsecond 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--
 | 
			
		||||
	}
 | 
			
		||||
	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
 | 
			
		||||
		microsecond: microsecond
 | 
			
		||||
		unix: u64(abs)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[inline]
 | 
			
		||||
fn calculate_date_from_offset(day_offset_ int) (int,int,int) {
 | 
			
		||||
	mut day_offset := day_offset_
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue