time.odin 9.8 KB

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