func DateTimeToEpoch(_ *transformctx.Ctx, datetime, fromTZ, unit string) (string, error) {
	if datetime == "" {
		return "", nil
	}
	t, _, err := parseDateTime(datetime, "", false, fromTZ, "")
	if err != nil {
		return "", err
	}
	switch unit {
	case epochUnitMilliseconds:
		return strconv.FormatInt(t.UnixNano()/int64(time.Millisecond), 10), nil
	case epochUnitSeconds:
		return strconv.FormatInt(t.Unix(), 10), nil
	default:
		return "", fmt.Errorf("unknown epoch unit '%s'", unit)
	}
}
DateTimeToEpoch parses a 'datetime' string intelligently, and returns its epoch number. 'fromTZ'
is only used if 'datetime' doesn't contain TZ info; if not specified, the parser will keep the
original TZ (or lack of it) of 'datetime'. 'unit' determines the time unit resolution of the
output epoch number.
package customfuncs
import (
	"fmt"
	"strconv"
	"time"
	"github.com/jf-tech/go-corelib/caches"
	"github.com/jf-tech/go-corelib/times"
	"github.com/jf-tech/omniparser/transformctx"
)
const (
	rfc3339NoTZ = "2006-01-02T15:04:05"
	second      = "SECOND"
	millisecond = "MILLISECOND"
)
func parseDateTime(datetime, layout string, layoutHasTZ bool, fromTZ, toTZ string) (time.Time, bool, error) {
	var t time.Time
	var hasTZ bool
	var err error
	if layout == "" {
		t, hasTZ, err = times.SmartParse(datetime)
		if err != nil {
			return time.Time{}, false, err
		}
	} else {
		t, err = time.Parse(layout, datetime)
		if err != nil {
			return time.Time{}, false, err
		}
		hasTZ = layoutHasTZ
	}
	if !hasTZ && fromTZ != "" {
		t, err = times.OverwriteTZ(t, fromTZ)
		if err != nil {
			return time.Time{}, false, err
		}
		hasTZ = true
	}
	if toTZ != "" {
		if hasTZ {
			t, err = times.ConvertTZ(t, toTZ)
		} else {
			t, err = times.OverwriteTZ(t, toTZ)
			hasTZ = true
		}
		if err != nil {
			return time.Time{}, false, err
		}
	}
	return t, hasTZ, nil
}
func rfc3339(t time.Time, hasTZ bool) string {
	if hasTZ {
		return t.Format(time.RFC3339)
	}
	return t.Format(rfc3339NoTZ)
}
func DateTimeToRFC3339(_ *transformctx.Ctx, datetime, fromTZ, toTZ string) (string, error) {
	if datetime == "" {
		return "", nil
	}
	t, hasTZ, err := parseDateTime(datetime, "", false, fromTZ, toTZ)
	if err != nil {
		return "", err
	}
	return rfc3339(t, hasTZ), nil
}
func DateTimeLayoutToRFC3339(_ *transformctx.Ctx, datetime, layout, layoutTZ, fromTZ, toTZ string) (string, error) {
	layoutTZFlag := false
	if layout != "" && layoutTZ != "" {
		var err error
		layoutTZFlag, err = strconv.ParseBool(layoutTZ)
		if err != nil {
			return "", err
		}
	}
	if datetime == "" {
		return "", nil
	}
	t, hasTZ, err := parseDateTime(datetime, layout, layoutTZFlag, fromTZ, toTZ)
	if err != nil {
		return "", err
	}
	return rfc3339(t, hasTZ), nil
}
const (
	epochUnitMilliseconds = "MILLISECOND"
	epochUnitSeconds      = "SECOND"
)