diff --git a/pkg/converters/ezbookkeeping_csv_file.go b/pkg/converters/ezbookkeeping_csv_file.go index c779582d..8abc252a 100644 --- a/pkg/converters/ezbookkeeping_csv_file.go +++ b/pkg/converters/ezbookkeeping_csv_file.go @@ -14,8 +14,8 @@ type EzBookKeepingCSVFileExporter struct { DataConverter } -const csvHeaderLine = "Time,Type,Category,Sub Category,Account,Amount,Account2,Account2 Amount,Tags,Comment\n" -const csvDataLineFormat = "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n" +const csvHeaderLine = "Time,Timezone,Type,Category,Sub Category,Account,Account Currency,Amount,Account2,Account2 Currency,Account2 Amount,Tags,Comment\n" +const csvDataLineFormat = "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n" // ToExportedContent returns the exported csv data func (e *EzBookKeepingCSVFileExporter) ToExportedContent(uid int64, timezone *time.Location, transactions []*models.Transaction, accountMap map[int64]*models.Account, categoryMap map[int64]*models.TransactionCategory, tagMap map[int64]*models.TransactionTag, allTagIndexs map[int64][]int64) ([]byte, error) { @@ -33,23 +33,27 @@ func (e *EzBookKeepingCSVFileExporter) ToExportedContent(uid int64, timezone *ti transactionTimeZone := time.FixedZone("Transaction Timezone", int(transaction.TimezoneUtcOffset)*60) transactionTime := utils.FormatUnixTimeToLongDateTimeWithoutSecond(utils.GetUnixTimeFromTransactionTime(transaction.TransactionTime), transactionTimeZone) + transactionTimezone := utils.FormatTimezoneOffset(transactionTimeZone) transactionType := e.getTransactionTypeName(transaction.Type) category := e.getTransactionCategoryName(transaction.CategoryId, categoryMap) subCategory := e.getTransactionSubCategoryName(transaction.CategoryId, categoryMap) account := e.getAccountName(transaction.AccountId, accountMap) + accountCurrency := e.getAccountCurrency(transaction.AccountId, accountMap) amount := e.getDisplayAmount(transaction.Amount) account2 := "" + account2Currency := "" account2Amount := "" if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_OUT { account2 = e.getAccountName(transaction.RelatedAccountId, accountMap) + account2Currency = e.getAccountCurrency(transaction.RelatedAccountId, accountMap) account2Amount = e.getDisplayAmount(transaction.RelatedAccountAmount) } tags := e.getTags(transaction.TransactionId, allTagIndexs, tagMap) comment := e.getComment(transaction.Comment) - ret.WriteString(fmt.Sprintf(csvDataLineFormat, transactionTime, transactionType, category, subCategory, account, amount, account2, account2Amount, tags, comment)) + ret.WriteString(fmt.Sprintf(csvDataLineFormat, transactionTime, transactionTimezone, transactionType, category, subCategory, account, accountCurrency, amount, account2, account2Currency, account2Amount, tags, comment)) } return []byte(ret.String()), nil @@ -109,6 +113,16 @@ func (e *EzBookKeepingCSVFileExporter) getAccountName(accountId int64, accountMa } } +func (e *EzBookKeepingCSVFileExporter) getAccountCurrency(accountId int64, accountMap map[int64]*models.Account) string { + account, exists := accountMap[accountId] + + if exists { + return account.Currency + } else { + return "" + } +} + func (e *EzBookKeepingCSVFileExporter) getDisplayAmount(amount int64) string { displayAmount := utils.Int64ToString(amount) integer := utils.SubString(displayAmount, 0, len(displayAmount)-2) diff --git a/pkg/utils/datetimes.go b/pkg/utils/datetimes.go index fde044cf..53dbd43e 100644 --- a/pkg/utils/datetimes.go +++ b/pkg/utils/datetimes.go @@ -1,6 +1,9 @@ package utils -import "time" +import ( + "fmt" + "time" +) const ( longDateTimeFormat = "2006-01-02 15:04:05" @@ -53,6 +56,24 @@ func ParseFromShortDateTime(t string, utcOffset int16) (time.Time, error) { return time.ParseInLocation(shortDateTimeFormat, t, timezone) } +// FormatTimezoneOffset returns "+/-HH:MM" format of timezone +func FormatTimezoneOffset(timezone *time.Location) string { + _, tzOffset := time.Now().In(timezone).Zone() + tzMinutesOffset := tzOffset / 60 + + sign := "+" + hourAbsOffset := tzMinutesOffset / 60 + minuteAbsOffset := tzMinutesOffset % 60 + + if hourAbsOffset < 0 { + sign = "-" + hourAbsOffset = -hourAbsOffset + minuteAbsOffset = -minuteAbsOffset + } + + return fmt.Sprintf("%s%02d:%02d", sign, hourAbsOffset, minuteAbsOffset) +} + // GetMinTransactionTimeFromUnixTime returns the minimum transaction time from unix time func GetMinTransactionTimeFromUnixTime(unixTime int64) int64 { return unixTime * 1000 diff --git a/pkg/utils/datetimes_test.go b/pkg/utils/datetimes_test.go index 4654270f..364a1967 100644 --- a/pkg/utils/datetimes_test.go +++ b/pkg/utils/datetimes_test.go @@ -60,6 +60,28 @@ func TestParseFromShortDateTime(t *testing.T) { assert.Equal(t, expectedValue, actualValue) } +func TestFormatTimezoneOffset(t *testing.T) { + timezone := time.FixedZone("Test Timezone", 120*60) + expectedValue := "+02:00" + actualValue := FormatTimezoneOffset(timezone) + assert.Equal(t, expectedValue, actualValue) + + timezone = time.FixedZone("Test Timezone", 345*60) + expectedValue = "+05:45" + actualValue = FormatTimezoneOffset(timezone) + assert.Equal(t, expectedValue, actualValue) + + timezone = time.FixedZone("Test Timezone", -720*60) + expectedValue = "-12:00" + actualValue = FormatTimezoneOffset(timezone) + assert.Equal(t, expectedValue, actualValue) + + timezone = time.FixedZone("Test Timezone", 0) + expectedValue = "+00:00" + actualValue = FormatTimezoneOffset(timezone) + assert.Equal(t, expectedValue, actualValue) +} + func TestGetMinTransactionTimeFromUnixTime(t *testing.T) { expectedValue := int64(1617228083000) actualValue := GetMinTransactionTimeFromUnixTime(1617228083)