mirror of https://github.com/vernonkeenan/lib
128 lines
3.4 KiB
Go
128 lines
3.4 KiB
Go
package app
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"reflect"
|
|
"time"
|
|
)
|
|
|
|
const dateFormat = "2006-01-02"
|
|
const dateTimeFormat = "2006-01-02T15:04:05-0800"
|
|
|
|
// FieldsAndValues is a struct that holds field names and corresponding values for an object.
|
|
type FieldsAndValues struct {
|
|
fieldNames []string
|
|
fieldValues []interface{}
|
|
}
|
|
|
|
// GetFieldsAndValues takes an object and returns a pointer to a FieldsAndValues struct
|
|
// containing the field names and corresponding values for each non-zero field,
|
|
// excluding the "ID" field.
|
|
func GetFieldsAndValues(obj interface{}) *FieldsAndValues {
|
|
result := &FieldsAndValues{
|
|
fieldNames: []string{},
|
|
fieldValues: []interface{}{},
|
|
}
|
|
|
|
v := reflect.ValueOf(obj).Elem()
|
|
t := v.Type()
|
|
|
|
for i := 0; i < t.NumField(); i++ {
|
|
field := t.Field(i)
|
|
fieldValue := v.Field(i)
|
|
|
|
if field.Name != "ID" {
|
|
if !IsZero(fieldValue) {
|
|
fmt.Printf("DEBUG: Adding field %s with value %v\n", field.Name, fieldValue.Interface())
|
|
result.fieldNames = append(result.fieldNames, field.Name)
|
|
result.fieldValues = append(result.fieldValues, fieldValue.Interface())
|
|
}
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// IsZero checks if a value is zero or considered zero-like (e.g., empty arrays or slices, nil pointers).
|
|
// Note that strings, including empty strings, are treated as non-zero values.
|
|
func IsZero(v reflect.Value) bool {
|
|
switch v.Kind() {
|
|
case reflect.String:
|
|
return false // Treat strings, including empty strings, as non-zero values
|
|
case reflect.Struct:
|
|
for i := 0; i < v.NumField(); i++ {
|
|
if !IsZero(v.Field(i)) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
case reflect.Ptr, reflect.Interface:
|
|
if v.IsNil() {
|
|
return true
|
|
}
|
|
return IsZero(v.Elem())
|
|
case reflect.Array, reflect.Slice:
|
|
return v.Len() == 0
|
|
default:
|
|
z := reflect.Zero(v.Type())
|
|
return v.Interface() == z.Interface()
|
|
}
|
|
}
|
|
|
|
// SqlDateToString takes a pointer to a sql.NullTime object and returns a pointer to a string
|
|
// representing the date in dateTimeFormat if the sql.NullTime object is valid, otherwise returns nil.
|
|
func SqlDateToString(d *sql.NullTime) *string {
|
|
if d == nil || !d.Valid {
|
|
return nil
|
|
}
|
|
|
|
dateStr := d.Time.Format(dateTimeFormat)
|
|
return &dateStr
|
|
}
|
|
|
|
// StringToSqlDate takes a pointer to a string representing a date and returns a pointer to
|
|
// a sql.NullTime object containing the parsed date if the string is not nil, otherwise returns nil.
|
|
func StringToSqlDate(s *string) *sql.NullTime {
|
|
if s == nil {
|
|
return nil
|
|
}
|
|
|
|
parsedTime, err := ParseDateTime(s)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
return &sql.NullTime{
|
|
Time: *parsedTime,
|
|
Valid: true,
|
|
}
|
|
}
|
|
|
|
// ParseDateTime takes a pointer to a string representing a date and returns a pointer to
|
|
// a time.Time object containing the parsed date if the string is in one of the supported formats.
|
|
// If the string is nil or not in a supported format, returns an error.
|
|
func ParseDateTime(dateStr *string) (*time.Time, error) {
|
|
if dateStr == nil {
|
|
return nil, fmt.Errorf("✋members.parseDateTime: dateStr is null")
|
|
}
|
|
loc, _ := time.LoadLocation("Local")
|
|
//
|
|
formats := []string{dateFormat, dateTimeFormat, time.RFC1123, time.RFC3339}
|
|
|
|
var parsedTime *time.Time
|
|
for _, format := range formats {
|
|
tm, err := time.ParseInLocation(format, *dateStr, loc)
|
|
if err == nil {
|
|
parsedTime = &tm
|
|
break
|
|
}
|
|
}
|
|
|
|
if parsedTime == nil {
|
|
return nil, fmt.Errorf("✋parseDateTime: input string doesn't match any of the expected formats")
|
|
}
|
|
|
|
return parsedTime, nil
|
|
}
|