obrimbasecli/essential/visible/service/helper/datetime/datetime.go

277 lines
7.0 KiB
Go

/*
|--------------------------------------------------------------------------
| Manifest
|--------------------------------------------------------------------------
|
| Name:
| - datetime
|
| Purpose:
| - Retrieve and format the current date and time from either the host
| system clock or a trusted framework-managed NTP source.
|
| Guideline:
| - Use local for host system clock retrieval.
| - Use trusted for framework-managed NTP retrieval.
| - Only use supported framework date/time format patterns.
| - All responses must be returned as standardized JSON objects.
|
| Example:
| - ObrimDatetime("local", map[string]any{"format":"yyyy-MM-dd"})
| - ObrimDatetime("trusted", map[string]any{"format":"timestampsec"})
|
|--------------------------------------------------------------------------
*/
/*
|--------------------------------------------------------------------------
| Credit
|--------------------------------------------------------------------------
|
| Contributor:
| - Rajon Ahmed
| - Scionite
|
|--------------------------------------------------------------------------
*/
package datetime
import (
"encoding/json"
"errors"
"net"
"time"
"go/module/essential/visible/service/helper/log"
"go/module/essential/visible/service/helper/status"
)
// ObrimDatetimePayload represents datetime payload data.
type ObrimDatetimePayload struct {
Value string `json:"value"`
}
// ObrimDatetimeOutput represents utility output structure.
type ObrimDatetimeOutput struct {
Status bool `json:"status"`
Code string `json:"code"`
Payload *ObrimDatetimePayload `json:"payload"`
}
// obrimDatetimePatternMap stores framework-supported format mappings.
var obrimDatetimePatternMap = map[string]string{
"yyyy-MM-dd": "2006-01-02",
"MMM dd, yyyy": "Jan 02, 2006",
"MMMM dd, yyyy": "January 02, 2006",
"EEE, MMM dd, yyyy": "Mon, Jan 02, 2006",
"EEE, MMMM dd, yyyy": "Mon, January 02, 2006",
"EEEE, MMM dd, yyyy": "Monday, Jan 02, 2006",
"EEEE, MMMM dd, yyyy": "Monday, January 02, 2006",
"HH:mm:ss": "15:04:05",
"hh:mm:ss a": "03:04:05 PM",
"yyyy-MM-dd HH:mm:ss": "2006-01-02 15:04:05",
"yyyy-MM-dd HH:mm:ss z": "2006-01-02 15:04:05 MST",
"yyyy-MM-dd'T'HH:mm:ssXXX": "2006-01-02T15:04:05Z07:00",
}
// ObrimDatetime retrieves and formats current date and time.
func ObrimDatetime(datetimeType string, config map[string]any) string {
status.ObrimStatus("INFO", "Initializing datetime utility.")
log.ObrimLog("INIT", "Datetime utility initialized.")
if err := obrimDatetimeValidate(datetimeType, config); err != nil {
status.ObrimStatus("ERROR", err.Error())
log.ObrimLog("ERROR", err.Error())
response := obrimDatetimeResponse(false, "FAILED_"+err.Error(), nil)
return response
}
format := config["format"].(string)
var currentTime time.Time
var err error
switch datetimeType {
case "local":
currentTime, err = obrimDatetimeLocal()
case "trusted":
currentTime, err = obrimDatetimeTrusted()
default:
err = errors.New("INVALID_TYPE")
}
if err != nil {
status.ObrimStatus("ERROR", err.Error())
log.ObrimLog("ERROR", err.Error())
response := obrimDatetimeResponse(false, "FAILED_"+err.Error(), nil)
return response
}
value, err := obrimDatetimeFormatApply(currentTime, format)
if err != nil {
status.ObrimStatus("ERROR", err.Error())
log.ObrimLog("ERROR", err.Error())
response := obrimDatetimeResponse(false, "FAILED_"+err.Error(), nil)
return response
}
payload := &ObrimDatetimePayload{
Value: value,
}
status.ObrimStatus("SUCCESS", "Datetime retrieved successfully.")
log.ObrimLog("SUCCESS", "Datetime retrieved successfully.")
return obrimDatetimeResponse(
true,
"SUCCESS_DATETIME_RETRIEVED",
payload,
)
}
// obrimDatetimeValidate validates utility inputs.
func obrimDatetimeValidate(datetimeType string, config map[string]any) error {
if datetimeType != "local" && datetimeType != "trusted" {
return errors.New("INVALID_TYPE")
}
formatValue, exists := config["format"]
if !exists {
return errors.New("INVALID_FORMAT")
}
format, ok := formatValue.(string)
if !ok {
return errors.New("INVALID_FORMAT")
}
if !obrimDatetimePatternValidate(format) {
return errors.New("INVALID_FORMAT")
}
return nil
}
// obrimDatetimePatternValidate validates supported patterns.
func obrimDatetimePatternValidate(pattern string) bool {
_, exists := obrimDatetimePatternMap[pattern]
return exists || pattern == "timestampsec" || pattern == "timestampmil"
}
// obrimDatetimePatternResolve resolves framework patterns.
func obrimDatetimePatternResolve(pattern string) (string, error) {
if pattern == "timestampsec" || pattern == "timestampmil" {
return pattern, nil
}
layout, exists := obrimDatetimePatternMap[pattern]
if !exists {
return "", errors.New("INVALID_FORMAT")
}
return layout, nil
}
// obrimDatetimeFormatApply applies formatting.
func obrimDatetimeFormatApply(value time.Time, pattern string) (string, error) {
layout, err := obrimDatetimePatternResolve(pattern)
if err != nil {
return "", err
}
switch pattern {
case "timestampsec":
return time.Unix(value.Unix(), 0).Format("1136239445"), nil
case "timestampmil":
return time.UnixMilli(value.UnixMilli()).Format("1136239445000"), nil
default:
return value.Format(layout), nil
}
}
// obrimDatetimeLocal retrieves local system time.
func obrimDatetimeLocal() (time.Time, error) {
return time.Now(), nil
}
// obrimDatetimeTrusted retrieves trusted time.
func obrimDatetimeTrusted() (time.Time, error) {
return obrimDatetimeTrustedSync()
}
// obrimDatetimeTrustedSync synchronizes trusted NTP time.
func obrimDatetimeTrustedSync() (time.Time, error) {
connection, err := net.DialTimeout(
"udp",
"time.google.com:123",
5*time.Second,
)
if err != nil {
return time.Time{}, errors.New("TRUSTED_TIME_RETRIEVAL")
}
defer connection.Close()
request := make([]byte, 48)
request[0] = 0x1B
if _, err = connection.Write(request); err != nil {
return time.Time{}, errors.New("TRUSTED_TIME_RETRIEVAL")
}
if err = connection.SetDeadline(time.Now().Add(5 * time.Second)); err != nil {
return time.Time{}, errors.New("TRUSTED_TIME_RETRIEVAL")
}
response := make([]byte, 48)
if _, err = connection.Read(response); err != nil {
return time.Time{}, errors.New("TRUSTED_TIME_RETRIEVAL")
}
seconds := uint64(response[40])<<24 |
uint64(response[41])<<16 |
uint64(response[42])<<8 |
uint64(response[43])
const ntpEpochOffset = 2208988800
unixSeconds := int64(seconds) - ntpEpochOffset
return time.Unix(unixSeconds, 0).UTC(), nil
}
// obrimDatetimeResponse builds standardized responses.
func obrimDatetimeResponse(
statusValue bool,
code string,
payload *ObrimDatetimePayload,
) string {
response := ObrimDatetimeOutput{
Status: statusValue,
Code: code,
Payload: payload,
}
data, err := json.Marshal(response)
if err != nil {
fallback := ObrimDatetimeOutput{
Status: false,
Code: "FAILED_RESPONSE_BUILD",
Payload: nil,
}
data, _ = json.Marshal(fallback)
}
return string(data)
}