| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420 |
- package xlsx
- import (
- "fmt"
- "math"
- "strconv"
- "strings"
- "time"
- )
- // CellType is an int type for storing metadata about the data type in the cell.
- type CellType int
- // Known types for cell values.
- const (
- CellTypeString CellType = iota
- CellTypeFormula
- CellTypeNumeric
- CellTypeBool
- CellTypeInline
- CellTypeError
- CellTypeDate
- CellTypeGeneral
- )
- // Cell is a high level structure intended to provide user access to
- // the contents of Cell within an xlsx.Row.
- type Cell struct {
- Row *Row
- Value string
- formula string
- style *Style
- numFmt string
- date1904 bool
- Hidden bool
- HMerge int
- VMerge int
- cellType CellType
- }
- // CellInterface defines the public API of the Cell.
- type CellInterface interface {
- String() string
- FormattedValue() string
- }
- // NewCell creates a cell and adds it to a row.
- func NewCell(r *Row) *Cell {
- return &Cell{Row: r, cellType: CellTypeGeneral}
- }
- // Merge with other cells, horizontally and/or vertically.
- func (c *Cell) Merge(hcells, vcells int) {
- c.HMerge = hcells
- c.VMerge = vcells
- }
- // Type returns the CellType of a cell. See CellType constants for more details.
- func (c *Cell) Type() CellType {
- return c.cellType
- }
- // SetString sets the value of a cell to a string.
- func (c *Cell) SetString(s string) {
- c.Value = s
- c.formula = ""
- c.cellType = CellTypeString
- }
- // String returns the value of a Cell as a string.
- func (c *Cell) String() string {
- return c.FormattedValue()
- }
- // SetFloat sets the value of a cell to a float.
- func (c *Cell) SetFloat(n float64) {
- c.SetFloatWithFormat(n, builtInNumFmt[builtInNumFmtIndex_GENERAL])
- }
- /*
- The following are samples of format samples.
- * "0.00e+00"
- * "0", "#,##0"
- * "0.00", "#,##0.00", "@"
- * "#,##0 ;(#,##0)", "#,##0 ;[red](#,##0)"
- * "#,##0.00;(#,##0.00)", "#,##0.00;[red](#,##0.00)"
- * "0%", "0.00%"
- * "0.00e+00", "##0.0e+0"
- */
- // SetFloatWithFormat sets the value of a cell to a float and applies
- // formatting to the cell.
- func (c *Cell) SetFloatWithFormat(n float64, format string) {
- // beauty the output when the float is small enough
- if n != 0 && n < 0.00001 {
- c.Value = strconv.FormatFloat(n, 'e', -1, 64)
- } else {
- c.Value = strconv.FormatFloat(n, 'f', -1, 64)
- }
- c.numFmt = format
- c.formula = ""
- c.cellType = CellTypeNumeric
- }
- var timeLocationUTC *time.Location
- func init() {
- timeLocationUTC, _ = time.LoadLocation("UTC")
- }
- func timeToUTCTime(t time.Time) time.Time {
- return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), timeLocationUTC)
- }
- func timeToExcelTime(t time.Time) float64 {
- return float64(t.Unix())/86400.0 + 25569.0
- }
- // SetDate sets the value of a cell to a float.
- func (c *Cell) SetDate(t time.Time) {
- c.SetDateTimeWithFormat(float64(int64(timeToExcelTime(timeToUTCTime(t)))), builtInNumFmt[14])
- }
- func (c *Cell) SetDateTime(t time.Time) {
- c.SetDateTimeWithFormat(timeToExcelTime(timeToUTCTime(t)), builtInNumFmt[14])
- }
- func (c *Cell) SetDateTimeWithFormat(n float64, format string) {
- c.Value = strconv.FormatFloat(n, 'f', -1, 64)
- c.numFmt = format
- c.formula = ""
- c.cellType = CellTypeDate
- }
- // Float returns the value of cell as a number.
- func (c *Cell) Float() (float64, error) {
- f, err := strconv.ParseFloat(c.Value, 64)
- if err != nil {
- return math.NaN(), err
- }
- return f, nil
- }
- // SetInt64 sets a cell's value to a 64-bit integer.
- func (c *Cell) SetInt64(n int64) {
- c.Value = fmt.Sprintf("%d", n)
- c.numFmt = builtInNumFmt[builtInNumFmtIndex_INT]
- c.formula = ""
- c.cellType = CellTypeNumeric
- }
- // Int64 returns the value of cell as 64-bit integer.
- func (c *Cell) Int64() (int64, error) {
- f, err := strconv.ParseInt(c.Value, 10, 64)
- if err != nil {
- return -1, err
- }
- return f, nil
- }
- // SetInt sets a cell's value to an integer.
- func (c *Cell) SetInt(n int) {
- c.Value = fmt.Sprintf("%d", n)
- c.numFmt = builtInNumFmt[builtInNumFmtIndex_INT]
- c.formula = ""
- c.cellType = CellTypeNumeric
- }
- // SetInt sets a cell's value to an integer.
- func (c *Cell) SetValue(n interface{}) {
- var s string
- switch n.(type) {
- case time.Time:
- c.SetDateTime(n.(time.Time))
- return
- case int:
- c.setGeneral(fmt.Sprintf("%v", n))
- return
- case int32:
- c.setGeneral(fmt.Sprintf("%v", n))
- return
- case int64:
- c.setGeneral(fmt.Sprintf("%v", n))
- return
- case float32:
- c.setGeneral(fmt.Sprintf("%v", n))
- return
- case float64:
- c.setGeneral(fmt.Sprintf("%v", n))
- return
- case string:
- s = n.(string)
- case []byte:
- s = string(n.([]byte))
- case nil:
- s = ""
- default:
- s = fmt.Sprintf("%v", n)
- }
- c.SetString(s)
- }
- // SetInt sets a cell's value to an integer.
- func (c *Cell) setGeneral(s string) {
- c.Value = s
- c.numFmt = builtInNumFmt[builtInNumFmtIndex_GENERAL]
- c.formula = ""
- c.cellType = CellTypeGeneral
- }
- // Int returns the value of cell as integer.
- // Has max 53 bits of precision
- // See: float64(int64(math.MaxInt))
- func (c *Cell) Int() (int, error) {
- f, err := strconv.ParseFloat(c.Value, 64)
- if err != nil {
- return -1, err
- }
- return int(f), nil
- }
- // SetBool sets a cell's value to a boolean.
- func (c *Cell) SetBool(b bool) {
- if b {
- c.Value = "1"
- } else {
- c.Value = "0"
- }
- c.cellType = CellTypeBool
- }
- // Bool returns a boolean from a cell's value.
- // TODO: Determine if the current return value is
- // appropriate for types other than CellTypeBool.
- func (c *Cell) Bool() bool {
- // If bool, just return the value.
- if c.cellType == CellTypeBool {
- return c.Value == "1"
- }
- // If numeric, base it on a non-zero.
- if c.cellType == CellTypeNumeric {
- return c.Value != "0"
- }
- // Return whether there's an empty string.
- return c.Value != ""
- }
- // SetFormula sets the format string for a cell.
- func (c *Cell) SetFormula(formula string) {
- c.formula = formula
- c.cellType = CellTypeFormula
- }
- // Formula returns the formula string for the cell.
- func (c *Cell) Formula() string {
- return c.formula
- }
- // GetStyle returns the Style associated with a Cell
- func (c *Cell) GetStyle() *Style {
- if c.style == nil {
- c.style = NewStyle()
- }
- return c.style
- }
- // SetStyle sets the style of a cell.
- func (c *Cell) SetStyle(style *Style) {
- c.style = style
- }
- // GetNumberFormat returns the number format string for a cell.
- func (c *Cell) GetNumberFormat() string {
- return c.numFmt
- }
- func (c *Cell) formatToFloat(format string) (string, error) {
- f, err := strconv.ParseFloat(c.Value, 64)
- if err != nil {
- return c.Value, err
- }
- return fmt.Sprintf(format, f), nil
- }
- func (c *Cell) formatToInt(format string) (string, error) {
- f, err := strconv.ParseFloat(c.Value, 64)
- if err != nil {
- return c.Value, err
- }
- return fmt.Sprintf(format, int(f)), nil
- }
- // SafeFormattedValue returns a value, and possibly an error condition
- // from a Cell. If it is possible to apply a format to the cell
- // value, it will do so, if not then an error will be returned, along
- // with the raw value of the Cell.
- func (c *Cell) SafeFormattedValue() (string, error) {
- var numberFormat = c.GetNumberFormat()
- if isTimeFormat(numberFormat) {
- return parseTime(c)
- }
- switch numberFormat {
- case builtInNumFmt[builtInNumFmtIndex_GENERAL], builtInNumFmt[builtInNumFmtIndex_STRING]:
- return c.Value, nil
- case builtInNumFmt[builtInNumFmtIndex_INT], "#,##0":
- return c.formatToInt("%d")
- case builtInNumFmt[builtInNumFmtIndex_FLOAT], "#,##0.00":
- return c.formatToFloat("%.2f")
- case "#,##0 ;(#,##0)", "#,##0 ;[red](#,##0)":
- f, err := strconv.ParseFloat(c.Value, 64)
- if err != nil {
- return c.Value, err
- }
- if f < 0 {
- i := int(math.Abs(f))
- return fmt.Sprintf("(%d)", i), nil
- }
- i := int(f)
- return fmt.Sprintf("%d", i), nil
- case "#,##0.00;(#,##0.00)", "#,##0.00;[red](#,##0.00)":
- f, err := strconv.ParseFloat(c.Value, 64)
- if err != nil {
- return c.Value, err
- }
- if f < 0 {
- return fmt.Sprintf("(%.2f)", f), nil
- }
- return fmt.Sprintf("%.2f", f), nil
- case "0%":
- f, err := strconv.ParseFloat(c.Value, 64)
- if err != nil {
- return c.Value, err
- }
- f = f * 100
- return fmt.Sprintf("%d%%", int(f)), nil
- case "0.00%":
- f, err := strconv.ParseFloat(c.Value, 64)
- if err != nil {
- return c.Value, err
- }
- f = f * 100
- return fmt.Sprintf("%.2f%%", f), nil
- case "0.00e+00", "##0.0e+0":
- return c.formatToFloat("%e")
- }
- return c.Value, nil
- }
- // FormattedValue returns the formatted version of the value.
- // If it's a string type, c.Value will just be returned. Otherwise,
- // it will attempt to apply Excel formatting to the value.
- func (c *Cell) FormattedValue() string {
- value, err := c.SafeFormattedValue()
- if err != nil {
- return err.Error()
- }
- return value
- }
- // parseTime returns a string parsed using time.Time
- func parseTime(c *Cell) (string, error) {
- f, err := strconv.ParseFloat(c.Value, 64)
- if err != nil {
- return c.Value, err
- }
- val := TimeFromExcelTime(f, c.date1904)
- format := c.GetNumberFormat()
- // Replace Excel placeholders with Go time placeholders.
- // For example, replace yyyy with 2006. These are in a specific order,
- // due to the fact that m is used in month, minute, and am/pm. It would
- // be easier to fix that with regular expressions, but if it's possible
- // to keep this simple it would be easier to maintain.
- replacements := []struct{ xltime, gotime string }{
- {"yyyy", "2006"},
- {"yy", "06"},
- {"dd", "02"},
- {"d", "2"},
- {"mmm", "Jan"},
- {"mmss", "0405"},
- {"ss", "05"},
- {"hh", "15"},
- {"h", "3"},
- {"mm:", "04:"},
- {":mm", ":04"},
- {"mm", "01"},
- {"am/pm", "pm"},
- {"m/", "1/"},
- {".0", ".9999"},
- }
- for _, repl := range replacements {
- format = strings.Replace(format, repl.xltime, repl.gotime, 1)
- }
- // If the hour is optional, strip it out, along with the
- // possible dangling colon that would remain.
- if val.Hour() < 1 {
- format = strings.Replace(format, "]:", "]", 1)
- format = strings.Replace(format, "[3]", "", 1)
- format = strings.Replace(format, "[15]", "", 1)
- } else {
- format = strings.Replace(format, "[3]", "3", 1)
- format = strings.Replace(format, "[15]", "15", 1)
- }
- return val.Format(format), nil
- }
- // isTimeFormat checks whether an Excel format string represents
- // a time.Time.
- func isTimeFormat(format string) bool {
- dateParts := []string{
- "yy", "hh", "am", "pm", "ss", "mm", ":",
- }
- for _, part := range dateParts {
- if strings.Contains(format, part) {
- return true
- }
- }
- return false
- }
|