2022-01-04 12:21:08 +03:00
|
|
|
// Copyright (c) 2019-2022 Alexander Medvednikov. All rights reserved.
|
2020-02-06 16:19:44 +03:00
|
|
|
// Use of this source code is governed by an MIT license
|
|
|
|
// that can be found in the LICENSE file.
|
|
|
|
module time
|
|
|
|
|
2022-04-29 15:57:08 +03:00
|
|
|
import strings
|
|
|
|
import math
|
|
|
|
|
2020-02-06 16:19:44 +03:00
|
|
|
// format returns a date string in "YYYY-MM-DD HH:MM" format (24h).
|
|
|
|
pub fn (t Time) format() string {
|
|
|
|
return t.get_fmt_str(.hyphen, .hhmm24, .yyyymmdd)
|
|
|
|
}
|
|
|
|
|
|
|
|
// format_ss returns a date string in "YYYY-MM-DD HH:MM:SS" format (24h).
|
|
|
|
pub fn (t Time) format_ss() string {
|
|
|
|
return t.get_fmt_str(.hyphen, .hhmmss24, .yyyymmdd)
|
|
|
|
}
|
|
|
|
|
2020-07-21 11:58:33 +03:00
|
|
|
// format_ss_milli returns a date string in "YYYY-MM-DD HH:MM:SS.123" format (24h).
|
|
|
|
pub fn (t Time) format_ss_milli() string {
|
|
|
|
return t.get_fmt_str(.hyphen, .hhmmss24_milli, .yyyymmdd)
|
|
|
|
}
|
|
|
|
|
|
|
|
// format_ss_micro returns a date string in "YYYY-MM-DD HH:MM:SS.123456" format (24h).
|
|
|
|
pub fn (t Time) format_ss_micro() string {
|
|
|
|
return t.get_fmt_str(.hyphen, .hhmmss24_micro, .yyyymmdd)
|
|
|
|
}
|
|
|
|
|
2020-02-06 16:19:44 +03:00
|
|
|
// hhmm returns a date string in "HH:MM" format (24h).
|
|
|
|
pub fn (t Time) hhmm() string {
|
|
|
|
return t.get_fmt_time_str(.hhmm24)
|
|
|
|
}
|
|
|
|
|
|
|
|
// hhmmss returns a date string in "HH:MM:SS" format (24h).
|
|
|
|
pub fn (t Time) hhmmss() string {
|
|
|
|
return t.get_fmt_time_str(.hhmmss24)
|
|
|
|
}
|
|
|
|
|
|
|
|
// hhmm12 returns a date string in "HH:MM" format (12h).
|
|
|
|
pub fn (t Time) hhmm12() string {
|
|
|
|
return t.get_fmt_time_str(.hhmm12)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ymmdd returns a date string in "YYYY-MM-DD" format.
|
|
|
|
pub fn (t Time) ymmdd() string {
|
|
|
|
return t.get_fmt_date_str(.hyphen, .yyyymmdd)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ddmmy returns a date string in "DD.MM.YYYY" format.
|
|
|
|
pub fn (t Time) ddmmy() string {
|
|
|
|
return t.get_fmt_date_str(.dot, .ddmmyyyy)
|
|
|
|
}
|
|
|
|
|
|
|
|
// md returns a date string in "MMM D" format.
|
|
|
|
pub fn (t Time) md() string {
|
|
|
|
return t.get_fmt_date_str(.space, .mmmd)
|
|
|
|
}
|
|
|
|
|
2022-04-29 15:57:08 +03:00
|
|
|
// appends ordinal suffix to a number
|
|
|
|
fn ordinal_suffix(n int) string {
|
|
|
|
if n > 3 && n < 21 {
|
|
|
|
return '${n}th'
|
|
|
|
}
|
|
|
|
match n % 10 {
|
|
|
|
1 {
|
|
|
|
return '${n}st'
|
|
|
|
}
|
|
|
|
2 {
|
|
|
|
return '${n}nd'
|
|
|
|
}
|
|
|
|
3 {
|
|
|
|
return '${n}rd'
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return '${n}th'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const tokens_2 = ['MM', 'DD', 'Do', 'YY', 'ss', 'kk', 'NN', 'mm', 'hh', 'HH', 'ZZ', 'dd', 'Qo',
|
|
|
|
'QQ', 'wo', 'ww']
|
|
|
|
|
|
|
|
const tokens_3 = ['MMM', 'DDD', 'ZZZ', 'ddd']
|
|
|
|
|
|
|
|
const tokens_4 = ['MMMM', 'DDDD', 'DDDo', 'dddd', 'YYYY']
|
|
|
|
|
|
|
|
// custom_format returns a date with custom format
|
2022-04-30 12:31:23 +03:00
|
|
|
//
|
2022-04-29 15:57:08 +03:00
|
|
|
// | | Token | Output |
|
|
|
|
// | :----------- | -------: | :--------- |
|
|
|
|
// | Month | M | 1 2 ... 11 12 |
|
|
|
|
// | | Mo | 1st 2nd ... 11th 12th |
|
|
|
|
// | | MM | 01 02 ... 11 12 |
|
|
|
|
// | | MMM | Jan Feb ... Nov Dec |
|
|
|
|
// | | MMMM | January February ... November December |
|
|
|
|
// | Quarter | Q | 1 2 3 4 |
|
|
|
|
// | | QQ | 01 02 03 04 |
|
|
|
|
// | | Qo | 1st 2nd 3rd 4th |
|
|
|
|
// | Day of Month | D | 1 2 ... 30 31 |
|
|
|
|
// | | Do | 1st 2nd ... 30th 31st |
|
|
|
|
// | | DD | 01 02 ... 30 31 |
|
|
|
|
// | Day of Year | DDD | 1 2 ... 364 365 |
|
|
|
|
// | | DDDo | 1st 2nd ... 364th 365th |
|
|
|
|
// | | DDDD | 001 002 ... 364 365 |
|
|
|
|
// | Day of Week | d | 0 1 ... 5 6 (Sun-Sat) |
|
|
|
|
// | | c | 1 2 ... 6 7 (Mon-Sun) |
|
|
|
|
// | | dd | Su Mo ... Fr Sa |
|
|
|
|
// | | ddd | Sun Mon ... Fri Sat |
|
|
|
|
// | | dddd | Sunday Monday ... Friday Saturday |
|
|
|
|
// | Week of Year | w | 1 2 ... 52 53 |
|
|
|
|
// | | wo | 1st 2nd ... 52nd 53rd |
|
|
|
|
// | | ww | 01 02 ... 52 53 |
|
|
|
|
// | Year | YY | 70 71 ... 29 30 |
|
|
|
|
// | | YYYY | 1970 1971 ... 2029 2030 |
|
|
|
|
// | Era | N | BC AD |
|
|
|
|
// | | NN | Before Christ, Anno Domini |
|
|
|
|
// | AM/PM | A | AM PM |
|
|
|
|
// | | a | am pm |
|
|
|
|
// | Hour | H | 0 1 ... 22 23 |
|
|
|
|
// | | HH | 00 01 ... 22 23 |
|
|
|
|
// | | h | 1 2 ... 11 12 |
|
|
|
|
// | | hh | 01 02 ... 11 12 |
|
|
|
|
// | | k | 1 2 ... 23 24 |
|
|
|
|
// | | kk | 01 02 ... 23 24 |
|
|
|
|
// | Minute | m | 0 1 ... 58 59 |
|
|
|
|
// | | mm | 00 01 ... 58 59 |
|
|
|
|
// | Second | s | 0 1 ... 58 59 |
|
|
|
|
// | | ss | 00 01 ... 58 59 |
|
|
|
|
// | Offset | Z | -7 -6 ... +5 +6 |
|
|
|
|
// | | ZZ | -0700 -0600 ... +0500 +0600 |
|
|
|
|
// | | ZZZ | -07:00 -06:00 ... +05:00 +06:00 |
|
|
|
|
pub fn (t Time) custom_format(s string) string {
|
|
|
|
mut tokens := []string{}
|
|
|
|
for i := 0; i < s.len; {
|
|
|
|
for j := 4; j > 0; j-- {
|
|
|
|
if i > s.len - j {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if j == 1 || (j == 2 && s[i..i + j] in time.tokens_2)
|
|
|
|
|| (j == 3 && s[i..i + j] in time.tokens_3)
|
|
|
|
|| (j == 4 && s[i..i + j] in time.tokens_4) {
|
|
|
|
tokens << s[i..i + j]
|
|
|
|
i += (j - 1)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
mut sb := strings.new_builder(128)
|
|
|
|
|
|
|
|
for token in tokens {
|
|
|
|
match token {
|
|
|
|
'M' {
|
|
|
|
sb.write_string(t.month.str())
|
|
|
|
}
|
|
|
|
'MM' {
|
|
|
|
sb.write_string('${t.month:02}')
|
|
|
|
}
|
|
|
|
'Mo' {
|
|
|
|
sb.write_string(ordinal_suffix(t.month))
|
|
|
|
}
|
|
|
|
'MMM' {
|
|
|
|
sb.write_string(long_months[t.month - 1][0..3])
|
|
|
|
}
|
|
|
|
'MMMM' {
|
|
|
|
sb.write_string(long_months[t.month - 1])
|
|
|
|
}
|
|
|
|
'D' {
|
|
|
|
sb.write_string(t.day.str())
|
|
|
|
}
|
|
|
|
'DD' {
|
|
|
|
sb.write_string('${t.day:02}')
|
|
|
|
}
|
|
|
|
'Do' {
|
|
|
|
sb.write_string(ordinal_suffix(t.day))
|
|
|
|
}
|
|
|
|
'DDD' {
|
|
|
|
sb.write_string((t.day + days_before[t.month - 1] + int(is_leap_year(t.year))).str())
|
|
|
|
}
|
|
|
|
'DDDD' {
|
|
|
|
sb.write_string('${t.day + days_before[t.month - 1] + int(is_leap_year(t.year)):03}')
|
|
|
|
}
|
|
|
|
'DDDo' {
|
|
|
|
sb.write_string(ordinal_suffix(t.day + days_before[t.month - 1] +
|
|
|
|
int(is_leap_year(t.year))))
|
|
|
|
}
|
|
|
|
'd' {
|
|
|
|
sb.write_string(t.day_of_week().str())
|
|
|
|
}
|
|
|
|
'dd' {
|
|
|
|
sb.write_string(long_days[t.day_of_week() - 1][0..2])
|
|
|
|
}
|
|
|
|
'ddd' {
|
|
|
|
sb.write_string(long_days[t.day_of_week() - 1][0..3])
|
|
|
|
}
|
|
|
|
'dddd' {
|
|
|
|
sb.write_string(long_days[t.day_of_week() - 1])
|
|
|
|
}
|
|
|
|
'YY' {
|
|
|
|
sb.write_string(t.year.str()[2..4])
|
|
|
|
}
|
|
|
|
'YYYY' {
|
|
|
|
sb.write_string(t.year.str())
|
|
|
|
}
|
|
|
|
'H' {
|
|
|
|
sb.write_string(t.hour.str())
|
|
|
|
}
|
|
|
|
'HH' {
|
|
|
|
sb.write_string('${t.hour:02}')
|
|
|
|
}
|
|
|
|
'h' {
|
|
|
|
sb.write_string((t.hour % 12).str())
|
|
|
|
}
|
|
|
|
'hh' {
|
|
|
|
sb.write_string('${(t.hour % 12):02}')
|
|
|
|
}
|
|
|
|
'm' {
|
|
|
|
sb.write_string(t.minute.str())
|
|
|
|
}
|
|
|
|
'mm' {
|
|
|
|
sb.write_string('${t.minute:02}')
|
|
|
|
}
|
|
|
|
's' {
|
|
|
|
sb.write_string(t.second.str())
|
|
|
|
}
|
|
|
|
'ss' {
|
|
|
|
sb.write_string('${t.second:02}')
|
|
|
|
}
|
|
|
|
'k' {
|
|
|
|
sb.write_string((t.hour + 1).str())
|
|
|
|
}
|
|
|
|
'kk' {
|
|
|
|
sb.write_string('${(t.hour + 1):02}')
|
|
|
|
}
|
|
|
|
'w' {
|
|
|
|
sb.write_string('${math.ceil((t.day + days_before[t.month - 1] +
|
|
|
|
int(is_leap_year(t.year))) / 7):.0}')
|
|
|
|
}
|
|
|
|
'ww' {
|
|
|
|
sb.write_string('${math.ceil((t.day + days_before[t.month - 1] +
|
|
|
|
int(is_leap_year(t.year))) / 7):02.0}')
|
|
|
|
}
|
|
|
|
'wo' {
|
|
|
|
sb.write_string(ordinal_suffix(int(math.ceil((t.day + days_before[t.month - 1] +
|
|
|
|
int(is_leap_year(t.year))) / 7))))
|
|
|
|
}
|
|
|
|
'Q' {
|
|
|
|
sb.write_string('${(t.month % 4) + 1}')
|
|
|
|
}
|
|
|
|
'QQ' {
|
|
|
|
sb.write_string('${(t.month % 4) + 1:02}')
|
|
|
|
}
|
|
|
|
'Qo' {
|
|
|
|
sb.write_string(ordinal_suffix((t.month % 4) + 1))
|
|
|
|
}
|
|
|
|
'c' {
|
|
|
|
sb.write_string('${t.day_of_week() + 1}')
|
|
|
|
}
|
|
|
|
'N' {
|
|
|
|
// TODO integrate BC
|
|
|
|
sb.write_string('AD')
|
|
|
|
}
|
|
|
|
'NN' {
|
|
|
|
// TODO integrate Before Christ
|
|
|
|
sb.write_string('Anno Domini')
|
|
|
|
}
|
|
|
|
'Z' {
|
|
|
|
mut hours := offset() / seconds_per_hour
|
|
|
|
if hours >= 0 {
|
|
|
|
sb.write_string('+$hours')
|
|
|
|
} else {
|
|
|
|
hours = -hours
|
|
|
|
sb.write_string('-$hours')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
'ZZ' {
|
|
|
|
// TODO update if minute differs?
|
|
|
|
mut hours := offset() / seconds_per_hour
|
|
|
|
if hours >= 0 {
|
|
|
|
sb.write_string('+${hours:02}00')
|
|
|
|
} else {
|
|
|
|
hours = -hours
|
|
|
|
sb.write_string('-${hours:02}00')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
'ZZZ' {
|
|
|
|
// TODO update if minute differs?
|
|
|
|
mut hours := offset() / seconds_per_hour
|
|
|
|
if hours >= 0 {
|
|
|
|
sb.write_string('+${hours:02}:00')
|
|
|
|
} else {
|
|
|
|
hours = -hours
|
|
|
|
sb.write_string('-${hours:02}:00')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
'a' {
|
|
|
|
if t.hour > 12 {
|
|
|
|
sb.write_string('pm')
|
|
|
|
} else {
|
|
|
|
sb.write_string('am')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
'A' {
|
|
|
|
if t.hour > 12 {
|
|
|
|
sb.write_string('PM')
|
|
|
|
} else {
|
|
|
|
sb.write_string('AM')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
sb.write_string(token)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return sb.str()
|
|
|
|
}
|
|
|
|
|
2020-02-06 16:19:44 +03:00
|
|
|
// clean returns a date string in a following format:
|
2020-09-10 14:26:01 +03:00
|
|
|
// - a date string in "HH:MM" format (24h) for current day
|
|
|
|
// - a date string in "MMM D HH:MM" format (24h) for date of current year
|
|
|
|
// - a date string formatted with format function for other dates
|
2020-02-06 16:19:44 +03:00
|
|
|
pub fn (t Time) clean() string {
|
2020-09-10 14:26:01 +03:00
|
|
|
znow := now()
|
2020-02-06 16:19:44 +03:00
|
|
|
// Today
|
2020-07-01 01:53:53 +03:00
|
|
|
if t.month == znow.month && t.year == znow.year && t.day == znow.day {
|
2020-02-06 16:19:44 +03:00
|
|
|
return t.get_fmt_time_str(.hhmm24)
|
|
|
|
}
|
|
|
|
// This year
|
2020-07-01 01:53:53 +03:00
|
|
|
if t.year == znow.year {
|
2020-02-06 16:19:44 +03:00
|
|
|
return t.get_fmt_str(.space, .hhmm24, .mmmd)
|
|
|
|
}
|
|
|
|
return t.format()
|
|
|
|
}
|
|
|
|
|
|
|
|
// clean12 returns a date string in a following format:
|
2020-09-10 14:26:01 +03:00
|
|
|
// - a date string in "HH:MM" format (12h) for current day
|
|
|
|
// - a date string in "MMM D HH:MM" format (12h) for date of current year
|
|
|
|
// - a date string formatted with format function for other dates
|
2020-02-06 16:19:44 +03:00
|
|
|
pub fn (t Time) clean12() string {
|
2020-09-10 14:26:01 +03:00
|
|
|
znow := now()
|
2020-02-06 16:19:44 +03:00
|
|
|
// Today
|
2020-07-01 01:53:53 +03:00
|
|
|
if t.month == znow.month && t.year == znow.year && t.day == znow.day {
|
2020-02-06 16:19:44 +03:00
|
|
|
return t.get_fmt_time_str(.hhmm12)
|
|
|
|
}
|
|
|
|
// This year
|
2020-07-01 01:53:53 +03:00
|
|
|
if t.year == znow.year {
|
2020-02-06 16:19:44 +03:00
|
|
|
return t.get_fmt_str(.space, .hhmm12, .mmmd)
|
|
|
|
}
|
|
|
|
return t.format()
|
|
|
|
}
|
|
|
|
|
|
|
|
// get_fmt_time_str returns a date string with specified FormatTime type.
|
|
|
|
pub fn (t Time) get_fmt_time_str(fmt_time FormatTime) string {
|
|
|
|
if fmt_time == .no_time {
|
|
|
|
return ''
|
|
|
|
}
|
|
|
|
tp := if t.hour > 11 { 'p.m.' } else { 'a.m.' }
|
2021-01-28 20:34:55 +03:00
|
|
|
hour_ := if t.hour > 12 {
|
2020-09-10 14:26:01 +03:00
|
|
|
t.hour - 12
|
|
|
|
} else if t.hour == 0 {
|
|
|
|
12
|
|
|
|
} else {
|
|
|
|
t.hour
|
|
|
|
}
|
2020-02-06 16:19:44 +03:00
|
|
|
return match fmt_time {
|
2021-01-28 20:34:55 +03:00
|
|
|
.hhmm12 { '$hour_:${t.minute:02d} $tp' }
|
2020-09-10 14:26:01 +03:00
|
|
|
.hhmm24 { '${t.hour:02d}:${t.minute:02d}' }
|
2021-01-28 20:34:55 +03:00
|
|
|
.hhmmss12 { '$hour_:${t.minute:02d}:${t.second:02d} $tp' }
|
2020-09-10 14:26:01 +03:00
|
|
|
.hhmmss24 { '${t.hour:02d}:${t.minute:02d}:${t.second:02d}' }
|
2020-12-06 17:19:39 +03:00
|
|
|
.hhmmss24_milli { '${t.hour:02d}:${t.minute:02d}:${t.second:02d}.${(t.microsecond / 1000):03d}' }
|
2020-09-10 14:26:01 +03:00
|
|
|
.hhmmss24_micro { '${t.hour:02d}:${t.minute:02d}:${t.second:02d}.${t.microsecond:06d}' }
|
|
|
|
else { 'unknown enumeration $fmt_time' }
|
2020-02-06 16:19:44 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// get_fmt_time_str returns a date string with specified
|
|
|
|
// FormatDelimiter and FormatDate type.
|
|
|
|
pub fn (t Time) get_fmt_date_str(fmt_dlmtr FormatDelimiter, fmt_date FormatDate) string {
|
|
|
|
if fmt_date == .no_date {
|
|
|
|
return ''
|
|
|
|
}
|
2021-08-11 09:26:02 +03:00
|
|
|
month := t.smonth()
|
2020-09-16 22:13:20 +03:00
|
|
|
year := '${(t.year % 100):02d}'
|
2020-09-10 14:26:01 +03:00
|
|
|
mut res := match fmt_date {
|
|
|
|
.ddmmyy { '${t.day:02d}|${t.month:02d}|$year' }
|
2020-09-16 22:13:20 +03:00
|
|
|
.ddmmyyyy { '${t.day:02d}|${t.month:02d}|${t.year:04d}' }
|
2020-09-10 14:26:01 +03:00
|
|
|
.mmddyy { '${t.month:02d}|${t.day:02d}|$year' }
|
2020-09-16 22:13:20 +03:00
|
|
|
.mmddyyyy { '${t.month:02d}|${t.day:02d}|${t.year:04d}' }
|
2020-09-10 14:26:01 +03:00
|
|
|
.mmmd { '$month|$t.day' }
|
|
|
|
.mmmdd { '$month|${t.day:02d}' }
|
2021-04-01 13:04:59 +03:00
|
|
|
.mmmddyy { '$month|${t.day:02d}|$year' }
|
2020-09-16 22:13:20 +03:00
|
|
|
.mmmddyyyy { '$month|${t.day:02d}|${t.year:04d}' }
|
|
|
|
.yyyymmdd { '${t.year:04d}|${t.month:02d}|${t.day:02d}' }
|
2021-04-01 13:04:59 +03:00
|
|
|
.yymmdd { '$year|${t.month:02d}|${t.day:02d}' }
|
2020-09-10 14:26:01 +03:00
|
|
|
else { 'unknown enumeration $fmt_date' }
|
|
|
|
}
|
|
|
|
del := match fmt_dlmtr {
|
|
|
|
.dot { '.' }
|
|
|
|
.hyphen { '-' }
|
|
|
|
.slash { '/' }
|
|
|
|
.space { ' ' }
|
|
|
|
.no_delimiter { '' }
|
|
|
|
}
|
|
|
|
res = res.replace('|', del)
|
2020-03-20 22:35:00 +03:00
|
|
|
return res
|
2020-02-06 16:19:44 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// get_fmt_str returns a date string with specified FormatDelimiter,
|
|
|
|
// FormatTime type, and FormatDate type.
|
|
|
|
pub fn (t Time) get_fmt_str(fmt_dlmtr FormatDelimiter, fmt_time FormatTime, fmt_date FormatDate) string {
|
|
|
|
if fmt_date == .no_date {
|
|
|
|
if fmt_time == .no_time {
|
|
|
|
// saving one function call although it's checked in
|
|
|
|
// t.get_fmt_time_str(fmt_time) in the beginning
|
|
|
|
return ''
|
2020-09-10 14:26:01 +03:00
|
|
|
} else {
|
2020-02-06 16:19:44 +03:00
|
|
|
return t.get_fmt_time_str(fmt_time)
|
|
|
|
}
|
2020-09-10 14:26:01 +03:00
|
|
|
} else {
|
2020-02-06 16:19:44 +03:00
|
|
|
if fmt_time != .no_time {
|
2021-08-11 09:26:02 +03:00
|
|
|
dstr := t.get_fmt_date_str(fmt_dlmtr, fmt_date)
|
|
|
|
tstr := t.get_fmt_time_str(fmt_time)
|
|
|
|
return '$dstr $tstr'
|
2020-09-10 14:26:01 +03:00
|
|
|
} else {
|
2020-02-06 16:19:44 +03:00
|
|
|
return t.get_fmt_date_str(fmt_dlmtr, fmt_date)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-06-29 22:14:36 +03:00
|
|
|
|
|
|
|
// This is just a TEMPORARY function for cookies and their expire dates
|
2020-09-10 14:26:01 +03:00
|
|
|
pub fn (t Time) utc_string() string {
|
|
|
|
day_str := t.weekday_str()
|
|
|
|
month_str := t.smonth()
|
|
|
|
utc_string := '$day_str, $t.day $month_str $t.year ${t.hour:02d}:${t.minute:02d}:${t.second:02d} UTC'
|
2020-06-29 22:14:36 +03:00
|
|
|
return utc_string
|
|
|
|
}
|