v / vlib / time / format.v
769 lines · 692 sloc · 22.82 KB · 8e35f4d9848f7ad35d857a187dddbfd2eca5e19d
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module time
5
6import strings
7
8fn iclamp(x int, a int, b int) int {
9 if x < a {
10 return a
11 }
12 if x > b {
13 return b
14 }
15 return x
16}
17
18// int_to_byte_array_no_pad fulfill buffer by part
19// it doesn't pad with leading zeros for performance reasons
20@[direct_array_access]
21fn int_to_byte_array_no_pad(value int, mut arr []u8, size int) {
22 mut num := value
23 if size <= 0 || num < 0 {
24 return
25 }
26
27 // Start from the end of the array
28 mut i := size - 1
29
30 // Convert each digit to a character and store it in the array
31 for num > 0 && i >= 0 {
32 arr[i] = (num % 10) + `0`
33 num /= 10
34 i--
35 }
36}
37
38// int_to_byte_array_no_pad fulfill buffer by part
39// it doesn't pad with leading zeros for performance reasons
40@[direct_array_access; unsafe]
41fn int_to_ptr_byte_array_no_pad(value int, arr_prt &u8, arr_len int) {
42 mut num := value
43 if arr_len <= 0 || num < 0 {
44 return
45 }
46
47 // Start from the end of the array
48 mut i := arr_len - 1
49
50 // Convert each digit to a character and store it in the array
51 for num > 0 && i >= 0 {
52 unsafe {
53 *(arr_prt + i) = (num % 10) + `0`
54 }
55 num /= 10
56 i--
57 }
58}
59
60// format returns a date string in "YYYY-MM-DD HH:mm" format (24h).
61@[manualfree]
62pub fn (t Time) format() string {
63 mut buf := [u8(`0`), `0`, `0`, `0`, `-`, `0`, `0`, `-`, `0`, `0`, ` `, `0`, `0`, `:`, `0`,
64 `0`]
65
66 defer {
67 unsafe { buf.free() }
68 }
69
70 int_to_byte_array_no_pad(t.year, mut buf, 4)
71 int_to_byte_array_no_pad(t.month, mut buf, 7)
72 int_to_byte_array_no_pad(t.day, mut buf, 10)
73
74 int_to_byte_array_no_pad(t.hour, mut buf, 13)
75 int_to_byte_array_no_pad(t.minute, mut buf, 16)
76
77 return buf.bytestr()
78}
79
80// format_ss returns a date string in "YYYY-MM-DD HH:mm:ss" format (24h).
81@[manualfree]
82pub fn (t Time) format_ss() string {
83 mut buf := [u8(`0`), `0`, `0`, `0`, `-`, `0`, `0`, `-`, `0`, `0`, ` `, `0`, `0`, `:`, `0`,
84 `0`, `:`, `0`, `0`]
85
86 defer {
87 unsafe { buf.free() }
88 }
89
90 int_to_byte_array_no_pad(t.year, mut buf, 4)
91 int_to_byte_array_no_pad(t.month, mut buf, 7)
92 int_to_byte_array_no_pad(t.day, mut buf, 10)
93
94 int_to_byte_array_no_pad(t.hour, mut buf, 13)
95 int_to_byte_array_no_pad(t.minute, mut buf, 16)
96 int_to_byte_array_no_pad(t.second, mut buf, 19)
97
98 return buf.bytestr()
99}
100
101// format_ss_milli returns a date string in "YYYY-MM-DD HH:mm:ss.123" format (24h).
102@[manualfree]
103pub fn (t Time) format_ss_milli() string {
104 mut buf := [u8(`0`), `0`, `0`, `0`, `-`, `0`, `0`, `-`, `0`, `0`, ` `, `0`, `0`, `:`, `0`,
105 `0`, `:`, `0`, `0`, `.`, `0`, `0`, `0`]
106
107 defer {
108 unsafe { buf.free() }
109 }
110
111 int_to_byte_array_no_pad(t.year, mut buf, 4)
112 int_to_byte_array_no_pad(t.month, mut buf, 7)
113 int_to_byte_array_no_pad(t.day, mut buf, 10)
114
115 int_to_byte_array_no_pad(t.hour, mut buf, 13)
116 int_to_byte_array_no_pad(t.minute, mut buf, 16)
117 int_to_byte_array_no_pad(t.second, mut buf, 19)
118
119 // Extract and format milliseconds
120 millis := t.nanosecond / 1_000_000
121 int_to_byte_array_no_pad(millis, mut buf, 23)
122
123 return buf.bytestr()
124}
125
126// format_ss_micro returns a date string in "YYYY-MM-DD HH:mm:ss.123456" format (24h).
127@[manualfree]
128pub fn (t Time) format_ss_micro() string {
129 mut buf := [u8(`0`), `0`, `0`, `0`, `-`, `0`, `0`, `-`, `0`, `0`, ` `, `0`, `0`, `:`, `0`,
130 `0`, `:`, `0`, `0`, `.`, `0`, `0`, `0`, `0`, `0`, `0`]
131
132 defer {
133 unsafe { buf.free() }
134 }
135
136 int_to_byte_array_no_pad(t.year, mut buf, 4)
137 int_to_byte_array_no_pad(t.month, mut buf, 7)
138 int_to_byte_array_no_pad(t.day, mut buf, 10)
139
140 int_to_byte_array_no_pad(t.hour, mut buf, 13)
141 int_to_byte_array_no_pad(t.minute, mut buf, 16)
142 int_to_byte_array_no_pad(t.second, mut buf, 19)
143
144 // Extract and format microseconds
145 micros := t.nanosecond / 1_000
146 int_to_byte_array_no_pad(micros, mut buf, 26)
147
148 return buf.bytestr()
149}
150
151// format_ss_nano returns a date string in "YYYY-MM-DD HH:mm:ss.123456789" format (24h).
152@[manualfree]
153pub fn (t Time) format_ss_nano() string {
154 mut buf := [u8(`0`), `0`, `0`, `0`, `-`, `0`, `0`, `-`, `0`, `0`, ` `, `0`, `0`, `:`, `0`,
155 `0`, `:`, `0`, `0`, `.`, `0`, `0`, `0`, `0`, `0`, `0`, `0`, `0`, `0`]
156
157 defer {
158 unsafe { buf.free() }
159 }
160
161 int_to_byte_array_no_pad(t.year, mut buf, 4)
162 int_to_byte_array_no_pad(t.month, mut buf, 7)
163 int_to_byte_array_no_pad(t.day, mut buf, 10)
164
165 int_to_byte_array_no_pad(t.hour, mut buf, 13)
166 int_to_byte_array_no_pad(t.minute, mut buf, 16)
167 int_to_byte_array_no_pad(t.second, mut buf, 19)
168
169 int_to_byte_array_no_pad(t.nanosecond, mut buf, 29) // Adjusted index for 9 digits
170
171 return buf.bytestr()
172}
173
174// 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)
175// RFC3339 is an Internet profile, based on the ISO 8601 standard for for representation of dates and times using the Gregorian calendar.
176// It is intended to improve consistency and interoperability, when representing and using date and time in Internet protocols.
177@[manualfree]
178pub fn (t Time) format_rfc3339() string {
179 mut buf := [u8(`0`), `0`, `0`, `0`, `-`, `0`, `0`, `-`, `0`, `0`, `T`, `0`, `0`, `:`, `0`,
180 `0`, `:`, `0`, `0`, `.`, `0`, `0`, `0`, `Z`]
181
182 defer {
183 unsafe { buf.free() }
184 }
185
186 t_ := time_with_unix(t)
187 if t_.is_local {
188 utc_time := t_.local_to_utc()
189 int_to_byte_array_no_pad(utc_time.year, mut buf, 4)
190 int_to_byte_array_no_pad(utc_time.month, mut buf, 7)
191 int_to_byte_array_no_pad(utc_time.day, mut buf, 10)
192 int_to_byte_array_no_pad(utc_time.hour, mut buf, 13)
193 int_to_byte_array_no_pad(utc_time.minute, mut buf, 16)
194 int_to_byte_array_no_pad(utc_time.second, mut buf, 19)
195 int_to_byte_array_no_pad(utc_time.nanosecond / 1_000_000, mut buf, 23)
196 } else {
197 int_to_byte_array_no_pad(t_.year, mut buf, 4)
198 int_to_byte_array_no_pad(t_.month, mut buf, 7)
199 int_to_byte_array_no_pad(t_.day, mut buf, 10)
200 int_to_byte_array_no_pad(t_.hour, mut buf, 13)
201 int_to_byte_array_no_pad(t_.minute, mut buf, 16)
202 int_to_byte_array_no_pad(t_.second, mut buf, 19)
203 int_to_byte_array_no_pad(t_.nanosecond / 1_000_000, mut buf, 23)
204 }
205
206 return buf.bytestr()
207}
208
209// format_rfc3339_micro returns a date string in "YYYY-MM-DDTHH:mm:ss.123456Z" format (24 hours, see https://www.rfc-editor.org/rfc/rfc3339.html)
210@[manualfree]
211pub fn (t Time) format_rfc3339_micro() string {
212 mut buf := [u8(`0`), `0`, `0`, `0`, `-`, `0`, `0`, `-`, `0`, `0`, `T`, `0`, `0`, `:`, `0`,
213 `0`, `:`, `0`, `0`, `.`, `0`, `0`, `0`, `0`, `0`, `0`, `Z`]
214
215 defer {
216 unsafe { buf.free() }
217 }
218
219 t_ := time_with_unix(t)
220 if t_.is_local {
221 utc_time := t_.local_to_utc()
222 int_to_byte_array_no_pad(utc_time.year, mut buf, 4)
223 int_to_byte_array_no_pad(utc_time.month, mut buf, 7)
224 int_to_byte_array_no_pad(utc_time.day, mut buf, 10)
225 int_to_byte_array_no_pad(utc_time.hour, mut buf, 13)
226 int_to_byte_array_no_pad(utc_time.minute, mut buf, 16)
227 int_to_byte_array_no_pad(utc_time.second, mut buf, 19)
228 int_to_byte_array_no_pad(utc_time.nanosecond / 1000, mut buf, 26)
229 } else {
230 int_to_byte_array_no_pad(t_.year, mut buf, 4)
231 int_to_byte_array_no_pad(t_.month, mut buf, 7)
232 int_to_byte_array_no_pad(t_.day, mut buf, 10)
233 int_to_byte_array_no_pad(t_.hour, mut buf, 13)
234 int_to_byte_array_no_pad(t_.minute, mut buf, 16)
235 int_to_byte_array_no_pad(t_.second, mut buf, 19)
236 int_to_byte_array_no_pad(t_.nanosecond / 1000, mut buf, 26)
237 }
238
239 return buf.bytestr()
240}
241
242// 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)
243@[manualfree]
244pub fn (t Time) format_rfc3339_nano() string {
245 mut buf := [u8(`0`), `0`, `0`, `0`, `-`, `0`, `0`, `-`, `0`, `0`, `T`, `0`, `0`, `:`, `0`,
246 `0`, `:`, `0`, `0`, `.`, `0`, `0`, `0`, `0`, `0`, `0`, `0`, `0`, `0`, `Z`]
247
248 defer {
249 unsafe { buf.free() }
250 }
251
252 t_ := time_with_unix(t)
253 if t_.is_local {
254 utc_time := t_.local_to_utc()
255 int_to_byte_array_no_pad(utc_time.year, mut buf, 4)
256 int_to_byte_array_no_pad(utc_time.month, mut buf, 7)
257 int_to_byte_array_no_pad(utc_time.day, mut buf, 10)
258 int_to_byte_array_no_pad(utc_time.hour, mut buf, 13)
259 int_to_byte_array_no_pad(utc_time.minute, mut buf, 16)
260 int_to_byte_array_no_pad(utc_time.second, mut buf, 19)
261 int_to_byte_array_no_pad(utc_time.nanosecond, mut buf, 29)
262 } else {
263 int_to_byte_array_no_pad(t_.year, mut buf, 4)
264 int_to_byte_array_no_pad(t_.month, mut buf, 7)
265 int_to_byte_array_no_pad(t_.day, mut buf, 10)
266 int_to_byte_array_no_pad(t_.hour, mut buf, 13)
267 int_to_byte_array_no_pad(t_.minute, mut buf, 16)
268 int_to_byte_array_no_pad(t_.second, mut buf, 19)
269 int_to_byte_array_no_pad(t_.nanosecond, mut buf, 29)
270 }
271
272 return buf.bytestr()
273}
274
275// hhmm returns a date string in "HH:mm" format (24h).
276@[manualfree]
277pub fn (t Time) hhmm() string {
278 mut buf := [u8(`0`), `0`, `:`, `0`, `0`]
279
280 defer {
281 unsafe { buf.free() }
282 }
283
284 int_to_byte_array_no_pad(t.hour, mut buf, 2)
285 int_to_byte_array_no_pad(t.minute, mut buf, 5)
286
287 return buf.bytestr()
288}
289
290// hhmmss returns a date string in "HH:mm:ss" format (24h).
291@[manualfree]
292pub fn (t Time) hhmmss() string {
293 mut buf := [u8(`0`), `0`, `:`, `0`, `0`, `:`, `0`, `0`]
294
295 defer {
296 unsafe { buf.free() }
297 }
298
299 int_to_byte_array_no_pad(t.hour, mut buf, 2)
300 int_to_byte_array_no_pad(t.minute, mut buf, 5)
301 int_to_byte_array_no_pad(t.second, mut buf, 8)
302
303 return buf.bytestr()
304}
305
306// hhmm12 returns a date string in "hh:mm" format (12h).
307pub fn (t Time) hhmm12() string {
308 return t.get_fmt_time_str(.hhmm12)
309}
310
311// ymmdd returns a date string in "YYYY-MM-DD" format.
312pub fn (t Time) ymmdd() string {
313 return t.get_fmt_date_str(.hyphen, .yyyymmdd)
314}
315
316// ddmmy returns a date string in "DD.MM.YYYY" format.
317pub fn (t Time) ddmmy() string {
318 return t.get_fmt_date_str(.dot, .ddmmyyyy)
319}
320
321// md returns a date string in "MMM D" format.
322pub fn (t Time) md() string {
323 return t.get_fmt_date_str(.space, .mmmd)
324}
325
326// TODO: test, improve performance
327// appends ordinal suffix to a number
328fn ordinal_suffix(n int) string {
329 if n > 3 && n < 21 {
330 return '${n}th'
331 }
332 match n % 10 {
333 1 {
334 return '${n}st'
335 }
336 2 {
337 return '${n}nd'
338 }
339 3 {
340 return '${n}rd'
341 }
342 else {
343 return '${n}th'
344 }
345 }
346}
347
348const tokens_2 = ['MM', 'Mo', 'DD', 'Do', 'YY', 'ss', 'kk', 'NN', 'mm', 'hh', 'HH', 'ii', 'ZZ',
349 'dd', 'Qo', 'QQ', 'wo', 'ww']
350const tokens_3 = ['MMM', 'DDD', 'ZZZ', 'ddd']
351const tokens_4 = ['MMMM', 'DDDD', 'DDDo', 'dddd', 'YYYY']
352
353// custom_format returns a date with custom format
354//
355// | Category | Token | Output |
356// |:-----------------|:------|:---------------------------------------|
357// | Era | N | BC AD |
358// | | NN | Before Christ, Anno Domini |
359// | Year | YY | 70 71 ... 29 30 |
360// | | YYYY | 1970 1971 ... 2029 2030 |
361// | Quarter | Q | 1 2 3 4 |
362// | | QQ | 01 02 03 04 |
363// | | Qo | 1st 2nd 3rd 4th |
364// | Month | M | 1 2 ... 11 12 |
365// | | Mo | 1st 2nd ... 11th 12th |
366// | | MM | 01 02 ... 11 12 |
367// | | MMM | Jan Feb ... Nov Dec |
368// | | MMMM | January February ... November December |
369// | Week of Year | w | 1 2 ... 52 53 |
370// | | wo | 1st 2nd ... 52nd 53rd |
371// | | ww | 01 02 ... 52 53 |
372// | Day of Month | D | 1 2 ... 30 31 |
373// | | Do | 1st 2nd ... 30th 31st |
374// | | DD | 01 02 ... 30 31 |
375// | Day of Year | DDD | 1 2 ... 364 365 |
376// | | DDDo | 1st 2nd ... 364th 365th |
377// | | DDDD | 001 002 ... 364 365 |
378// | Day of Week | d | 0 1 ... 5 6 (Sun-Sat) |
379// | | c | 1 2 ... 6 7 (Mon-Sun) |
380// | | dd | Su Mo ... Fr Sa |
381// | | ddd | Sun Mon ... Fri Sat |
382// | | dddd | Sunday Monday ... Friday Saturday |
383// | AM/PM | A | AM PM |
384// | | a | am pm |
385// | Hour | H | 0 1 ... 22 23 |
386// | | HH | 00 01 ... 22 23 |
387// | | h | 1 2 ... 11 12 |
388// | | hh | 01 02 ... 11 12 |
389// | | i | 0 1 ... 11 12 1 ... 11 |
390// | | ii | 00 01 ... 11 12 01 ... 11 |
391// | | k | 1 2 ... 23 24 |
392// | | kk | 01 02 ... 23 24 |
393// | Minute | m | 0 1 ... 58 59 |
394// | | mm | 00 01 ... 58 59 |
395// | Second | s | 0 1 ... 58 59 |
396// | | ss | 00 01 ... 58 59 |
397// | Offset | Z | -7 -6 ... +5 +6 |
398// | | ZZ | -0700 -0600 ... +0500 +0600 |
399// | | ZZZ | -07:00 -06:00 ... +05:00 +06:00 |
400//
401// Usage:
402// ```v
403// println(time.now().custom_format('MMMM Mo YY N kk:mm:ss A')) // output like: January 1st 22 AD 13:45:33 PM
404// ```
405pub fn (t Time) custom_format(s string) string {
406 mut tokens := []string{}
407 for i := 0; i < s.len; {
408 for j := 4; j > 0; j-- {
409 if i > s.len - j {
410 continue
411 }
412 if j == 1 || (j == 2 && s[i..i + j] in tokens_2)
413 || (j == 3 && s[i..i + j] in tokens_3)
414 || (j == 4 && s[i..i + j] in tokens_4) {
415 tokens << s[i..i + j]
416 i += (j - 1)
417 break
418 }
419 }
420 i++
421 }
422 mut sb := strings.new_builder(128)
423
424 for token in tokens {
425 match token {
426 'M' {
427 sb.write_string(t.month.str())
428 }
429 'MM' {
430 sb.write_string('${t.month:02}')
431 }
432 'Mo' {
433 sb.write_string(ordinal_suffix(t.month))
434 }
435 'MMM' {
436 sb.write_string(long_months[iclamp(0, t.month - 1, 11)][0..3])
437 }
438 'MMMM' {
439 sb.write_string(long_months[iclamp(0, t.month - 1, 11)])
440 }
441 'D' {
442 sb.write_string(t.day.str())
443 }
444 'DD' {
445 sb.write_string('${t.day:02}')
446 }
447 'Do' {
448 sb.write_string(ordinal_suffix(t.day))
449 }
450 'DDD' {
451 sb.write_string((t.year_day()).str())
452 }
453 'DDDD' {
454 sb.write_string('${t.year_day():03}')
455 }
456 'DDDo' {
457 sb.write_string(ordinal_suffix(t.year_day()))
458 }
459 'd' {
460 sb.write_string('${t.day_of_week() % 7}')
461 }
462 'dd' {
463 sb.write_string(long_days[iclamp(0, t.day_of_week() - 1, 6)][0..2])
464 }
465 'ddd' {
466 sb.write_string(long_days[iclamp(0, t.day_of_week() - 1, 6)][0..3])
467 }
468 'dddd' {
469 sb.write_string(long_days[iclamp(0, t.day_of_week() - 1, 6)])
470 }
471 'YY' {
472 sb.write_string(t.year.str()#[2..4])
473 }
474 'YYYY' {
475 sb.write_string(t.year.str())
476 }
477 'H' {
478 sb.write_string(t.hour.str())
479 }
480 'HH' {
481 sb.write_string('${t.hour:02}')
482 }
483 'h' {
484 h := (t.hour + 11) % 12 + 1
485 sb.write_string(h.str())
486 }
487 'hh' {
488 h := (t.hour + 11) % 12 + 1
489 sb.write_string('${h:02}')
490 }
491 'i' {
492 h := if t.hour > 12 { t.hour - 12 } else { t.hour }
493 sb.write_string(h.str())
494 }
495 'ii' {
496 h := if t.hour > 12 { t.hour - 12 } else { t.hour }
497 sb.write_string('${h:02}')
498 }
499 'm' {
500 sb.write_string(t.minute.str())
501 }
502 'mm' {
503 sb.write_string('${t.minute:02}')
504 }
505 's' {
506 sb.write_string(t.second.str())
507 }
508 'ss' {
509 sb.write_string('${t.second:02}')
510 }
511 'k' {
512 h := if t.hour == 0 { 24 } else { t.hour }
513 sb.write_string(h.str())
514 }
515 'kk' {
516 h := if t.hour == 0 { 24 } else { t.hour }
517 sb.write_string('${h:02}')
518 }
519 'w' {
520 sb.write_string('${t.week_of_year():.0}')
521 }
522 'ww' {
523 sb.write_string('${t.week_of_year():02.0}')
524 }
525 'wo' {
526 sb.write_string(ordinal_suffix(t.week_of_year()))
527 }
528 'Q' {
529 sb.write_string('${(t.month - 1) / 3 + 1}')
530 }
531 'QQ' {
532 sb.write_string('${(t.month - 1) / 3 + 1:02}')
533 }
534 'Qo' {
535 sb.write_string(ordinal_suffix((t.month - 1) / 3 + 1))
536 }
537 'c' {
538 sb.write_string('${t.day_of_week()}')
539 }
540 'N' {
541 // TODO: integrate BC
542 sb.write_string('AD')
543 }
544 'NN' {
545 // TODO: integrate Before Christ
546 sb.write_string('Anno Domini')
547 }
548 'Z' {
549 mut hours := offset() / seconds_per_hour
550 if hours >= 0 {
551 sb.write_string('+${hours}')
552 } else {
553 hours = -hours
554 sb.write_string('-${hours}')
555 }
556 }
557 'ZZ' {
558 // TODO: update if minute differs?
559 mut hours := offset() / seconds_per_hour
560 if hours >= 0 {
561 sb.write_string('+${hours:02}00')
562 } else {
563 hours = -hours
564 sb.write_string('-${hours:02}00')
565 }
566 }
567 'ZZZ' {
568 // TODO: update if minute differs?
569 mut hours := offset() / seconds_per_hour
570 if hours >= 0 {
571 sb.write_string('+${hours:02}:00')
572 } else {
573 hours = -hours
574 sb.write_string('-${hours:02}:00')
575 }
576 }
577 'a' {
578 if t.hour < 12 {
579 sb.write_string('am')
580 } else {
581 sb.write_string('pm')
582 }
583 }
584 'A' {
585 if t.hour < 12 {
586 sb.write_string('AM')
587 } else {
588 sb.write_string('PM')
589 }
590 }
591 else {
592 sb.write_string(token)
593 }
594 }
595 }
596 return sb.str()
597}
598
599// clean returns a date string in a clean form.
600// It has the following format:
601// - a date string in "HH:mm" format (24h) for current day
602// - a date string in "MMM D HH:mm" format (24h) for date of current year
603// - a date string formatted with format function for other dates
604@[deprecated: 'use `custom_format()` or `get_fmt_*()` instead']
605@[deprecated_after: '2026-09-30']
606pub fn (t Time) clean() string {
607 znow := now()
608 // Today
609 if t.month == znow.month && t.year == znow.year && t.day == znow.day {
610 return t.get_fmt_time_str(.hhmm24)
611 }
612 // This year
613 if t.year == znow.year {
614 return t.get_fmt_str(.space, .hhmm24, .mmmd)
615 }
616 return t.format()
617}
618
619// clean12 returns a date string in a clean form.
620// It has the following format:
621// - a date string in "hh:mm" format (12h) for current day
622// - a date string in "MMM D hh:mm" format (12h) for date of current year
623// - a date string formatted with format function for other dates
624@[deprecated: 'use `custom_format()` or `get_fmt_*()` instead']
625@[deprecated_after: '2026-09-30']
626pub fn (t Time) clean12() string {
627 znow := now()
628 // Today
629 if t.month == znow.month && t.year == znow.year && t.day == znow.day {
630 return t.get_fmt_time_str(.hhmm12)
631 }
632 // This year
633 if t.year == znow.year {
634 return t.get_fmt_str(.space, .hhmm12, .mmmd)
635 }
636 return t.format()
637}
638
639// get_fmt_time_str returns a date string with specified FormatTime type.
640pub fn (t Time) get_fmt_time_str(fmt_time FormatTime) string {
641 if fmt_time == .no_time {
642 return ''
643 }
644 tp := if t.hour > 11 { 'p.m.' } else { 'a.m.' }
645 hour_ := if t.hour > 12 {
646 t.hour - 12
647 } else if t.hour == 0 {
648 12
649 } else {
650 t.hour
651 }
652 return match fmt_time {
653 .hhmm12 { '${hour_}:${t.minute:02d} ${tp}' }
654 .hhmm24 { '${t.hour:02d}:${t.minute:02d}' }
655 .hhmmss12 { '${hour_}:${t.minute:02d}:${t.second:02d} ${tp}' }
656 .hhmmss24 { '${t.hour:02d}:${t.minute:02d}:${t.second:02d}' }
657 .hhmmss24_milli { '${t.hour:02d}:${t.minute:02d}:${t.second:02d}.${(t.nanosecond / 1_000_000):03d}' }
658 .hhmmss24_micro { '${t.hour:02d}:${t.minute:02d}:${t.second:02d}.${(t.nanosecond / 1_000):06d}' }
659 .hhmmss24_nano { '${t.hour:02d}:${t.minute:02d}:${t.second:02d}.${t.nanosecond:06d}' }
660 else { 'unknown enumeration ${fmt_time}' }
661 }
662}
663
664// get_fmt_time_str returns a date string with specified
665// FormatDelimiter and FormatDate type.
666pub fn (t Time) get_fmt_date_str(fmt_dlmtr FormatDelimiter, fmt_date FormatDate) string {
667 if fmt_date == .no_date {
668 return ''
669 }
670 month := t.smonth()
671 year := '${(t.year % 100):02d}'
672 mut res := match fmt_date {
673 .ddmmyy { '${t.day:02d}|${t.month:02d}|${year}' }
674 .ddmmyyyy { '${t.day:02d}|${t.month:02d}|${t.year:04d}' }
675 .mmddyy { '${t.month:02d}|${t.day:02d}|${year}' }
676 .mmddyyyy { '${t.month:02d}|${t.day:02d}|${t.year:04d}' }
677 .mmmd { '${month}|${t.day}' }
678 .mmmdd { '${month}|${t.day:02d}' }
679 .mmmddyy { '${month}|${t.day:02d}|${year}' }
680 .mmmddyyyy { '${month}|${t.day:02d}|${t.year:04d}' }
681 .yyyymmdd { '${t.year:04d}|${t.month:02d}|${t.day:02d}' }
682 .yymmdd { '${year}|${t.month:02d}|${t.day:02d}' }
683 else { 'unknown enumeration ${fmt_date}' }
684 }
685
686 del := match fmt_dlmtr {
687 .dot { '.' }
688 .hyphen { '-' }
689 .slash { '/' }
690 .space { ' ' }
691 .no_delimiter { '' }
692 }
693
694 res = res.replace('|', del)
695 return res
696}
697
698// get_fmt_str returns a date string with specified FormatDelimiter, FormatTime type, and FormatDate type.
699pub fn (t Time) get_fmt_str(fmt_dlmtr FormatDelimiter, fmt_time FormatTime, fmt_date FormatDate) string {
700 if fmt_date == .no_date {
701 if fmt_time == .no_time {
702 // saving one function call although it's checked in
703 // t.get_fmt_time_str(fmt_time) in the beginning
704 return ''
705 } else {
706 return t.get_fmt_time_str(fmt_time)
707 }
708 } else {
709 if fmt_time != .no_time {
710 dstr := t.get_fmt_date_str(fmt_dlmtr, fmt_date)
711 tstr := t.get_fmt_time_str(fmt_time)
712 return '${dstr} ${tstr}'
713 } else {
714 return t.get_fmt_date_str(fmt_dlmtr, fmt_date)
715 }
716 }
717}
718
719// utc_string returns a date string in the legacy UTC cookie/header format.
720@[deprecated: 'use `http_header_string()` instead']
721@[deprecated_after: '2026-09-30']
722pub fn (t Time) utc_string() string {
723 day_str := t.weekday_str()
724 month_str := t.smonth()
725 utc_string := '${day_str}, ${t.day} ${month_str} ${t.year} ${t.hour:02d}:${t.minute:02d}:${t.second:02d} UTC'
726 return utc_string
727}
728
729// http_header_string returns a date string in the format used in HTTP headers, as defined in RFC 2616.
730// e.g. "Sun, 06 Nov 1994 08:49:37 GMT"
731@[manualfree]
732pub fn (t Time) http_header_string() string {
733 mut buf := []u8{cap: 29}
734 t.push_to_http_header(mut buf)
735
736 return buf.bytestr()
737}
738
739// push_to_http_header returns a date string in the format used in HTTP headers, as defined in RFC 2616.
740// e.g. "Sun, 06 Nov 1994 08:49:37 GMT"
741pub fn (t Time) push_to_http_header(mut buffer []u8) {
742 day_str := t.weekday_str()
743 month_str := t.smonth()
744
745 mut buf := [day_str[0], day_str[1], day_str[2], `,`, ` `, `0`, `0`, ` `, month_str[0], month_str[1],
746 month_str[2], ` `, `0`, `0`, `0`, `0`, ` `, `0`, `0`, `:`, `0`, `0`, `:`, `0`, `0`, ` `,
747 `G`, `M`, `T`]!
748 unsafe {
749 int_to_ptr_byte_array_no_pad(t.day, &buf[5], 2)
750 int_to_ptr_byte_array_no_pad(t.year, &buf[12], 4)
751 int_to_ptr_byte_array_no_pad(t.hour, &buf[17], 2)
752 int_to_ptr_byte_array_no_pad(t.minute, &buf[20], 2)
753 int_to_ptr_byte_array_no_pad(t.second, &buf[23], 2)
754 }
755 unsafe {
756 buffer.push_many(&buf[0], buf.len)
757 }
758}
759
760// mceil returns the least integer value greater than or equal to x.
761fn mceil(x f64) f64 {
762 if x > 0 {
763 return 1 + int(x)
764 }
765 if x < 0 {
766 return -int(-x)
767 }
768 return 0
769}
770