Cthulhu is no longer a fork. Now it is a screen reader written in Golang.
This commit is contained in:
1 parent
a998fe67de
commit
069fc674dc
708 files changed
+112482
-532
No files matched your search
@@ -0,0 +1,154 @@
|
||||
// Copyright (c) 2026 Storm Dragon
|
||||
// Authors: Storm Dragon, Angus Kola
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package datetime
|
||||
|
||||
/*
|
||||
#include <locale.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
*/
|
||||
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))
|
||||
}
|
||||
Reference in new issue
Block a user