This closes #1212, init support for 1900 or 1904 date system

This commit is contained in:
xuri 2022-04-30 09:54:11 +08:00 committed by GitHub
parent 0f93bd23c9
commit 856ee57c40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 94 additions and 49 deletions

64
cell.go
View File

@ -109,8 +109,12 @@ func (f *File) GetCellType(sheet, axis string) (CellType, error) {
// bool
// nil
//
// Note that default date format is m/d/yy h:mm of time.Time type value. You can
// set numbers format by SetCellStyle() method.
// Note that default date format is m/d/yy h:mm of time.Time type value. You
// can set numbers format by SetCellStyle() method. If you need to set the
// specialized date in Excel like January 0, 1900 or February 29, 1900, these
// times can not representation in Go language time.Time data type. Please set
// the cell value as number 0 or 60, then create and bind the date-time number
// format style for the cell.
func (f *File) SetCellValue(sheet, axis string, value interface{}) error {
var err error
switch v := value.(type) {
@ -240,7 +244,7 @@ func setCellTime(value time.Time) (t string, b string, isNum bool, err error) {
// setCellDuration prepares cell type and value by given Go time.Duration type
// time duration.
func setCellDuration(value time.Duration) (t string, v string) {
v = strconv.FormatFloat(value.Seconds()/86400.0, 'f', -1, 32)
v = strconv.FormatFloat(value.Seconds()/86400, 'f', -1, 32)
return
}
@ -752,26 +756,8 @@ func (f *File) SetCellHyperLink(sheet, axis, link, linkType string, opts ...Hype
return nil
}
// GetCellRichText provides a function to get rich text of cell by given
// worksheet.
func (f *File) GetCellRichText(sheet, cell string) (runs []RichTextRun, err error) {
ws, err := f.workSheetReader(sheet)
if err != nil {
return
}
cellData, _, _, err := f.prepareCell(ws, cell)
if err != nil {
return
}
siIdx, err := strconv.Atoi(cellData.V)
if err != nil || cellData.T != "s" {
return
}
sst := f.sharedStringsReader()
if len(sst.SI) <= siIdx || siIdx < 0 {
return
}
si := sst.SI[siIdx]
// getCellRichText returns rich text of cell by given string item.
func getCellRichText(si *xlsxSI) (runs []RichTextRun) {
for _, v := range si.R {
run := RichTextRun{
Text: v.T.Val,
@ -803,6 +789,29 @@ func (f *File) GetCellRichText(sheet, cell string) (runs []RichTextRun, err erro
return
}
// GetCellRichText provides a function to get rich text of cell by given
// worksheet.
func (f *File) GetCellRichText(sheet, cell string) (runs []RichTextRun, err error) {
ws, err := f.workSheetReader(sheet)
if err != nil {
return
}
cellData, _, _, err := f.prepareCell(ws, cell)
if err != nil {
return
}
siIdx, err := strconv.Atoi(cellData.V)
if err != nil || cellData.T != "s" {
return
}
sst := f.sharedStringsReader()
if len(sst.SI) <= siIdx || siIdx < 0 {
return
}
runs = getCellRichText(&sst.SI[siIdx])
return
}
// newRpr create run properties for the rich text by given font format.
func newRpr(fnt *Font) *xlsxRPr {
rpr := xlsxRPr{}
@ -1099,17 +1108,20 @@ func (f *File) formattedValue(s int, v string, raw bool) string {
if styleSheet.CellXfs.Xf[s].NumFmtID != nil {
numFmtID = *styleSheet.CellXfs.Xf[s].NumFmtID
}
date1904, wb := false, f.workbookReader()
if wb != nil && wb.WorkbookPr != nil {
date1904 = wb.WorkbookPr.Date1904
}
ok := builtInNumFmtFunc[numFmtID]
if ok != nil {
return ok(v, builtInNumFmt[numFmtID])
return ok(v, builtInNumFmt[numFmtID], date1904)
}
if styleSheet == nil || styleSheet.NumFmts == nil {
return v
}
for _, xlsxFmt := range styleSheet.NumFmts.NumFmt {
if xlsxFmt.NumFmtID == numFmtID {
return format(v, xlsxFmt.FormatCode)
return format(v, xlsxFmt.FormatCode, date1904)
}
}
return v

View File

@ -479,8 +479,8 @@ func parseFormatChartSet(formatSet string) (*formatChart, error) {
},
Format: formatPicture{
FPrintsWithSheet: true,
XScale: 1.0,
YScale: 1.0,
XScale: 1,
YScale: 1,
},
Legend: formatChartLegend{
Position: "bottom",

View File

@ -36,7 +36,7 @@ func timeToExcelTime(t time.Time) (float64, error) {
// TODO in future this should probably also handle date1904 and like TimeFromExcelTime
if t.Before(excelMinTime1900) {
return 0.0, nil
return 0, nil
}
tt := t
@ -58,7 +58,7 @@ func timeToExcelTime(t time.Time) (float64, error) {
// program that had the majority market share at the time; Lotus 1-2-3.
// https://www.myonlinetraininghub.com/excel-date-and-time
if t.After(excelBuggyPeriodStart) {
result += 1.0
result++
}
return result, nil
}

View File

@ -34,7 +34,7 @@ type numberFormat struct {
section []nfp.Section
t time.Time
sectionIdx int
isNumeric, hours, seconds bool
date1904, isNumeric, hours, seconds bool
number float64
ap, afterPoint, beforePoint, localCode, result, value, valueSectionType string
}
@ -287,9 +287,9 @@ func (nf *numberFormat) prepareNumberic(value string) {
// format provides a function to return a string parse by number format
// expression. If the given number format is not supported, this will return
// the original cell value.
func format(value, numFmt string) string {
func format(value, numFmt string, date1904 bool) string {
p := nfp.NumberFormatParser()
nf := numberFormat{section: p.Parse(numFmt), value: value}
nf := numberFormat{section: p.Parse(numFmt), value: value, date1904: date1904}
nf.number, nf.valueSectionType = nf.getValueSectionType(value)
nf.prepareNumberic(value)
for i, section := range nf.section {
@ -315,7 +315,7 @@ func format(value, numFmt string) string {
// positiveHandler will be handling positive selection for a number format
// expression.
func (nf *numberFormat) positiveHandler() (result string) {
nf.t, nf.hours, nf.seconds = timeFromExcelTime(nf.number, false), false, false
nf.t, nf.hours, nf.seconds = timeFromExcelTime(nf.number, nf.date1904), false, false
for i, token := range nf.section[nf.sectionIdx].Items {
if inStrSlice(supportedTokenTypes, token.TType, true) == -1 || token.TType == nfp.TokenTypeGeneral {
result = nf.value

View File

@ -1005,7 +1005,7 @@ func TestNumFmt(t *testing.T) {
{"-8.0450685976001E-21", "0_);[Red]\\(0\\)", "(0)"},
{"-8.04506", "0_);[Red]\\(0\\)", "(8)"},
} {
result := format(item[0], item[1])
result := format(item[0], item[1], false)
assert.Equal(t, item[2], result, item)
}
}

View File

@ -31,8 +31,8 @@ import (
func parseFormatPictureSet(formatSet string) (*formatPicture, error) {
format := formatPicture{
FPrintsWithSheet: true,
XScale: 1.0,
YScale: 1.0,
XScale: 1,
YScale: 1,
}
err := json.Unmarshal(parseFormatSet(formatSet), &format)
return &format, err

View File

@ -25,8 +25,8 @@ func parseFormatShapeSet(formatSet string) (*formatShape, error) {
Height: 160,
Format: formatPicture{
FPrintsWithSheet: true,
XScale: 1.0,
YScale: 1.0,
XScale: 1,
YScale: 1,
},
Line: formatLine{Width: 1},
}

View File

@ -754,7 +754,7 @@ var currencyNumFmt = map[int]string{
// builtInNumFmtFunc defined the format conversion functions map. Partial format
// code doesn't support currently and will return original string.
var builtInNumFmtFunc = map[int]func(v string, format string) string{
var builtInNumFmtFunc = map[int]func(v, format string, date1904 bool) string{
0: format,
1: formatToInt,
2: formatToFloat,
@ -847,7 +847,7 @@ var criteriaType = map[string]string{
// formatToInt provides a function to convert original string to integer
// format as string type by given built-in number formats code and cell
// string.
func formatToInt(v string, format string) string {
func formatToInt(v, format string, date1904 bool) string {
f, err := strconv.ParseFloat(v, 64)
if err != nil {
return v
@ -858,7 +858,7 @@ func formatToInt(v string, format string) string {
// formatToFloat provides a function to convert original string to float
// format as string type by given built-in number formats code and cell
// string.
func formatToFloat(v string, format string) string {
func formatToFloat(v, format string, date1904 bool) string {
f, err := strconv.ParseFloat(v, 64)
if err != nil {
return v
@ -868,7 +868,7 @@ func formatToFloat(v string, format string) string {
// formatToA provides a function to convert original string to special format
// as string type by given built-in number formats code and cell string.
func formatToA(v string, format string) string {
func formatToA(v, format string, date1904 bool) string {
f, err := strconv.ParseFloat(v, 64)
if err != nil {
return v
@ -883,7 +883,7 @@ func formatToA(v string, format string) string {
// formatToB provides a function to convert original string to special format
// as string type by given built-in number formats code and cell string.
func formatToB(v string, format string) string {
func formatToB(v, format string, date1904 bool) string {
f, err := strconv.ParseFloat(v, 64)
if err != nil {
return v
@ -896,7 +896,7 @@ func formatToB(v string, format string) string {
// formatToC provides a function to convert original string to special format
// as string type by given built-in number formats code and cell string.
func formatToC(v string, format string) string {
func formatToC(v, format string, date1904 bool) string {
f, err := strconv.ParseFloat(v, 64)
if err != nil {
return v
@ -907,7 +907,7 @@ func formatToC(v string, format string) string {
// formatToD provides a function to convert original string to special format
// as string type by given built-in number formats code and cell string.
func formatToD(v string, format string) string {
func formatToD(v, format string, date1904 bool) string {
f, err := strconv.ParseFloat(v, 64)
if err != nil {
return v
@ -918,7 +918,7 @@ func formatToD(v string, format string) string {
// formatToE provides a function to convert original string to special format
// as string type by given built-in number formats code and cell string.
func formatToE(v string, format string) string {
func formatToE(v, format string, date1904 bool) string {
f, err := strconv.ParseFloat(v, 64)
if err != nil {
return v

View File

@ -33,6 +33,10 @@ type WorkbookPrOptionPtr interface {
}
type (
// Date1904 is an option used for WorkbookPrOption, that indicates whether
// to use a 1900 or 1904 date system when converting serial date-times in
// the workbook to dates
Date1904 bool
// FilterPrivacy is an option used for WorkbookPrOption
FilterPrivacy bool
)
@ -116,6 +120,7 @@ func (f *File) workBookWriter() {
// SetWorkbookPrOptions provides a function to sets workbook properties.
//
// Available options:
// Date1904(bool)
// FilterPrivacy(bool)
// CodeName(string)
func (f *File) SetWorkbookPrOptions(opts ...WorkbookPrOption) error {
@ -131,6 +136,11 @@ func (f *File) SetWorkbookPrOptions(opts ...WorkbookPrOption) error {
return nil
}
// setWorkbookPrOption implements the WorkbookPrOption interface.
func (o Date1904) setWorkbookPrOption(pr *xlsxWorkbookPr) {
pr.Date1904 = bool(o)
}
// setWorkbookPrOption implements the WorkbookPrOption interface.
func (o FilterPrivacy) setWorkbookPrOption(pr *xlsxWorkbookPr) {
pr.FilterPrivacy = bool(o)
@ -144,6 +154,7 @@ func (o CodeName) setWorkbookPrOption(pr *xlsxWorkbookPr) {
// GetWorkbookPrOptions provides a function to gets workbook properties.
//
// Available options:
// Date1904(bool)
// FilterPrivacy(bool)
// CodeName(string)
func (f *File) GetWorkbookPrOptions(opts ...WorkbookPrOptionPtr) error {
@ -156,7 +167,17 @@ func (f *File) GetWorkbookPrOptions(opts ...WorkbookPrOptionPtr) error {
}
// getWorkbookPrOption implements the WorkbookPrOption interface and get the
// filter privacy of thw workbook.
// date1904 of the workbook.
func (o *Date1904) getWorkbookPrOption(pr *xlsxWorkbookPr) {
if pr == nil {
*o = false
return
}
*o = Date1904(pr.Date1904)
}
// getWorkbookPrOption implements the WorkbookPrOption interface and get the
// filter privacy of the workbook.
func (o *FilterPrivacy) getWorkbookPrOption(pr *xlsxWorkbookPr) {
if pr == nil {
*o = false
@ -166,7 +187,7 @@ func (o *FilterPrivacy) getWorkbookPrOption(pr *xlsxWorkbookPr) {
}
// getWorkbookPrOption implements the WorkbookPrOption interface and get the
// code name of thw workbook.
// code name of the workbook.
func (o *CodeName) getWorkbookPrOption(pr *xlsxWorkbookPr) {
if pr == nil {
*o = ""

View File

@ -10,6 +10,7 @@ import (
func ExampleFile_SetWorkbookPrOptions() {
f := NewFile()
if err := f.SetWorkbookPrOptions(
Date1904(false),
FilterPrivacy(false),
CodeName("code"),
); err != nil {
@ -21,9 +22,13 @@ func ExampleFile_SetWorkbookPrOptions() {
func ExampleFile_GetWorkbookPrOptions() {
f := NewFile()
var (
date1904 Date1904
filterPrivacy FilterPrivacy
codeName CodeName
)
if err := f.GetWorkbookPrOptions(&date1904); err != nil {
fmt.Println(err)
}
if err := f.GetWorkbookPrOptions(&filterPrivacy); err != nil {
fmt.Println(err)
}
@ -31,10 +36,12 @@ func ExampleFile_GetWorkbookPrOptions() {
fmt.Println(err)
}
fmt.Println("Defaults:")
fmt.Printf("- date1904: %t\n", date1904)
fmt.Printf("- filterPrivacy: %t\n", filterPrivacy)
fmt.Printf("- codeName: %q\n", codeName)
// Output:
// Defaults:
// - date1904: false
// - filterPrivacy: true
// - codeName: ""
}
@ -42,6 +49,11 @@ func ExampleFile_GetWorkbookPrOptions() {
func TestWorkbookPr(t *testing.T) {
f := NewFile()
wb := f.workbookReader()
wb.WorkbookPr = nil
var date1904 Date1904
assert.NoError(t, f.GetWorkbookPrOptions(&date1904))
assert.Equal(t, false, bool(date1904))
wb.WorkbookPr = nil
var codeName CodeName
assert.NoError(t, f.GetWorkbookPrOptions(&codeName))