time.odin 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. package time
  2. import "base:intrinsics"
  3. import dt "core:time/datetime"
  4. Duration :: distinct i64
  5. Nanosecond :: Duration(1)
  6. Microsecond :: 1000 * Nanosecond
  7. Millisecond :: 1000 * Microsecond
  8. Second :: 1000 * Millisecond
  9. Minute :: 60 * Second
  10. Hour :: 60 * Minute
  11. MIN_DURATION :: Duration(-1 << 63)
  12. MAX_DURATION :: Duration(1<<63 - 1)
  13. IS_SUPPORTED :: _IS_SUPPORTED
  14. Time :: struct {
  15. _nsec: i64, // Measured in UNIX nanonseconds
  16. }
  17. Month :: enum int {
  18. January = 1,
  19. February,
  20. March,
  21. April,
  22. May,
  23. June,
  24. July,
  25. August,
  26. September,
  27. October,
  28. November,
  29. December,
  30. }
  31. Weekday :: enum int {
  32. Sunday = 0,
  33. Monday,
  34. Tuesday,
  35. Wednesday,
  36. Thursday,
  37. Friday,
  38. Saturday,
  39. }
  40. Stopwatch :: struct {
  41. running: bool,
  42. _start_time: Tick,
  43. _accumulation: Duration,
  44. }
  45. now :: proc "contextless" () -> Time {
  46. return _now()
  47. }
  48. sleep :: proc "contextless" (d: Duration) {
  49. _sleep(d)
  50. }
  51. stopwatch_start :: proc "contextless" (stopwatch: ^Stopwatch) {
  52. if !stopwatch.running {
  53. stopwatch._start_time = tick_now()
  54. stopwatch.running = true
  55. }
  56. }
  57. stopwatch_stop :: proc "contextless" (stopwatch: ^Stopwatch) {
  58. if stopwatch.running {
  59. stopwatch._accumulation += tick_diff(stopwatch._start_time, tick_now())
  60. stopwatch.running = false
  61. }
  62. }
  63. stopwatch_reset :: proc "contextless" (stopwatch: ^Stopwatch) {
  64. stopwatch._accumulation = {}
  65. stopwatch.running = false
  66. }
  67. stopwatch_duration :: proc "contextless" (stopwatch: Stopwatch) -> Duration {
  68. if !stopwatch.running {
  69. return stopwatch._accumulation
  70. }
  71. return stopwatch._accumulation + tick_diff(stopwatch._start_time, tick_now())
  72. }
  73. diff :: proc "contextless" (start, end: Time) -> Duration {
  74. d := end._nsec - start._nsec
  75. return Duration(d)
  76. }
  77. since :: proc "contextless" (start: Time) -> Duration {
  78. return diff(start, now())
  79. }
  80. duration_nanoseconds :: proc "contextless" (d: Duration) -> i64 {
  81. return i64(d)
  82. }
  83. duration_microseconds :: proc "contextless" (d: Duration) -> f64 {
  84. return duration_seconds(d) * 1e6
  85. }
  86. duration_milliseconds :: proc "contextless" (d: Duration) -> f64 {
  87. return duration_seconds(d) * 1e3
  88. }
  89. duration_seconds :: proc "contextless" (d: Duration) -> f64 {
  90. sec := d / Second
  91. nsec := d % Second
  92. return f64(sec) + f64(nsec)/1e9
  93. }
  94. duration_minutes :: proc "contextless" (d: Duration) -> f64 {
  95. min := d / Minute
  96. nsec := d % Minute
  97. return f64(min) + f64(nsec)/(60*1e9)
  98. }
  99. duration_hours :: proc "contextless" (d: Duration) -> f64 {
  100. hour := d / Hour
  101. nsec := d % Hour
  102. return f64(hour) + f64(nsec)/(60*60*1e9)
  103. }
  104. duration_round :: proc "contextless" (d, m: Duration) -> Duration {
  105. _less_than_half :: #force_inline proc "contextless" (x, y: Duration) -> bool {
  106. return u64(x)+u64(x) < u64(y)
  107. }
  108. if m <= 0 {
  109. return d
  110. }
  111. r := d % m
  112. if d < 0 {
  113. r = -r
  114. if _less_than_half(r, m) {
  115. return d + r
  116. }
  117. if d1 := d-m+r; d1 < d {
  118. return d1
  119. }
  120. return MIN_DURATION
  121. }
  122. if _less_than_half(r, m) {
  123. return d - r
  124. }
  125. if d1 := d+m-r; d1 > d {
  126. return d1
  127. }
  128. return MAX_DURATION
  129. }
  130. duration_truncate :: proc "contextless" (d, m: Duration) -> Duration {
  131. return d if m <= 0 else d - d%m
  132. }
  133. date :: proc "contextless" (t: Time) -> (year: int, month: Month, day: int) {
  134. year, month, day, _ = _abs_date(_time_abs(t), true)
  135. return
  136. }
  137. year :: proc "contextless" (t: Time) -> (year: int) {
  138. year, _, _, _ = _date(t, true)
  139. return
  140. }
  141. month :: proc "contextless" (t: Time) -> (month: Month) {
  142. _, month, _, _ = _date(t, true)
  143. return
  144. }
  145. day :: proc "contextless" (t: Time) -> (day: int) {
  146. _, _, day, _ = _date(t, true)
  147. return
  148. }
  149. weekday :: proc "contextless" (t: Time) -> (weekday: Weekday) {
  150. abs := _time_abs(t)
  151. sec := (abs + u64(Weekday.Monday) * SECONDS_PER_DAY) % SECONDS_PER_WEEK
  152. return Weekday(int(sec) / SECONDS_PER_DAY)
  153. }
  154. clock :: proc { clock_from_time, clock_from_duration, clock_from_stopwatch }
  155. clock_from_time :: proc "contextless" (t: Time) -> (hour, min, sec: int) {
  156. return clock_from_seconds(_time_abs(t))
  157. }
  158. clock_from_duration :: proc "contextless" (d: Duration) -> (hour, min, sec: int) {
  159. return clock_from_seconds(u64(d/1e9))
  160. }
  161. clock_from_stopwatch :: proc "contextless" (s: Stopwatch) -> (hour, min, sec: int) {
  162. return clock_from_duration(stopwatch_duration(s))
  163. }
  164. clock_from_seconds :: proc "contextless" (nsec: u64) -> (hour, min, sec: int) {
  165. sec = int(nsec % SECONDS_PER_DAY)
  166. hour = sec / SECONDS_PER_HOUR
  167. sec -= hour * SECONDS_PER_HOUR
  168. min = sec / SECONDS_PER_MINUTE
  169. sec -= min * SECONDS_PER_MINUTE
  170. return
  171. }
  172. read_cycle_counter :: proc "contextless" () -> u64 {
  173. return u64(intrinsics.read_cycle_counter())
  174. }
  175. unix :: proc "contextless" (sec: i64, nsec: i64) -> Time {
  176. sec, nsec := sec, nsec
  177. if nsec < 0 || nsec >= 1e9 {
  178. n := nsec / 1e9
  179. sec += n
  180. nsec -= n * 1e9
  181. if nsec < 0 {
  182. nsec += 1e9
  183. sec -= 1
  184. }
  185. }
  186. return Time{(sec*1e9 + nsec)}
  187. }
  188. to_unix_seconds :: time_to_unix
  189. time_to_unix :: proc "contextless" (t: Time) -> i64 {
  190. return t._nsec/1e9
  191. }
  192. to_unix_nanoseconds :: time_to_unix_nano
  193. time_to_unix_nano :: proc "contextless" (t: Time) -> i64 {
  194. return t._nsec
  195. }
  196. time_add :: proc "contextless" (t: Time, d: Duration) -> Time {
  197. return Time{t._nsec + i64(d)}
  198. }
  199. // Accurate sleep borrowed from: https://blat-blatnik.github.io/computerBear/making-accurate-sleep-function/
  200. //
  201. // Accuracy seems to be pretty good out of the box on Linux, to within around 4µs worst case.
  202. // On Windows it depends but is comparable with regular sleep in the worst case.
  203. // To get the same kind of accuracy as on Linux, have your program call `windows.timeBeginPeriod(1)` to
  204. // tell Windows to use a more accurate timer for your process.
  205. // Additionally your program should call `windows.timeEndPeriod(1)` once you're done with `accurate_sleep`.
  206. accurate_sleep :: proc "contextless" (d: Duration) {
  207. to_sleep, estimate, mean, m2, count: Duration
  208. to_sleep = d
  209. estimate = 5 * Millisecond
  210. mean = 5 * Millisecond
  211. count = 1
  212. for to_sleep > estimate {
  213. start := tick_now()
  214. sleep(1 * Millisecond)
  215. observed := tick_since(start)
  216. to_sleep -= observed
  217. count += 1
  218. delta := observed - mean
  219. mean += delta / count
  220. m2 += delta * (observed - mean)
  221. stddev := intrinsics.sqrt(f64(m2) / f64(count - 1))
  222. estimate = mean + Duration(stddev)
  223. }
  224. start := tick_now()
  225. for to_sleep > tick_since(start) {
  226. // prevent the spinlock from taking the thread hostage, still accurate enough
  227. _yield()
  228. // NOTE: it might be possible that it yields for too long, in that case it should spinlock freely for a while
  229. // TODO: needs actual testing done to check if that's the case
  230. }
  231. }
  232. ABSOLUTE_ZERO_YEAR :: i64(-292277022399) // Day is chosen so that 2001-01-01 is Monday in the calculations
  233. ABSOLUTE_TO_INTERNAL :: i64(-9223371966579724800) // i64((ABSOLUTE_ZERO_YEAR - 1) * 365.2425 * SECONDS_PER_DAY);
  234. INTERNAL_TO_ABSOLUTE :: -ABSOLUTE_TO_INTERNAL
  235. UNIX_TO_INTERNAL :: i64((1969*365 + 1969/4 - 1969/100 + 1969/400) * SECONDS_PER_DAY)
  236. INTERNAL_TO_UNIX :: -UNIX_TO_INTERNAL
  237. WALL_TO_INTERNAL :: i64((1884*365 + 1884/4 - 1884/100 + 1884/400) * SECONDS_PER_DAY)
  238. INTERNAL_TO_WALL :: -WALL_TO_INTERNAL
  239. UNIX_TO_ABSOLUTE :: UNIX_TO_INTERNAL + INTERNAL_TO_ABSOLUTE
  240. ABSOLUTE_TO_UNIX :: -UNIX_TO_ABSOLUTE
  241. @(private)
  242. _date :: proc "contextless" (t: Time, full: bool) -> (year: int, month: Month, day: int, yday: int) {
  243. year, month, day, yday = _abs_date(_time_abs(t), full)
  244. return
  245. }
  246. @(private)
  247. _time_abs :: proc "contextless" (t: Time) -> u64 {
  248. return u64(t._nsec/1e9 + UNIX_TO_ABSOLUTE)
  249. }
  250. @(private)
  251. _abs_date :: proc "contextless" (abs: u64, full: bool) -> (year: int, month: Month, day: int, yday: int) {
  252. d := abs / SECONDS_PER_DAY
  253. // 400 year cycles
  254. n := d / DAYS_PER_400_YEARS
  255. y := 400 * n
  256. d -= DAYS_PER_400_YEARS * n
  257. // Cut-off 100 year cycles
  258. n = d / DAYS_PER_100_YEARS
  259. n -= n >> 2
  260. y += 100 * n
  261. d -= DAYS_PER_100_YEARS * n
  262. // Cut-off 4 year cycles
  263. n = d / DAYS_PER_4_YEARS
  264. y += 4 * n
  265. d -= DAYS_PER_4_YEARS * n
  266. n = d / 365
  267. n -= n >> 2
  268. y += n
  269. d -= 365 * n
  270. year = int(i64(y) + ABSOLUTE_ZERO_YEAR)
  271. yday = int(d)
  272. if !full {
  273. return
  274. }
  275. day = yday
  276. if is_leap_year(year) {
  277. switch {
  278. case day > 31+29-1:
  279. day -= 1
  280. case day == 31+29-1:
  281. month = .February
  282. day = 29
  283. return
  284. }
  285. }
  286. month = Month(day / 31)
  287. end := int(days_before[int(month)+1])
  288. begin: int
  289. if day >= end {
  290. (^int)(&month)^ += 1
  291. begin = end
  292. } else {
  293. begin = int(days_before[month])
  294. }
  295. (^int)(&month)^ += 1 // January is 1
  296. day = day - begin + 1
  297. return
  298. }
  299. components_to_time :: proc "contextless" (#any_int year, #any_int month, #any_int day, #any_int hour, #any_int minute, #any_int second: i64, #any_int nsec := i64(0)) -> (t: Time, ok: bool) {
  300. this_date, err := dt.components_to_datetime(year, month, day, hour, minute, second, nsec)
  301. if err != .None {
  302. return
  303. }
  304. return compound_to_time(this_date)
  305. }
  306. compound_to_time :: proc "contextless" (datetime: dt.DateTime) -> (t: Time, ok: bool) {
  307. unix_epoch := dt.DateTime{{1970, 1, 1}, {0, 0, 0, 0}}
  308. delta, err := dt.sub(datetime, unix_epoch)
  309. ok = err == .None
  310. seconds := delta.days * 86_400 + delta.seconds
  311. nanoseconds := i128(seconds) * 1e9 + i128(delta.nanos)
  312. // Can this moment be represented in i64 worth of nanoseconds?
  313. // min(Time): 1677-09-21 00:12:44.145224192 +0000 UTC
  314. // max(Time): 2262-04-11 23:47:16.854775807 +0000 UTC
  315. if nanoseconds < i128(min(i64)) || nanoseconds > i128(max(i64)) {
  316. return {}, false
  317. }
  318. return Time{_nsec=i64(nanoseconds)}, true
  319. }
  320. datetime_to_time :: proc{components_to_time, compound_to_time}
  321. is_leap_year :: proc "contextless" (year: int) -> (leap: bool) {
  322. return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
  323. }
  324. days_before := [?]i32{
  325. 0,
  326. 31,
  327. 31 + 28,
  328. 31 + 28 + 31,
  329. 31 + 28 + 31 + 30,
  330. 31 + 28 + 31 + 30 + 31,
  331. 31 + 28 + 31 + 30 + 31 + 30,
  332. 31 + 28 + 31 + 30 + 31 + 30 + 31,
  333. 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
  334. 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
  335. 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
  336. 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
  337. 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
  338. }
  339. SECONDS_PER_MINUTE :: 60
  340. SECONDS_PER_HOUR :: 60 * SECONDS_PER_MINUTE
  341. SECONDS_PER_DAY :: 24 * SECONDS_PER_HOUR
  342. SECONDS_PER_WEEK :: 7 * SECONDS_PER_DAY
  343. DAYS_PER_400_YEARS :: 365*400 + 97
  344. DAYS_PER_100_YEARS :: 365*100 + 24
  345. DAYS_PER_4_YEARS :: 365*4 + 1