// Copyright (c) 2026 Storm Dragon // Authors: Storm Dragon, Angus Kola // SPDX-License-Identifier: GPL-3.0-or-later package datetime /* #include #include #include */ import "C" import ( "time" "unsafe" ) type Option struct { Value string Label string } var timeFormats = []struct { Value string Spec string }{ {"locale", "%X"}, {"12-hour", "%I:%M %p"}, {"12-hour-seconds", "%I:%M:%S %p"}, {"24-hour-seconds", "%H:%M:%S"}, {"24-hour-seconds-words", "%H hours, %M minutes and %S seconds"}, {"24-hour", "%H:%M"}, {"24-hour-words", "%H hours and %M minutes"}, } var dateFormats = []struct { Value string Spec string }{ {"locale", "%x"}, {"numbers-dm", "%d/%m"}, {"numbers-md", "%m/%d"}, {"numbers-dmy", "%d/%m/%Y"}, {"numbers-mdy", "%m/%d/%Y"}, {"numbers-ymd", "%Y/%m/%d"}, {"full-dm", "%A, %-d %B"}, {"full-md", "%A, %B %-d"}, {"full-dmy", "%A, %-d %B, %Y"}, {"full-mdy", "%A, %B %-d, %Y"}, {"full-ymd", "%Y. %B %-d, %A"}, {"abbreviated-dm", "%a, %-d %b"}, {"abbreviated-md", "%a, %b %-d"}, {"abbreviated-dmy", "%a, %-d %b, %Y"}, {"abbreviated-mdy", "%a, %b %-d, %Y"}, {"abbreviated-ymd", "%Y. %b %-d, %a"}, } func init() { locale := C.CString("") defer C.free(unsafe.Pointer(locale)) C.setlocale(C.LC_TIME, locale) } func TimeOptions(now time.Time) []Option { return options(timeFormats, now) } func DateOptions(now time.Time) []Option { return options(dateFormats, now) } func options(formats []struct { Value string Spec string }, now time.Time) []Option { result := make([]Option, 0, len(formats)) for _, item := range formats { result = append(result, Option{Value: item.Value, Label: format(now, item.Spec)}) } return result } func FormatTime(value string, now time.Time) (string, bool) { return formatValue(timeFormats, value, now) } func FormatDate(value string, now time.Time) (string, bool) { return formatValue(dateFormats, value, now) } func ValidTime(value string) bool { return valid(timeFormats, value) } func ValidDate(value string) bool { return valid(dateFormats, value) } func NormalizeDate(value string) string { switch value { case "ymd": return "numbers-ymd" case "mdy": return "numbers-mdy" case "dmy": return "numbers-dmy" default: return value } } func formatValue(formats []struct { Value string Spec string }, value string, now time.Time) (string, bool) { for _, item := range formats { if item.Value == value { return format(now, item.Spec), true } } return "", false } func valid(formats []struct { Value string Spec string }, value string) bool { for _, item := range formats { if item.Value == value { return true } } return false } func format(now time.Time, spec string) string { cSpec := C.CString(spec) defer C.free(unsafe.Pointer(cSpec)) tm := C.struct_tm{ tm_year: C.int(now.Year() - 1900), tm_mon: C.int(int(now.Month()) - 1), tm_mday: C.int(now.Day()), tm_hour: C.int(now.Hour()), tm_min: C.int(now.Minute()), tm_sec: C.int(now.Second()), tm_wday: C.int(int(now.Weekday())), tm_yday: C.int(now.YearDay() - 1), tm_isdst: -1, } buffer := make([]C.char, 512) length := C.strftime(&buffer[0], C.size_t(len(buffer)), cSpec, &tm) return C.GoStringN(&buffer[0], C.int(length)) }