mirror of
https://github.com/bearlanguageorg/bear.git
synced 2026-08-26 14:57:18 +00:00
Time lib and http
This commit is contained in:
@@ -157,6 +157,23 @@ Build builtins:
|
|||||||
`examples/build.vrmm` for a tour (targets: `main`, `multi`, `test`, `bench`,
|
`examples/build.vrmm` for a tour (targets: `main`, `multi`, `test`, `bench`,
|
||||||
`deploy`, `clean`).
|
`deploy`, `clean`).
|
||||||
|
|
||||||
|
## Standard library modules
|
||||||
|
|
||||||
|
The toolchain ships a small stdlib in `lib/`, imported by name like in V
|
||||||
|
(`import os` resolves against `lib/` and `vendor/` from any directory).
|
||||||
|
Module functions are namespaced: `os.exists()`, `json.encode()`, ...
|
||||||
|
|
||||||
|
| module | functions |
|
||||||
|
| --- | --- |
|
||||||
|
| `os` | filesystem + process: `exists`, `is_dir`, `is_file`, `mkdir`, `remove`, `copy`, `list_dir`, `glob`, `join`, `base`, `dir`, `read_lines`, `write_lines`, `cwd`, `env`, `exec` |
|
||||||
|
| `json` | `encode`, `decode`, `pretty` — objects become structs, integral numbers decode as ints, `null` ⇄ `none` |
|
||||||
|
| `strings` | `length`, `lines`, `split`, `join`, `replace`, `contains`, `starts_with`, `ends_with`, `upper`, `lower`, `trim`, `pad`, `pad_left`, `repeat`, `format`, `capitalize` |
|
||||||
|
| `math` | `abs`, `min`, `max`, `floor`, `ceil`, `round`, `sqrt`, `pow`, `clamp`, `sign`, `pi` |
|
||||||
|
| `http` | `get(url)`, `post(url, data)`, `get_text(url)` — return `{status, body}`; network failures throw (catchable), HTTP errors (404) are normal responses |
|
||||||
|
| `time` | `now()` (epoch s), `ms()` (epoch ms), `format(t, "YYYY-MM-DD HH:mm:ss")`, `date(t)`, `clock(t)`, `parse("2026-08-25 13:36:45")` — Moment-style tokens |
|
||||||
|
|
||||||
|
Examples: `examples/imports.vr`, `examples/http.vr`, `examples/time.vr`.
|
||||||
|
|
||||||
## The VuurRaaf language
|
## The VuurRaaf language
|
||||||
|
|
||||||
A small, V-flavored language. Values are 64-bit integers, 64-bit floats,
|
A small, V-flavored language. Values are 64-bit integers, 64-bit floats,
|
||||||
@@ -260,6 +277,10 @@ fn main() {
|
|||||||
- bitwise operators: `& | ^ ~ << >>`
|
- bitwise operators: `& | ^ ~ << >>`
|
||||||
- host builtins: `read_file` / `write_file`, `args()`, `getenv` / `setenv`,
|
- host builtins: `read_file` / `write_file`, `args()`, `getenv` / `setenv`,
|
||||||
`exit`, `sleep`, `time()`, `type(x)`, `str(x)`, `int(x)`, `split` / `join`
|
`exit`, `sleep`, `time()`, `type(x)`, `str(x)`, `int(x)`, `split` / `join`
|
||||||
|
- HTTP: `http_get(url)` / `http_post(url, data)` return `{status, body}`
|
||||||
|
structs and throw on network failure (catchable) — or use `import http`
|
||||||
|
- date/time: `now()` (epoch s), `time_ms()`, `format_time(t, "YYYY-MM-DD")`,
|
||||||
|
`parse_time(s)` (ISO timestamps) — or use `import time`
|
||||||
- JSON: `json_encode(x)` / `json_decode(s)` — objects become structs, arrays
|
- JSON: `json_encode(x)` / `json_decode(s)` — objects become structs, arrays
|
||||||
become arrays, integral numbers decode as ints, `null` decodes to `none`
|
become arrays, integral numbers decode as ints, `null` decodes to `none`
|
||||||
- `none`: a literal for "no value" (JSON null); `x == none` compares,
|
- `none`: a literal for "no value" (JSON null); `x == none` compares,
|
||||||
|
|||||||
+21
-6
@@ -232,12 +232,17 @@ fn (mut c Checker) check_stmt(st Stmt) ! {
|
|||||||
c.loop_depth--
|
c.loop_depth--
|
||||||
}
|
}
|
||||||
.for_in_stmt {
|
.for_in_stmt {
|
||||||
seq := c.check_expr(st.expr)!
|
// for x in EnumName { ... } iterates the enum's variants
|
||||||
// iterate enums and arrays; unknown is allowed (dynamic)
|
if st.expr.kind == .ident && st.expr.name in c.enums {
|
||||||
if seq.kind == .int_t || seq.kind == .float_t || seq.kind == .bool_t || seq.kind == .none_t {
|
c.types[st.target] = TypeInfo{ kind: .enum_t, name: st.expr.name }
|
||||||
return error('cannot iterate a ${type_name(seq.kind)} (line ${st.line})')
|
} else {
|
||||||
|
seq := c.check_expr(st.expr)!
|
||||||
|
// iterate enums and arrays; unknown is allowed (dynamic)
|
||||||
|
if seq.kind == .int_t || seq.kind == .float_t || seq.kind == .bool_t || seq.kind == .none_t {
|
||||||
|
return error('cannot iterate a ${type_name(seq.kind)} (line ${st.line})')
|
||||||
|
}
|
||||||
|
c.types[st.target] = TypeInfo{ kind: .unknown }
|
||||||
}
|
}
|
||||||
c.types[st.target] = TypeInfo{ kind: .unknown }
|
|
||||||
if st.idx_target.len > 0 {
|
if st.idx_target.len > 0 {
|
||||||
c.types[st.idx_target] = TypeInfo{ kind: .int_t }
|
c.types[st.idx_target] = TypeInfo{ kind: .int_t }
|
||||||
}
|
}
|
||||||
@@ -328,9 +333,14 @@ fn (mut c Checker) check_expr(e Expr) !TypeInfo {
|
|||||||
TypeInfo{ kind: .unknown }
|
TypeInfo{ kind: .unknown }
|
||||||
}
|
}
|
||||||
.field {
|
.field {
|
||||||
|
// enum variant: Color.red — the base is the enum type name, not a
|
||||||
|
// variable, so it must be resolved before check_expr on the base
|
||||||
|
if e.left.kind == .ident && e.left.name in c.enums {
|
||||||
|
return TypeInfo{ kind: .enum_t, name: e.left.name }
|
||||||
|
}
|
||||||
base := c.check_expr(*e.left)!
|
base := c.check_expr(*e.left)!
|
||||||
c.expect_struct_like(base, 'field access', e.line)!
|
c.expect_struct_like(base, 'field access', e.line)!
|
||||||
// enum variant: Color.red → enum_t
|
// enum variant on an enum-typed receiver: Color.red → enum_t
|
||||||
if base.kind == .enum_t {
|
if base.kind == .enum_t {
|
||||||
return TypeInfo{ kind: .enum_t, name: base.name }
|
return TypeInfo{ kind: .enum_t, name: base.name }
|
||||||
}
|
}
|
||||||
@@ -599,6 +609,11 @@ fn builtin_result_type(name string) TypeInfo {
|
|||||||
'build_glob', 'build_ls' { TypeInfo{ kind: .array_t } }
|
'build_glob', 'build_ls' { TypeInfo{ kind: .array_t } }
|
||||||
'build_run', 'build_test', 'build_bench', 'build_clean', 'build_exec_status',
|
'build_run', 'build_test', 'build_bench', 'build_clean', 'build_exec_status',
|
||||||
'build_exists', 'build_mkdir', 'build_rm', 'build_copy' { TypeInfo{ kind: .int_t } }
|
'build_exists', 'build_mkdir', 'build_rm', 'build_copy' { TypeInfo{ kind: .int_t } }
|
||||||
|
// HTTP client: returns a {status, body} struct
|
||||||
|
'http_get', 'http_post' { TypeInfo{ kind: .struct_t } }
|
||||||
|
// date/time
|
||||||
|
'now', 'time_ms', 'parse_time' { TypeInfo{ kind: .int_t } }
|
||||||
|
'format_time' { TypeInfo{ kind: .string_t } }
|
||||||
else { TypeInfo{ kind: .unknown } }
|
else { TypeInfo{ kind: .unknown } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -974,6 +974,14 @@ fn builtin_spec(name string) (int, int) {
|
|||||||
'build_is_dir' { native_build_is_dir, 1 }
|
'build_is_dir' { native_build_is_dir, 1 }
|
||||||
'cwd' { native_cwd, 0 }
|
'cwd' { native_cwd, 0 }
|
||||||
'json_pretty' { native_json_pretty, 1 }
|
'json_pretty' { native_json_pretty, 1 }
|
||||||
|
// HTTP client
|
||||||
|
'http_get' { native_http_get, 1 }
|
||||||
|
'http_post' { native_http_post, 2 }
|
||||||
|
// date/time
|
||||||
|
'now' { native_now, 0 }
|
||||||
|
'time_ms' { native_time_ms, 0 }
|
||||||
|
'format_time' { native_format_time, 2 }
|
||||||
|
'parse_time' { native_parse_time, 1 }
|
||||||
else { -1, 0 }
|
else { -1, 0 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,3 +141,13 @@ const native_repeat = 164
|
|||||||
const native_build_is_dir = 165
|
const native_build_is_dir = 165
|
||||||
const native_cwd = 166
|
const native_cwd = 166
|
||||||
const native_json_pretty = 167
|
const native_json_pretty = 167
|
||||||
|
|
||||||
|
// HTTP client builtins
|
||||||
|
const native_http_get = 168
|
||||||
|
const native_http_post = 169
|
||||||
|
|
||||||
|
// date/time builtins
|
||||||
|
const native_now = 170
|
||||||
|
const native_time_ms = 171
|
||||||
|
const native_format_time = 172
|
||||||
|
const native_parse_time = 173
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
// http.vr — the HTTP client (needs network access).
|
||||||
|
// vr run examples/http.vr
|
||||||
|
//
|
||||||
|
// http.get / http.post return a struct with `status` (int) and `body`
|
||||||
|
// (string). Network failures throw and can be caught with try/catch;
|
||||||
|
// HTTP-level errors (404, 500) come back as normal responses.
|
||||||
|
|
||||||
|
import http
|
||||||
|
import json
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let res = http.get("https://httpbin.org/get")
|
||||||
|
println("status: " + str(res.status))
|
||||||
|
|
||||||
|
let data = json.decode(res.body)
|
||||||
|
println("your IP (from httpbin): " + str(data.origin))
|
||||||
|
|
||||||
|
let post = http.post("https://httpbin.org/post", "name=vuurraaf")
|
||||||
|
let echo = json.decode(post.body)
|
||||||
|
println("echoed: " + str(echo.data))
|
||||||
|
|
||||||
|
// a 404 is a response, not an exception
|
||||||
|
let nf = http.get("https://httpbin.org/status/404")
|
||||||
|
println("not found status: " + str(nf.status))
|
||||||
|
|
||||||
|
// network failures are catchable
|
||||||
|
try {
|
||||||
|
http.get("http://127.0.0.1:1/nope")
|
||||||
|
println("unreachable")
|
||||||
|
} catch e {
|
||||||
|
println("caught connection error")
|
||||||
|
}
|
||||||
|
println("done")
|
||||||
|
}
|
||||||
+75
-1
@@ -5,6 +5,15 @@ import os
|
|||||||
import json
|
import json
|
||||||
import strings
|
import strings
|
||||||
import math
|
import math
|
||||||
|
import http
|
||||||
|
import time
|
||||||
|
|
||||||
|
enum Suit {
|
||||||
|
hearts
|
||||||
|
diamonds
|
||||||
|
clubs
|
||||||
|
spades
|
||||||
|
}
|
||||||
|
|
||||||
fn add(a, b) {
|
fn add(a, b) {
|
||||||
return a + b
|
return a + b
|
||||||
@@ -310,7 +319,7 @@ fn test_module_imports() {
|
|||||||
}
|
}
|
||||||
assert os.base("a/b/c.vr") == "c.vr"
|
assert os.base("a/b/c.vr") == "c.vr"
|
||||||
assert os.dir("a/b/c.vr") == "a/b"
|
assert os.dir("a/b/c.vr") == "a/b"
|
||||||
assert len(os.glob("lib/*.vr")) == 4
|
assert len(os.glob("lib/*.vr")) == 6
|
||||||
let tmp = os.join(os.cwd(), "tmp_test.txt")
|
let tmp = os.join(os.cwd(), "tmp_test.txt")
|
||||||
os.write_lines(tmp, ["x", "y"])
|
os.write_lines(tmp, ["x", "y"])
|
||||||
assert len(os.read_lines(tmp)) == 2
|
assert len(os.read_lines(tmp)) == 2
|
||||||
@@ -381,6 +390,71 @@ fn test_closures() {
|
|||||||
assert tag("warn") == "[warn]"
|
assert tag("warn") == "[warn]"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn test_enums() {
|
||||||
|
// variant values
|
||||||
|
assert Suit.hearts == 0
|
||||||
|
assert Suit.spades == 3
|
||||||
|
// enum.count()
|
||||||
|
assert Suit.hearts.count() == 4
|
||||||
|
// enum.to_string()
|
||||||
|
assert Suit.spades.to_string() == "spades"
|
||||||
|
// enums in expressions
|
||||||
|
let next = Suit.hearts + 1
|
||||||
|
assert next == 1
|
||||||
|
// enum iteration
|
||||||
|
let names = []
|
||||||
|
for s in Suit {
|
||||||
|
names = push(names, s.to_string())
|
||||||
|
}
|
||||||
|
assert len(names) == 4
|
||||||
|
assert names[0] == "hearts"
|
||||||
|
assert names[3] == "spades"
|
||||||
|
// match on enum values
|
||||||
|
assert suit_code(Suit.clubs) == "c"
|
||||||
|
assert suit_code(Suit.hearts) == "h"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn suit_code(s) {
|
||||||
|
match s {
|
||||||
|
Suit.hearts { return "h" }
|
||||||
|
Suit.clubs { return "c" }
|
||||||
|
else { return "?" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_http_errors() {
|
||||||
|
// offline-safe: connecting to a closed local port fails fast
|
||||||
|
try {
|
||||||
|
http.get("http://127.0.0.1:1/nope")
|
||||||
|
assert false
|
||||||
|
} catch e {
|
||||||
|
assert len(e) > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_time() {
|
||||||
|
let t = time.now()
|
||||||
|
assert t > 1700000000
|
||||||
|
// format produces fixed-width strings
|
||||||
|
let day = time.date(t)
|
||||||
|
assert len(day) == 10
|
||||||
|
assert day[4] == "-"[0]
|
||||||
|
let full = time.format(t, "YYYY-MM-DD HH:mm:ss")
|
||||||
|
assert len(full) == 19
|
||||||
|
// parse round-trips within a second
|
||||||
|
let back = time.parse(full)
|
||||||
|
assert abs(back - t) <= 1
|
||||||
|
// ms is more precise than seconds
|
||||||
|
assert time.ms() > t * 1000
|
||||||
|
// invalid input throws
|
||||||
|
try {
|
||||||
|
time.parse("not a date")
|
||||||
|
assert false
|
||||||
|
} catch e {
|
||||||
|
assert len(e) > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn test_failing() {
|
fn test_failing() {
|
||||||
// this one is meant to fail — shows up in `vr test` output
|
// this one is meant to fail — shows up in `vr test` output
|
||||||
assert 1 == 2
|
assert 1 == 2
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
// time.vr — date/time helpers.
|
||||||
|
// vr run examples/time.vr
|
||||||
|
//
|
||||||
|
// Tokens follow the Moment.js style: YYYY, MM, DD, HH, mm, ss, with
|
||||||
|
// literals like "-" and ":". All values are Unix epoch seconds.
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let now = time.now()
|
||||||
|
println("epoch seconds: " + str(now))
|
||||||
|
println("date: " + time.date(now))
|
||||||
|
println("clock: " + time.clock(now))
|
||||||
|
println("full: " + time.format(now, "YYYY-MM-DD HH:mm:ss"))
|
||||||
|
println("custom: " + time.format(now, "MM/DD/YYYY"))
|
||||||
|
|
||||||
|
// parse a timestamp back to epoch seconds
|
||||||
|
let parsed = time.parse("2026-08-25 12:00:00")
|
||||||
|
println("parsed epoch: " + str(parsed))
|
||||||
|
|
||||||
|
// milliseconds for timing
|
||||||
|
let start = time.ms()
|
||||||
|
let x = 0
|
||||||
|
for i in 0..100000 {
|
||||||
|
x = x + i
|
||||||
|
}
|
||||||
|
let elapsed = time.ms() - start
|
||||||
|
println("loop took " + str(elapsed) + "ms (checksum " + str(x) + ")")
|
||||||
|
}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
// http.vr — HTTP client (part of the VuurRaaf stdlib).
|
||||||
|
//
|
||||||
|
// import http
|
||||||
|
// let res = http.get("https://api.example.com/status")
|
||||||
|
// println(str(res.status))
|
||||||
|
// println(res.body)
|
||||||
|
//
|
||||||
|
// http.get / http.post return a struct with `status` (int) and `body`
|
||||||
|
// (string). Network failures (DNS, refused, timeout) throw, so they can be
|
||||||
|
// caught with try/catch; HTTP-level errors (404, 500) are normal responses.
|
||||||
|
|
||||||
|
fn get(url) {
|
||||||
|
return http_get(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post(url, data) {
|
||||||
|
return http_post(url, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get_text returns just the response body as a string.
|
||||||
|
fn get_text(url) {
|
||||||
|
return http_get(url).body
|
||||||
|
}
|
||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
// time.vr — date/time helpers (part of the VuurRaaf stdlib).
|
||||||
|
//
|
||||||
|
// import time
|
||||||
|
// println(time.format(time.now(), "YYYY-MM-DD HH:mm:ss"))
|
||||||
|
// println(time.format(time.now(), "YYYY-MM-DD"))
|
||||||
|
//
|
||||||
|
// Tokens follow the Moment.js style: YYYY (year), MM (month), DD (day),
|
||||||
|
// HH (24h hour), mm (minute), ss (second), plus literals like "-", ":".
|
||||||
|
// All values are Unix epoch seconds. `time.parse` accepts common ISO
|
||||||
|
// timestamps like "2026-08-25 14:03:22".
|
||||||
|
|
||||||
|
fn now() {
|
||||||
|
return now()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ms() {
|
||||||
|
return time_ms()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format(t, spec) {
|
||||||
|
return format_time(t, spec)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(s) {
|
||||||
|
return parse_time(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// date(t) formats just the calendar day: YYYY-MM-DD.
|
||||||
|
fn date(t) {
|
||||||
|
return format_time(t, "YYYY-MM-DD")
|
||||||
|
}
|
||||||
|
|
||||||
|
// clock(t) formats just the wall clock: HH:mm:ss.
|
||||||
|
fn clock(t) {
|
||||||
|
return format_time(t, "HH:mm:ss")
|
||||||
|
}
|
||||||
+43
@@ -11,6 +11,7 @@ import os
|
|||||||
import math
|
import math
|
||||||
import rand
|
import rand
|
||||||
import time
|
import time
|
||||||
|
import net.http
|
||||||
import obj
|
import obj
|
||||||
import compiler
|
import compiler
|
||||||
import assembler
|
import assembler
|
||||||
@@ -647,12 +648,54 @@ fn (mut v Vm) native(id int, _argc int) ! {
|
|||||||
s := v.json_pretty_value(x, 0) or { return error('json_pretty: ${err.msg()}') }
|
s := v.json_pretty_value(x, 0) or { return error('json_pretty: ${err.msg()}') }
|
||||||
v.push(v.alloc_str(s))!
|
v.push(v.alloc_str(s))!
|
||||||
}
|
}
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// HTTP client
|
||||||
|
native_http_get {
|
||||||
|
url := v.pop_str()!
|
||||||
|
resp := http.get(url) or { return error('http_get: ${err.msg()}') }
|
||||||
|
v.push_http_response(resp)!
|
||||||
|
}
|
||||||
|
native_http_post {
|
||||||
|
data := v.pop_str()!
|
||||||
|
url := v.pop_str()!
|
||||||
|
resp := http.post(url, data) or { return error('http_post: ${err.msg()}') }
|
||||||
|
v.push_http_response(resp)!
|
||||||
|
}
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// date/time
|
||||||
|
native_now {
|
||||||
|
v.push(v.enc_int(time.now().unix()))!
|
||||||
|
}
|
||||||
|
native_time_ms {
|
||||||
|
v.push(v.enc_int(time.now().unix_milli()))!
|
||||||
|
}
|
||||||
|
native_format_time {
|
||||||
|
spec := v.pop_str()!
|
||||||
|
t := v.dec_int(v.pop()!)
|
||||||
|
v.push(v.alloc_str(time.unix(t).custom_format(spec)))!
|
||||||
|
}
|
||||||
|
native_parse_time {
|
||||||
|
s := v.pop_str()!
|
||||||
|
t := time.parse(s) or { return error('parse_time: ${err.msg()}') }
|
||||||
|
v.push(v.enc_int(t.unix()))!
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
return error('unknown native builtin ${id}')
|
return error('unknown native builtin ${id}')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// push_http_response wraps an HTTP response as a {status, body} struct value
|
||||||
|
// so scripts can read res.status and res.body.
|
||||||
|
fn (mut v Vm) push_http_response(resp http.Response) ! {
|
||||||
|
body_h := v.alloc_str(resp.body)
|
||||||
|
mut fields := []Field{len: 2}
|
||||||
|
fields[0] = Field{ name: 'status', val: v.enc_int(i64(resp.status_code)) }
|
||||||
|
fields[1] = Field{ name: 'body', val: body_h }
|
||||||
|
v.structs << StructVal{ fields: fields }
|
||||||
|
v.push(v.mkstruct_handle(v.structs.len - 1))!
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// format() — a small printf-style formatter: %d %i %f %s %x %X %% with
|
// format() — a small printf-style formatter: %d %i %f %s %x %X %% with
|
||||||
// optional width, left-align (-), zero-padding (0), and precision (.N).
|
// optional width, left-align (-), zero-padding (0), and precision (.N).
|
||||||
|
|||||||
@@ -140,3 +140,13 @@ const native_repeat = 164
|
|||||||
const native_build_is_dir = 165
|
const native_build_is_dir = 165
|
||||||
const native_cwd = 166
|
const native_cwd = 166
|
||||||
const native_json_pretty = 167
|
const native_json_pretty = 167
|
||||||
|
|
||||||
|
// HTTP client builtins
|
||||||
|
const native_http_get = 168
|
||||||
|
const native_http_post = 169
|
||||||
|
|
||||||
|
// date/time builtins
|
||||||
|
const native_now = 170
|
||||||
|
const native_time_ms = 171
|
||||||
|
const native_format_time = 172
|
||||||
|
const native_parse_time = 173
|
||||||
|
|||||||
Reference in New Issue
Block a user