excelize/numfmt.go

1260 lines
50 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright 2016 - 2023 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to and
// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.16 or later.
package excelize
import (
"fmt"
"math"
"strconv"
"strings"
"time"
"github.com/xuri/nfp"
)
// languageInfo defined the required fields of localization support for number
// format.
type languageInfo struct {
apFmt string
tags []string
localMonth func(t time.Time, abbr int) string
}
// numberFormat directly maps the number format parser runtime required
// fields.
type numberFormat struct {
opts *Options
cellType CellType
section []nfp.Section
t time.Time
sectionIdx int
date1904, isNumeric, hours, seconds, useMillisecond bool
number float64
ap, localCode, result, value, valueSectionType string
switchArgument, currencyString string
fracHolder, fracPadding, intHolder, intPadding, expBaseLen int
percent int
useCommaSep, usePointer, usePositive, useScientificNotation bool
}
var (
// supportedTokenTypes list the supported number format token types currently.
supportedTokenTypes = []string{
nfp.TokenSubTypeCurrencyString,
nfp.TokenSubTypeLanguageInfo,
nfp.TokenTypeColor,
nfp.TokenTypeCurrencyLanguage,
nfp.TokenTypeDateTimes,
nfp.TokenTypeDecimalPoint,
nfp.TokenTypeElapsedDateTimes,
nfp.TokenTypeExponential,
nfp.TokenTypeGeneral,
nfp.TokenTypeHashPlaceHolder,
nfp.TokenTypeLiteral,
nfp.TokenTypePercent,
nfp.TokenTypeSwitchArgument,
nfp.TokenTypeTextPlaceHolder,
nfp.TokenTypeThousandsSeparator,
nfp.TokenTypeZeroPlaceHolder,
}
// supportedNumberTokenTypes list the supported number token types.
supportedNumberTokenTypes = []string{
nfp.TokenTypeExponential,
nfp.TokenTypeHashPlaceHolder,
nfp.TokenTypePercent,
nfp.TokenTypeZeroPlaceHolder,
}
// supportedDateTimeTokenTypes list the supported date and time token types.
supportedDateTimeTokenTypes = []string{
nfp.TokenTypeDateTimes,
nfp.TokenTypeElapsedDateTimes,
}
// supportedLanguageInfo directly maps the supported language ID and tags.
supportedLanguageInfo = map[string]languageInfo{
"36": {tags: []string{"af"}, localMonth: localMonthsNameAfrikaans, apFmt: apFmtAfrikaans},
"445": {tags: []string{"bn-IN"}, localMonth: localMonthsNameBangla, apFmt: nfp.AmPm[0]},
"4": {tags: []string{"zh-Hans"}, localMonth: localMonthsNameChinese1, apFmt: nfp.AmPm[2]},
"7804": {tags: []string{"zh"}, localMonth: localMonthsNameChinese1, apFmt: nfp.AmPm[2]},
"804": {tags: []string{"zh-CN"}, localMonth: localMonthsNameChinese1, apFmt: nfp.AmPm[2]},
"1004": {tags: []string{"zh-SG"}, localMonth: localMonthsNameChinese2, apFmt: nfp.AmPm[2]},
"7C04": {tags: []string{"zh-Hant"}, localMonth: localMonthsNameChinese3, apFmt: nfp.AmPm[2]},
"C04": {tags: []string{"zh-HK"}, localMonth: localMonthsNameChinese2, apFmt: nfp.AmPm[2]},
"1404": {tags: []string{"zh-MO"}, localMonth: localMonthsNameChinese3, apFmt: nfp.AmPm[2]},
"404": {tags: []string{"zh-TW"}, localMonth: localMonthsNameChinese3, apFmt: nfp.AmPm[2]},
"9": {tags: []string{"en"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0]},
"1000": {tags: []string{
"aa", "aa-DJ", "aa-ER", "aa-ER", "aa-NA", "agq", "agq-CM", "ak", "ak-GH", "sq-ML",
"gsw-LI", "gsw-CH", "ar-TD", "ar-KM", "ar-DJ", "ar-ER", "ar-IL", "ar-MR", "ar-PS",
"ar-SO", "ar-SS", "ar-SD", "ar-001", "ast", "ast-ES", "asa", "asa-TZ", "ksf", "ksf-CM",
"bm", "bm-Latn-ML", "bas", "bas-CM", "bem", "bem-ZM", "bez", "bez-TZ", "byn", "byn-ER",
"brx", "brx-IN", "ca-AD", "ca-FR", "ca-IT", "ceb", "ceb-Latn", "ceb-Latn-PH", "tzm-Latn-MA",
"ccp", "ccp-Cakm", "ccp-Cakm-BD", "ccp-Cakm-IN", "ce-RU", "cgg", "cgg-UG", "cu-RU", "swc",
"swc-CD", "kw", "ke-GB", "da-GL", "dua", "dua-CM", "nl-AW", "nl-BQ", "nl-CW", "nl-SX",
"nl-SR", "dz", "ebu", "ebu-KE", "en-AS", "en-AI", "en-AG", "en-AT", "en-BS", "en-BB",
"en-BE", "en-BM", "en-BW", "en-IO", "en-VG", "en-BI", "en-CM", "en-KY", "en-CX", "en-CC",
"en-CK", "en-CY", "en-DK", "en-DM", "en-ER", "en-150", "en-FK", "en-FI", "en-FJ", "en-GM",
"en-DE", "en-GH", "en-GI", "en-GD", "en-GU", "en-GG", "en-GY", "en-IM", "en-IL", "en-JE",
"en-KE", "en-KI", "en-LS", "en-LR", "en-MO", "en-MG", "en-MW", "en-MT", "en-MH", "en-MU",
"en-FM", "en-MS", "en-NA", "en-NR", "en-NL", "en-NG", "en-NU", "en-NF", "en-MP", "en-PK",
"en-PW", "en-PG", "en-PN", "en-PR", "en-RW", "en-KN", "en-LC", "en-VC", "en-WS", "en-SC",
"en-SL", "en-SX", "en-SI", "en-SB", "en-SS", "en-SH", "en-SD", "en-SZ", "en-SE", "en-CH",
"en-TZ", "en-TK", "en-TO", "en-TC", "en-TV", "en-UG", "en-UM", "en-VI", "en-VU", "en-001",
"en-ZM", "eo", "eo-001", "ee", "ee-GH", "ee-TG", "ewo", "ewo-CM", "fo-DK", "fr-DZ",
"fr-BJ", "fr-BF", "fr-BI", "fr-CF", "fr-TD", "fr-KM", "fr-CG", "fr-DJ", "fr-GQ", "fr-GF",
"fr-PF", "fr-GA", "fr-GP", "fr-GN", "fr-MG", "fr-MQ", "fr-MR", "fr-MU", "fr-YT", "fr-NC",
"fr-NE", "fr-RW", "fr-BL", "fr-MF", "fr-PM", "fr-SC", "fr-SY", "fr-TG", "fr-TN", "fr-VU",
"fr-WF", "fur", "fur-IT", "ff-Latn-BF", "ff-CM", "ff-Latn-CM", "ff-Latn-GM", "ff-Latn-GH",
"ff-GN", "ff-Latn-GN", "ff-Latn-GW", "ff-Latn-LR", "ff-MR", "ff-Latn-MR", "ff-Latn-NE",
"ff-Latn-SL", "lg", "lg-UG", "de-BE", "de-IT", "el-CY", "guz", "guz-KE", "ha-Latn-GH",
"ha-Latn-NG", "ia-FR", "ia-001", "it-SM", "it-VA", "jv", "jv-Latn", "jv-Latn-ID", "dyo",
"dyo-SN", "kea", "kea-CV", "kab", "kab-DZ", "kkj", "kkj-CM", "kln", "kln-KE", "kam",
"kam-KE", "ks-Arab-IN", "ki", "ki-KE", "sw-TZ", "sw-UG", "ko-KP", "khq", "khq-ML", "ses",
"ses-ML", "nmg", "nmq-CM", "ku-Arab-IR", "lkt", "lkt-US", "lag", "lag-TZ", "ln", "ln-AO",
"ln-CF", "ln-CD", "nds", "nds-DE", "nds-NL", "lu", "lu-CD", "luo", "luo", "luo-KE", "luy",
"luy-KE", "jmc", "jmc-TZ", "mgh", "mgh-MZ", "kde", "kde-TZ", "mg", "mg-MG", "gv", "gv-IM",
"mas", "mas-KE", "mas-TZ", "mas-IR", "mer", "mer-KE", "mgo", "mgo-CM", "mfe", "mfe-MU",
"mua", "mua-CM", "nqo", "nqo-GN", "nqa", "naq-NA", "nnh", "nnh-CM", "jgo", "jgo-CM",
"lrc-IQ", "lrc-IR", "nd", "nd-ZW", "nb-SJ", "nus", "nus-SD", "nus-SS", "nyn", "nyn-UG",
"om-KE", "os", "os-GE", "os-RU", "ps-PK", "fa-AF", "pt-AO", "pt-CV", "pt-GQ", "pt-GW",
"pt-LU", "pt-MO", "pt-MZ", "pt-ST", "pt-CH", "pt-TL", "prg-001", "ksh", "ksh-DE", "rof",
"rof-TZ", "rn", "rn-BI", "ru-BY", "ru-KZ", "ru-KG", "ru-UA", "rwk", "rwk-TZ", "ssy",
"ssy-ER", "saq", "saq-KE", "sg", "sq-CF", "sbp", "sbp-TZ", "seh", "seh-MZ", "ksb", "ksb-TZ",
"sn", "sn-Latn", "sn-Latn-ZW", "xog", "xog-UG", "so-DJ", "so-ET", "so-KE", "nr", "nr-ZA",
"st-LS", "es-BZ", "es-BR", "es-PH", "zgh", "zgh-Tfng-MA", "zgh-Tfng", "ss", "ss-ZA",
"ss-SZ", "sv-AX", "shi", "shi-Tfng", "shi-Tfng-MA", "shi-Latn", "shi-Latn-MA", "dav",
"dav-KE", "ta-MY", "ta-SG", "twq", "twq-NE", "teo", "teo-KE", "teo-UG", "bo-IN", "tig",
"tig-ER", "to", "to-TO", "tr-CY", "uz-Arab", "us-Arab-AF", "vai", "vai-Vaii",
"vai-Vaii-LR", "vai-Latn-LR", "vai-Latn", "vo", "vo-001", "vun", "vun-TZ", "wae",
"wae-CH", "wal", "wae-ET", "yav", "yav-CM", "yo-BJ", "dje", "dje-NE",
}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0]},
"C09": {tags: []string{"en-AU"}, localMonth: localMonthsNameEnglish, apFmt: strings.ToLower(nfp.AmPm[0])},
"2829": {tags: []string{"en-BZ"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0]},
"1009": {tags: []string{"en-CA"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0]},
"2409": {tags: []string{"en-029"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0]},
"3C09": {tags: []string{"en-HK"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0]},
"4009": {tags: []string{"en-IN"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0]},
"1809": {tags: []string{"en-IE"}, localMonth: localMonthsNameEnglish, apFmt: strings.ToLower(nfp.AmPm[0])},
"2009": {tags: []string{"en-JM"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0]},
"4409": {tags: []string{"en-MY"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0]},
"1409": {tags: []string{"en-NZ"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0]},
"3409": {tags: []string{"en-PH"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0]},
"4809": {tags: []string{"en-SG"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0]},
"1C09": {tags: []string{"en-ZA"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0]},
"2C09": {tags: []string{"en-TT"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0]},
"4C09": {tags: []string{"en-AE"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0]},
"809": {tags: []string{"en-GB"}, localMonth: localMonthsNameEnglish, apFmt: strings.ToLower(nfp.AmPm[0])},
"409": {tags: []string{"en-US"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0]},
"3009": {tags: []string{"en-ZW"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0]},
"C": {tags: []string{"fr"}, localMonth: localMonthsNameFrench, apFmt: nfp.AmPm[0]},
"7": {tags: []string{"de"}, localMonth: localMonthsNameGerman, apFmt: nfp.AmPm[0]},
"C07": {tags: []string{"de-AT"}, localMonth: localMonthsNameAustria, apFmt: nfp.AmPm[0]},
"407": {tags: []string{"de-DE"}, localMonth: localMonthsNameGerman, apFmt: nfp.AmPm[0]},
"3C": {tags: []string{"ga"}, localMonth: localMonthsNameIrish, apFmt: apFmtIrish},
"83C": {tags: []string{"ga-IE"}, localMonth: localMonthsNameIrish, apFmt: apFmtIrish},
"10": {tags: []string{"it"}, localMonth: localMonthsNameItalian, apFmt: nfp.AmPm[0]},
"11": {tags: []string{"ja"}, localMonth: localMonthsNameChinese3, apFmt: apFmtJapanese},
"411": {tags: []string{"ja-JP"}, localMonth: localMonthsNameChinese3, apFmt: apFmtJapanese},
"12": {tags: []string{"ko"}, localMonth: localMonthsNameKorean, apFmt: apFmtKorean},
"412": {tags: []string{"ko-KR"}, localMonth: localMonthsNameKorean, apFmt: apFmtKorean},
"7C50": {tags: []string{"mn-Mong"}, localMonth: localMonthsNameTraditionalMongolian, apFmt: nfp.AmPm[0]},
"850": {tags: []string{"mn-Mong-CN"}, localMonth: localMonthsNameTraditionalMongolian, apFmt: nfp.AmPm[0]},
"C50": {tags: []string{"mn-Mong-MN"}, localMonth: localMonthsNameTraditionalMongolian, apFmt: nfp.AmPm[0]},
"19": {tags: []string{"ru"}, localMonth: localMonthsNameRussian, apFmt: nfp.AmPm[0]},
"819": {tags: []string{"ru-MD"}, localMonth: localMonthsNameRussian, apFmt: nfp.AmPm[0]},
"419": {tags: []string{"ru-RU"}, localMonth: localMonthsNameRussian, apFmt: nfp.AmPm[0]},
"A": {tags: []string{"es"}, localMonth: localMonthsNameSpanish, apFmt: apFmtSpanish},
"2C0A": {tags: []string{"es-AR"}, localMonth: localMonthsNameSpanish, apFmt: apFmtSpanish},
"200A": {tags: []string{"es-VE"}, localMonth: localMonthsNameSpanish, apFmt: apFmtSpanish},
"400A": {tags: []string{"es-BO"}, localMonth: localMonthsNameSpanish, apFmt: apFmtSpanish},
"340A": {tags: []string{"es-CL"}, localMonth: localMonthsNameSpanish, apFmt: apFmtSpanish},
"240A": {tags: []string{"es-CO"}, localMonth: localMonthsNameSpanish, apFmt: apFmtSpanish},
"140A": {tags: []string{"es-CR"}, localMonth: localMonthsNameSpanish, apFmt: apFmtSpanish},
"5C0A": {tags: []string{"es-CU"}, localMonth: localMonthsNameSpanish, apFmt: apFmtCuba},
"1C0A": {tags: []string{"es-DO"}, localMonth: localMonthsNameSpanish, apFmt: apFmtSpanish},
"300A": {tags: []string{"es-EC"}, localMonth: localMonthsNameSpanish, apFmt: apFmtSpanish},
"440A": {tags: []string{"es-SV"}, localMonth: localMonthsNameSpanish, apFmt: apFmtSpanish},
"1E": {tags: []string{"th"}, localMonth: localMonthsNameThai, apFmt: nfp.AmPm[0]},
"41E": {tags: []string{"th-TH"}, localMonth: localMonthsNameThai, apFmt: nfp.AmPm[0]},
"51": {tags: []string{"bo"}, localMonth: localMonthsNameTibetan, apFmt: apFmtTibetan},
"451": {tags: []string{"bo-CN"}, localMonth: localMonthsNameTibetan, apFmt: apFmtTibetan},
"1F": {tags: []string{"tr"}, localMonth: localMonthsNameTurkish, apFmt: apFmtTurkish},
"41F": {tags: []string{"tr-TR"}, localMonth: localMonthsNameTurkish, apFmt: apFmtTurkish},
"52": {tags: []string{"cy"}, localMonth: localMonthsNameWelsh, apFmt: apFmtWelsh},
"452": {tags: []string{"cy-GB"}, localMonth: localMonthsNameWelsh, apFmt: apFmtWelsh},
"2A": {tags: []string{"vi"}, localMonth: localMonthsNameVietnamese, apFmt: apFmtVietnamese},
"42A": {tags: []string{"vi-VN"}, localMonth: localMonthsNameVietnamese, apFmt: apFmtVietnamese},
"88": {tags: []string{"wo"}, localMonth: localMonthsNameWolof, apFmt: apFmtWolof},
"488": {tags: []string{"wo-SN"}, localMonth: localMonthsNameWolof, apFmt: apFmtWolof},
"34": {tags: []string{"xh"}, localMonth: localMonthsNameXhosa, apFmt: nfp.AmPm[0]},
"434": {tags: []string{"xh-ZA"}, localMonth: localMonthsNameXhosa, apFmt: nfp.AmPm[0]},
"78": {tags: []string{"ii"}, localMonth: localMonthsNameYi, apFmt: apFmtYi},
"478": {tags: []string{"ii-CN"}, localMonth: localMonthsNameYi, apFmt: apFmtYi},
"35": {tags: []string{"zu"}, localMonth: localMonthsNameZulu, apFmt: nfp.AmPm[0]},
"435": {tags: []string{"zu-ZA"}, localMonth: localMonthsNameZulu, apFmt: nfp.AmPm[0]},
}
// monthNamesBangla list the month names in the Bangla.
monthNamesBangla = []string{
"\u099C\u09BE\u09A8\u09C1\u09AF\u09BC\u09BE\u09B0\u09C0",
"\u09AB\u09C7\u09AC\u09CD\u09B0\u09C1\u09AF\u09BC\u09BE\u09B0\u09C0",
"\u09AE\u09BE\u09B0\u09CD\u099A",
"\u098F\u09AA\u09CD\u09B0\u09BF\u09B2",
"\u09AE\u09C7",
"\u099C\u09C1\u09A8",
"\u099C\u09C1\u09B2\u09BE\u0987",
"\u0986\u0997\u09B8\u09CD\u099F",
"\u09B8\u09C7\u09AA\u09CD\u099F\u09C7\u09AE\u09CD\u09AC\u09B0",
"\u0985\u0995\u09CD\u099F\u09CB\u09AC\u09B0",
"\u09A8\u09AD\u09C7\u09AE\u09CD\u09AC\u09B0",
"\u09A1\u09BF\u09B8\u09C7\u09AE\u09CD\u09AC\u09B0",
}
// monthNamesAfrikaans list the month names in the Afrikaans.
monthNamesAfrikaans = []string{"Januarie", "Februarie", "Maart", "April", "Mei", "Junie", "Julie", "Augustus", "September", "Oktober", "November", "Desember"}
// monthNamesAfrikaansAbbr lists the month name abbreviations in Afrikaans
monthNamesAfrikaansAbbr = []string{"Jan.", "Feb.", "Maa.", "Apr.", "Mei", "Jun.", "Jul.", "Aug.", "Sep.", "Okt.", "Nov.", "Des."}
// monthNamesChinese list the month names in the Chinese.
monthNamesChinese = []string{"一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一", "十二"}
// monthNamesChineseAbbr1 list the month number and character abbreviation in Chinese
monthNamesChineseAbbrPlus = []string{"0月", "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月"}
// monthNamesChinesePlus list the month names in Chinese plus the character 月
monthNamesChinesePlus = []string{"一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"}
// monthNamesKoreanAbbrPlus lists out the month number plus 월 for the Korean language
monthNamesKoreanAbbrPlus = []string{"0월", "1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월"}
// monthNamesTradMongolian lists the month number for use with traditional Mongolian
monthNamesTradMongolian = []string{"M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12"}
// monthNamesFrench list the month names in the French.
monthNamesFrench = []string{"janvier", "février", "mars", "avril", "mai", "juin", "juillet", "août", "septembre", "octobre", "novembre", "décembre"}
// monthNamesFrenchAbbr lists the month name abbreviations in French
monthNamesFrenchAbbr = []string{"janv.", "févr.", "mars", "avri.", "mai", "juin", "juil.", "août", "sept.", "octo.", "nove.", "déce."}
// monthNamesGerman list the month names in the German.
monthNamesGerman = []string{"Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"}
// monthNamesGermanAbbr list the month abbreviations in German
monthNamesGermanAbbr = []string{"Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"}
// monthNamesAustria list the month names in the Austria.
monthNamesAustria = []string{"Jänner", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"}
// monthNamesAustriaAbbr list the month name abbreviations in Austrian
monthNamesAustriaAbbr = []string{"Jän", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"}
// monthNamesIrish list the month names in the Irish.
monthNamesIrish = []string{"Eanáir", "Feabhra", "Márta", "Aibreán", "Bealtaine", "Meitheamh", "Iúil", "Lúnasa", "Meán Fómhair", "Deireadh Fómhair", "Samhain", "Nollaig"}
// monthNamesIrishAbbr lists the month abbreviations in Irish
monthNamesIrishAbbr = []string{"Ean", "Feabh", "Márta", "Aib", "Beal", "Meith", "Iúil", "Lún", "MFómh", "DFómh", "Samh", "Noll"}
// monthNamesItalian list the month names in the Italian.
monthNamesItalian = []string{"gennaio", "febbraio", "marzo", "aprile", "maggio", "giugno", "luglio", "agosto", "settembre", "ottobre", "novembre", "dicembre"}
// monthNamesItalianAbbr list the month name abbreviations in Italian
monthNamesItalianAbbr = []string{"gen", "feb", "mar", "apr", "mag", "giu", "lug", "ago", "set", "ott", "nov", "dic"}
// monthNamesRussian list the month names in the Russian.
monthNamesRussian = []string{"январь", "февраль", "март", "апрель", "май", "июнь", "июль", "август", "сентябрь", "октябрь", "ноябрь", "декабрь"}
// monthNamesRussianAbbr list the month abbreviations for Russian.
monthNamesRussianAbbr = []string{"янв.", "фев.", "март", "апр.", "май", "июнь", "июль", "авг.", "сен.", "окт.", "ноя.", "дек."}
// monthNamesSpanish list the month names in the Spanish.
monthNamesSpanish = []string{"enero", "febrero", "marzo", "abril", "mayo", "junio", "julio", "agosto", "septiembre", "octubre", "noviembre", "diciembre"}
// monthNamesSpanishAbbr list the month abbreviations in Spanish
monthNamesSpanishAbbr = []string{"ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sep", "oct", "nov", "dic"}
// monthNamesThai list the month names in the Thai.
monthNamesThai = []string{
"\u0e21\u0e01\u0e23\u0e32\u0e04\u0e21",
"\u0e01\u0e38\u0e21\u0e20\u0e32\u0e1e\u0e31\u0e19\u0e18\u0e4c",
"\u0e21\u0e35\u0e19\u0e32\u0e04\u0e21",
"\u0e40\u0e21\u0e29\u0e32\u0e22\u0e19",
"\u0e1e\u0e24\u0e29\u0e20\u0e32\u0e04\u0e21",
"\u0e21\u0e34\u0e16\u0e38\u0e19\u0e32\u0e22\u0e19",
"\u0e01\u0e23\u0e01\u0e0e\u0e32\u0e04\u0e21",
"\u0e2a\u0e34\u0e07\u0e2b\u0e32\u0e04\u0e21",
"\u0e01\u0e31\u0e19\u0e22\u0e32\u0e22\u0e19",
"\u0e15\u0e38\u0e25\u0e32\u0e04\u0e21",
"\u0e1e\u0e24\u0e28\u0e08\u0e34\u0e01\u0e32\u0e22\u0e19",
"\u0e18\u0e31\u0e19\u0e27\u0e32\u0e04\u0e21",
}
// monthNamesTibetan list the month names in the Tibetan.
monthNamesTibetan = []string{
"\u0f5f\u0fb3\u0f0b\u0f56\u0f0b\u0f51\u0f44\u0f0b\u0f54\u0f7c\u0f0b",
"\u0f5f\u0fb3\u0f0b\u0f56\u0f0b\u0f42\u0f49\u0f72\u0f66\u0f0b\u0f54\u0f0b",
"\u0f5f\u0fb3\u0f0b\u0f56\u0f0b\u0f42\u0f66\u0f74\u0f58\u0f0b\u0f54\u0f0b",
"\u0f5f\u0fb3\u0f0b\u0f56\u0f0b\u0f56\u0f5e\u0f72\u0f0b\u0f54\u0f0b",
"\u0f5f\u0fb3\u0f0b\u0f56\u0f0b\u0f63\u0f94\u0f0b\u0f54\u0f0b",
"\u0f5f\u0fb3\u0f0b\u0f56\u0f0b\u0f51\u0fb2\u0f74\u0f42\u0f0b\u0f54\u0f0b",
"\u0f5f\u0fb3\u0f0b\u0f56\u0f0b\u0f56\u0f51\u0f74\u0f53\u0f0b\u0f54\u0f0b",
"\u0f5f\u0fb3\u0f0b\u0f56\u0f0b\u0f56\u0f62\u0f92\u0fb1\u0f51\u0f0b\u0f54\u0f0b",
"\u0f5f\u0fb3\u0f0b\u0f56\u0f0b\u0f51\u0f42\u0f74\u0f0b\u0f54\u0f0b",
"\u0f66\u0fa4\u0fb1\u0f72\u0f0b\u0f5f\u0fb3\u0f0b\u0f56\u0f45\u0f74\u0f0b\u0f54\u0f0d",
"\u0f5f\u0fb3\u0f0b\u0f56\u0f0b\u0f56\u0f45\u0f74\u0f0b\u0f42\u0f45\u0f72\u0f42\u0f0b\u0f54\u0f0b",
"\u0f5f\u0fb3\u0f0b\u0f56\u0f0b\u0f56\u0f45\u0f74\u0f0b\u0f42\u0f49\u0f72\u0f66\u0f0b\u0f54\u0f0b",
}
// monthNamesTibetanAbbr lists the month name abbreviations in Tibetan
monthNamesTibetanAbbr = []string{
"\u0f5f\u0fb3\u0f0b\u0f21",
"\u0f5f\u0fb3\u0f0b\u0f22",
"\u0f5f\u0fb3\u0f0b\u0f23",
"\u0f5f\u0fb3\u0f0b\u0f24",
"\u0f5f\u0fb3\u0f0b\u0f25",
"\u0f5f\u0fb3\u0f0b\u0f26",
"\u0f5f\u0fb3\u0f0b\u0f27",
"\u0f5f\u0fb3\u0f0b\u0f28",
"\u0f5f\u0fb3\u0f0b\u0f29",
"\u0f5f\u0fb3\u0f0b\u0f21\u0f20",
"\u0f5f\u0fb3\u0f0b\u0f21\u0f21",
"\u0f5f\u0fb3\u0f0b\u0f21\u0f22",
}
// monthNamesTurkish list the month names in the Turkish.
monthNamesTurkish = []string{"Ocak", "Şubat", "Mart", "Nisan", "Mayıs", "Haziran", "Temmuz", "Ağustos", "Eylül", "Ekim", "Kasım", "Aralık"}
// monthNamesTurkishAbbr lists the month name abbreviations in Turkish, this prevents string concatenation
monthNamesTurkishAbbr = []string{"Oca", "Şub", "Mar", "Nis", "May", "Haz", "Tem", "Ağu", "Eyl", "Eki", "Kas", "Ara"}
// monthNamesVietnamese list the month name used for Vietnamese
monthNamesVietnamese = []string{"Tháng 1", "Tháng 2", "Tháng 3", "Tháng 4", "Tháng 5", "Tháng 6", "Tháng 7", "Tháng 8", "Tháng 9", "Tháng 10", "Tháng 11", "Tháng 12"}
// monthNamesVietnameseAbbr3 list the mid-form abbreviation for Vietnamese months
monthNamesVietnameseAbbr3 = []string{"Thg 1", "Thg 2", "Thg 3", "Thg 4", "Thg 5", "Thg 6", "Thg 7", "Thg 8", "Thg 9", "Thg 10", "Thg 11", "Thg 12"}
// monthNamesVietnameseAbbr5 list the short-form abbreviation for Vietnamese months
monthNamesVietnameseAbbr5 = []string{"T 1", "T 2", "T 3", "T 4", "T 5", "T 6", "T 7", "T 8", "T 9", "T 10", "T 11", "T 12"}
// monthNamesWelsh list the month names in the Welsh.
monthNamesWelsh = []string{"Ionawr", "Chwefror", "Mawrth", "Ebrill", "Mai", "Mehefin", "Gorffennaf", "Awst", "Medi", "Hydref", "Tachwedd", "Rhagfyr"}
// monthNamesWelshAbbr lists the month name abbreviations in Welsh, this prevents string concatenation
monthNamesWelshAbbr = []string{"Ion", "Chwef", "Maw", "Ebr", "Mai", "Meh", "Gorff", "Awst", "Medi", "Hyd", "Tach", "Rhag"}
// monthNamesWolof list the month names in the Wolof.
monthNamesWolof = []string{"Samwiye", "Fewriye", "Maars", "Awril", "Me", "Suwe", "Sullet", "Ut", "Septàmbar", "Oktoobar", "Noowàmbar", "Desàmbar"}
// monthNamesWolofAbbr list the month name abbreviations in Wolof, this prevents string concatenation
monthNamesWolofAbbr = []string{"Sam.", "Few.", "Maa", "Awr.", "Me", "Suw", "Sul.", "Ut", "Sept.", "Okt.", "Now.", "Des."}
// monthNamesXhosa list the month names in the Xhosa.
monthNamesXhosa = []string{"uJanuwari", "uFebuwari", "uMatshi", "uAprili", "uMeyi", "uJuni", "uJulayi", "uAgasti", "uSeptemba", "uOktobha", "uNovemba", "uDisemba"}
// monthNamesXhosaAbbr list the month abbreviations in the Xhosa, this prevents string concatenation
monthNamesXhosaAbbr = []string{"uJan.", "uFeb.", "uMat.", "uEpr.", "uMey.", "uJun.", "uJul.", "uAg.", "uSep.", "uOkt.", "uNov.", "uDis."}
// monthNamesYi list the month names in the Yi.
monthNamesYi = []string{"\ua2cd", "\ua44d", "\ua315", "\ua1d6", "\ua26c", "\ua0d8", "\ua3c3", "\ua246", "\ua22c", "\ua2b0", "\ua2b0\ua2aa", "\ua2b0\ua44b"}
// monthNamesYiSuffix lists the month names in Yi with the "\ua1aa" suffix
monthNamesYiSuffix = []string{"\ua2cd\ua1aa", "\ua44d\ua1aa", "\ua315\ua1aa", "\ua1d6\ua1aa", "\ua26c\ua1aa", "\ua0d8\ua1aa", "\ua3c3\ua1aa", "\ua246\ua1aa", "\ua22c\ua1aa", "\ua2b0\ua1aa", "\ua2b0\ua2aa\ua1aa", "\ua2b0\ua44b\ua1aa"}
// monthNamesZulu list the month names in the Zulu.
monthNamesZulu = []string{"Januwari", "Febhuwari", "Mashi", "Ephreli", "Meyi", "Juni", "Julayi", "Agasti", "Septemba", "Okthoba", "Novemba", "Disemba"}
// monthNamesZuluAbbr list the month name abbreviations in Zulu
monthNamesZuluAbbr = []string{"Jan", "Feb", "Mas", "Eph", "Mey", "Jun", "Jul", "Agas", "Sep", "Okt", "Nov", "Dis"}
// apFmtAfrikaans defined the AM/PM name in the Afrikaans.
apFmtAfrikaans = "vm./nm."
// apFmtCuba defined the AM/PM name in the Cuba.
apFmtCuba = "a.m./p.m."
// apFmtIrish defined the AM/PM name in the Irish.
apFmtIrish = "r.n./i.n."
// apFmtJapanese defined the AM/PM name in the Japanese.
apFmtJapanese = "午前/午後"
// apFmtKorean defined the AM/PM name in the Korean.
apFmtKorean = "오전/오후"
// apFmtSpanish defined the AM/PM name in the Spanish.
apFmtSpanish = "a. m./p. m."
// apFmtTibetan defined the AM/PM name in the Tibetan.
apFmtTibetan = "\u0f66\u0f94\u0f0b\u0f51\u0fb2\u0f7c\u0f0b/\u0f55\u0fb1\u0f72\u0f0b\u0f51\u0fb2\u0f7c\u0f0b"
// apFmtTurkish defined the AM/PM name in the Turkish.
apFmtTurkish = "\u00F6\u00F6/\u00F6\u0053"
// apFmtVietnamese defined the AM/PM name in the Vietnamese.
apFmtVietnamese = "SA/CH"
// apFmtWolof defined the AM/PM name in the Wolof.
apFmtWolof = "Sub/Ngo"
// apFmtYi defined the AM/PM name in the Yi.
apFmtYi = "\ua3b8\ua111/\ua06f\ua2d2"
// apFmtWelsh defined the AM/PM name in the Welsh.
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
// decimal point.
func (nf *numberFormat) prepareNumberic(value string) {
if nf.cellType != CellTypeNumber && nf.cellType != CellTypeDate {
return
}
if nf.isNumeric, _, _ = isNumeric(value); !nf.isNumeric {
return
}
}
// 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, date1904 bool, cellType CellType, opts *Options) string {
p := nfp.NumberFormatParser()
nf := numberFormat{opts: opts, section: p.Parse(numFmt), value: value, date1904: date1904, cellType: cellType}
nf.number, nf.valueSectionType = nf.getValueSectionType(value)
nf.prepareNumberic(value)
for i, section := range nf.section {
nf.sectionIdx = i
if section.Type != nf.valueSectionType {
continue
}
if nf.isNumeric {
switch section.Type {
case nfp.TokenSectionPositive:
return nf.positiveHandler()
case nfp.TokenSectionNegative:
return nf.negativeHandler()
default:
return nf.zeroHandler()
}
}
return nf.textHandler()
}
return value
}
// getNumberPartLen returns the length of integer and fraction parts for the
// numeric.
func getNumberPartLen(n float64) (int, int) {
parts := strings.Split(strconv.FormatFloat(math.Abs(n), 'f', -1, 64), ".")
if len(parts) == 2 {
return len(parts[0]), len(parts[1])
}
return len(parts[0]), 0
}
// getNumberFmtConf generate the number format padding and place holder
// configurations.
func (nf *numberFormat) getNumberFmtConf() {
for _, token := range nf.section[nf.sectionIdx].Items {
if token.TType == nfp.TokenTypeHashPlaceHolder {
if nf.usePointer {
nf.fracHolder += len(token.TValue)
} else {
nf.intHolder += len(token.TValue)
}
}
if token.TType == nfp.TokenTypeExponential {
nf.useScientificNotation = true
}
if token.TType == nfp.TokenTypeThousandsSeparator {
nf.useCommaSep = true
}
if token.TType == nfp.TokenTypePercent {
nf.percent += len(token.TValue)
}
if token.TType == nfp.TokenTypeDecimalPoint {
nf.usePointer = true
}
if token.TType == nfp.TokenTypeSwitchArgument {
nf.switchArgument = token.TValue
}
if token.TType == nfp.TokenTypeZeroPlaceHolder {
if nf.usePointer {
if nf.useScientificNotation {
nf.expBaseLen += len(token.TValue)
continue
}
nf.fracPadding += len(token.TValue)
continue
}
nf.intPadding += len(token.TValue)
}
}
}
// printNumberLiteral apply literal tokens for the pre-formatted text.
func (nf *numberFormat) printNumberLiteral(text string) string {
var result string
var useLiteral, useZeroPlaceHolder bool
if nf.usePositive {
result += "-"
}
for i, token := range nf.section[nf.sectionIdx].Items {
if token.TType == nfp.TokenTypeCurrencyLanguage {
if err, changeNumFmtCode := nf.currencyLanguageHandler(i, token); err != nil || changeNumFmtCode {
return nf.value
}
result += nf.currencyString
}
if token.TType == nfp.TokenTypeLiteral {
if useZeroPlaceHolder {
useLiteral = true
}
result += token.TValue
}
if token.TType == nfp.TokenTypeZeroPlaceHolder {
if useLiteral && useZeroPlaceHolder {
return nf.value
}
if !useZeroPlaceHolder {
useZeroPlaceHolder = true
result += text
}
}
}
return nf.printSwitchArgument(result)
}
// printCommaSep format number with thousands separator.
func printCommaSep(text string) string {
var (
target strings.Builder
subStr = strings.Split(text, ".")
length = len(subStr[0])
)
for i := 0; i < length; i++ {
if i > 0 && (length-i)%3 == 0 {
target.WriteString(",")
}
target.WriteString(string(text[i]))
}
if len(subStr) == 2 {
target.WriteString(".")
target.WriteString(subStr[1])
}
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
// zero padding and percentage symbol.
func (nf *numberFormat) printBigNumber(decimal float64, fracLen int) string {
var exp float64
if nf.percent > 0 {
exp = 1
}
result := strings.TrimLeft(strconv.FormatFloat(decimal*math.Pow(100, exp), 'f', -1, 64), "-")
if nf.useCommaSep {
result = printCommaSep(result)
}
if fracLen > 0 {
if parts := strings.Split(result, "."); len(parts) == 2 {
fracPartLen := len(parts[1])
if fracPartLen < fracLen {
result = fmt.Sprintf("%s%s", result, strings.Repeat("0", fracLen-fracPartLen))
}
if fracPartLen > fracLen {
result = fmt.Sprintf("%s.%s", parts[0], parts[1][:fracLen])
}
} else {
result = fmt.Sprintf("%s.%s", result, strings.Repeat("0", fracLen))
}
}
if nf.percent > 0 {
return fmt.Sprintf("%s%%", result)
}
return result
}
// numberHandler handling number format expression for positive and negative
// numeric.
func (nf *numberFormat) numberHandler() string {
var (
num = nf.number
intPart, fracPart = getNumberPartLen(nf.number)
intLen, fracLen int
result string
)
nf.getNumberFmtConf()
if intLen = intPart; nf.intPadding > intPart {
intLen = nf.intPadding
}
if fracLen = fracPart; fracPart > nf.fracHolder+nf.fracPadding {
fracLen = nf.fracHolder + nf.fracPadding
}
if nf.fracPadding > fracPart {
fracLen = nf.fracPadding
}
if isNum, precision, decimal := isNumeric(nf.value); isNum {
if precision > 15 && intLen+fracLen > 15 {
return nf.printNumberLiteral(nf.printBigNumber(decimal, fracLen))
}
}
paddingLen := intLen + fracLen
if fracLen > 0 {
paddingLen++
}
flag := "f"
if nf.useScientificNotation {
if nf.expBaseLen != 2 {
return nf.value
}
flag = "E"
}
fmtCode := fmt.Sprintf("%%0%d.%d%s%s", paddingLen, fracLen, flag, strings.Repeat("%%", nf.percent))
if nf.percent > 0 {
num *= math.Pow(100, float64(nf.percent))
}
if result = fmt.Sprintf(fmtCode, math.Abs(num)); nf.useCommaSep {
result = printCommaSep(result)
}
return nf.printNumberLiteral(result)
}
// dateTimeHandler handling data and time number format expression for a
// positive numeric.
func (nf *numberFormat) dateTimeHandler() string {
nf.t, nf.hours, nf.seconds = timeFromExcelTime(nf.number, nf.date1904), false, false
for i, token := range nf.section[nf.sectionIdx].Items {
if token.TType == nfp.TokenTypeCurrencyLanguage {
if err, changeNumFmtCode := nf.currencyLanguageHandler(i, token); err != nil || changeNumFmtCode {
return nf.value
}
nf.result += nf.currencyString
}
if token.TType == nfp.TokenTypeDateTimes {
nf.dateTimesHandler(i, token)
}
if token.TType == nfp.TokenTypeElapsedDateTimes {
nf.elapsedDateTimesHandler(token)
}
if token.TType == nfp.TokenTypeLiteral {
nf.result += token.TValue
continue
}
if token.TType == nfp.TokenTypeDecimalPoint {
nf.result += "."
}
if token.TType == nfp.TokenTypeSwitchArgument {
nf.switchArgument = token.TValue
}
if token.TType == nfp.TokenTypeZeroPlaceHolder {
zeroHolderLen := len(token.TValue)
if zeroHolderLen > 3 {
zeroHolderLen = 3
}
nf.result += fmt.Sprintf("%03d", nf.t.Nanosecond()/1e6)[:zeroHolderLen]
}
}
return nf.printSwitchArgument(nf.result)
}
// positiveHandler will be handling positive selection for a number format
// expression.
func (nf *numberFormat) positiveHandler() string {
var fmtNum bool
for _, token := range nf.section[nf.sectionIdx].Items {
if inStrSlice(supportedTokenTypes, token.TType, true) == -1 || token.TType == nfp.TokenTypeGeneral {
return nf.value
}
if inStrSlice(supportedNumberTokenTypes, token.TType, true) != -1 {
fmtNum = true
}
if inStrSlice(supportedDateTimeTokenTypes, token.TType, true) != -1 {
if fmtNum || nf.number < 0 {
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.numberHandler()
}
// currencyLanguageHandler will be handling currency and language types tokens
// for a number format expression.
func (nf *numberFormat) currencyLanguageHandler(i int, token nfp.Token) (error, bool) {
for _, part := range token.Parts {
if inStrSlice(supportedTokenTypes, part.Token.TType, true) == -1 {
return ErrUnsupportedNumberFormat, false
}
if part.Token.TType == nfp.TokenSubTypeLanguageInfo {
if strings.EqualFold(part.Token.TValue, "F800") { // [$-x-sysdate]
if nf.opts != nil && nf.opts.LongDateFmtCode != "" {
nf.value = format(nf.value, nf.opts.LongDateFmtCode, nf.date1904, nf.cellType, nf.opts)
return nil, true
}
part.Token.TValue = "409"
}
if strings.EqualFold(part.Token.TValue, "F400") { // [$-x-systime]
if nf.opts != nil && nf.opts.LongTimeFmtCode != "" {
nf.value = format(nf.value, nf.opts.LongTimeFmtCode, nf.date1904, nf.cellType, nf.opts)
return nil, true
}
part.Token.TValue = "409"
}
if _, ok := supportedLanguageInfo[strings.ToUpper(part.Token.TValue)]; !ok {
return ErrUnsupportedNumberFormat, false
}
nf.localCode = strings.ToUpper(part.Token.TValue)
}
if part.Token.TType == nfp.TokenSubTypeCurrencyString {
nf.currencyString = part.Token.TValue
}
}
return nil, false
}
// localAmPm return AM/PM name by supported language ID.
func (nf *numberFormat) localAmPm(ap string) string {
if languageInfo, ok := supportedLanguageInfo[nf.localCode]; ok {
return languageInfo.apFmt
}
return ap
}
// localMonthsNameEnglish returns the English name of the month.
func localMonthsNameEnglish(t time.Time, abbr int) string {
if abbr == 3 {
return t.Month().String()[:3]
}
if abbr == 4 {
return t.Month().String()
}
return t.Month().String()[:1]
}
// localMonthsNameAfrikaans returns the Afrikaans name of the month.
func localMonthsNameAfrikaans(t time.Time, abbr int) string {
if abbr == 3 {
return monthNamesAfrikaansAbbr[int(t.Month())-1]
}
if abbr == 4 {
return monthNamesAfrikaans[int(t.Month())-1]
}
return monthNamesAfrikaansAbbr[int(t.Month())-1][:1]
}
// localMonthsNameAustria returns the Austria name of the month.
func localMonthsNameAustria(t time.Time, abbr int) string {
if abbr == 3 {
return monthNamesAustriaAbbr[int(t.Month())-1]
}
if abbr == 4 {
return monthNamesAustria[int(t.Month())-1]
}
return monthNamesAustriaAbbr[int(t.Month())-1][:1]
}
// localMonthsNameBangla returns the German name of the month.
func localMonthsNameBangla(t time.Time, abbr int) string {
if abbr == 3 || abbr == 4 {
return monthNamesBangla[int(t.Month())-1]
}
return string([]rune(monthNamesBangla[int(t.Month())-1])[:1])
}
// localMonthsNameFrench returns the French name of the month.
func localMonthsNameFrench(t time.Time, abbr int) string {
if abbr == 3 {
month := monthNamesFrench[int(t.Month())-1]
if len([]rune(month)) <= 4 {
return monthNamesFrench[int(t.Month())-1]
}
return monthNamesFrenchAbbr[int(t.Month())-1]
}
if abbr == 4 {
return monthNamesFrench[int(t.Month())-1]
}
return monthNamesFrenchAbbr[int(t.Month())-1][:1]
}
// localMonthsNameIrish returns the Irish name of the month.
func localMonthsNameIrish(t time.Time, abbr int) string {
if abbr == 3 {
return monthNamesIrishAbbr[int(t.Month()-1)]
}
if abbr == 4 {
return monthNamesIrish[int(t.Month())-1]
}
return monthNamesIrishAbbr[int(t.Month())-1][:1]
}
// localMonthsNameItalian returns the Italian name of the month.
func localMonthsNameItalian(t time.Time, abbr int) string {
if abbr == 3 {
return monthNamesItalianAbbr[int(t.Month())-1]
}
if abbr == 4 {
return monthNamesItalian[int(t.Month())-1]
}
return monthNamesItalianAbbr[int(t.Month())-1][:1]
}
// localMonthsNameGerman returns the German name of the month.
func localMonthsNameGerman(t time.Time, abbr int) string {
if abbr == 3 {
return monthNamesGermanAbbr[int(t.Month())-1]
}
if abbr == 4 {
return monthNamesGerman[int(t.Month())-1]
}
return monthNamesGermanAbbr[int(t.Month())-1][:1]
}
// localMonthsNameChinese1 returns the Chinese name of the month.
func localMonthsNameChinese1(t time.Time, abbr int) string {
if abbr == 3 {
return monthNamesChineseAbbrPlus[int(t.Month())]
}
if abbr == 4 {
return monthNamesChinesePlus[int(t.Month())-1]
}
return monthNamesChinese[int(t.Month())-1]
}
// localMonthsNameChinese2 returns the Chinese name of the month.
func localMonthsNameChinese2(t time.Time, abbr int) string {
if abbr == 3 || abbr == 4 {
return monthNamesChinesePlus[int(t.Month())-1]
}
return monthNamesChinese[int(t.Month())-1]
}
// localMonthsNameChinese3 returns the Chinese name of the month.
func localMonthsNameChinese3(t time.Time, abbr int) string {
if abbr == 3 || abbr == 4 {
return monthNamesChineseAbbrPlus[int(t.Month())]
}
return strconv.Itoa(int(t.Month()))
}
// localMonthsNameKorean returns the Korean name of the month.
func localMonthsNameKorean(t time.Time, abbr int) string {
if abbr == 3 || abbr == 4 {
return monthNamesKoreanAbbrPlus[int(t.Month())]
}
return strconv.Itoa(int(t.Month()))
}
// localMonthsNameTraditionalMongolian returns the Traditional Mongolian name of
// the month.
func localMonthsNameTraditionalMongolian(t time.Time, abbr int) string {
if abbr == 5 {
return "M"
}
return monthNamesTradMongolian[int(t.Month()-1)]
}
// localMonthsNameRussian returns the Russian name of the month.
func localMonthsNameRussian(t time.Time, abbr int) string {
if abbr == 3 {
month := monthNamesRussian[int(t.Month())-1]
if len([]rune(month)) <= 4 {
return month
}
return monthNamesRussianAbbr[int(t.Month())-1]
}
if abbr == 4 {
return monthNamesRussian[int(t.Month())-1]
}
return string([]rune(monthNamesRussian[int(t.Month())-1])[:1])
}
// localMonthsNameSpanish returns the Spanish name of the month.
func localMonthsNameSpanish(t time.Time, abbr int) string {
if abbr == 3 {
return monthNamesSpanishAbbr[int(t.Month())-1]
}
if abbr == 4 {
return monthNamesSpanish[int(t.Month())-1]
}
return monthNamesSpanishAbbr[int(t.Month())-1][:1]
}
// localMonthsNameThai returns the Thai name of the month.
func localMonthsNameThai(t time.Time, abbr int) string {
if abbr == 3 {
r := []rune(monthNamesThai[int(t.Month())-1])
return string(r[:1]) + "." + string(r[len(r)-2:len(r)-1]) + "."
}
if abbr == 4 {
return monthNamesThai[int(t.Month())-1]
}
return string([]rune(monthNamesThai[int(t.Month())-1])[:1])
}
// localMonthsNameTibetan returns the Tibetan name of the month.
func localMonthsNameTibetan(t time.Time, abbr int) string {
if abbr == 3 {
return monthNamesTibetanAbbr[int(t.Month())-1]
}
if abbr == 5 {
if t.Month() == 10 {
return "\u0f66"
}
return "\u0f5f"
}
return monthNamesTibetan[int(t.Month())-1]
}
// localMonthsNameTurkish returns the Turkish name of the month.
func localMonthsNameTurkish(t time.Time, abbr int) string {
if abbr == 3 {
return monthNamesTurkishAbbr[int(t.Month())-1]
}
if abbr == 4 {
return monthNamesTurkish[int(t.Month())-1]
}
return string([]rune(monthNamesTurkishAbbr[int(t.Month())-1])[:1])
}
// localMonthsNameWelsh returns the Welsh name of the month.
func localMonthsNameWelsh(t time.Time, abbr int) string {
if abbr == 3 {
return monthNamesWelshAbbr[int(t.Month())-1]
}
if abbr == 4 {
return monthNamesWelsh[int(t.Month())-1]
}
return monthNamesWelshAbbr[int(t.Month())-1][:1]
}
// localMonthsNameVietnamese returns the Vietnamese name of the month.
func localMonthsNameVietnamese(t time.Time, abbr int) string {
if abbr == 3 {
return monthNamesVietnameseAbbr3[int(t.Month()-1)]
}
if abbr == 5 {
return monthNamesVietnameseAbbr5[int(t.Month()-1)]
}
return monthNamesVietnamese[int(t.Month()-1)]
}
// localMonthsNameWolof returns the Wolof name of the month.
func localMonthsNameWolof(t time.Time, abbr int) string {
if abbr == 3 {
return monthNamesWolofAbbr[int(t.Month())-1]
}
if abbr == 4 {
return monthNamesWolof[int(t.Month())-1]
}
return monthNamesWolof[int(t.Month())-1][:1]
}
// localMonthsNameXhosa returns the Xhosa name of the month.
func localMonthsNameXhosa(t time.Time, abbr int) string {
if abbr == 3 {
return monthNamesXhosaAbbr[int(t.Month())-1]
}
if abbr == 4 {
return monthNamesXhosa[int(t.Month())-1]
}
return "u"
}
// localMonthsNameYi returns the Yi name of the month.
func localMonthsNameYi(t time.Time, abbr int) string {
if abbr == 3 || abbr == 4 {
return monthNamesYiSuffix[int(t.Month()-1)]
}
return string([]rune(monthNamesYi[int(t.Month())-1])[:1])
}
// localMonthsNameZulu returns the Zulu name of the month.
func localMonthsNameZulu(t time.Time, abbr int) string {
if abbr == 3 {
return monthNamesZuluAbbr[int(t.Month()-1)]
}
if abbr == 4 {
return monthNamesZulu[int(t.Month())-1]
}
return monthNamesZuluAbbr[int(t.Month())-1][:1]
}
// localMonthName return months name by supported language ID.
func (nf *numberFormat) localMonthsName(abbr int) string {
if languageInfo, ok := supportedLanguageInfo[nf.localCode]; ok {
return languageInfo.localMonth(nf.t, abbr)
}
return localMonthsNameEnglish(nf.t, abbr)
}
// dateTimesHandler will be handling date and times types tokens for a number
// format expression.
func (nf *numberFormat) dateTimesHandler(i int, token nfp.Token) {
if idx := inStrSlice(nfp.AmPm, strings.ToUpper(token.TValue), false); idx != -1 {
if nf.ap == "" {
nextHours := nf.hoursNext(i)
aps := strings.Split(nf.localAmPm(token.TValue), "/")
nf.ap = aps[0]
if nextHours >= 12 {
nf.ap = aps[1]
}
}
nf.result += nf.ap
return
}
if strings.Contains(strings.ToUpper(token.TValue), "M") {
l := len(token.TValue)
if l == 1 && !nf.hours && !nf.secondsNext(i) {
nf.result += strconv.Itoa(int(nf.t.Month()))
return
}
if l == 2 && !nf.hours && !nf.secondsNext(i) {
nf.result += fmt.Sprintf("%02d", int(nf.t.Month()))
return
}
if l == 3 {
nf.result += nf.localMonthsName(3)
return
}
if l == 4 || l > 5 {
nf.result += nf.localMonthsName(4)
return
}
if l == 5 {
nf.result += nf.localMonthsName(5)
return
}
}
nf.yearsHandler(i, token)
nf.daysHandler(i, token)
nf.hoursHandler(i, token)
nf.minutesHandler(token)
nf.secondsHandler(token)
}
// yearsHandler will be handling years in the date and times types tokens for a
// number format expression.
func (nf *numberFormat) yearsHandler(i int, token nfp.Token) {
years := strings.Contains(strings.ToUpper(token.TValue), "Y")
if years && len(token.TValue) <= 2 {
nf.result += strconv.Itoa(nf.t.Year())[2:]
return
}
if years && len(token.TValue) > 2 {
nf.result += strconv.Itoa(nf.t.Year())
return
}
}
// daysHandler will be handling days in the date and times types tokens for a
// number format expression.
func (nf *numberFormat) daysHandler(i int, token nfp.Token) {
if strings.Contains(strings.ToUpper(token.TValue), "D") {
switch len(token.TValue) {
case 1:
nf.result += strconv.Itoa(nf.t.Day())
return
case 2:
nf.result += fmt.Sprintf("%02d", nf.t.Day())
return
case 3:
nf.result += nf.t.Weekday().String()[:3]
return
default:
nf.result += nf.t.Weekday().String()
return
}
}
}
// hoursHandler will be handling hours in the date and times types tokens for a
// number format expression.
func (nf *numberFormat) hoursHandler(i int, token nfp.Token) {
nf.hours = strings.Contains(strings.ToUpper(token.TValue), "H")
if nf.hours {
h := nf.t.Hour()
ap, ok := nf.apNext(i)
if ok {
nf.ap = ap[0]
if h >= 12 {
nf.ap = ap[1]
}
if h > 12 {
h -= 12
}
}
if nf.ap != "" && nf.hoursNext(i) == -1 && h > 12 {
h -= 12
}
switch len(token.TValue) {
case 1:
nf.result += strconv.Itoa(h)
return
default:
nf.result += fmt.Sprintf("%02d", h)
return
}
}
}
// minutesHandler will be handling minutes in the date and times types tokens
// for a number format expression.
func (nf *numberFormat) minutesHandler(token nfp.Token) {
if strings.Contains(strings.ToUpper(token.TValue), "M") {
nf.hours = false
switch len(token.TValue) {
case 1:
nf.result += strconv.Itoa(nf.t.Minute())
return
default:
nf.result += fmt.Sprintf("%02d", nf.t.Minute())
return
}
}
}
// secondsHandler will be handling seconds in the date and times types tokens
// for a number format expression.
func (nf *numberFormat) secondsHandler(token nfp.Token) {
if nf.seconds = strings.Contains(strings.ToUpper(token.TValue), "S"); !nf.seconds {
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
// for a number format expression.
func (nf *numberFormat) elapsedDateTimesHandler(token nfp.Token) {
if strings.Contains(strings.ToUpper(token.TValue), "H") {
nf.result += fmt.Sprintf("%.f", nf.t.Sub(excel1900Epoc).Hours())
return
}
if strings.Contains(strings.ToUpper(token.TValue), "M") {
nf.result += fmt.Sprintf("%.f", nf.t.Sub(excel1900Epoc).Minutes())
return
}
if strings.Contains(strings.ToUpper(token.TValue), "S") {
nf.result += fmt.Sprintf("%.f", nf.t.Sub(excel1900Epoc).Seconds())
return
}
}
// hoursNext detects if a token of type hours exists after a given tokens list.
func (nf *numberFormat) hoursNext(i int) int {
tokens := nf.section[nf.sectionIdx].Items
for idx := i + 1; idx < len(tokens); idx++ {
if tokens[idx].TType == nfp.TokenTypeDateTimes {
if strings.Contains(strings.ToUpper(tokens[idx].TValue), "H") {
t := timeFromExcelTime(nf.number, false)
return t.Hour()
}
}
}
return -1
}
// apNext detects if a token of type AM/PM exists after a given tokens list.
func (nf *numberFormat) apNext(i int) ([]string, bool) {
tokens := nf.section[nf.sectionIdx].Items
for idx := i + 1; idx < len(tokens); idx++ {
if tokens[idx].TType == nfp.TokenTypeDateTimes {
if strings.Contains(strings.ToUpper(tokens[idx].TValue), "H") {
return nil, false
}
if i := inStrSlice(nfp.AmPm, tokens[idx].TValue, false); i != -1 {
return strings.Split(nf.localAmPm(tokens[idx].TValue), "/"), true
}
}
}
return nil, false
}
// secondsNext detects if a token of type seconds exists after a given tokens
// list.
func (nf *numberFormat) secondsNext(i int) bool {
tokens := nf.section[nf.sectionIdx].Items
for idx := i + 1; idx < len(tokens); idx++ {
if tokens[idx].TType == nfp.TokenTypeDateTimes {
return strings.Contains(strings.ToUpper(tokens[idx].TValue), "S")
}
}
return false
}
// negativeHandler will be handling negative selection for a number format
// expression.
func (nf *numberFormat) negativeHandler() (result string) {
for _, token := range nf.section[nf.sectionIdx].Items {
if inStrSlice(supportedTokenTypes, token.TType, true) == -1 || token.TType == nfp.TokenTypeGeneral {
return nf.value
}
if inStrSlice(supportedDateTimeTokenTypes, token.TType, true) != -1 {
return nf.value
}
}
return nf.numberHandler()
}
// zeroHandler will be handling zero selection for a number format expression.
func (nf *numberFormat) zeroHandler() string {
return nf.value
}
// textHandler will be handling text selection for a number format expression.
func (nf *numberFormat) textHandler() (result string) {
for _, token := range nf.section[nf.sectionIdx].Items {
if token.TType == nfp.TokenTypeLiteral {
result += token.TValue
}
if token.TType == nfp.TokenTypeTextPlaceHolder || token.TType == nfp.TokenTypeZeroPlaceHolder {
result += nf.value
}
}
return result
}
// getValueSectionType returns its applicable number format expression section
// based on the given value.
func (nf *numberFormat) getValueSectionType(value string) (float64, string) {
if nf.cellType != CellTypeNumber && nf.cellType != CellTypeDate {
return 0, nfp.TokenSectionText
}
isNum, _, _ := isNumeric(value)
if !isNum {
return 0, nfp.TokenSectionText
}
number, _ := strconv.ParseFloat(value, 64)
if number > 0 {
return number, nfp.TokenSectionPositive
}
if number < 0 {
var hasNeg bool
for _, sec := range nf.section {
if sec.Type == nfp.TokenSectionNegative {
hasNeg = true
}
}
if !hasNeg {
nf.usePositive = true
return number, nfp.TokenSectionPositive
}
return number, nfp.TokenSectionNegative
}
return number, nfp.TokenSectionZero
}