support importing feidee mymoney web export data

This commit is contained in:
MaysWind
2024-09-12 00:00:22 +08:00
parent 5a31118c96
commit d15a862e5b
16 changed files with 547 additions and 5 deletions
@@ -0,0 +1,229 @@
package converters
import (
"bytes"
"time"
"github.com/shakinm/xlsReader/xls"
"github.com/mayswind/ezbookkeeping/pkg/errs"
"github.com/mayswind/ezbookkeeping/pkg/utils"
)
// feideeMymoneyTransactionExcelFileDataTable defines the structure of feidee mymoney transaction plain text data table
type feideeMymoneyTransactionExcelFileDataTable struct {
workbook *xls.Workbook
headerLineColumnNames []string
}
// feideeMymoneyTransactionExcelFileDataRow defines the structure of feidee mymoney transaction plain text data row
type feideeMymoneyTransactionExcelFileDataRow struct {
sheet *xls.Sheet
rowIndex int
}
// feideeMymoneyTransactionExcelFileDataRowIterator defines the structure of feidee mymoney transaction plain text data row iterator
type feideeMymoneyTransactionExcelFileDataRowIterator struct {
dataTable *feideeMymoneyTransactionExcelFileDataTable
currentTableIndex int
currentRowIndexInTable int
}
// DataRowCount returns the total count of data row
func (t *feideeMymoneyTransactionExcelFileDataTable) DataRowCount() int {
allSheets := t.workbook.GetSheets()
totalDataRowCount := 0
for i := 0; i < len(allSheets); i++ {
sheet := allSheets[i]
if sheet.GetNumberRows() <= 1 {
continue
}
totalDataRowCount += sheet.GetNumberRows() - 1
}
return totalDataRowCount
}
// HeaderLineColumnNames returns the header column name list
func (t *feideeMymoneyTransactionExcelFileDataTable) HeaderLineColumnNames() []string {
return t.headerLineColumnNames
}
// DataRowIterator returns the iterator of data row
func (t *feideeMymoneyTransactionExcelFileDataTable) DataRowIterator() ImportedDataRowIterator {
return &feideeMymoneyTransactionExcelFileDataRowIterator{
dataTable: t,
currentTableIndex: 0,
currentRowIndexInTable: 0,
}
}
// ColumnCount returns the total count of column in this data row
func (r *feideeMymoneyTransactionExcelFileDataRow) ColumnCount() int {
row, err := r.sheet.GetRow(r.rowIndex)
if err != nil {
return 0
}
return len(row.GetCols())
}
// GetData returns the data in the specified column index
func (r *feideeMymoneyTransactionExcelFileDataRow) GetData(columnIndex int) string {
row, err := r.sheet.GetRow(r.rowIndex)
if err != nil {
return ""
}
cell, err := row.GetCol(columnIndex)
if err != nil {
return ""
}
return cell.GetString()
}
// GetTime returns the time in the specified column index
func (r *feideeMymoneyTransactionExcelFileDataRow) GetTime(columnIndex int, timezoneOffset int16) (time.Time, error) {
str := r.GetData(columnIndex)
if utils.IsValidLongDateTimeFormat(str) {
return utils.ParseFromLongDateTime(str, timezoneOffset)
}
if utils.IsValidLongDateTimeWithoutSecondFormat(str) {
return utils.ParseFromLongDateTimeWithoutSecond(str, timezoneOffset)
}
if utils.IsValidLongDateFormat(str) {
return utils.ParseFromLongDateTimeWithoutSecond(str+" 00:00", timezoneOffset)
}
return time.Unix(0, 0), errs.ErrTransactionTimeInvalid
}
// GetTimezoneOffset returns the time zone offset in the specified column index
func (r *feideeMymoneyTransactionExcelFileDataRow) GetTimezoneOffset(columnIndex int) (*time.Location, error) {
return nil, errs.ErrNotSupported
}
// HasNext returns whether the iterator does not reach the end
func (t *feideeMymoneyTransactionExcelFileDataRowIterator) HasNext() bool {
allSheets := t.dataTable.workbook.GetSheets()
if t.currentTableIndex >= len(allSheets) {
return false
}
currentSheet := allSheets[t.currentTableIndex]
if t.currentRowIndexInTable+1 < currentSheet.GetNumberRows() {
return true
}
for i := t.currentTableIndex + 1; i < len(allSheets); i++ {
sheet := allSheets[i]
if sheet.GetNumberRows() <= 1 {
continue
}
return true
}
return false
}
// Next returns the next imported data row
func (t *feideeMymoneyTransactionExcelFileDataRowIterator) Next() ImportedDataRow {
allSheets := t.dataTable.workbook.GetSheets()
currentRowIndexInTable := t.currentRowIndexInTable
for i := t.currentTableIndex; i < len(allSheets); i++ {
sheet := allSheets[i]
if currentRowIndexInTable+1 < sheet.GetNumberRows() {
t.currentRowIndexInTable++
currentRowIndexInTable = t.currentRowIndexInTable
break
}
t.currentTableIndex++
t.currentRowIndexInTable = 0
currentRowIndexInTable = 0
}
if t.currentTableIndex >= len(allSheets) {
return nil
}
currentSheet := allSheets[t.currentTableIndex]
if t.currentRowIndexInTable >= currentSheet.GetNumberRows() {
return nil
}
return &feideeMymoneyTransactionExcelFileDataRow{
sheet: &currentSheet,
rowIndex: t.currentRowIndexInTable,
}
}
func createNewFeideeMymoneyTransactionExcelFileDataTable(data []byte) (*feideeMymoneyTransactionExcelFileDataTable, error) {
reader := bytes.NewReader(data)
workbook, err := xls.OpenReader(reader)
if err != nil {
return nil, err
}
allSheets := workbook.GetSheets()
var headerRowItems []string
for i := 0; i < len(allSheets); i++ {
sheet := allSheets[i]
if sheet.GetNumberRows() < 1 {
continue
}
row, err := sheet.GetRow(0)
if err != nil {
return nil, err
}
cells := row.GetCols()
if i == 0 {
for j := 0; j < len(cells); j++ {
headerItem := cells[j].GetString()
if headerItem == "" {
break
}
headerRowItems = append(headerRowItems, headerItem)
}
} else {
for j := 0; j < min(len(cells), len(headerRowItems)); j++ {
headerItem := cells[j].GetString()
if headerItem != headerRowItems[j] {
return nil, errs.ErrFieldsInMultiTableAreDifferent
}
}
}
}
return &feideeMymoneyTransactionExcelFileDataTable{
workbook: &workbook,
headerLineColumnNames: headerRowItems,
}, nil
}
@@ -0,0 +1,50 @@
package converters
import (
"github.com/mayswind/ezbookkeeping/pkg/core"
"github.com/mayswind/ezbookkeeping/pkg/models"
)
// feideeMymoneyTransactionDataXlsImporter defines the structure of feidee mymoney xls importer for transaction data
type feideeMymoneyTransactionDataXlsImporter struct {
DataTableTransactionDataImporter
}
var feideeMymoneyDataColumnNameMapping = map[DataTableColumn]string{
DATA_TABLE_TRANSACTION_TIME: "日期",
DATA_TABLE_TRANSACTION_TYPE: "交易类型",
DATA_TABLE_CATEGORY: "分类",
DATA_TABLE_SUB_CATEGORY: "子分类",
DATA_TABLE_ACCOUNT_NAME: "账户1",
DATA_TABLE_AMOUNT: "金额",
DATA_TABLE_RELATED_ACCOUNT_NAME: "账户2",
DATA_TABLE_DESCRIPTION: "备注",
}
var feideeMymoneyTransactionTypeNameMapping = map[models.TransactionType]string{
models.TRANSACTION_TYPE_MODIFY_BALANCE: "余额变更",
models.TRANSACTION_TYPE_INCOME: "收入",
models.TRANSACTION_TYPE_EXPENSE: "支出",
models.TRANSACTION_TYPE_TRANSFER: "转账",
}
// Initialize an feidee mymoney transaction data xls file importer singleton instance
var (
FeideeMymoneyTransactionDataXlsImporter = &feideeMymoneyTransactionDataXlsImporter{
DataTableTransactionDataImporter{
dataColumnMapping: feideeMymoneyDataColumnNameMapping,
transactionTypeMapping: feideeMymoneyTransactionTypeNameMapping,
},
}
)
// ParseImportedData returns the imported data by parsing the feidee mymoney transaction xls data
func (c *feideeMymoneyTransactionDataXlsImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, accountMap map[string]*models.Account, categoryMap map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionTag, error) {
dataTable, err := createNewFeideeMymoneyTransactionExcelFileDataTable(data)
if err != nil {
return nil, nil, nil, nil, err
}
return c.parseImportedData(ctx, user, dataTable, defaultTimezoneOffset, accountMap, categoryMap, tagMap)
}
@@ -0,0 +1,102 @@
package converters
import (
"os"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/mayswind/ezbookkeeping/pkg/core"
"github.com/mayswind/ezbookkeeping/pkg/models"
"github.com/mayswind/ezbookkeeping/pkg/utils"
)
func TestFeideeMymoneyTransactionDataXlsImporterParseImportedData_MinimumValidData(t *testing.T) {
converter := FeideeMymoneyTransactionDataXlsImporter
context := core.NewNullContext()
user := &models.User{
Uid: 1234567890,
DefaultCurrency: "CNY",
}
testdata, err := os.ReadFile("../../testdata/feidee_mymoney_test_file.xls")
assert.Nil(t, err)
allNewTransactions, allNewAccounts, allNewSubCategories, allNewTags, err := converter.ParseImportedData(context, user, testdata, 0, nil, nil, nil)
assert.Nil(t, err)
assert.Equal(t, 6, len(allNewTransactions))
assert.Equal(t, 2, len(allNewAccounts))
assert.Equal(t, 5, len(allNewSubCategories))
assert.Equal(t, 0, len(allNewTags))
assert.Equal(t, int64(1234567890), allNewTransactions[0].Uid)
assert.Equal(t, models.TRANSACTION_DB_TYPE_MODIFY_BALANCE, allNewTransactions[0].Type)
assert.Equal(t, "2024-09-01 00:00:00", utils.FormatUnixTimeToLongDateTime(utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime), time.UTC))
assert.Equal(t, int64(12345), allNewTransactions[0].Amount)
assert.Equal(t, "Test Account", allNewTransactions[0].OriginalSourceAccountName)
assert.Equal(t, "", allNewTransactions[0].OriginalCategoryName)
assert.Equal(t, int64(1234567890), allNewTransactions[1].Uid)
assert.Equal(t, models.TRANSACTION_DB_TYPE_INCOME, allNewTransactions[1].Type)
assert.Equal(t, "2024-09-01 01:23:45", utils.FormatUnixTimeToLongDateTime(utils.GetUnixTimeFromTransactionTime(allNewTransactions[1].TransactionTime), time.UTC))
assert.Equal(t, int64(12), allNewTransactions[1].Amount)
assert.Equal(t, "Test Account", allNewTransactions[1].OriginalSourceAccountName)
assert.Equal(t, "Test Category", allNewTransactions[1].OriginalCategoryName)
assert.Equal(t, int64(1234567890), allNewTransactions[2].Uid)
assert.Equal(t, models.TRANSACTION_DB_TYPE_EXPENSE, allNewTransactions[2].Type)
assert.Equal(t, "2024-09-01 12:34:56", utils.FormatUnixTimeToLongDateTime(utils.GetUnixTimeFromTransactionTime(allNewTransactions[2].TransactionTime), time.UTC))
assert.Equal(t, int64(100), allNewTransactions[2].Amount)
assert.Equal(t, "Test Account2", allNewTransactions[2].OriginalSourceAccountName)
assert.Equal(t, "Test Category2", allNewTransactions[2].OriginalCategoryName)
assert.Equal(t, int64(1234567890), allNewTransactions[3].Uid)
assert.Equal(t, models.TRANSACTION_DB_TYPE_TRANSFER_OUT, allNewTransactions[3].Type)
assert.Equal(t, "2024-09-01 23:59:59", utils.FormatUnixTimeToLongDateTime(utils.GetUnixTimeFromTransactionTime(allNewTransactions[3].TransactionTime), time.UTC))
assert.Equal(t, int64(5), allNewTransactions[3].Amount)
assert.Equal(t, "Test Comment5", allNewTransactions[3].Comment)
assert.Equal(t, "Test Account", allNewTransactions[3].OriginalSourceAccountName)
assert.Equal(t, "Test Account2", allNewTransactions[3].OriginalDestinationAccountName)
assert.Equal(t, "Test Category3", allNewTransactions[3].OriginalCategoryName)
assert.Equal(t, int64(1234567890), allNewTransactions[4].Uid)
assert.Equal(t, models.TRANSACTION_DB_TYPE_INCOME, allNewTransactions[4].Type)
assert.Equal(t, "2024-09-10 00:00:00", utils.FormatUnixTimeToLongDateTime(utils.GetUnixTimeFromTransactionTime(allNewTransactions[4].TransactionTime), time.UTC))
assert.Equal(t, int64(-54300), allNewTransactions[4].Amount)
assert.Equal(t, "Test Account2", allNewTransactions[4].OriginalSourceAccountName)
assert.Equal(t, "Test Category5", allNewTransactions[4].OriginalCategoryName)
assert.Equal(t, int64(1234567890), allNewTransactions[5].Uid)
assert.Equal(t, models.TRANSACTION_DB_TYPE_EXPENSE, allNewTransactions[5].Type)
assert.Equal(t, "2024-09-11 05:06:00", utils.FormatUnixTimeToLongDateTime(utils.GetUnixTimeFromTransactionTime(allNewTransactions[5].TransactionTime), time.UTC))
assert.Equal(t, int64(-12340), allNewTransactions[5].Amount)
assert.Equal(t, "Line1\nLine2", allNewTransactions[5].Comment)
assert.Equal(t, "Test Account", allNewTransactions[5].OriginalSourceAccountName)
assert.Equal(t, "Test Category4", allNewTransactions[5].OriginalCategoryName)
assert.Equal(t, int64(1234567890), allNewAccounts[0].Uid)
assert.Equal(t, "Test Account", allNewAccounts[0].Name)
assert.Equal(t, "CNY", allNewAccounts[0].Currency)
assert.Equal(t, int64(1234567890), allNewAccounts[1].Uid)
assert.Equal(t, "Test Account2", allNewAccounts[1].Name)
assert.Equal(t, "CNY", allNewAccounts[1].Currency)
assert.Equal(t, int64(1234567890), allNewSubCategories[0].Uid)
assert.Equal(t, "Test Category", allNewSubCategories[0].Name)
assert.Equal(t, int64(1234567890), allNewSubCategories[1].Uid)
assert.Equal(t, "Test Category5", allNewSubCategories[1].Name)
assert.Equal(t, int64(1234567890), allNewSubCategories[2].Uid)
assert.Equal(t, "Test Category2", allNewSubCategories[2].Name)
assert.Equal(t, int64(1234567890), allNewSubCategories[3].Uid)
assert.Equal(t, "Test Category4", allNewSubCategories[3].Name)
assert.Equal(t, int64(1234567890), allNewSubCategories[4].Uid)
assert.Equal(t, "Test Category3", allNewSubCategories[4].Name)
}
@@ -19,6 +19,8 @@ func GetTransactionDataImporter(fileType string) (TransactionDataImporter, error
return EzBookKeepingTransactionDataCSVFileConverter, nil
} else if fileType == "ezbookkeeping_tsv" {
return EzBookKeepingTransactionDataTSVFileConverter, nil
} else if fileType == "feidee_mymoney_xls" {
return FeideeMymoneyTransactionDataXlsImporter, nil
} else {
return nil, errs.ErrImportFileTypeNotSupported
}
+1
View File
@@ -15,4 +15,5 @@ var (
ErrDestinationAccountNameCannotBeBlank = NewNormalError(NormalSubcategoryConverter, 8, http.StatusBadRequest, "destination account name cannot be blank")
ErrAmountInvalid = NewNormalError(NormalSubcategoryConverter, 9, http.StatusBadRequest, "transaction amount is invalid")
ErrGeographicLocationInvalid = NewNormalError(NormalSubcategoryConverter, 10, http.StatusBadRequest, "geographic location is invalid")
ErrFieldsInMultiTableAreDifferent = NewNormalError(NormalSubcategoryConverter, 11, http.StatusBadRequest, "fields in multiple table headers are different")
)
+2 -1
View File
@@ -136,7 +136,8 @@ func ParseFromLongDateTime(t string, utcOffset int16) (time.Time, error) {
}
// ParseFromLongDateTimeWithoutSecond parses a formatted string in long date time format (no second)
func ParseFromLongDateTimeWithoutSecond(t string, timezone *time.Location) (time.Time, error) {
func ParseFromLongDateTimeWithoutSecond(t string, utcOffset int16) (time.Time, error) {
timezone := time.FixedZone("Timezone", int(utcOffset)*60)
return time.ParseInLocation(longDateTimeWithoutSecondFormat, t, timezone)
}
+1 -1
View File
@@ -133,7 +133,7 @@ func TestParseFromLongDateTime(t *testing.T) {
func TestParseFromLongDateTimeWithoutSecond(t *testing.T) {
expectedValue := int64(1691947440)
actualTime, err := ParseFromLongDateTimeWithoutSecond("2023-08-13 17:24", time.UTC)
actualTime, err := ParseFromLongDateTimeWithoutSecond("2023-08-13 17:24", 0)
assert.Equal(t, nil, err)
actualValue := actualTime.Unix()
+21 -3
View File
@@ -3,9 +3,12 @@ package utils
import "regexp"
var (
usernamePattern = regexp.MustCompile("^(?i)[a-z0-9_-]+$")
emailPattern = regexp.MustCompile("^(?i)(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])$")
hexRGBColorPattern = regexp.MustCompile("^(?i)([0-9a-f]{6}|[0-9a-f]{3})$")
usernamePattern = regexp.MustCompile("^(?i)[a-z0-9_-]+$")
emailPattern = regexp.MustCompile("^(?i)(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])$")
hexRGBColorPattern = regexp.MustCompile("^(?i)([0-9a-f]{6}|[0-9a-f]{3})$")
longDateTimePattern = regexp.MustCompile("^([1-9][0-9]{3})-(0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-9]|3[01]) ([0-1][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$")
longDateTimeWithoutSecondPattern = regexp.MustCompile("^([1-9][0-9]{3})-(0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-9]|3[01]) ([0-1][0-9]|2[0-3]):([0-5][0-9])$")
longDatePattern = regexp.MustCompile("^([1-9][0-9]{3})-(0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-9]|3[01])$")
)
// IsValidUsername reports whether username is valid
@@ -22,3 +25,18 @@ func IsValidEmail(email string) bool {
func IsValidHexRGBColor(color string) bool {
return hexRGBColorPattern.MatchString(color)
}
// IsValidLongDateTimeFormat reports whether long date time is valid format
func IsValidLongDateTimeFormat(datetime string) bool {
return longDateTimePattern.MatchString(datetime)
}
// IsValidLongDateTimeWithoutSecondFormat reports long date time without seconds is valid format
func IsValidLongDateTimeWithoutSecondFormat(datetime string) bool {
return longDateTimeWithoutSecondPattern.MatchString(datetime)
}
// IsValidLongDateFormat reports long date is valid format
func IsValidLongDateFormat(date string) bool {
return longDatePattern.MatchString(date)
}
+117
View File
@@ -112,3 +112,120 @@ func TestIsValidHexRGBColor_InvalidHexRGBColor(t *testing.T) {
actualValue = IsValidHexRGBColor(color)
assert.Equal(t, expectedValue, actualValue)
}
func TestIsValidLongDateTimeFormat_ValidLongDateTimeFormat(t *testing.T) {
datetime := "2024-09-01 12:34:56"
expectedValue := true
actualValue := IsValidLongDateTimeFormat(datetime)
assert.Equal(t, expectedValue, actualValue)
datetime = "2024-10-01 00:00:00"
expectedValue = true
actualValue = IsValidLongDateTimeFormat(datetime)
assert.Equal(t, expectedValue, actualValue)
datetime = "9999-12-31 23:59:59"
expectedValue = true
actualValue = IsValidLongDateTimeFormat(datetime)
assert.Equal(t, expectedValue, actualValue)
}
func TestIsValidLongDateTimeFormat_InvalidLongDateTimeFormat(t *testing.T) {
datetime := "2024-09-01"
expectedValue := false
actualValue := IsValidLongDateTimeFormat(datetime)
assert.Equal(t, expectedValue, actualValue)
datetime = "2024-09-01 12"
expectedValue = false
actualValue = IsValidLongDateTimeFormat(datetime)
assert.Equal(t, expectedValue, actualValue)
datetime = "2024-09-01 12:34"
expectedValue = false
actualValue = IsValidLongDateTimeFormat(datetime)
assert.Equal(t, expectedValue, actualValue)
}
func TestIsValidLongDateTimeWithoutSecondFormat_ValidLongDateTimeWithoutSecondFormat(t *testing.T) {
datetime := "2024-09-01 12:34"
expectedValue := true
actualValue := IsValidLongDateTimeWithoutSecondFormat(datetime)
assert.Equal(t, expectedValue, actualValue)
datetime = "2024-10-01 00:00"
expectedValue = true
actualValue = IsValidLongDateTimeWithoutSecondFormat(datetime)
assert.Equal(t, expectedValue, actualValue)
datetime = "9999-12-31 23:59"
expectedValue = true
actualValue = IsValidLongDateTimeWithoutSecondFormat(datetime)
assert.Equal(t, expectedValue, actualValue)
}
func TestIsValidLongDateTimeWithoutSecondFormat_InvalidLongDateTimeWithoutSecondFormat(t *testing.T) {
datetime := "2024-09-01"
expectedValue := false
actualValue := IsValidLongDateTimeWithoutSecondFormat(datetime)
assert.Equal(t, expectedValue, actualValue)
datetime = "2024-09-01 12"
expectedValue = false
actualValue = IsValidLongDateTimeWithoutSecondFormat(datetime)
assert.Equal(t, expectedValue, actualValue)
datetime = "2024-09-01 12:34:56"
expectedValue = false
actualValue = IsValidLongDateTimeWithoutSecondFormat(datetime)
assert.Equal(t, expectedValue, actualValue)
}
func TestIsValidLongDateFormat_ValidLongDateFormat(t *testing.T) {
datetime := "2024-09-01"
expectedValue := true
actualValue := IsValidLongDateFormat(datetime)
assert.Equal(t, expectedValue, actualValue)
datetime = "9999-12-31"
expectedValue = true
actualValue = IsValidLongDateFormat(datetime)
assert.Equal(t, expectedValue, actualValue)
}
func TestIsValidLongDateFormat_InvalidLongDateFormat(t *testing.T) {
datetime := "24-09-01"
expectedValue := false
actualValue := IsValidLongDateFormat(datetime)
assert.Equal(t, expectedValue, actualValue)
datetime = "2024-9-1"
expectedValue = false
actualValue = IsValidLongDateFormat(datetime)
assert.Equal(t, expectedValue, actualValue)
datetime = "2024-09-1"
expectedValue = false
actualValue = IsValidLongDateFormat(datetime)
assert.Equal(t, expectedValue, actualValue)
datetime = "2024-9-01"
expectedValue = false
actualValue = IsValidLongDateFormat(datetime)
assert.Equal(t, expectedValue, actualValue)
datetime = "2024-09-01 12"
expectedValue = false
actualValue = IsValidLongDateFormat(datetime)
assert.Equal(t, expectedValue, actualValue)
datetime = "2024-09-01 12:34"
expectedValue = false
actualValue = IsValidLongDateFormat(datetime)
assert.Equal(t, expectedValue, actualValue)
datetime = "2024-09-01 12:34:56"
expectedValue = false
actualValue = IsValidLongDateFormat(datetime)
assert.Equal(t, expectedValue, actualValue)
}