219 lines
5.6 KiB
Go
219 lines
5.6 KiB
Go
package datetime
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"log"
|
|
"net"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
obrimDatetimeTypeLocal = "local"
|
|
obrimDatetimeTypeTrusted = "trusted"
|
|
|
|
obrimDatetimeCodeSuccess = "SUCCESS_DATETIME_RETRIEVED"
|
|
obrimDatetimeCodeInvalidType = "FAILED_INVALID_TYPE"
|
|
obrimDatetimeCodeInvalidFormat = "FAILED_INVALID_FORMAT"
|
|
obrimDatetimeCodeTrustedTime = "FAILED_TRUSTED_TIME_RETRIEVAL"
|
|
obrimDatetimeCodeInternal = "FAILED_INTERNAL"
|
|
obrimDatetimeTrustedNTPPoolServer = "pool.ntp.org:123"
|
|
)
|
|
|
|
// ObrimDatetimePayload represents datetime payload data.
|
|
type ObrimDatetimePayload struct {
|
|
Value string `json:"value"`
|
|
}
|
|
|
|
// ObrimDatetimeResponse represents the standard utility response.
|
|
type ObrimDatetimeResponse struct {
|
|
Status bool `json:"status"`
|
|
Code string `json:"code"`
|
|
Payload *ObrimDatetimePayload `json:"payload"`
|
|
}
|
|
|
|
// obrimDatetimePatternMap stores 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": time.RFC3339,
|
|
}
|
|
|
|
// ObrimDatetime retrieves and formats current datetime from a selected source.
|
|
func ObrimDatetime(datetimeType string, config map[string]string) ObrimDatetimeResponse {
|
|
log.Println("[datetime] initialization started")
|
|
|
|
if code := obrimDatetimeValidate(datetimeType, config); code != "" {
|
|
log.Printf("[datetime] validation failed: %s", code)
|
|
return obrimDatetimeResponse(false, code, "")
|
|
}
|
|
|
|
format := config["format"]
|
|
|
|
var (
|
|
currentTime time.Time
|
|
err error
|
|
)
|
|
|
|
switch datetimeType {
|
|
case obrimDatetimeTypeLocal:
|
|
currentTime = obrimDatetimeLocal()
|
|
|
|
case obrimDatetimeTypeTrusted:
|
|
currentTime, err = obrimDatetimeTrusted()
|
|
if err != nil {
|
|
log.Printf("[datetime] trusted retrieval failed: %v", err)
|
|
return obrimDatetimeResponse(false, obrimDatetimeCodeTrustedTime, "")
|
|
}
|
|
|
|
default:
|
|
return obrimDatetimeResponse(false, obrimDatetimeCodeInvalidType, "")
|
|
}
|
|
|
|
value := obrimDatetimeFormatApply(currentTime, format)
|
|
|
|
log.Println("[datetime] retrieval completed successfully")
|
|
|
|
return obrimDatetimeResponse(true, obrimDatetimeCodeSuccess, value)
|
|
}
|
|
|
|
// obrimDatetimeValidate validates utility inputs.
|
|
func obrimDatetimeValidate(datetimeType string, config map[string]string) string {
|
|
switch datetimeType {
|
|
case obrimDatetimeTypeLocal, obrimDatetimeTypeTrusted:
|
|
default:
|
|
return obrimDatetimeCodeInvalidType
|
|
}
|
|
|
|
format, exists := config["format"]
|
|
if !exists || format == "" {
|
|
return obrimDatetimeCodeInvalidFormat
|
|
}
|
|
|
|
if !obrimDatetimePatternValidate(format) {
|
|
return obrimDatetimeCodeInvalidFormat
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
// obrimDatetimePatternValidate validates supported format patterns.
|
|
func obrimDatetimePatternValidate(pattern string) bool {
|
|
_, exists := obrimDatetimePatternMap[pattern]
|
|
|
|
if exists {
|
|
return true
|
|
}
|
|
|
|
switch pattern {
|
|
case "timestampsec", "timestampmil":
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// obrimDatetimePatternResolve resolves patterns into layouts.
|
|
func obrimDatetimePatternResolve(pattern string) (string, bool) {
|
|
layout, exists := obrimDatetimePatternMap[pattern]
|
|
return layout, exists
|
|
}
|
|
|
|
// obrimDatetimeFormatApply formats datetime values.
|
|
func obrimDatetimeFormatApply(datetimeValue time.Time, pattern string) string {
|
|
switch pattern {
|
|
case "timestampsec":
|
|
return strconv.FormatInt(datetimeValue.Unix(), 10)
|
|
|
|
case "timestampmil":
|
|
return strconv.FormatInt(datetimeValue.UnixMilli(), 10)
|
|
}
|
|
|
|
layout, exists := obrimDatetimePatternResolve(pattern)
|
|
if !exists {
|
|
return ""
|
|
}
|
|
|
|
return datetimeValue.Format(layout)
|
|
}
|
|
|
|
// obrimDatetimeResponse builds standardized responses.
|
|
func obrimDatetimeResponse(status bool, code string, value string) ObrimDatetimeResponse {
|
|
if !status {
|
|
return ObrimDatetimeResponse{
|
|
Status: false,
|
|
Code: code,
|
|
Payload: nil,
|
|
}
|
|
}
|
|
|
|
return ObrimDatetimeResponse{
|
|
Status: true,
|
|
Code: code,
|
|
Payload: &ObrimDatetimePayload{
|
|
Value: value,
|
|
},
|
|
}
|
|
}
|
|
|
|
// obrimDatetimeLocal retrieves local system time.
|
|
func obrimDatetimeLocal() time.Time {
|
|
return time.Now()
|
|
}
|
|
|
|
// obrimDatetimeTrusted retrieves trusted NTP time.
|
|
func obrimDatetimeTrusted() (time.Time, error) {
|
|
return obrimDatetimeTrustedSync()
|
|
}
|
|
|
|
// obrimDatetimeTrustedSync synchronizes with framework-managed NTP source.
|
|
func obrimDatetimeTrustedSync() (time.Time, error) {
|
|
connection, err := net.DialTimeout("udp", obrimDatetimeTrustedNTPPoolServer, 5*time.Second)
|
|
if err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
|
|
defer connection.Close()
|
|
|
|
request := make([]byte, 48)
|
|
request[0] = 0x1B
|
|
|
|
if _, err = connection.Write(request); err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
|
|
if err = connection.SetDeadline(time.Now().Add(5 * time.Second)); err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
|
|
response := make([]byte, 48)
|
|
|
|
if _, err = connection.Read(response); err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
|
|
seconds := binary.BigEndian.Uint32(response[40:44])
|
|
fraction := binary.BigEndian.Uint32(response[44:48])
|
|
|
|
const ntpEpochOffset = 2208988800
|
|
|
|
unixSeconds := int64(seconds) - ntpEpochOffset
|
|
nanoseconds := (int64(fraction) * 1e9) >> 32
|
|
|
|
if unixSeconds < 0 {
|
|
return time.Time{}, fmt.Errorf("invalid ntp timestamp")
|
|
}
|
|
|
|
return time.Unix(unixSeconds, nanoseconds).UTC(), nil
|
|
}
|