This closes #660, supports currency string, and switches argument for the number format code

- Support round millisecond for the date time
- Update built-in number formats mapping
- Update unit tests
- Upgrade dependencies package
This commit is contained in:
xuri 2023-05-04 02:52:26 +00:00 committed by GitHub
parent 7c221cf295
commit bbdb83abf0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 146 additions and 58 deletions

View File

@ -407,7 +407,7 @@ func TestInsertCols(t *testing.T) {
f := NewFile() f := NewFile()
sheet1 := f.GetSheetName(0) sheet1 := f.GetSheetName(0)
fillCells(f, sheet1, 10, 10) assert.NoError(t, fillCells(f, sheet1, 10, 10))
assert.NoError(t, f.SetCellHyperLink(sheet1, "A5", "https://github.com/xuri/excelize", "External")) assert.NoError(t, f.SetCellHyperLink(sheet1, "A5", "https://github.com/xuri/excelize", "External"))
assert.NoError(t, f.MergeCell(sheet1, "A1", "C3")) assert.NoError(t, f.MergeCell(sheet1, "A1", "C3"))
@ -430,7 +430,7 @@ func TestRemoveCol(t *testing.T) {
f := NewFile() f := NewFile()
sheet1 := f.GetSheetName(0) sheet1 := f.GetSheetName(0)
fillCells(f, sheet1, 10, 15) assert.NoError(t, fillCells(f, sheet1, 10, 15))
assert.NoError(t, f.SetCellHyperLink(sheet1, "A5", "https://github.com/xuri/excelize", "External")) assert.NoError(t, f.SetCellHyperLink(sheet1, "A5", "https://github.com/xuri/excelize", "External"))
assert.NoError(t, f.SetCellHyperLink(sheet1, "C5", "https://github.com", "External")) assert.NoError(t, f.SetCellHyperLink(sheet1, "C5", "https://github.com", "External"))

View File

@ -750,10 +750,10 @@ func TestSetCellStyleNumberFormat(t *testing.T) {
idxTbl := []int{0, 1, 2, 3, 4, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49} idxTbl := []int{0, 1, 2, 3, 4, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49}
value := []string{"37947.7500001", "-37947.7500001", "0.007", "2.1", "String"} value := []string{"37947.7500001", "-37947.7500001", "0.007", "2.1", "String"}
expected := [][]string{ expected := [][]string{
{"37947.7500001", "37948", "37947.75", "37,948", "37,947.75", "3794775%", "3794775.00%", "3.79E+04", "37947.7500001", "37947.7500001", "11-22-03", "22-Nov-03", "22-Nov", "Nov-03", "6:00 pm", "6:00:00 pm", "18:00", "18:00:00", "11/22/03 18:00", "37,948 ", "37,948 ", "37,947.75 ", "37,947.75 ", "37947.7500001", "37947.7500001", "37947.7500001", "37947.7500001", "00:00", "910746:00:00", "0000.0", "37947.7500001", "37947.7500001"}, {"37947.7500001", "37948", "37947.75", "37,948", "37,947.75", "3794775%", "3794775.00%", "3.79E+04", "37947.7500001", "37947.7500001", "11-22-03", "22-Nov-03", "22-Nov", "Nov-03", "6:00 PM", "6:00:00 PM", "18:00", "18:00:00", "11/22/03 18:00", "37,948 ", "37,948 ", "37,947.75 ", "37,947.75 ", "37947.7500001", "37947.7500001", "37947.7500001", "37947.7500001", "00:00", "910746:00:00", "00:00.0", "37947.7500001", "37947.7500001"},
{"-37947.7500001", "-37948", "-37947.75", "-37,948", "-37,947.75", "-3794775%", "-3794775.00%", "-3.79E+04", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "(37,948)", "(37,948)", "(37,947.75)", "(37,947.75)", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001"}, {"-37947.7500001", "-37948", "-37947.75", "-37,948", "-37,947.75", "-3794775%", "-3794775.00%", "-3.79E+04", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "(37,948)", "(37,948)", "(37,947.75)", "(37,947.75)", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001"},
{"0.007", "0", "0.01", "0", "0.01", "1%", "0.70%", "7.00E-03", "0.007", "0.007", "12-30-99", "30-Dec-99", "30-Dec", "Dec-99", "0:10 am", "0:10:04 am", "00:10", "00:10:04", "12/30/99 00:10", "0 ", "0 ", "0.01 ", "0.01 ", "0.007", "0.007", "0.007", "0.007", "10:04", "0:10:04", "1004.0", "0.007", "0.007"}, {"0.007", "0", "0.01", "0", "0.01", "1%", "0.70%", "7.00E-03", "0.007", "0.007", "12-30-99", "30-Dec-99", "30-Dec", "Dec-99", "0:10 AM", "0:10:05 AM", "00:10", "00:10:05", "12/30/99 00:10", "0 ", "0 ", "0.01 ", "0.01 ", "0.007", "0.007", "0.007", "0.007", "10:05", "0:10:05", "10:04.8", "0.007", "0.007"},
{"2.1", "2", "2.10", "2", "2.10", "210%", "210.00%", "2.10E+00", "2.1", "2.1", "01-01-00", "1-Jan-00", "1-Jan", "Jan-00", "2:24 am", "2:24:00 am", "02:24", "02:24:00", "1/1/00 02:24", "2 ", "2 ", "2.10 ", "2.10 ", "2.1", "2.1", "2.1", "2.1", "24:00", "50:24:00", "2400.0", "2.1", "2.1"}, {"2.1", "2", "2.10", "2", "2.10", "210%", "210.00%", "2.10E+00", "2.1", "2.1", "01-01-00", "1-Jan-00", "1-Jan", "Jan-00", "2:24 AM", "2:24:00 AM", "02:24", "02:24:00", "1/1/00 02:24", "2 ", "2 ", "2.10 ", "2.10 ", "2.1", "2.1", "2.1", "2.1", "24:00", "50:24:00", "24:00.0", "2.1", "2.1"},
{"String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String"}, {"String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String"},
} }

2
go.mod
View File

@ -7,7 +7,7 @@ require (
github.com/richardlehane/mscfb v1.0.4 github.com/richardlehane/mscfb v1.0.4
github.com/stretchr/testify v1.8.0 github.com/stretchr/testify v1.8.0
github.com/xuri/efp v0.0.0-20230422071738-01f4e37c47e9 github.com/xuri/efp v0.0.0-20230422071738-01f4e37c47e9
github.com/xuri/nfp v0.0.0-20230428090735-b50b0f0358f4 github.com/xuri/nfp v0.0.0-20230503010013-3f38cdbb0b83
golang.org/x/crypto v0.8.0 golang.org/x/crypto v0.8.0
golang.org/x/image v0.5.0 golang.org/x/image v0.5.0
golang.org/x/net v0.9.0 golang.org/x/net v0.9.0

4
go.sum
View File

@ -17,8 +17,8 @@ github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PK
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/xuri/efp v0.0.0-20230422071738-01f4e37c47e9 h1:ge5g8vsTQclA5lXDi+PuiAFw5GMIlMHOB/5e1hsf96E= github.com/xuri/efp v0.0.0-20230422071738-01f4e37c47e9 h1:ge5g8vsTQclA5lXDi+PuiAFw5GMIlMHOB/5e1hsf96E=
github.com/xuri/efp v0.0.0-20230422071738-01f4e37c47e9/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI= github.com/xuri/efp v0.0.0-20230422071738-01f4e37c47e9/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
github.com/xuri/nfp v0.0.0-20230428090735-b50b0f0358f4 h1:YoU/1S7L25dvNepEir3Fg2aU9iGmDyE4gWKoEswWXts= github.com/xuri/nfp v0.0.0-20230503010013-3f38cdbb0b83 h1:xVwnvkzzi+OiwhIkWOXvh1skFI6bagk8OvGuazM80Rw=
github.com/xuri/nfp v0.0.0-20230428090735-b50b0f0358f4/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ= github.com/xuri/nfp v0.0.0-20230503010013-3f38cdbb0b83/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=

152
numfmt.go
View File

@ -36,9 +36,10 @@ type numberFormat struct {
section []nfp.Section section []nfp.Section
t time.Time t time.Time
sectionIdx int sectionIdx int
date1904, isNumeric, hours, seconds bool date1904, isNumeric, hours, seconds, useMillisecond bool
number float64 number float64
ap, localCode, result, value, valueSectionType string ap, localCode, result, value, valueSectionType string
switchArgument, currencyString string
fracHolder, fracPadding, intHolder, intPadding, expBaseLen int fracHolder, fracPadding, intHolder, intPadding, expBaseLen int
percent int percent int
useCommaSep, usePointer, usePositive, useScientificNotation bool useCommaSep, usePointer, usePositive, useScientificNotation bool
@ -47,6 +48,7 @@ type numberFormat struct {
var ( var (
// supportedTokenTypes list the supported number format token types currently. // supportedTokenTypes list the supported number format token types currently.
supportedTokenTypes = []string{ supportedTokenTypes = []string{
nfp.TokenSubTypeCurrencyString,
nfp.TokenSubTypeLanguageInfo, nfp.TokenSubTypeLanguageInfo,
nfp.TokenTypeColor, nfp.TokenTypeColor,
nfp.TokenTypeCurrencyLanguage, nfp.TokenTypeCurrencyLanguage,
@ -58,23 +60,20 @@ var (
nfp.TokenTypeHashPlaceHolder, nfp.TokenTypeHashPlaceHolder,
nfp.TokenTypeLiteral, nfp.TokenTypeLiteral,
nfp.TokenTypePercent, nfp.TokenTypePercent,
nfp.TokenTypeSwitchArgument,
nfp.TokenTypeTextPlaceHolder, nfp.TokenTypeTextPlaceHolder,
nfp.TokenTypeThousandsSeparator, nfp.TokenTypeThousandsSeparator,
nfp.TokenTypeZeroPlaceHolder, nfp.TokenTypeZeroPlaceHolder,
} }
// supportedNumberTokenTypes list the supported number token types. // supportedNumberTokenTypes list the supported number token types.
supportedNumberTokenTypes = []string{ supportedNumberTokenTypes = []string{
nfp.TokenTypeColor, nfp.TokenTypeExponential,
nfp.TokenTypeDecimalPoint,
nfp.TokenTypeHashPlaceHolder, nfp.TokenTypeHashPlaceHolder,
nfp.TokenTypeLiteral,
nfp.TokenTypePercent, nfp.TokenTypePercent,
nfp.TokenTypeThousandsSeparator,
nfp.TokenTypeZeroPlaceHolder, nfp.TokenTypeZeroPlaceHolder,
} }
// supportedDateTimeTokenTypes list the supported date and time token types. // supportedDateTimeTokenTypes list the supported date and time token types.
supportedDateTimeTokenTypes = []string{ supportedDateTimeTokenTypes = []string{
nfp.TokenTypeCurrencyLanguage,
nfp.TokenTypeDateTimes, nfp.TokenTypeDateTimes,
nfp.TokenTypeElapsedDateTimes, nfp.TokenTypeElapsedDateTimes,
} }
@ -357,6 +356,30 @@ var (
apFmtYi = "\ua3b8\ua111/\ua06f\ua2d2" apFmtYi = "\ua3b8\ua111/\ua06f\ua2d2"
// apFmtWelsh defined the AM/PM name in the Welsh. // apFmtWelsh defined the AM/PM name in the Welsh.
apFmtWelsh = "yb/yh" apFmtWelsh = "yb/yh"
// switchArgumentFunc defined the switch argument printer function
switchArgumentFunc = map[string]func(s string) string{
"[DBNum1]": func(s string) string {
r := strings.NewReplacer(
"0", "\u25cb", "1", "\u4e00", "2", "\u4e8c", "3", "\u4e09", "4", "\u56db",
"5", "\u4e94", "6", "\u516d", "7", "\u4e03", "8", "\u516b", "9", "\u4e5d",
)
return r.Replace(s)
},
"[DBNum2]": func(s string) string {
r := strings.NewReplacer(
"0", "\u96f6", "1", "\u58f9", "2", "\u8d30", "3", "\u53c1", "4", "\u8086",
"5", "\u4f0d", "6", "\u9646", "7", "\u67d2", "8", "\u634c", "9", "\u7396",
)
return r.Replace(s)
},
"[DBNum3]": func(s string) string {
r := strings.NewReplacer(
"0", "\uff10", "1", "\uff11", "2", "\uff12", "3", "\uff13", "4", "\uff14",
"5", "\uff15", "6", "\uff16", "7", "\uff17", "8", "\uff18", "9", "\uff19",
)
return r.Replace(s)
},
}
) )
// prepareNumberic split the number into two before and after parts by a // prepareNumberic split the number into two before and after parts by a
@ -431,6 +454,9 @@ func (nf *numberFormat) getNumberFmtConf() {
if token.TType == nfp.TokenTypeDecimalPoint { if token.TType == nfp.TokenTypeDecimalPoint {
nf.usePointer = true nf.usePointer = true
} }
if token.TType == nfp.TokenTypeSwitchArgument {
nf.switchArgument = token.TValue
}
if token.TType == nfp.TokenTypeZeroPlaceHolder { if token.TType == nfp.TokenTypeZeroPlaceHolder {
if nf.usePointer { if nf.usePointer {
if nf.useScientificNotation { if nf.useScientificNotation {
@ -448,20 +474,34 @@ func (nf *numberFormat) getNumberFmtConf() {
// printNumberLiteral apply literal tokens for the pre-formatted text. // printNumberLiteral apply literal tokens for the pre-formatted text.
func (nf *numberFormat) printNumberLiteral(text string) string { func (nf *numberFormat) printNumberLiteral(text string) string {
var result string var result string
var useZeroPlaceHolder bool var useLiteral, useZeroPlaceHolder bool
if nf.usePositive { if nf.usePositive {
result += "-" result += "-"
} }
for _, token := range nf.section[nf.sectionIdx].Items { for i, token := range nf.section[nf.sectionIdx].Items {
if token.TType == nfp.TokenTypeCurrencyLanguage {
if err := nf.currencyLanguageHandler(i, token); err != nil {
return nf.value
}
result += nf.currencyString
}
if token.TType == nfp.TokenTypeLiteral { if token.TType == nfp.TokenTypeLiteral {
if useZeroPlaceHolder {
useLiteral = true
}
result += token.TValue result += token.TValue
} }
if !useZeroPlaceHolder && token.TType == nfp.TokenTypeZeroPlaceHolder { if token.TType == nfp.TokenTypeZeroPlaceHolder {
useZeroPlaceHolder = true if useLiteral && useZeroPlaceHolder {
result += text return nf.value
}
if !useZeroPlaceHolder {
useZeroPlaceHolder = true
result += text
}
} }
} }
return result return nf.printSwitchArgument(result)
} }
// printCommaSep format number with thousands separator. // printCommaSep format number with thousands separator.
@ -484,6 +524,17 @@ func printCommaSep(text string) string {
return target.String() return target.String()
} }
// printSwitchArgument format number with switch argument.
func (nf *numberFormat) printSwitchArgument(text string) string {
if nf.switchArgument == "" {
return text
}
if fn, ok := switchArgumentFunc[nf.switchArgument]; ok {
return fn(text)
}
return nf.value
}
// printBigNumber format number which precision great than 15 with fraction // printBigNumber format number which precision great than 15 with fraction
// zero padding and percentage symbol. // zero padding and percentage symbol.
func (nf *numberFormat) printBigNumber(decimal float64, fracLen int) string { func (nf *numberFormat) printBigNumber(decimal float64, fracLen int) string {
@ -561,14 +612,14 @@ func (nf *numberFormat) numberHandler() string {
// dateTimeHandler handling data and time number format expression for a // dateTimeHandler handling data and time number format expression for a
// positive numeric. // positive numeric.
func (nf *numberFormat) dateTimeHandler() (result string) { func (nf *numberFormat) dateTimeHandler() string {
nf.t, nf.hours, nf.seconds = timeFromExcelTime(nf.number, nf.date1904), false, false nf.t, nf.hours, nf.seconds = timeFromExcelTime(nf.number, nf.date1904), false, false
for i, token := range nf.section[nf.sectionIdx].Items { for i, token := range nf.section[nf.sectionIdx].Items {
if token.TType == nfp.TokenTypeCurrencyLanguage { if token.TType == nfp.TokenTypeCurrencyLanguage {
if err := nf.currencyLanguageHandler(i, token); err != nil { if err := nf.currencyLanguageHandler(i, token); err != nil {
result = nf.value return nf.value
return
} }
nf.result += nf.currencyString
} }
if token.TType == nfp.TokenTypeDateTimes { if token.TType == nfp.TokenTypeDateTimes {
nf.dateTimesHandler(i, token) nf.dateTimesHandler(i, token)
@ -583,15 +634,18 @@ func (nf *numberFormat) dateTimeHandler() (result string) {
if token.TType == nfp.TokenTypeDecimalPoint { if token.TType == nfp.TokenTypeDecimalPoint {
nf.result += "." nf.result += "."
} }
if token.TType == nfp.TokenTypeSwitchArgument {
nf.switchArgument = token.TValue
}
if token.TType == nfp.TokenTypeZeroPlaceHolder { if token.TType == nfp.TokenTypeZeroPlaceHolder {
zeroHolderLen := len(token.TValue) zeroHolderLen := len(token.TValue)
if zeroHolderLen > 3 { if zeroHolderLen > 3 {
zeroHolderLen = 3 zeroHolderLen = 3
} }
nf.result += strings.Repeat("0", zeroHolderLen) nf.result += fmt.Sprintf("%03d", nf.t.Nanosecond()/1e6)[:zeroHolderLen]
} }
} }
return nf.result return nf.printSwitchArgument(nf.result)
} }
// positiveHandler will be handling positive selection for a number format // positiveHandler will be handling positive selection for a number format
@ -609,13 +663,26 @@ func (nf *numberFormat) positiveHandler() string {
if fmtNum || nf.number < 0 { if fmtNum || nf.number < 0 {
return nf.value return nf.value
} }
var useDateTimeTokens bool
for _, token := range nf.section[nf.sectionIdx].Items {
if inStrSlice(supportedDateTimeTokenTypes, token.TType, false) != -1 {
if useDateTimeTokens && nf.useMillisecond {
return nf.value
}
useDateTimeTokens = true
}
if inStrSlice(supportedNumberTokenTypes, token.TType, false) != -1 {
if token.TType == nfp.TokenTypeZeroPlaceHolder {
nf.useMillisecond = true
continue
}
return nf.value
}
}
return nf.dateTimeHandler() return nf.dateTimeHandler()
} }
} }
if fmtNum { return nf.numberHandler()
return nf.numberHandler()
}
return nf.value
} }
// currencyLanguageHandler will be handling currency and language types tokens // currencyLanguageHandler will be handling currency and language types tokens
@ -626,11 +693,16 @@ func (nf *numberFormat) currencyLanguageHandler(i int, token nfp.Token) (err err
err = ErrUnsupportedNumberFormat err = ErrUnsupportedNumberFormat
return return
} }
if _, ok := supportedLanguageInfo[strings.ToUpper(part.Token.TValue)]; !ok { if part.Token.TType == nfp.TokenSubTypeLanguageInfo {
err = ErrUnsupportedNumberFormat if _, ok := supportedLanguageInfo[strings.ToUpper(part.Token.TValue)]; !ok {
return err = ErrUnsupportedNumberFormat
return
}
nf.localCode = strings.ToUpper(part.Token.TValue)
}
if part.Token.TType == nfp.TokenSubTypeCurrencyString {
nf.currencyString = part.Token.TValue
} }
nf.localCode = strings.ToUpper(part.Token.TValue)
} }
return return
} }
@ -1039,17 +1111,17 @@ func (nf *numberFormat) minutesHandler(token nfp.Token) {
// secondsHandler will be handling seconds in the date and times types tokens // secondsHandler will be handling seconds in the date and times types tokens
// for a number format expression. // for a number format expression.
func (nf *numberFormat) secondsHandler(token nfp.Token) { func (nf *numberFormat) secondsHandler(token nfp.Token) {
nf.seconds = strings.Contains(strings.ToUpper(token.TValue), "S") if nf.seconds = strings.Contains(strings.ToUpper(token.TValue), "S"); !nf.seconds {
if nf.seconds { return
switch len(token.TValue) {
case 1:
nf.result += strconv.Itoa(nf.t.Second())
return
default:
nf.result += fmt.Sprintf("%02d", nf.t.Second())
return
}
} }
if !nf.useMillisecond {
nf.t = nf.t.Add(time.Duration(math.Round(float64(nf.t.Nanosecond())/1e9)) * time.Second)
}
if len(token.TValue) == 1 {
nf.result += strconv.Itoa(nf.t.Second())
return
}
nf.result += fmt.Sprintf("%02d", nf.t.Second())
} }
// elapsedDateTimesHandler will be handling elapsed date and times types tokens // elapsedDateTimesHandler will be handling elapsed date and times types tokens
@ -1114,23 +1186,15 @@ func (nf *numberFormat) secondsNext(i int) bool {
// negativeHandler will be handling negative selection for a number format // negativeHandler will be handling negative selection for a number format
// expression. // expression.
func (nf *numberFormat) negativeHandler() (result string) { func (nf *numberFormat) negativeHandler() (result string) {
fmtNum := true
for _, token := range nf.section[nf.sectionIdx].Items { for _, token := range nf.section[nf.sectionIdx].Items {
if inStrSlice(supportedTokenTypes, token.TType, true) == -1 || token.TType == nfp.TokenTypeGeneral { if inStrSlice(supportedTokenTypes, token.TType, true) == -1 || token.TType == nfp.TokenTypeGeneral {
return nf.value return nf.value
} }
if inStrSlice(supportedNumberTokenTypes, token.TType, true) != -1 {
continue
}
if inStrSlice(supportedDateTimeTokenTypes, token.TType, true) != -1 { if inStrSlice(supportedDateTimeTokenTypes, token.TType, true) != -1 {
return nf.value return nf.value
} }
fmtNum = false
} }
if fmtNum { return nf.numberHandler()
return nf.numberHandler()
}
return nf.value
} }
// zeroHandler will be handling zero selection for a number format expression. // zeroHandler will be handling zero selection for a number format expression.

View File

@ -4,6 +4,7 @@ import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/xuri/nfp"
) )
func TestNumFmt(t *testing.T) { func TestNumFmt(t *testing.T) {
@ -67,7 +68,7 @@ func TestNumFmt(t *testing.T) {
{"43528", "[$-409]MM/DD/YYYY", "03/04/2019"}, {"43528", "[$-409]MM/DD/YYYY", "03/04/2019"},
{"43528", "[$-409]MM/DD/YYYY am/pm", "03/04/2019 AM"}, {"43528", "[$-409]MM/DD/YYYY am/pm", "03/04/2019 AM"},
{"43528", "[$-111]MM/DD/YYYY", "43528"}, {"43528", "[$-111]MM/DD/YYYY", "43528"},
{"43528", "[$US-409]MM/DD/YYYY", "43528"}, {"43528", "[$US-409]MM/DD/YYYY", "US03/04/2019"},
{"43543.586539351854", "AM/PM h h:mm", "PM 14 2:04"}, {"43543.586539351854", "AM/PM h h:mm", "PM 14 2:04"},
{"text", "AM/PM h h:mm", "text"}, {"text", "AM/PM h h:mm", "text"},
{"44562.189571759256", "[$-36]mmm dd yyyy h:mm AM/PM", "Jan. 01 2022 4:32 vm."}, {"44562.189571759256", "[$-36]mmm dd yyyy h:mm AM/PM", "Jan. 01 2022 4:32 vm."},
@ -1017,6 +1018,18 @@ func TestNumFmt(t *testing.T) {
{"-1234.5678", "0.00;-0.00", "-1234.57"}, {"-1234.5678", "0.00;-0.00", "-1234.57"},
{"-1234.5678", "0.00%%", "-12345678.00%%"}, {"-1234.5678", "0.00%%", "-12345678.00%%"},
{"2.1", "mmss.0000", "2400.000"}, {"2.1", "mmss.0000", "2400.000"},
{"0.007", "[h]:mm:ss.0", "0:10:04.8"},
{"0.007", "[h]:mm:ss.00", "0:10:04.80"},
{"0.007", "[h]:mm:ss.000", "0:10:04.800"},
{"0.007", "[h]:mm:ss.0000", "0:10:04.800"},
{"123", "[h]:mm,:ss.0", "2952:00,:00.0"},
{"123", "yy-.dd", "00-.02"},
{"123", "[DBNum1][$-804]yyyy\"年\"m\"月\";@", "\u4e00\u4e5d\u25cb\u25cb\u5e74\u4e94\u6708"},
{"123", "[DBNum2][$-804]yyyy\"年\"m\"月\";@", "\u58f9\u7396\u96f6\u96f6\u5e74\u4f0d\u6708"},
{"123", "[DBNum3][$-804]yyyy\"年\"m\"月\";@", "\uff11\uff19\uff10\uff10\u5e74\uff15\u6708"},
{"1234567890", "[DBNum1][$-804]0.00", "\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u25cb.\u25cb\u25cb"},
{"1234567890", "[DBNum2][$-804]0.00", "\u58f9\u8d30\u53c1\u8086\u4f0d\u9646\u67d2\u634c\u7396\u96f6.\u96f6\u96f6"},
{"1234567890", "[DBNum3][$-804]0.00", "\uff11\uff12\uff13\uff14\uff15\uff16\uff17\uff18\uff19\uff10.\uff10\uff10"},
{"1234.5678", "0.00###", "1234.5678"}, {"1234.5678", "0.00###", "1234.5678"},
{"1234.5678", "00000.00###", "01234.5678"}, {"1234.5678", "00000.00###", "01234.5678"},
{"-1234.5678", "00000.00###;;", ""}, {"-1234.5678", "00000.00###;;", ""},
@ -1029,14 +1042,23 @@ func TestNumFmt(t *testing.T) {
{"1.234E-16", "0.000000000000000000", "0.000000000000000123"}, {"1.234E-16", "0.000000000000000000", "0.000000000000000123"},
{"1.234E-16", "0.000000000000000000%", "0.000000000000012340%"}, {"1.234E-16", "0.000000000000000000%", "0.000000000000012340%"},
{"1.234E-16", "0.000000000000000000%%%%", "0.000000000000012340%"}, {"1.234E-16", "0.000000000000000000%%%%", "0.000000000000012340%"},
{"1234.5678", "[$$-409]#,##0.00", "$1,234.57"},
// Unsupported number format // Unsupported number format
{"37947.7500001", "0.00000000E+000", "37947.7500001"}, {"37947.7500001", "0.00000000E+000", "37947.7500001"},
{"123", "[$kr.-46F]#,##0.00", "123"},
{"123", "[$kr.-46F]MM/DD/YYYY", "123"},
{"123", "[DBNum4][$-804]yyyy\"年\"m\"月\";@", "123"},
// Invalid number format // Invalid number format
{"123", "x0.00s", "123"}, {"123", "x0.00s", "123"},
{"123", "[h]:m00m:ss", "123"},
{"123", "yy-00dd", "123"},
{"123", "yy-##dd", "123"},
{"123", "xx[h]:mm,:ss.0xx", "xx2952:00,:00.0xx"},
{"-123", "x0.00s", "-123"}, {"-123", "x0.00s", "-123"},
{"-1234.5678", ";E+;", "-1234.5678"}, {"-1234.5678", ";E+;", "-1234.5678"},
{"1234.5678", "E+;", "1234.5678"}, {"1234.5678", "E+;", "1234.5678"},
{"1234.5678", "00000.00###s", "1234.5678"}, {"1234.5678", "00000.00###s", "1234.5678"},
{"1234.5678", "0.0xxx00", "1234.5678"},
{"-1234.5678", "00000.00###;s;", "-1234.5678"}, {"-1234.5678", "00000.00###;s;", "-1234.5678"},
} { } {
result := format(item[0], item[1], false, CellTypeNumber) result := format(item[0], item[1], false, CellTypeNumber)
@ -1055,4 +1077,6 @@ func TestNumFmt(t *testing.T) {
assert.Equal(t, item[2], result, item) assert.Equal(t, item[2], result, item)
} }
} }
nf := numberFormat{}
assert.Equal(t, ErrUnsupportedNumberFormat, nf.currencyLanguageHandler(0, nfp.Token{Parts: []nfp.Part{{}}}))
} }

View File

@ -305,7 +305,7 @@ func TestRemoveRow(t *testing.T) {
colCount = 10 colCount = 10
rowCount = 10 rowCount = 10
) )
fillCells(f, sheet1, colCount, rowCount) assert.NoError(t, fillCells(f, sheet1, colCount, rowCount))
assert.NoError(t, f.SetCellHyperLink(sheet1, "A5", "https://github.com/xuri/excelize", "External")) assert.NoError(t, f.SetCellHyperLink(sheet1, "A5", "https://github.com/xuri/excelize", "External"))
@ -368,7 +368,7 @@ func TestInsertRows(t *testing.T) {
colCount = 10 colCount = 10
rowCount = 10 rowCount = 10
) )
fillCells(f, sheet1, colCount, rowCount) assert.NoError(t, fillCells(f, sheet1, colCount, rowCount))
assert.NoError(t, f.SetCellHyperLink(sheet1, "A5", "https://github.com/xuri/excelize", "External")) assert.NoError(t, f.SetCellHyperLink(sheet1, "A5", "https://github.com/xuri/excelize", "External"))

View File

@ -41,8 +41,8 @@ var builtInNumFmt = map[int]string{
15: "d-mmm-yy", 15: "d-mmm-yy",
16: "d-mmm", 16: "d-mmm",
17: "mmm-yy", 17: "mmm-yy",
18: "h:mm am/pm", 18: "h:mm AM/PM",
19: "h:mm:ss am/pm", 19: "h:mm:ss AM/PM",
20: "hh:mm", 20: "hh:mm",
21: "hh:mm:ss", 21: "hh:mm:ss",
22: "m/d/yy hh:mm", 22: "m/d/yy hh:mm",
@ -56,7 +56,7 @@ var builtInNumFmt = map[int]string{
44: `_("$"* #,##0.00_);_("$"* \(#,##0.00\);_("$"* "-"??_);_(@_)`, 44: `_("$"* #,##0.00_);_("$"* \(#,##0.00\);_("$"* "-"??_);_(@_)`,
45: "mm:ss", 45: "mm:ss",
46: "[h]:mm:ss", 46: "[h]:mm:ss",
47: "mmss.0", 47: "mm:ss.0",
48: "##0.0E+0", 48: "##0.0E+0",
49: "@", 49: "@",
} }