mirror of
https://github.com/neilotoole/sq.git
synced 2024-12-19 22:21:56 +03:00
134 lines
3.4 KiB
Go
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)
|
|
}
|