sq/cli/output/xlsxw/datetime.go
2023-11-19 18:06:36 -07:00

134 lines
3.4 KiB
Go

package xlsxw
import (
"math"
"time"
excelize "github.com/xuri/excelize/v2"
"github.com/neilotoole/sq/libsq/core/errz"
"github.com/neilotoole/sq/libsq/core/options"
)
var (
// OptDatetimeFormat is Excel's custom datetime format string.
OptDatetimeFormat = options.NewString(
"format.excel.datetime",
"",
0,
"yyyy-mm-dd hh:mm",
func(s string) error {
err := validateDatetimeFormatString(s)
return errz.Wrap(err, "config: format.excel.datetime: invalid format string")
},
"Timestamp format string for Excel datetime values",
`Timestamp format for datetime values: that is, for values that have
both a date and time component. The exact format is specific to
Microsoft Excel, but is broadly similar to strftime.
Examples:
"yyyy-mm-dd hh:mm" 1989-11-09 16:07
"dd/mm/yy h:mm am/pm" 09/11/89 4:07 pm
"dd-mmm-yy h:mm:ss AM/PM" 09-Nov-89 4:07:01 PM
`,
options.TagOutput,
)
// OptDateFormat is Excel's custom date-only format string.
OptDateFormat = options.NewString(
"format.excel.date",
"",
0,
"yyyy-mm-dd",
func(s string) error {
err := validateDatetimeFormatString(s)
return errz.Wrap(err, "config: format.excel.date: invalid format string")
},
"Date format string for Excel date-only values",
`Date format string for Microsoft Excel date-only values. The exact format
is specific to Excel, but is broadly similar to strftime.
Examples:
"yyyy-mm-dd" 1989-11-09
"dd/mm/yy" 09/11/89
"dd-mmm-yy" 09-Nov-89`,
options.TagOutput,
)
// OptTimeFormat is Excel's custom time format string.
OptTimeFormat = options.NewString(
"format.excel.time",
"",
0,
"hh:mm:ss",
func(s string) error {
err := validateDatetimeFormatString(s)
return errz.Wrap(err, "config: format.excel.time: invalid format string")
},
"Time format string for Excel time-only values",
`Time format string for Microsoft Excel time-only values. The exact format is
specific to Excel, but is broadly similar to strftime.
Examples:
"hh:mm:ss" 16:07:10
"h:mm am/pm" 4:07 pm
"h:mm:ss AM/PM" 4:07:01 PM
Note that time-only values are sometimes programmatically indistinguishable
from datetime values. In that situation, use format.excel.datetime instead.`,
options.TagOutput,
)
)
func validateDatetimeFormatString(s string) error {
xl := excelize.NewFile()
defer func() { _ = xl.Close() }()
_, err := xl.NewStyle(&excelize.Style{CustomNumFmt: &s})
return errz.Err(err)
}
// timeOnlyToExcelFloat returns a float value for the time-only part
// of t. This is needed because Excel really prefers that time values
// are represented as a float.
//
// See: https://xuri.me/excelize/en/cell.html#SetCellStyle
func timeOnlyToExcelFloat(t time.Time) (float64, error) {
now := time.Now().UTC()
t2 := time.Date(
now.Year(),
now.Month(),
now.Day(),
t.Hour(),
t.Minute(),
t.Second(),
t.Nanosecond(),
now.Location(),
)
f, err := timeToExcelTime(t2, false)
if err != nil {
return 0, errz.Err(err)
}
// We're only interested in the fractional part
_, f = math.Modf(f)
return f, nil
}
// timeOnlyStringToExcelFloat is a convenience wrapper
// around timeOnlyToExcelFloat, that handles a time-only string
// such as "16:07:04".
func timeOnlyStringToExcelFloat(s string) (float64, error) {
t, err := time.Parse(time.TimeOnly, s)
if err != nil {
return 0, errz.Err(err)
}
return timeOnlyToExcelFloat(t)
}