From e5ecf05e999d3ddb2e9774b504474a21a28ecae4 Mon Sep 17 00:00:00 2001
From: jano3 <jano@bob.co.za>
Date: Thu, 13 Jun 2024 15:25:38 +0200
Subject: [PATCH] Add shareable utils functions from SL

---
 date_utils/date_utils.go     | 48 ++++++++++++++++++++++++++++++++
 handler_utils/request.go     | 11 ++++++++
 string_utils/string_utils.go | 54 +++++++++++++++++++++++++++++++++++-
 3 files changed, 112 insertions(+), 1 deletion(-)

diff --git a/date_utils/date_utils.go b/date_utils/date_utils.go
index c88b39a..5f0ad1b 100644
--- a/date_utils/date_utils.go
+++ b/date_utils/date_utils.go
@@ -2,6 +2,7 @@ package date_utils
 
 import (
 	"fmt"
+	"github.com/jinzhu/now"
 	"reflect"
 	"strconv"
 	"strings"
@@ -107,6 +108,33 @@ func CurrentDate() time.Time {
 	return currentDate
 }
 
+func TodayStart() string {
+	currentTime := CurrentDate()
+	return DateDBFormattedString(now.With(currentTime).BeginningOfDay())
+}
+
+func TodayEnd() string {
+	currentTime := CurrentDate()
+	return DateDBFormattedString(now.With(currentTime).EndOfDay())
+}
+
+func StartOfDay(date time.Time) time.Time {
+	day := now.With(date.In(CurrentLocation())).BeginningOfDay()
+	return day
+}
+
+func EndOfDay(date time.Time) time.Time {
+	// Subtract one second from the start of the next day so that we have 23:59:59 instead of 23:59:58.999999
+	day := now.With(date.AddDate(0, 0, 1).In(CurrentLocation())).BeginningOfDay().Add(-time.Second)
+	return day
+}
+
+// BeginningOfNextDay is useful for specifying intervals where the beginning of the next day is excluded e.g. date < beginningOfNextDay
+func BeginningOfNextDay(date time.Time) time.Time {
+	day := now.With(date.AddDate(0, 0, 1).In(CurrentLocation())).BeginningOfDay()
+	return day
+}
+
 func DateEqual(date1, date2 time.Time) bool {
 	y1, m1, d1 := date1.Date()
 	y2, m2, d2 := date2.Date()
@@ -132,6 +160,26 @@ func TimeBefore(a string, b string) bool {
 	return hoursA < hoursB
 }
 
+// DatePtrToString converts a time.Time pointer to a string in the format "2006-01-02 15:04:05".
+func DatePtrToString(date *time.Time) string {
+	if date == nil {
+		return ""
+	}
+	return date.In(CurrentLocation()).Format(DateLayoutYearMonthDayTime())
+}
+
+// DatePtrToTimeString converts a time.Time pointer to a string in the format "15:04". If the pointer is nil, it returns "--:--".
+func DatePtrToTimeString(date *time.Time) string {
+	if date != nil {
+		// Convert to local time
+		DateLocal(date)
+		// Return formatted as time only
+		return date.Format("15:04")
+	}
+
+	return "--:--"
+}
+
 // ConvertToNoDateTimeString  - Converts a PSQL Time type to Go Time type
 func ConvertToNoDateTimeString(timeString *string) (*string, error) {
 	parsedTime, err := time.Parse("15:04:05", *timeString)
diff --git a/handler_utils/request.go b/handler_utils/request.go
index ef101c4..ec8ce8e 100644
--- a/handler_utils/request.go
+++ b/handler_utils/request.go
@@ -3,7 +3,9 @@ package handler_utils
 import (
 	"bytes"
 	"context"
+	"crypto/sha1"
 	"crypto/sha256"
+	"encoding/base64"
 	"encoding/hex"
 	"io"
 	"net/http"
@@ -111,3 +113,12 @@ func FindHeaderValue(headers map[string]string, key string) string {
 	}
 	return ""
 }
+
+// HashRequestBody does a simple SHA1 hash of the provided body string. This is useful for checking for duplicates, but
+// should not be used for any security purposes.
+func HashRequestBody(body string) string {
+	algorithm := sha1.New()
+	algorithm.Write([]byte(body))
+	sha := base64.URLEncoding.EncodeToString(algorithm.Sum(nil))
+	return sha
+}
diff --git a/string_utils/string_utils.go b/string_utils/string_utils.go
index 07dd05f..7e776f6 100644
--- a/string_utils/string_utils.go
+++ b/string_utils/string_utils.go
@@ -7,6 +7,7 @@ import (
 	"github.com/jaytaylor/html2text"
 	"github.com/samber/lo"
 	"gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/number_utils"
+	"math"
 	"regexp"
 	"strconv"
 	"strings"
@@ -233,7 +234,7 @@ func Int64SliceToStringSlice(numbers []int64) []string {
 }
 
 func Float64ToString(number float64, precision int) string {
-	return strconv.FormatFloat(number, 'f', precision, 64)
+	return strconv.FormatFloat(number_utils.RoundFloat(number), 'f', precision, 64)
 }
 
 func Float64ToStringWithPrec(number float64, prec int) string {
@@ -383,3 +384,54 @@ func HTMLStringToTextBytes(html string) ([]byte, error) {
 	}
 	return []byte(text), nil
 }
+
+func BoolToYesNo(val bool) string {
+	if val {
+		return "Yes"
+	}
+	return "No"
+}
+
+func FormatCurrency(amount float64, currencySymbol string, formatZero bool) string {
+	if amount == 0 && !formatZero {
+		return "-"
+	}
+
+	isNegative := amount < 0
+	amount = math.Abs(amount)
+
+	amountString := addSeparator(amount, " ")
+
+	currencySymbol = strings.TrimSpace(currencySymbol)
+	if len(currencySymbol) > 0 {
+		currencySymbol += " "
+	}
+
+	format := currencySymbol + "%s"
+	if isNegative {
+		format = " - " + format
+	}
+	return strings.TrimSpace(fmt.Sprintf(format, amountString))
+}
+
+func reverseString(s string) string {
+	stringRunes := []rune(s)
+	for i, j := 0, len(stringRunes)-1; i < j; i, j = i+1, j-1 {
+		stringRunes[i], stringRunes[j] = stringRunes[j], stringRunes[i]
+	}
+
+	return string(stringRunes)
+}
+
+func addSeparator(f float64, seperator string) string {
+	str := reverseString(fmt.Sprintf("%.2f", f))
+	re := regexp.MustCompile(`([\d]){3}`)
+	str = strings.TrimSpace(re.ReplaceAllString(str, fmt.Sprintf(`$0%s`, seperator)))
+
+	return reverseString(str)
+}
+
+func IsAllDigits(str string) bool {
+	_, err := number_utils.StringToInt(str)
+	return err == nil
+}
-- 
GitLab