Skip to content

Commit

Permalink
spreadsheet: support setting date/time cell values
Browse files Browse the repository at this point in the history
  • Loading branch information
tbaliance committed Sep 5, 2017
1 parent 7562c4e commit 70051ae
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 3 deletions.
8 changes: 8 additions & 0 deletions optional.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

package gooxml

import "fmt"

// Float64 returns a copy of v as a pointer.
func Float64(v float64) *float64 {
x := v
Expand Down Expand Up @@ -48,3 +50,9 @@ func String(v string) *string {
x := v
return &x
}

// String returns a copy of v as a pointer.
func Stringf(f string, args ...interface{}) *string {
x := fmt.Sprintf(f, args...)
return &x
}
60 changes: 57 additions & 3 deletions spreadsheet/cell.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,21 @@
// Public License version 3.0 as published by the Free Software Foundation and
// appearing in the file LICENSE included in the packaging of this file. A
// commercial license can be purchased by contacting [email protected].

package spreadsheet

import (
"errors"
"log"
"strconv"
"time"

"baliance.com/gooxml"
sml "baliance.com/gooxml/schema/schemas.openxmlformats.org/spreadsheetml"
)

const iso8601Format = "2006-01-02T15:04:05Z07:00"

// Cell is a single cell within a sheet.
type Cell struct {
w *Workbook
Expand Down Expand Up @@ -64,12 +69,59 @@ func (c Cell) SetNumber(v float64) {
c.x.TAttr = sml.ST_CellTypeN
}

// SetBool sets the cell type to boolean and the value to the given boolean value.
// SetBool sets the cell type to boolean and the value to the given boolean
// value.
func (c Cell) SetBool(v bool) {
c.x.V = gooxml.String(strconv.Itoa(boolToInt(v)))
c.x.V = gooxml.String(strconv.Itoa(b2i(v)))
c.x.TAttr = sml.ST_CellTypeB
}

func asUTC(d time.Time) time.Time {
// Excel appears to interpret and serial dates in the local timezone, so
// first ensure the time is converted internally.
d = d.Local()

// Then to avoid any daylight savings differences showing up between our
// epoch and the current time, we 'cast' the time to UTC and later subtract
// from the epoch in UTC.
return time.Date(d.Year(), d.Month(), d.Day(), d.Hour(),
d.Minute(), d.Second(), d.Nanosecond(), time.UTC)
}

// SetTime sets the cell value to a date. It's stored as the number of days past
// th sheet epoch. When we support v5 strict, we can store an ISO 8601 date
// string directly, however that's not allowed with v5 transitional (even
// though it works in Excel).
func (c Cell) SetTime(d time.Time) {
d = asUTC(d)
epoch := c.w.Epoch()
if d.Before(epoch) {
// the ECMA 376 standard says these works, but Excel doesn't appear to
// support negative serial dates
log.Printf("times before 1900 are not supported")
return
}
delta := d.Sub(epoch)
c.x.V = gooxml.Stringf("%G", delta.Hours()/24)
}

// SetDate sets the cell value to a date. It's stored as the number of days past
// th sheet epoch. When we support v5 strict, we can store an ISO 8601 date
// string directly, however that's not allowed with v5 transitional (even
// though it works in Excel).
func (c Cell) SetDate(d time.Time) {
d = asUTC(d)
epoch := c.w.Epoch()
if d.Before(epoch) {
// the ECMA 376 standard says these works, but Excel doesn't appear to
// support negative serial dates
log.Printf("dates before 1900 are not supported")
return
}
delta := d.Sub(epoch)
c.x.V = gooxml.Stringf("%d", int(delta.Hours()/24))
}

// SetStyle applies a style to the cell. This style is referenced in the generated XML
// via CellStyle.Index().
func (c Cell) SetStyle(cs CellStyle) {
Expand Down Expand Up @@ -100,10 +152,12 @@ func (c Cell) GetValue() (string, error) {
case sml.ST_CellTypeE:
case sml.ST_CellTypeN:
case sml.ST_CellTypeStr:
default:
}
return "", errors.New("unsupported cell type")
}
func boolToInt(v bool) int {

func b2i(v bool) int {
if v {
return 1
}
Expand Down

0 comments on commit 70051ae

Please sign in to comment.