/* |-------------------------------------------------------------------------- | 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) }