cell.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. package xlsx
  2. import (
  3. "fmt"
  4. "math"
  5. "strconv"
  6. "strings"
  7. "time"
  8. )
  9. const (
  10. maxNonScientificNumber = 1e11
  11. minNonScientificNumber = 1e-9
  12. )
  13. // CellType is an int type for storing metadata about the data type in the cell.
  14. type CellType int
  15. // Known types for cell values.
  16. const (
  17. CellTypeString CellType = iota
  18. CellTypeFormula
  19. CellTypeNumeric
  20. CellTypeBool
  21. CellTypeInline
  22. CellTypeError
  23. CellTypeDate
  24. CellTypeGeneral
  25. )
  26. func (ct CellType) Ptr() *CellType {
  27. return &ct
  28. }
  29. // Cell is a high level structure intended to provide user access to
  30. // the contents of Cell within an xlsx.Row.
  31. type Cell struct {
  32. Row *Row
  33. Value string
  34. formula string
  35. style *Style
  36. NumFmt string
  37. date1904 bool
  38. Hidden bool
  39. HMerge int
  40. VMerge int
  41. cellType CellType
  42. }
  43. // CellInterface defines the public API of the Cell.
  44. type CellInterface interface {
  45. String() string
  46. FormattedValue() string
  47. }
  48. // NewCell creates a cell and adds it to a row.
  49. func NewCell(r *Row) *Cell {
  50. return &Cell{Row: r}
  51. }
  52. // Merge with other cells, horizontally and/or vertically.
  53. func (c *Cell) Merge(hcells, vcells int) {
  54. c.HMerge = hcells
  55. c.VMerge = vcells
  56. }
  57. // Type returns the CellType of a cell. See CellType constants for more details.
  58. func (c *Cell) Type() CellType {
  59. return c.cellType
  60. }
  61. // SetString sets the value of a cell to a string.
  62. func (c *Cell) SetString(s string) {
  63. c.Value = s
  64. c.formula = ""
  65. c.cellType = CellTypeString
  66. }
  67. // String returns the value of a Cell as a string. If you'd like to
  68. // see errors returned from formatting then please use
  69. // Cell.FormattedValue() instead.
  70. func (c *Cell) String() string {
  71. // To preserve the String() interface we'll throw away errors.
  72. // Not that using FormattedValue is therefore strongly
  73. // preferred.
  74. value, _ := c.FormattedValue()
  75. return value
  76. }
  77. // SetFloat sets the value of a cell to a float.
  78. func (c *Cell) SetFloat(n float64) {
  79. c.SetValue(n)
  80. }
  81. //GetTime returns the value of a Cell as a time.Time
  82. func (c *Cell) GetTime(date1904 bool) (t time.Time, err error) {
  83. f, err := c.Float()
  84. if err != nil {
  85. return t, err
  86. }
  87. return TimeFromExcelTime(f, date1904), nil
  88. }
  89. /*
  90. The following are samples of format samples.
  91. * "0.00e+00"
  92. * "0", "#,##0"
  93. * "0.00", "#,##0.00", "@"
  94. * "#,##0 ;(#,##0)", "#,##0 ;[red](#,##0)"
  95. * "#,##0.00;(#,##0.00)", "#,##0.00;[red](#,##0.00)"
  96. * "0%", "0.00%"
  97. * "0.00e+00", "##0.0e+0"
  98. */
  99. // SetFloatWithFormat sets the value of a cell to a float and applies
  100. // formatting to the cell.
  101. func (c *Cell) SetFloatWithFormat(n float64, format string) {
  102. // beauty the output when the float is small enough
  103. if n != 0 && n < 0.00001 {
  104. c.Value = strconv.FormatFloat(n, 'e', -1, 64)
  105. } else {
  106. c.Value = strconv.FormatFloat(n, 'f', -1, 64)
  107. }
  108. c.NumFmt = format
  109. c.formula = ""
  110. c.cellType = CellTypeNumeric
  111. }
  112. var timeLocationUTC, _ = time.LoadLocation("UTC")
  113. func TimeToUTCTime(t time.Time) time.Time {
  114. return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), timeLocationUTC)
  115. }
  116. func TimeToExcelTime(t time.Time) float64 {
  117. return float64(t.UnixNano())/8.64e13 + 25569.0
  118. }
  119. // DateTimeOptions are additional options for exporting times
  120. type DateTimeOptions struct {
  121. // Location allows calculating times in other timezones/locations
  122. Location *time.Location
  123. // ExcelTimeFormat is the string you want excel to use to format the datetime
  124. ExcelTimeFormat string
  125. }
  126. var (
  127. DefaultDateFormat = builtInNumFmt[14]
  128. DefaultDateTimeFormat = builtInNumFmt[22]
  129. DefaultDateOptions = DateTimeOptions{
  130. Location: timeLocationUTC,
  131. ExcelTimeFormat: DefaultDateFormat,
  132. }
  133. DefaultDateTimeOptions = DateTimeOptions{
  134. Location: timeLocationUTC,
  135. ExcelTimeFormat: DefaultDateTimeFormat,
  136. }
  137. )
  138. // SetDate sets the value of a cell to a float.
  139. func (c *Cell) SetDate(t time.Time) {
  140. c.SetDateWithOptions(t, DefaultDateOptions)
  141. }
  142. func (c *Cell) SetDateTime(t time.Time) {
  143. c.SetDateWithOptions(t, DefaultDateTimeOptions)
  144. }
  145. // SetDateWithOptions allows for more granular control when exporting dates and times
  146. func (c *Cell) SetDateWithOptions(t time.Time, options DateTimeOptions) {
  147. _, offset := t.In(options.Location).Zone()
  148. t = time.Unix(t.Unix()+int64(offset), 0)
  149. c.SetDateTimeWithFormat(TimeToExcelTime(t.In(timeLocationUTC)), options.ExcelTimeFormat)
  150. }
  151. func (c *Cell) SetDateTimeWithFormat(n float64, format string) {
  152. c.Value = strconv.FormatFloat(n, 'f', -1, 64)
  153. c.NumFmt = format
  154. c.formula = ""
  155. c.cellType = CellTypeDate
  156. }
  157. // Float returns the value of cell as a number.
  158. func (c *Cell) Float() (float64, error) {
  159. f, err := strconv.ParseFloat(c.Value, 64)
  160. if err != nil {
  161. return math.NaN(), err
  162. }
  163. return f, nil
  164. }
  165. // SetInt64 sets a cell's value to a 64-bit integer.
  166. func (c *Cell) SetInt64(n int64) {
  167. c.SetValue(n)
  168. }
  169. // Int64 returns the value of cell as 64-bit integer.
  170. func (c *Cell) Int64() (int64, error) {
  171. f, err := strconv.ParseInt(c.Value, 10, 64)
  172. if err != nil {
  173. return -1, err
  174. }
  175. return f, nil
  176. }
  177. // GeneralNumeric returns the value of the cell as a string. It is formatted very closely to the the XLSX spec for how
  178. // to display values when the storage type is Number and the format type is General. It is not 100% identical to the
  179. // spec but is as close as you can get using the built in Go formatting tools.
  180. func (c *Cell) GeneralNumeric() (string, error) {
  181. f, err := strconv.ParseFloat(c.Value, 64)
  182. if err != nil {
  183. return c.Value, err
  184. }
  185. absF := math.Abs(f)
  186. // When using General format, numbers that are less than 1e-9 (0.000000001) and greater than or equal to
  187. // 1e11 (100,000,000,000) should be shown in scientific notation.
  188. // Numbers less than the number after zero, are assumed to be zero.
  189. if (absF >= math.SmallestNonzeroFloat64 && absF < minNonScientificNumber) || absF >= maxNonScientificNumber {
  190. return strconv.FormatFloat(f, 'E', -1, 64), nil
  191. }
  192. // This format (fmt="f", prec=-1) will prevent padding with zeros and will never switch to scientific notation.
  193. // However, it will show more than 11 characters for very precise numbers, and this cannot be changed.
  194. // You could also use fmt="g", prec=11, which doesn't pad with zeros and allows the correct precision,
  195. // but it will use scientific notation on numbers less than 1e-4. That value is hardcoded in Go and cannot be
  196. // configured or disabled.
  197. return strconv.FormatFloat(f, 'f', -1, 64), nil
  198. }
  199. // GeneralNumericWithoutScientific returns numbers that are always formatted as numbers, but it does not follow
  200. // the rules for when XLSX should switch to scientific notation, since sometimes scientific notation is not desired,
  201. // even if that is how the document is supposed to be formatted.
  202. func (c *Cell) GeneralNumericWithoutScientific() (string, error) {
  203. f, err := strconv.ParseFloat(c.Value, 64)
  204. if err != nil {
  205. return c.Value, err
  206. }
  207. // This format (fmt="f", prec=-1) will prevent padding with zeros and will never switch to scientific notation.
  208. // However, it will show more than 11 characters for very precise numbers, and this cannot be changed.
  209. // You could also use fmt="g", prec=11, which doesn't pad with zeros and allows the correct precision,
  210. // but it will use scientific notation on numbers less than 1e-4. That value is hardcoded in Go and cannot be
  211. // configured or disabled.
  212. return strconv.FormatFloat(f, 'f', -1, 64), nil
  213. }
  214. // SetInt sets a cell's value to an integer.
  215. func (c *Cell) SetInt(n int) {
  216. c.SetValue(n)
  217. }
  218. // SetInt sets a cell's value to an integer.
  219. func (c *Cell) SetValue(n interface{}) {
  220. switch t := n.(type) {
  221. case time.Time:
  222. c.SetDateTime(n.(time.Time))
  223. return
  224. case int, int8, int16, int32, int64, float32, float64:
  225. c.setGeneral(fmt.Sprintf("%v", n))
  226. case string:
  227. c.SetString(t)
  228. case []byte:
  229. c.SetString(string(t))
  230. case nil:
  231. c.SetString("")
  232. default:
  233. c.SetString(fmt.Sprintf("%v", n))
  234. }
  235. }
  236. // SetInt sets a cell's value to an integer.
  237. func (c *Cell) setGeneral(s string) {
  238. c.Value = s
  239. c.NumFmt = builtInNumFmt[builtInNumFmtIndex_GENERAL]
  240. c.formula = ""
  241. c.cellType = CellTypeGeneral
  242. }
  243. // Int returns the value of cell as integer.
  244. // Has max 53 bits of precision
  245. // See: float64(int64(math.MaxInt))
  246. func (c *Cell) Int() (int, error) {
  247. f, err := strconv.ParseFloat(c.Value, 64)
  248. if err != nil {
  249. return -1, err
  250. }
  251. return int(f), nil
  252. }
  253. // SetBool sets a cell's value to a boolean.
  254. func (c *Cell) SetBool(b bool) {
  255. if b {
  256. c.Value = "1"
  257. } else {
  258. c.Value = "0"
  259. }
  260. c.cellType = CellTypeBool
  261. }
  262. // Bool returns a boolean from a cell's value.
  263. // TODO: Determine if the current return value is
  264. // appropriate for types other than CellTypeBool.
  265. func (c *Cell) Bool() bool {
  266. // If bool, just return the value.
  267. if c.cellType == CellTypeBool {
  268. return c.Value == "1"
  269. }
  270. // If numeric, base it on a non-zero.
  271. if c.cellType == CellTypeNumeric || c.cellType == CellTypeGeneral {
  272. return c.Value != "0"
  273. }
  274. // Return whether there's an empty string.
  275. return c.Value != ""
  276. }
  277. // SetFormula sets the format string for a cell.
  278. func (c *Cell) SetFormula(formula string) {
  279. c.formula = formula
  280. c.cellType = CellTypeFormula
  281. }
  282. // Formula returns the formula string for the cell.
  283. func (c *Cell) Formula() string {
  284. return c.formula
  285. }
  286. // GetStyle returns the Style associated with a Cell
  287. func (c *Cell) GetStyle() *Style {
  288. if c.style == nil {
  289. c.style = NewStyle()
  290. }
  291. return c.style
  292. }
  293. // SetStyle sets the style of a cell.
  294. func (c *Cell) SetStyle(style *Style) {
  295. c.style = style
  296. }
  297. // GetNumberFormat returns the number format string for a cell.
  298. func (c *Cell) GetNumberFormat() string {
  299. return c.NumFmt
  300. }
  301. func (c *Cell) formatToFloat(format string) (string, error) {
  302. f, err := strconv.ParseFloat(c.Value, 64)
  303. if err != nil {
  304. return c.Value, err
  305. }
  306. return fmt.Sprintf(format, f), nil
  307. }
  308. func (c *Cell) formatToInt(format string) (string, error) {
  309. f, err := strconv.ParseFloat(c.Value, 64)
  310. if err != nil {
  311. return c.Value, err
  312. }
  313. return fmt.Sprintf(format, int(f)), nil
  314. }
  315. // FormattedValue returns a value, and possibly an error condition
  316. // from a Cell. If it is possible to apply a format to the cell
  317. // value, it will do so, if not then an error will be returned, along
  318. // with the raw value of the Cell.
  319. //
  320. // This is the documentation of the "General" Format in the Office Open XML spec:
  321. //
  322. // Numbers
  323. // The application shall attempt to display the full number up to 11 digits (inc. decimal point). If the number is too
  324. // large*, the application shall attempt to show exponential format. If the number has too many significant digits, the
  325. // display shall be truncated. The optimal method of display is based on the available cell width. If the number cannot
  326. // be displayed using any of these formats in the available width, the application shall show "#" across the width of
  327. // the cell.
  328. //
  329. // Conditions for switching to exponential format:
  330. // 1. The cell value shall have at least five digits for xE-xx
  331. // 2. If the exponent is bigger than the size allowed, a floating point number cannot fit, so try exponential notation.
  332. // 3. Similarly, for negative exponents, check if there is space for even one (non-zero) digit in floating point format**.
  333. // 4. Finally, if there isn't room for all of the significant digits in floating point format (for a negative exponent),
  334. // exponential format shall display more digits if the exponent is less than -3. (The 3 is because E-xx takes 4
  335. // characters, and the leading 0 in floating point takes only 1 character. Thus, for an exponent less than -3, there is
  336. // more than 3 additional leading 0's, more than enough to compensate for the size of the E-xx.)
  337. //
  338. // Floating point rule:
  339. // For general formatting in cells, max overall length for cell display is 11, not including negative sign, but includes
  340. // leading zeros and decimal separator.***
  341. //
  342. // Added Notes:
  343. // * "If the number is too large" can also mean "if the number has more than 11 digits", so greater than or equal to
  344. // 1e11 and less than 1e-9.
  345. // ** Means that you should switch to scientific if there would be 9 zeros after the decimal (the decimal and first zero
  346. // count against the 11 character limit), so less than 1e9.
  347. // *** The way this is written, you can get numbers that are more than 11 characters because the golang Float fmt
  348. // does not support adjusting the precision while not padding with zeros, while also not switching to scientific
  349. // notation too early.
  350. func (c *Cell) FormattedValue() (string, error) {
  351. var numberFormat = c.GetNumberFormat()
  352. if isTimeFormat(numberFormat) {
  353. return parseTime(c)
  354. }
  355. switch numberFormat {
  356. case builtInNumFmt[builtInNumFmtIndex_GENERAL]:
  357. if c.cellType == CellTypeNumeric {
  358. // If the cell type is Numeric, format the string the way it should be shown to the user.
  359. return c.GeneralNumeric()
  360. }
  361. return c.Value, nil
  362. case builtInNumFmt[builtInNumFmtIndex_STRING]:
  363. return c.Value, nil
  364. case builtInNumFmt[builtInNumFmtIndex_INT], "#,##0":
  365. return c.formatToInt("%d")
  366. case builtInNumFmt[builtInNumFmtIndex_FLOAT], "#,##0.00":
  367. return c.formatToFloat("%.2f")
  368. case "#,##0 ;(#,##0)", "#,##0 ;[red](#,##0)":
  369. f, err := strconv.ParseFloat(c.Value, 64)
  370. if err != nil {
  371. return c.Value, err
  372. }
  373. if f < 0 {
  374. i := int(math.Abs(f))
  375. return fmt.Sprintf("(%d)", i), nil
  376. }
  377. i := int(f)
  378. return fmt.Sprintf("%d", i), nil
  379. case "#,##0.00;(#,##0.00)", "#,##0.00;[red](#,##0.00)":
  380. f, err := strconv.ParseFloat(c.Value, 64)
  381. if err != nil {
  382. return c.Value, err
  383. }
  384. if f < 0 {
  385. return fmt.Sprintf("(%.2f)", f), nil
  386. }
  387. return fmt.Sprintf("%.2f", f), nil
  388. case "0%":
  389. f, err := strconv.ParseFloat(c.Value, 64)
  390. if err != nil {
  391. return c.Value, err
  392. }
  393. f = f * 100
  394. return fmt.Sprintf("%d%%", int(f)), nil
  395. case "0.00%":
  396. f, err := strconv.ParseFloat(c.Value, 64)
  397. if err != nil {
  398. return c.Value, err
  399. }
  400. f = f * 100
  401. return fmt.Sprintf("%.2f%%", f), nil
  402. case "0.00e+00", "##0.0e+0":
  403. return c.formatToFloat("%e")
  404. }
  405. return c.Value, nil
  406. }
  407. // parseTime returns a string parsed using time.Time
  408. func parseTime(c *Cell) (string, error) {
  409. f, err := strconv.ParseFloat(c.Value, 64)
  410. if err != nil {
  411. return c.Value, err
  412. }
  413. val := TimeFromExcelTime(f, c.date1904)
  414. format := c.GetNumberFormat()
  415. // Replace Excel placeholders with Go time placeholders.
  416. // For example, replace yyyy with 2006. These are in a specific order,
  417. // due to the fact that m is used in month, minute, and am/pm. It would
  418. // be easier to fix that with regular expressions, but if it's possible
  419. // to keep this simple it would be easier to maintain.
  420. // Full-length month and days (e.g. March, Tuesday) have letters in them that would be replaced
  421. // by other characters below (such as the 'h' in March, or the 'd' in Tuesday) below.
  422. // First we convert them to arbitrary characters unused in Excel Date formats, and then at the end,
  423. // turn them to what they should actually be.
  424. // Based off: http://www.ozgrid.com/Excel/CustomFormats.htm
  425. replacements := []struct{ xltime, gotime string }{
  426. {"yyyy", "2006"},
  427. {"yy", "06"},
  428. {"mmmm", "%%%%"},
  429. {"dddd", "&&&&"},
  430. {"dd", "02"},
  431. {"d", "2"},
  432. {"mmm", "Jan"},
  433. {"mmss", "0405"},
  434. {"ss", "05"},
  435. {"mm:", "04:"},
  436. {":mm", ":04"},
  437. {"mm", "01"},
  438. {"am/pm", "pm"},
  439. {"m/", "1/"},
  440. {"%%%%", "January"},
  441. {"&&&&", "Monday"},
  442. }
  443. // It is the presence of the "am/pm" indicator that determins
  444. // if this is a 12 hour or 24 hours time format, not the
  445. // number of 'h' characters.
  446. if is12HourTime(format) {
  447. format = strings.Replace(format, "hh", "03", 1)
  448. format = strings.Replace(format, "h", "3", 1)
  449. } else {
  450. format = strings.Replace(format, "hh", "15", 1)
  451. format = strings.Replace(format, "h", "15", 1)
  452. }
  453. for _, repl := range replacements {
  454. format = strings.Replace(format, repl.xltime, repl.gotime, 1)
  455. }
  456. // If the hour is optional, strip it out, along with the
  457. // possible dangling colon that would remain.
  458. if val.Hour() < 1 {
  459. format = strings.Replace(format, "]:", "]", 1)
  460. format = strings.Replace(format, "[03]", "", 1)
  461. format = strings.Replace(format, "[3]", "", 1)
  462. format = strings.Replace(format, "[15]", "", 1)
  463. } else {
  464. format = strings.Replace(format, "[3]", "3", 1)
  465. format = strings.Replace(format, "[15]", "15", 1)
  466. }
  467. return val.Format(format), nil
  468. }
  469. // isTimeFormat checks whether an Excel format string represents
  470. // a time.Time.
  471. func isTimeFormat(format string) bool {
  472. dateParts := []string{
  473. "yy", "hh", "h", "am/pm", "AM/PM", "A/P", "a/p", "ss", "mm", ":",
  474. }
  475. for _, part := range dateParts {
  476. if strings.Contains(format, part) {
  477. return true
  478. }
  479. }
  480. return false
  481. }
  482. // is12HourTime checks whether an Excel time format string is a 12
  483. // hours form.
  484. func is12HourTime(format string) bool {
  485. return strings.Contains(format, "am/pm") || strings.Contains(format, "AM/PM") || strings.Contains(format, "a/p") || strings.Contains(format, "A/P")
  486. }