From b25e127dbf4928de2277849181e75248d4cb09db Mon Sep 17 00:00:00 2001 From: iman Date: Mon, 14 Oct 2024 14:56:37 +0330 Subject: [PATCH] feat [date]: add jalaali to georgian date. add function JalaaliToGregorianDate which returns georgian format time.Time. add function JalaaliToGregorian which returns string in format %04d/%02d/%02d. --- README.md | 32 ++++++++----- date/date_test.go | 81 +++++++++++++++++++++++++++++++++ date/jalaali_to_georgian.go | 89 +++++++++++++++++++++++++++++++++++++ go.mod | 1 + go.sum | 14 ++---- 5 files changed, 196 insertions(+), 21 deletions(-) create mode 100644 date/date_test.go create mode 100644 date/jalaali_to_georgian.go diff --git a/README.md b/README.md index eca2878..4d36943 100644 --- a/README.md +++ b/README.md @@ -17,10 +17,10 @@ - [x] Digits - [x] Validate Bank card number. - [x] Find Bank's name by Card number. -- [X] Check Iranian Sheba(IBAN) validation and recognize bank information by sheba code. -- [X] Add and remove commas to numbers. -- [X] Find city and province name by national code(code-e Melli). -- [X] Validate Iranian national number(code-e Melli). +- [x] Check Iranian Sheba(IBAN) validation and recognize bank information by sheba code. +- [x] Add and remove commas to numbers. +- [x] Find city and province name by national code(code-e Melli). +- [x] Validate Iranian national number(code-e Melli). #### How to use it? @@ -40,7 +40,7 @@ And pass it ##### Bills ```go -result := bills.GetBillType(params) // تلفن ثابت +result := bills.GetBillType(params) // تلفن ثابت amount := bills.GetCurrency(params) //120000 barcode := bills.GetBarCode(params) // 111775320014000012070160 verify := bills.VerifyBillID(params) //true @@ -50,9 +50,16 @@ verify := bills.VerifyBillID(params) //true ##### Digits ```go -num2wordFa := digits.DigitToWord("۱۵۶۷۸۹") // صد پنجاه و شش هزار هفتصد هشتاد و نه -num2wordEn := digits.DigitToWord("156789") // صد پنجاه و شش هزار هفتصد هشتاد و نه -Negative := digits.DigitToWord("-156789") // منفی صد پنجاه و شش هزار هفتصد هشتاد و نه +num2wordFa := digits.DigitToWord("۱۵۶۷۸۹") // صد پنجاه و شش هزار هفتصد هشتاد و نه +num2wordEn := digits.DigitToWord("156789") // صد پنجاه و شش هزار هفتصد هشتاد و نه +Negative := digits.DigitToWord("-156789") // منفی صد پنجاه و شش هزار هفتصد هشتاد و نه +``` + +#### Date + +```go +georgianDate, err := JalaaliToGregorianDate("1401/02/03") // Time.Date(2022,4,23,0,0,0,0,time.UTC) +georgianDateString, err := JalaaliToGregorian("1401/02/03") // "2022/04/23" ``` #### Bank @@ -71,6 +78,7 @@ bank,error := CardInfo("6219861034529007") // saman, nil ``` ###### Check Iranian Sheba + The types of results are : ```go @@ -99,11 +107,12 @@ sheba := shebaCode.IsSheba() // { false } addComma := digits.AddCommas(14555478854) removeComma := digits.RemoveCommas("4,555,522,212,12") -fmt.Printf("\n ADD COMMA : %v \n", addComma) // 14,555,478,854 -fmt.Printf("\n REMOVE COMMA : %v \n", removeComma)// 455552221212 +fmt.Printf("\n ADD COMMA : %v \n", addComma) // 14,555,478,854 +fmt.Printf("\n REMOVE COMMA : %v \n", removeComma)// 455552221212 ``` ###### Get Place and city By NationalID + ```go getPlaceByIranNationalId := city.GetPlaceByIranNationalId("0499370899") fmt.Printf("\n Result : %v \n", getPlaceByIranNationalId) @@ -111,10 +120,11 @@ fmt.Printf("\n Result : %v \n", getPlaceByIranNationalId) ``` ###### Validate Iranian national number(code-e Melli) + ```go verifyIranianNationalId := national_id.Validate("0067749828") verifyIranianNationalIdFalse := national_id.Validate("0684159415") fmt.Printf("\n Validate NationalID : %v \n", verifyIranianNationalId) // true fmt.Printf("\n Validate NationalIDFalse : %v \n", verifyIranianNationalIdFalse) // false -``` \ No newline at end of file +``` diff --git a/date/date_test.go b/date/date_test.go new file mode 100644 index 0000000..4371b62 --- /dev/null +++ b/date/date_test.go @@ -0,0 +1,81 @@ +package date + +import ( + "errors" + "fmt" + "testing" + "time" +) + +func TestJalaaliToGregorianDate(t *testing.T) { + tests := []struct { + input string + outPut time.Time + err error + shouldError bool + }{ + {"1401/02/03", time.Date(2022, 4, 23, 0, 0, 0, 0, time.UTC), nil, false}, + {"1401-02-03", time.Date(2022, 4, 23, 0, 0, 0, 0, time.UTC), nil, false}, + {"1401-02/03", time.Time{}, ErrInvalidFormat, true}, + {"1401/02-03", time.Time{}, ErrInvalidFormat, true}, + {"1401-02-0A", time.Time{}, nil, true}, + {"1401-0B-03", time.Time{}, nil, true}, + {"140C-02-03", time.Time{}, nil, true}, + } + + for _, tt := range tests { + t.Run(fmt.Sprintf("Testing input: %s", tt.input), func(t *testing.T) { + georgianDate, err := JalaaliToGregorianDate(tt.input) + if tt.shouldError { + if err == nil { + t.Errorf("expected error for input %v but got none", tt.input) + } else if tt.err != nil && !errors.Is(err, tt.err) { + t.Errorf("unexpected error for input %v: got %v, expected %v", tt.input, err, tt.err) + } + } else { + if err != nil { + t.Errorf("did not expect error for input %v but got %v", tt.input, err) + } else if !georgianDate.Equal(tt.outPut) { + t.Errorf("wrong result for input %v: got %v, expected %v", tt.input, georgianDate, tt.outPut) + } + } + }) + } +} + +func TestJalaaliToGregorian(t *testing.T) { + tests := []struct { + input string + outPut string + err error + shouldError bool + }{ + {"1401/02/03", "2022/04/23", nil, false}, + {"1401-02-03", "2022/04/23", nil, false}, + {"1401-02/03", "", ErrInvalidFormat, true}, + {"1401/02-03", "", ErrInvalidFormat, true}, + {"1401-02-0A", "", nil, true}, + {"1401-0B-03", "", nil, true}, + {"140C-02-03", "", nil, true}, + } + + for _, tt := range tests { + t.Run(fmt.Sprintf("Testing input: %s", tt.input), func(t *testing.T) { + georgian, err := JalaaliToGregorian(tt.input) + if tt.shouldError { + if err == nil { + t.Errorf("expected error for input %v but got none", tt.input) + } else if tt.err != nil && !errors.Is(err, tt.err) { + t.Errorf("unexpected error for input %v: got %v, expected %v", tt.input, err, tt.err) + } + } else { + if err != nil { + t.Errorf("did not expect error for input %v but got %v", tt.input, err) + } else if georgian != tt.outPut { + t.Errorf("wrong result for input %v: got %v, expected %v", tt.input, georgian, tt.outPut) + } + } + }) + } + +} diff --git a/date/jalaali_to_georgian.go b/date/jalaali_to_georgian.go new file mode 100644 index 0000000..694a61c --- /dev/null +++ b/date/jalaali_to_georgian.go @@ -0,0 +1,89 @@ +package date + +import ( + "fmt" + "strconv" + "strings" + "time" + + "github.com/jalaali/go-jalaali" +) + +var ErrInvalidFormat error = fmt.Errorf("invalid date format") + +func JalaaliToGregorianDate(jalaaliDate string) (time.Time, error) { + var parts []string + + if strings.Contains(jalaaliDate, "/") { + parts = strings.Split(jalaaliDate, "/") + } else if strings.Contains(jalaaliDate, "-") { + parts = strings.Split(jalaaliDate, "-") + } else { + return time.Time{}, ErrInvalidFormat + } + + if len(parts) != 3 { + return time.Time{}, ErrInvalidFormat + } + + year, err := strconv.Atoi(parts[0]) + if err != nil { + return time.Time{}, err + } + + month, err := strconv.Atoi(parts[1]) + if err != nil { + return time.Time{}, err + } + + day, err := strconv.Atoi(parts[2]) + if err != nil { + return time.Time{}, err + } + + gYear, gMonth, gDay, err := jalaali.ToGregorian(year, jalaali.Month(month), day) + if err != nil { + return time.Time{}, err + } + + return time.Date(gYear, time.Month(gMonth), gDay, 0, 0, 0, 0, time.UTC), nil +} + +func JalaaliToGregorian(jalaaliDate string) (string, error) { + var parts []string + + if strings.Contains(jalaaliDate, "/") { + parts = strings.Split(jalaaliDate, "/") + } else if strings.Contains(jalaaliDate, "-") { + parts = strings.Split(jalaaliDate, "-") + } else { + return "", ErrInvalidFormat + } + + if len(parts) != 3 { + return "", ErrInvalidFormat + } + + year, err := strconv.Atoi(parts[0]) + if err != nil { + return "", err + } + + month, err := strconv.Atoi(parts[1]) + if err != nil { + return "", err + } + + day, err := strconv.Atoi(parts[2]) + if err != nil { + return "", err + } + + // Convert Jalaali to Gregorian + gYear, gMonth, gDay, err := jalaali.ToGregorian(year, jalaali.Month(month), day) + if err != nil { + return "", err + } + // Return the Gregorian date as time.Time + return fmt.Sprintf("%04d/%02d/%02d", gYear, gMonth, gDay), nil +} diff --git a/go.mod b/go.mod index 906c5ee..e611a0a 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.13 require ( github.com/dustin/go-humanize v1.0.1 + github.com/jalaali/go-jalaali v0.0.0-20210801064154-80525e88d958 github.com/mavihq/persian v0.0.0-20231020110200-3e779b10be51 moul.io/number-to-words v0.7.0 ) diff --git a/go.sum b/go.sum index 39cd27d..d22e148 100644 --- a/go.sum +++ b/go.sum @@ -3,14 +3,11 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/mavihq/persian v0.0.0-20180720112018-07dac0a5e732 h1:aDTrUCLFUtKN+CdvsvQMm7hXnOan6MsY3GHQLkKtc14= -github.com/mavihq/persian v0.0.0-20180720112018-07dac0a5e732/go.mod h1:JLh+/M/1DlhfTOPJRRygjs9r/t+lr7vPP3OpA+Wax/4= +github.com/jalaali/go-jalaali v0.0.0-20210801064154-80525e88d958 h1:qxLoi6CAcXVzjfvu+KXIXJOAsQB62LXjsfbOaErsVzE= +github.com/jalaali/go-jalaali v0.0.0-20210801064154-80525e88d958/go.mod h1:Wqfu7mjUHj9WDzSSPI5KfBclTTEnLveRUFr/ujWnTgE= github.com/mavihq/persian v0.0.0-20231020110200-3e779b10be51 h1:G6qR/VC7bYzVsGnOfq/upquMJMNzOwNGy5QEPGbrTU0= github.com/mavihq/persian v0.0.0-20231020110200-3e779b10be51/go.mod h1:JLh+/M/1DlhfTOPJRRygjs9r/t+lr7vPP3OpA+Wax/4= github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28= @@ -22,11 +19,10 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tailscale/depaware v0.0.0-20201214215404-77d1e9757027/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8= -github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= @@ -61,10 +57,8 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -moul.io/number-to-words v0.6.0 h1:w5ZaDTFASB0v9SI6fedlwLs5DXnMl9Q7cMlMsatnIKg= -moul.io/number-to-words v0.6.0/go.mod h1:y2h2Dy3ksovv3n7oHDypgxqCNc4X9COF0zM3jABnrnA= moul.io/number-to-words v0.7.0 h1:1VYBGHsp8dyknFBb5BYS9asQahW+KKBIUY2p66TcGhw= moul.io/number-to-words v0.7.0/go.mod h1:v3u54wL7EdO9t7GLHiHXvtvzz2P4oBhuXZyfjB21agE=