Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cache time representation. #149

Merged
merged 11 commits into from
Aug 22, 2024
58 changes: 52 additions & 6 deletions chronicles/log_output.nim
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import
strutils, times, macros, options, os,
dynamic_scope_types
strutils, times, macros, options, os, timestamp,
dynamic_scope_types, stew/[objects, byteutils]

when defined(js):
import
Expand Down Expand Up @@ -422,15 +422,61 @@ macro append*(o: var AnyOutput,
result.add newCall("append", o, arg2)
for arg in restArgs: result.add newCall("append", o, arg)

proc rfcTimestamp: string =
now().format("yyyy-MM-dd HH:mm:ss'.'fffzzz")

proc epochTimestamp: string =
formatFloat(epochTime(), ffDecimal, 6)

var
cachedMinutes {.threadvar.}: int64
cachedTimeArray {.threadvar.}: array[17, byte] # "yyyy-MM-dd HH:mm:"
cachedZoneArray {.threadvar.}: array[6, byte] # "zzz"

proc getSecondsPart(timestamp: Time): string =
var res = "00.000"
let
sec = timestamp.toUnix() mod 60
msec = timestamp.nanosecond() div 1_000_000

if sec > 0 and sec <= 9:
res[1] = chr(ord('0') + sec)
elif sec >= 10:
res[0] = chr(ord('0') + (sec div 10))
res[1] = chr(ord('0') + (sec mod 10))

if msec > 0 and msec <= 9:
res[5] = chr(ord('0') + msec)
elif msec >= 10 and msec <= 99:
res[4] = chr(ord('0') + (msec div 10))
res[5] = chr(ord('0') + (msec mod 10))
elif msec >= 100:
let tmp = msec mod 100
res[3] = chr(ord('0') + (msec div 100))
res[4] = chr(ord('0') + (tmp div 10))
res[5] = chr(ord('0') + (tmp mod 10))
res

proc getFastDateTimeString(): string =
let
timestamp = getFastTime()
minutes = timestamp.toUnix() div 60

if minutes != cachedMinutes:
cachedMinutes = minutes
let datetime = timestamp.local()
block:
# Cache string representation of first part (without seconds)
let tmp = datetime.format("yyyy-MM-dd HH:mm:")
cachedTimeArray = toArray(17, tmp.toOpenArrayByte(0, 16))
block:
# Cache string representation of zone part
let tmp = datetime.format("zzz")
cachedZoneArray = toArray(6, tmp.toOpenArrayByte(0, 5))

string.fromBytes(cachedTimeArray) & timestamp.getSecondsPart() &
string.fromBytes(cachedZoneArray)

template timestamp(record): string =
when record.timestamps == RfcTime:
rfcTimestamp()
getFastDateTimeString()
else:
epochTimestamp()

Expand Down
42 changes: 42 additions & 0 deletions chronicles/timestamp.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@

{.push raises: [].}

import std/times
export times

when defined(macos) or defined(macosx) or defined(osx):
from posix import Timeval
proc gettimeofday(tp: var Timeval, tzp: pointer = nil) {.
importc: "gettimeofday", header: "<sys/time.h>", sideEffect.}
elif defined(windows):
type
FILETIME {.final, pure, completeStruct.} = object
dwLowDateTime: uint32
dwHighDateTime: uint32
proc getSystemTimeAsFileTime*(lpSystemTimeAsFileTime: var FILETIME) {.
importc: "GetSystemTimeAsFileTime", dynlib: "kernel32", stdcall,
sideEffect.}
else:
const CLOCK_REALTIME_COARSE = 5
from std/posix import Timespec, Time, clock_gettime

proc getFastTime*(): times.Time =
when defined(js):
let
millis = newDate().getTime()
seconds = millis div 1_000
nanos = (millis mod 1_000) * 1_000_000
initTime(seconds, nanos)
elif defined(macosx):
var a {.noinit.}: Timeval
gettimeofday(a)
initTime(a.tv_sec.int64, int(a.tv_usec) * 1_000)
elif defined(windows):
var f {.noinit.}: FILETIME
getSystemTimeAsFileTime(f)
let tmp = uint64(f.dwLowDateTime) or (uint64(f.dwHighDateTime) shl 32)
fromWinTime(cast[int64](tmp))
else:
var ts {.noinit.}: Timespec
discard clock_gettime(CLOCK_REALTIME_COARSE, ts)
initTime(int64(ts.tv_sec), int(ts.tv_nsec))