330 lines
7.3 KiB
Go
330 lines
7.3 KiB
Go
/*
|
|
|--------------------------------------------------------------------------
|
|
| Metadata
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| Name:
|
|
| - Datetime Utility
|
|
|
|
|
| Purpose:
|
|
| - Retrieve the current date and time from either the host system
|
|
| clock or a trusted framework-managed NTP source and return the
|
|
| result using a framework-supported date/time format pattern.
|
|
|
|
|
| Guideline:
|
|
| - Use ObrimDatetime() as the primary entry point.
|
|
| - Always provide a valid datetime type.
|
|
| - Supported types are "local" and "trusted".
|
|
| - Always provide the "format" configuration key.
|
|
| - Use only framework-supported format patterns.
|
|
| - Expect all successful results to be returned as strings.
|
|
| - Use "local" when host system time is sufficient.
|
|
| - Use "trusted" when synchronized network time is required.
|
|
| - Handle both success and failure response codes.
|
|
|
|
|
| Example:
|
|
| - ObrimDatetime(
|
|
| "local",
|
|
| map[string]any{
|
|
| "format": "yyyy-MM-dd",
|
|
| },
|
|
| )
|
|
|
|
|
| - ObrimDatetime(
|
|
| "trusted",
|
|
| map[string]any{
|
|
| "format": "yyyy-MM-dd HH:mm:ss z",
|
|
| },
|
|
| )
|
|
|
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Credit
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| Contributor:
|
|
| - Rajon Ahmed
|
|
| - Scionite
|
|
|
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
package datetime
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"log"
|
|
"net"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
obrimDatetimeTypeLocal = "local"
|
|
obrimDatetimeTypeTrusted = "trusted"
|
|
|
|
ObrimDatetimeCodeSuccessRetrieved = "SUCCESS_DATETIME_RETRIEVED"
|
|
|
|
ObrimDatetimeCodeFailedInvalidType = "FAILED_INVALID_TYPE"
|
|
ObrimDatetimeCodeFailedInvalidFormat = "FAILED_INVALID_FORMAT"
|
|
ObrimDatetimeCodeFailedTrustedTimeRetrieval = "FAILED_TRUSTED_TIME_RETRIEVAL"
|
|
)
|
|
|
|
const obrimDatetimeTrustedNtpPool = "pool.ntp.org:123"
|
|
|
|
// ObrimDatetimePayload defines the success payload structure.
|
|
type ObrimDatetimePayload struct {
|
|
Value string `json:"value"`
|
|
}
|
|
|
|
// ObrimDatetimeResponse defines the utility response structure.
|
|
type ObrimDatetimeResponse 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": time.RFC3339,
|
|
}
|
|
|
|
// ObrimDatetime retrieves and formats current date and time.
|
|
func ObrimDatetime(
|
|
datetimeType string,
|
|
config map[string]any,
|
|
) map[string]any {
|
|
log.Println("[datetime] initialization started")
|
|
|
|
format, code := obrimDatetimeValidate(datetimeType, config)
|
|
if code != "" {
|
|
log.Printf("[datetime] validation failed: %s", code)
|
|
|
|
return obrimDatetimeResponse(
|
|
false,
|
|
code,
|
|
"",
|
|
)
|
|
}
|
|
|
|
var datetimeValue time.Time
|
|
var err error
|
|
|
|
switch datetimeType {
|
|
case obrimDatetimeTypeLocal:
|
|
datetimeValue = obrimDatetimeLocal()
|
|
|
|
case obrimDatetimeTypeTrusted:
|
|
datetimeValue, err = obrimDatetimeTrusted()
|
|
if err != nil {
|
|
log.Printf(
|
|
"[datetime] trusted time retrieval failed: %v",
|
|
err,
|
|
)
|
|
|
|
return obrimDatetimeResponse(
|
|
false,
|
|
ObrimDatetimeCodeFailedTrustedTimeRetrieval,
|
|
"",
|
|
)
|
|
}
|
|
}
|
|
|
|
formattedValue := obrimDatetimeFormatApply(
|
|
datetimeValue,
|
|
format,
|
|
)
|
|
|
|
log.Println("[datetime] datetime retrieved successfully")
|
|
|
|
return obrimDatetimeResponse(
|
|
true,
|
|
ObrimDatetimeCodeSuccessRetrieved,
|
|
formattedValue,
|
|
)
|
|
}
|
|
|
|
// obrimDatetimeValidate validates utility inputs.
|
|
func obrimDatetimeValidate(
|
|
datetimeType string,
|
|
config map[string]any,
|
|
) (string, string) {
|
|
switch datetimeType {
|
|
case obrimDatetimeTypeLocal:
|
|
case obrimDatetimeTypeTrusted:
|
|
default:
|
|
return "", ObrimDatetimeCodeFailedInvalidType
|
|
}
|
|
|
|
formatValue, exists := config["format"]
|
|
if !exists {
|
|
return "", ObrimDatetimeCodeFailedInvalidFormat
|
|
}
|
|
|
|
format, ok := formatValue.(string)
|
|
if !ok || format == "" {
|
|
return "", ObrimDatetimeCodeFailedInvalidFormat
|
|
}
|
|
|
|
if !obrimDatetimePatternValidate(format) {
|
|
return "", ObrimDatetimeCodeFailedInvalidFormat
|
|
}
|
|
|
|
return format, ""
|
|
}
|
|
|
|
// obrimDatetimePatternValidate validates supported patterns.
|
|
func obrimDatetimePatternValidate(pattern string) bool {
|
|
switch pattern {
|
|
case "timestampsec":
|
|
return true
|
|
|
|
case "timestampmil":
|
|
return true
|
|
}
|
|
|
|
_, exists := obrimDatetimePatternMap[pattern]
|
|
|
|
return exists
|
|
}
|
|
|
|
// obrimDatetimePatternResolve resolves pattern mappings.
|
|
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, _ := obrimDatetimePatternResolve(pattern)
|
|
|
|
return datetimeValue.Format(layout)
|
|
}
|
|
|
|
// obrimDatetimeResponse builds utility responses.
|
|
func obrimDatetimeResponse(
|
|
status bool,
|
|
code string,
|
|
value string,
|
|
) map[string]any {
|
|
if !status {
|
|
return map[string]any{
|
|
"status": false,
|
|
"code": code,
|
|
"payload": nil,
|
|
}
|
|
}
|
|
|
|
return map[string]any{
|
|
"status": true,
|
|
"code": code,
|
|
"payload": map[string]any{
|
|
"value": value,
|
|
},
|
|
}
|
|
}
|
|
|
|
// obrimDatetimeLocal retrieves host system time.
|
|
func obrimDatetimeLocal() time.Time {
|
|
return time.Now()
|
|
}
|
|
|
|
// obrimDatetimeTrusted retrieves trusted NTP time.
|
|
func obrimDatetimeTrusted() (time.Time, error) {
|
|
return obrimDatetimeTrustedSync()
|
|
}
|
|
|
|
// obrimDatetimeTrustedSync synchronizes trusted time from NTP.
|
|
func obrimDatetimeTrustedSync() (time.Time, error) {
|
|
connection, err := net.DialTimeout(
|
|
"udp",
|
|
obrimDatetimeTrustedNtpPool,
|
|
5*time.Second,
|
|
)
|
|
if err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
|
|
defer connection.Close()
|
|
|
|
var 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
|
|
}
|
|
|
|
var response = make([]byte, 48)
|
|
|
|
if _, err = connection.Read(response); err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
|
|
var ntpSeconds = binary.BigEndian.Uint32(
|
|
response[40:44],
|
|
)
|
|
|
|
var ntpFraction = binary.BigEndian.Uint32(
|
|
response[44:48],
|
|
)
|
|
|
|
const ntpEpochOffset = 2208988800
|
|
|
|
var unixSeconds = int64(ntpSeconds) - ntpEpochOffset
|
|
|
|
var nanoseconds = (int64(ntpFraction) * 1e9) >> 32
|
|
|
|
if unixSeconds < 0 {
|
|
return time.Time{}, fmt.Errorf(
|
|
"invalid ntp timestamp",
|
|
)
|
|
}
|
|
|
|
return time.Unix(
|
|
unixSeconds,
|
|
nanoseconds,
|
|
).UTC(), nil
|
|
}
|