mirror of
https://github.com/vlang/v.git
synced 2025-09-09 07:15:50 -04:00
time: microoptimise the Time formating methods (use custom number->string conversion, instead of string interpolation) (#20917)
This commit is contained in:
parent
87320f8f93
commit
d5370bd220
@ -5,54 +5,243 @@ module time
|
||||
|
||||
import strings
|
||||
|
||||
// int_to_byte_array_no_pad fulfill buffer by part
|
||||
// it doesn't pad with leading zeros for performance reasons
|
||||
@[direct_array_access]
|
||||
fn int_to_byte_array_no_pad(value int, mut arr []u8, size int) {
|
||||
mut num := value
|
||||
if size <= 0 || num < 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Start from the end of the array
|
||||
mut i := size - 1
|
||||
|
||||
// Convert each digit to a character and store it in the array
|
||||
for num > 0 && i >= 0 {
|
||||
arr[i] = (num % 10) + `0`
|
||||
num /= 10
|
||||
i--
|
||||
}
|
||||
}
|
||||
|
||||
// format returns a date string in "YYYY-MM-DD HH:mm" format (24h).
|
||||
@[manualfree]
|
||||
pub fn (t Time) format() string {
|
||||
return '${t.year:04d}-${t.month:02d}-${t.day:02d} ${t.hour:02d}:${t.minute:02d}'
|
||||
mut buf := [u8(`0`), `0`, `0`, `0`, `-`, `0`, `0`, `-`, `0`, `0`, ` `, `0`, `0`, `:`, `0`,
|
||||
`0`]
|
||||
|
||||
defer {
|
||||
unsafe { buf.free() }
|
||||
}
|
||||
|
||||
int_to_byte_array_no_pad(t.year, mut buf, 4)
|
||||
int_to_byte_array_no_pad(t.month, mut buf, 7)
|
||||
int_to_byte_array_no_pad(t.day, mut buf, 10)
|
||||
|
||||
int_to_byte_array_no_pad(t.hour, mut buf, 13)
|
||||
int_to_byte_array_no_pad(t.minute, mut buf, 16)
|
||||
|
||||
return buf.bytestr()
|
||||
}
|
||||
|
||||
// format_ss returns a date string in "YYYY-MM-DD HH:mm:ss" format (24h).
|
||||
@[manualfree]
|
||||
pub fn (t Time) format_ss() string {
|
||||
return '${t.year:04d}-${t.month:02d}-${t.day:02d} ${t.hour:02d}:${t.minute:02d}:${t.second:02d}'
|
||||
mut buf := [u8(`0`), `0`, `0`, `0`, `-`, `0`, `0`, `-`, `0`, `0`, ` `, `0`, `0`, `:`, `0`,
|
||||
`0`, `:`, `0`, `0`]
|
||||
|
||||
defer {
|
||||
unsafe { buf.free() }
|
||||
}
|
||||
|
||||
int_to_byte_array_no_pad(t.year, mut buf, 4)
|
||||
int_to_byte_array_no_pad(t.month, mut buf, 7)
|
||||
int_to_byte_array_no_pad(t.day, mut buf, 10)
|
||||
|
||||
int_to_byte_array_no_pad(t.hour, mut buf, 13)
|
||||
int_to_byte_array_no_pad(t.minute, mut buf, 16)
|
||||
int_to_byte_array_no_pad(t.second, mut buf, 19)
|
||||
|
||||
return buf.bytestr()
|
||||
}
|
||||
|
||||
// format_ss_milli returns a date string in "YYYY-MM-DD HH:mm:ss.123" format (24h).
|
||||
@[manualfree]
|
||||
pub fn (t Time) format_ss_milli() string {
|
||||
return '${t.year:04d}-${t.month:02d}-${t.day:02d} ${t.hour:02d}:${t.minute:02d}:${t.second:02d}.${(t.nanosecond / 1_000_000):03d}'
|
||||
mut buf := [u8(`0`), `0`, `0`, `0`, `-`, `0`, `0`, `-`, `0`, `0`, ` `, `0`, `0`, `:`, `0`,
|
||||
`0`, `:`, `0`, `0`, `.`, `0`, `0`, `0`]
|
||||
|
||||
defer {
|
||||
unsafe { buf.free() }
|
||||
}
|
||||
|
||||
int_to_byte_array_no_pad(t.year, mut buf, 4)
|
||||
int_to_byte_array_no_pad(t.month, mut buf, 7)
|
||||
int_to_byte_array_no_pad(t.day, mut buf, 10)
|
||||
|
||||
int_to_byte_array_no_pad(t.hour, mut buf, 13)
|
||||
int_to_byte_array_no_pad(t.minute, mut buf, 16)
|
||||
int_to_byte_array_no_pad(t.second, mut buf, 19)
|
||||
|
||||
// Extract and format milliseconds
|
||||
millis := t.nanosecond / 1_000_000
|
||||
int_to_byte_array_no_pad(millis, mut buf, 23)
|
||||
|
||||
return buf.bytestr()
|
||||
}
|
||||
|
||||
// format_ss_micro returns a date string in "YYYY-MM-DD HH:mm:ss.123456" format (24h).
|
||||
@[manualfree]
|
||||
pub fn (t Time) format_ss_micro() string {
|
||||
return '${t.year:04d}-${t.month:02d}-${t.day:02d} ${t.hour:02d}:${t.minute:02d}:${t.second:02d}.${(t.nanosecond / 1_000):06d}'
|
||||
mut buf := [u8(`0`), `0`, `0`, `0`, `-`, `0`, `0`, `-`, `0`, `0`, ` `, `0`, `0`, `:`, `0`,
|
||||
`0`, `:`, `0`, `0`, `.`, `0`, `0`, `0`, `0`, `0`, `0`]
|
||||
|
||||
defer {
|
||||
unsafe { buf.free() }
|
||||
}
|
||||
|
||||
int_to_byte_array_no_pad(t.year, mut buf, 4)
|
||||
int_to_byte_array_no_pad(t.month, mut buf, 7)
|
||||
int_to_byte_array_no_pad(t.day, mut buf, 10)
|
||||
|
||||
int_to_byte_array_no_pad(t.hour, mut buf, 13)
|
||||
int_to_byte_array_no_pad(t.minute, mut buf, 16)
|
||||
int_to_byte_array_no_pad(t.second, mut buf, 19)
|
||||
|
||||
// Extract and format microseconds
|
||||
micros := t.nanosecond / 1_000
|
||||
int_to_byte_array_no_pad(micros, mut buf, 26)
|
||||
|
||||
return buf.bytestr()
|
||||
}
|
||||
|
||||
// format_ss_nano returns a date string in "YYYY-MM-DD HH:mm:ss.123456789" format (24h).
|
||||
@[manualfree]
|
||||
pub fn (t Time) format_ss_nano() string {
|
||||
return '${t.year:04d}-${t.month:02d}-${t.day:02d} ${t.hour:02d}:${t.minute:02d}:${t.second:02d}.${t.nanosecond:09d}'
|
||||
mut buf := [u8(`0`), `0`, `0`, `0`, `-`, `0`, `0`, `-`, `0`, `0`, ` `, `0`, `0`, `:`, `0`,
|
||||
`0`, `:`, `0`, `0`, `.`, `0`, `0`, `0`, `0`, `0`, `0`, `0`, `0`, `0`]
|
||||
|
||||
defer {
|
||||
unsafe { buf.free() }
|
||||
}
|
||||
|
||||
int_to_byte_array_no_pad(t.year, mut buf, 4)
|
||||
int_to_byte_array_no_pad(t.month, mut buf, 7)
|
||||
int_to_byte_array_no_pad(t.day, mut buf, 10)
|
||||
|
||||
int_to_byte_array_no_pad(t.hour, mut buf, 13)
|
||||
int_to_byte_array_no_pad(t.minute, mut buf, 16)
|
||||
int_to_byte_array_no_pad(t.second, mut buf, 19)
|
||||
|
||||
int_to_byte_array_no_pad(t.nanosecond, mut buf, 29) // Adjusted index for 9 digits
|
||||
|
||||
return buf.bytestr()
|
||||
}
|
||||
|
||||
// format_rfc3339 returns a date string in "YYYY-MM-DDTHH:mm:ss.123Z" format (24 hours, see https://www.rfc-editor.org/rfc/rfc3339.html)
|
||||
// RFC3339 is an Internet profile, based on the ISO 8601 standard for for representation of dates and times using the Gregorian calendar.
|
||||
// It is intended to improve consistency and interoperability, when representing and using date and time in Internet protocols.
|
||||
@[markused]
|
||||
@[manualfree; markused]
|
||||
pub fn (t Time) format_rfc3339() string {
|
||||
u := t.local_to_utc()
|
||||
return '${u.year:04d}-${u.month:02d}-${u.day:02d}T${u.hour:02d}:${u.minute:02d}:${u.second:02d}.${(u.nanosecond / 1_000_000):03d}Z'
|
||||
mut buf := [u8(`0`), `0`, `0`, `0`, `-`, `0`, `0`, `-`, `0`, `0`, `T`, `0`, `0`, `:`, `0`,
|
||||
`0`, `:`, `0`, `0`, `.`, `0`, `0`, `0`, `Z`]
|
||||
|
||||
defer {
|
||||
unsafe { buf.free() }
|
||||
}
|
||||
|
||||
if t.unix == 0 && t.nanosecond == 0 {
|
||||
return buf.bytestr()
|
||||
}
|
||||
|
||||
if t.is_local {
|
||||
utc_time := t.local_to_utc()
|
||||
int_to_byte_array_no_pad(utc_time.year, mut buf, 4)
|
||||
int_to_byte_array_no_pad(utc_time.month, mut buf, 7)
|
||||
int_to_byte_array_no_pad(utc_time.day, mut buf, 10)
|
||||
int_to_byte_array_no_pad(utc_time.hour, mut buf, 13)
|
||||
int_to_byte_array_no_pad(utc_time.minute, mut buf, 16)
|
||||
int_to_byte_array_no_pad(utc_time.second, mut buf, 19)
|
||||
int_to_byte_array_no_pad(utc_time.nanosecond / 1_000_000, mut buf, 23)
|
||||
} else {
|
||||
int_to_byte_array_no_pad(t.year, mut buf, 4)
|
||||
int_to_byte_array_no_pad(t.month, mut buf, 7)
|
||||
int_to_byte_array_no_pad(t.day, mut buf, 10)
|
||||
int_to_byte_array_no_pad(t.hour, mut buf, 13)
|
||||
int_to_byte_array_no_pad(t.minute, mut buf, 16)
|
||||
int_to_byte_array_no_pad(t.second, mut buf, 19)
|
||||
int_to_byte_array_no_pad(t.nanosecond / 1_000_000, mut buf, 23)
|
||||
}
|
||||
|
||||
return buf.bytestr()
|
||||
}
|
||||
|
||||
// format_rfc3339_nano returns a date string in "YYYY-MM-DDTHH:mm:ss.123456789Z" format (24 hours, see https://www.rfc-editor.org/rfc/rfc3339.html)
|
||||
@[manualfree]
|
||||
pub fn (t Time) format_rfc3339_nano() string {
|
||||
u := t.local_to_utc()
|
||||
return '${u.year:04d}-${u.month:02d}-${u.day:02d}T${u.hour:02d}:${u.minute:02d}:${u.second:02d}.${(u.nanosecond):09d}Z'
|
||||
mut buf := [u8(`0`), `0`, `0`, `0`, `-`, `0`, `0`, `-`, `0`, `0`, `T`, `0`, `0`, `:`, `0`,
|
||||
`0`, `:`, `0`, `0`, `.`, `0`, `0`, `0`, `0`, `0`, `0`, `0`, `0`, `0`, `Z`]
|
||||
|
||||
defer {
|
||||
unsafe { buf.free() }
|
||||
}
|
||||
|
||||
if t.unix == 0 && t.nanosecond == 0 {
|
||||
return buf.bytestr()
|
||||
}
|
||||
|
||||
if t.is_local {
|
||||
utc_time := t.local_to_utc()
|
||||
int_to_byte_array_no_pad(utc_time.year, mut buf, 4)
|
||||
int_to_byte_array_no_pad(utc_time.month, mut buf, 7)
|
||||
int_to_byte_array_no_pad(utc_time.day, mut buf, 10)
|
||||
int_to_byte_array_no_pad(utc_time.hour, mut buf, 13)
|
||||
int_to_byte_array_no_pad(utc_time.minute, mut buf, 16)
|
||||
int_to_byte_array_no_pad(utc_time.second, mut buf, 19)
|
||||
int_to_byte_array_no_pad(utc_time.nanosecond, mut buf, 29)
|
||||
} else {
|
||||
int_to_byte_array_no_pad(t.year, mut buf, 4)
|
||||
int_to_byte_array_no_pad(t.month, mut buf, 7)
|
||||
int_to_byte_array_no_pad(t.day, mut buf, 10)
|
||||
int_to_byte_array_no_pad(t.hour, mut buf, 13)
|
||||
int_to_byte_array_no_pad(t.minute, mut buf, 16)
|
||||
int_to_byte_array_no_pad(t.second, mut buf, 19)
|
||||
int_to_byte_array_no_pad(t.nanosecond, mut buf, 29)
|
||||
}
|
||||
|
||||
return buf.bytestr()
|
||||
}
|
||||
|
||||
// hhmm returns a date string in "HH:mm" format (24h).
|
||||
@[manualfree]
|
||||
pub fn (t Time) hhmm() string {
|
||||
return '${t.hour:02d}:${t.minute:02d}'
|
||||
mut buf := [u8(`0`), `0`, `:`, `0`, `0`]
|
||||
|
||||
defer {
|
||||
unsafe { buf.free() }
|
||||
}
|
||||
|
||||
int_to_byte_array_no_pad(t.hour, mut buf, 2)
|
||||
int_to_byte_array_no_pad(t.minute, mut buf, 5)
|
||||
|
||||
return buf.bytestr()
|
||||
}
|
||||
|
||||
// hhmmss returns a date string in "HH:mm:ss" format (24h).
|
||||
@[manualfree]
|
||||
pub fn (t Time) hhmmss() string {
|
||||
return '${t.hour:02d}:${t.minute:02d}:${t.second:02d}'
|
||||
mut buf := [u8(`0`), `0`, `:`, `0`, `0`, `:`, `0`, `0`]
|
||||
|
||||
defer {
|
||||
unsafe { buf.free() }
|
||||
}
|
||||
|
||||
int_to_byte_array_no_pad(t.hour, mut buf, 2)
|
||||
int_to_byte_array_no_pad(t.minute, mut buf, 5)
|
||||
int_to_byte_array_no_pad(t.second, mut buf, 8)
|
||||
|
||||
return buf.bytestr()
|
||||
}
|
||||
|
||||
// hhmm12 returns a date string in "hh:mm" format (12h).
|
||||
@ -75,6 +264,7 @@ pub fn (t Time) md() string {
|
||||
return t.get_fmt_date_str(.space, .mmmd)
|
||||
}
|
||||
|
||||
// TODO test, improve performance
|
||||
// appends ordinal suffix to a number
|
||||
fn ordinal_suffix(n int) string {
|
||||
if n > 3 && n < 21 {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import time
|
||||
import math
|
||||
|
||||
const time_to_test = time.Time{
|
||||
const local_time_to_test = time.Time{
|
||||
year: 1980
|
||||
month: 7
|
||||
day: 11
|
||||
@ -10,6 +10,19 @@ const time_to_test = time.Time{
|
||||
second: 42
|
||||
nanosecond: 123456789
|
||||
unix: 332198622
|
||||
is_local: true
|
||||
}
|
||||
|
||||
const utc_time_to_test = time.Time{
|
||||
year: 1980
|
||||
month: 7
|
||||
day: 11
|
||||
hour: 21
|
||||
minute: 23
|
||||
second: 42
|
||||
nanosecond: 123456789
|
||||
unix: 332198622
|
||||
is_local: false
|
||||
}
|
||||
|
||||
fn test_is_leap_year() {
|
||||
@ -83,39 +96,64 @@ fn test_unix() {
|
||||
|
||||
fn test_format_rfc3339() {
|
||||
// assert '1980-07-11T19:23:42.123Z'
|
||||
res := time_to_test.format_rfc3339()
|
||||
res := local_time_to_test.format_rfc3339()
|
||||
assert res.ends_with('23:42.123Z')
|
||||
assert res.starts_with('1980-07-1')
|
||||
assert res.contains('T')
|
||||
|
||||
// assert '1980-07-11T19:23:42.123Z'
|
||||
utc_res := utc_time_to_test.format_rfc3339()
|
||||
assert utc_res.ends_with('23:42.123Z')
|
||||
assert utc_res.starts_with('1980-07-1')
|
||||
assert utc_res.contains('T')
|
||||
}
|
||||
|
||||
fn test_format_rfc3339_nano() {
|
||||
res := time_to_test.format_rfc3339_nano()
|
||||
res := local_time_to_test.format_rfc3339_nano()
|
||||
assert res.ends_with('23:42.123456789Z')
|
||||
assert res.starts_with('1980-07-1')
|
||||
assert res.contains('T')
|
||||
|
||||
utc_res := utc_time_to_test.format_rfc3339_nano()
|
||||
assert utc_res.ends_with('23:42.123456789Z')
|
||||
assert utc_res.starts_with('1980-07-1')
|
||||
assert utc_res.contains('T')
|
||||
}
|
||||
|
||||
fn test_format_ss() {
|
||||
assert '11.07.1980 21:23:42' == time_to_test.get_fmt_str(.dot, .hhmmss24, .ddmmyyyy)
|
||||
assert '11.07.1980 21:23:42' == local_time_to_test.get_fmt_str(.dot, .hhmmss24, .ddmmyyyy)
|
||||
|
||||
assert '11.07.1980 21:23:42' == utc_time_to_test.get_fmt_str(.dot, .hhmmss24, .ddmmyyyy)
|
||||
}
|
||||
|
||||
fn test_format_ss_milli() {
|
||||
assert '11.07.1980 21:23:42.123' == time_to_test.get_fmt_str(.dot, .hhmmss24_milli,
|
||||
assert '11.07.1980 21:23:42.123' == local_time_to_test.get_fmt_str(.dot, .hhmmss24_milli,
|
||||
.ddmmyyyy)
|
||||
assert '1980-07-11 21:23:42.123' == time_to_test.format_ss_milli()
|
||||
assert '1980-07-11 21:23:42.123' == local_time_to_test.format_ss_milli()
|
||||
|
||||
assert '11.07.1980 21:23:42.123' == utc_time_to_test.get_fmt_str(.dot, .hhmmss24_milli,
|
||||
.ddmmyyyy)
|
||||
assert '1980-07-11 21:23:42.123' == utc_time_to_test.format_ss_milli()
|
||||
}
|
||||
|
||||
fn test_format_ss_micro() {
|
||||
assert '11.07.1980 21:23:42.123456' == time_to_test.get_fmt_str(.dot, .hhmmss24_micro,
|
||||
assert '11.07.1980 21:23:42.123456' == local_time_to_test.get_fmt_str(.dot, .hhmmss24_micro,
|
||||
.ddmmyyyy)
|
||||
assert '1980-07-11 21:23:42.123456' == time_to_test.format_ss_micro()
|
||||
assert '1980-07-11 21:23:42.123456' == local_time_to_test.format_ss_micro()
|
||||
|
||||
assert '11.07.1980 21:23:42.123456' == utc_time_to_test.get_fmt_str(.dot, .hhmmss24_micro,
|
||||
.ddmmyyyy)
|
||||
assert '1980-07-11 21:23:42.123456' == utc_time_to_test.format_ss_micro()
|
||||
}
|
||||
|
||||
fn test_format_ss_nano() {
|
||||
assert '11.07.1980 21:23:42.123456789' == time_to_test.get_fmt_str(.dot, .hhmmss24_nano,
|
||||
assert '11.07.1980 21:23:42.123456789' == local_time_to_test.get_fmt_str(.dot, .hhmmss24_nano,
|
||||
.ddmmyyyy)
|
||||
assert '1980-07-11 21:23:42.123456789' == time_to_test.format_ss_nano()
|
||||
assert '1980-07-11 21:23:42.123456789' == local_time_to_test.format_ss_nano()
|
||||
|
||||
assert '11.07.1980 21:23:42.123456789' == utc_time_to_test.get_fmt_str(.dot, .hhmmss24_nano,
|
||||
.ddmmyyyy)
|
||||
assert '1980-07-11 21:23:42.123456789' == utc_time_to_test.format_ss_nano()
|
||||
}
|
||||
|
||||
fn test_smonth() {
|
||||
@ -194,23 +232,23 @@ fn test_add() {
|
||||
d_nanoseconds := 13
|
||||
duration := time.Duration(d_seconds * time.second + d_nanoseconds * time.nanosecond)
|
||||
// dump(duration.debug())
|
||||
t1 := time_to_test
|
||||
t1 := local_time_to_test
|
||||
// dump(t1.debug())
|
||||
t2 := time_to_test.add(duration)
|
||||
t2 := local_time_to_test.add(duration)
|
||||
// dump(t2.debug())
|
||||
assert t2.second == t1.second + d_seconds
|
||||
assert t2.nanosecond == t1.nanosecond + d_nanoseconds
|
||||
assert t2.unix == t1.unix + d_seconds
|
||||
assert t2.is_local == t1.is_local
|
||||
//
|
||||
t3 := time_to_test.add(-duration)
|
||||
t3 := local_time_to_test.add(-duration)
|
||||
// dump(t3.debug())
|
||||
assert t3.second == t1.second - d_seconds
|
||||
assert t3.nanosecond == t1.nanosecond - d_nanoseconds
|
||||
assert t3.unix == t1.unix - d_seconds
|
||||
assert t3.is_local == t1.is_local
|
||||
//
|
||||
t4 := time_to_test.as_local()
|
||||
t4 := local_time_to_test.as_local()
|
||||
// dump(t4.debug())
|
||||
t5 := t4.add(duration)
|
||||
// dump(t5.debug())
|
||||
@ -219,13 +257,15 @@ fn test_add() {
|
||||
|
||||
fn test_add_days() {
|
||||
num_of_days := 3
|
||||
t := time_to_test.add_days(num_of_days)
|
||||
assert t.day == time_to_test.day + num_of_days
|
||||
assert t.unix == time_to_test.unix + 86400 * num_of_days
|
||||
t := local_time_to_test.add_days(num_of_days)
|
||||
assert t.day == local_time_to_test.day + num_of_days
|
||||
assert t.unix == local_time_to_test.unix + 86400 * num_of_days
|
||||
}
|
||||
|
||||
fn test_str() {
|
||||
assert '1980-07-11 21:23:42' == time_to_test.str()
|
||||
assert '1980-07-11 21:23:42' == local_time_to_test.str()
|
||||
|
||||
assert '1980-07-11 21:23:42' == utc_time_to_test.str()
|
||||
}
|
||||
|
||||
// not optimal test but will find obvious bugs
|
||||
@ -322,7 +362,9 @@ fn test_recursive_local_call() {
|
||||
}
|
||||
|
||||
fn test_strftime() {
|
||||
assert '1980 July 11' == time_to_test.strftime('%Y %B %d')
|
||||
assert '1980 July 11' == local_time_to_test.strftime('%Y %B %d')
|
||||
|
||||
assert '1980 July 11' == utc_time_to_test.strftime('%Y %B %d')
|
||||
}
|
||||
|
||||
fn test_add_seconds_to_time() {
|
||||
|
@ -86,7 +86,12 @@ fn benchmark_measure_encode_by_type() ! {
|
||||
println(@FN)
|
||||
dump('👈')
|
||||
measure_json_encode_old_vs_new(StructType[string]{})!
|
||||
println('time.Time]{}')
|
||||
measure_json_encode_old_vs_new(StructType[time.Time]{})!
|
||||
println('time.utc()')
|
||||
measure_json_encode_old_vs_new(StructType[time.Time]{time.utc()})!
|
||||
println('time.now()')
|
||||
measure_json_encode_old_vs_new(StructType[time.Time]{time.now()})!
|
||||
measure_json_encode_old_vs_new(StructType[int]{})!
|
||||
measure_json_encode_old_vs_new(StructType[f64]{})!
|
||||
measure_json_encode_old_vs_new(StructType[bool]{})!
|
||||
|
Loading…
x
Reference in New Issue
Block a user