Compare commits
	
		
			3 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| a1668b6e5a | |||
| 3a17edfaf0 | |||
| 3320a9c19d | 
| @@ -12,6 +12,8 @@ import ( | ||||
| var reflectTypeStr = reflect.TypeOf("") | ||||
|  | ||||
| func FromError(err error) *ExErr { | ||||
|  | ||||
| 	//goland:noinspection GoTypeAssertionOnErrors | ||||
| 	if verr, ok := err.(*ExErr); ok { | ||||
| 		// A simple ExErr | ||||
| 		return verr | ||||
|   | ||||
| @@ -86,3 +86,28 @@ func MessageMatch(e error, matcher func(string) bool) bool { | ||||
|  | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // OriginalError returns the lowest level error, probably the original/external error that was originally wrapped | ||||
| func OriginalError(e error) error { | ||||
| 	if e == nil { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	//goland:noinspection GoTypeAssertionOnErrors | ||||
| 	bmerr, ok := e.(*ExErr) | ||||
| 	for !ok { | ||||
| 		return e | ||||
| 	} | ||||
|  | ||||
| 	for bmerr.OriginalError != nil { | ||||
| 		bmerr = bmerr.OriginalError | ||||
| 	} | ||||
|  | ||||
| 	if bmerr.WrappedErr != nil { | ||||
| 		if werr, ok := bmerr.WrappedErr.(error); ok { | ||||
| 			return werr | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return bmerr | ||||
| } | ||||
|   | ||||
							
								
								
									
										18
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								go.mod
									
									
									
									
									
								
							| @@ -9,9 +9,9 @@ require ( | ||||
| 	github.com/rs/xid v1.6.0 | ||||
| 	github.com/rs/zerolog v1.33.0 | ||||
| 	go.mongodb.org/mongo-driver v1.16.1 | ||||
| 	golang.org/x/crypto v0.26.0 | ||||
| 	golang.org/x/sys v0.24.0 | ||||
| 	golang.org/x/term v0.23.0 | ||||
| 	golang.org/x/crypto v0.27.0 | ||||
| 	golang.org/x/sys v0.25.0 | ||||
| 	golang.org/x/term v0.24.0 | ||||
| ) | ||||
|  | ||||
| require ( | ||||
| @@ -21,7 +21,7 @@ require ( | ||||
| ) | ||||
|  | ||||
| require ( | ||||
| 	github.com/bytedance/sonic v1.12.1 // indirect | ||||
| 	github.com/bytedance/sonic v1.12.2 // indirect | ||||
| 	github.com/bytedance/sonic/loader v0.2.0 // indirect | ||||
| 	github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect | ||||
| 	github.com/chenzhuoyu/iasm v0.9.1 // indirect | ||||
| @@ -32,7 +32,7 @@ require ( | ||||
| 	github.com/gin-contrib/sse v0.1.0 // indirect | ||||
| 	github.com/go-playground/locales v0.14.1 // indirect | ||||
| 	github.com/go-playground/universal-translator v0.18.1 // indirect | ||||
| 	github.com/go-playground/validator/v10 v10.22.0 // indirect | ||||
| 	github.com/go-playground/validator/v10 v10.22.1 // indirect | ||||
| 	github.com/goccy/go-json v0.10.3 // indirect | ||||
| 	github.com/golang/snappy v0.0.4 // indirect | ||||
| 	github.com/google/uuid v1.5.0 // indirect | ||||
| @@ -53,10 +53,10 @@ require ( | ||||
| 	github.com/xdg-go/scram v1.1.2 // indirect | ||||
| 	github.com/xdg-go/stringprep v1.0.4 // indirect | ||||
| 	github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect | ||||
| 	golang.org/x/arch v0.9.0 // indirect | ||||
| 	golang.org/x/image v0.19.0 // indirect | ||||
| 	golang.org/x/net v0.28.0 // indirect | ||||
| 	golang.org/x/text v0.17.0 // indirect | ||||
| 	golang.org/x/arch v0.10.0 // indirect | ||||
| 	golang.org/x/image v0.20.0 // indirect | ||||
| 	golang.org/x/net v0.29.0 // indirect | ||||
| 	golang.org/x/text v0.18.0 // indirect | ||||
| 	google.golang.org/protobuf v1.34.2 // indirect | ||||
| 	gopkg.in/yaml.v3 v3.0.1 // indirect | ||||
| 	modernc.org/libc v1.37.6 // indirect | ||||
|   | ||||
							
								
								
									
										18
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								go.sum
									
									
									
									
									
								
							| @@ -28,6 +28,8 @@ github.com/bytedance/sonic v1.12.0 h1:YGPgxF9xzaCNvd/ZKdQ28yRovhfMFZQjuk6fKBzZ3l | ||||
| github.com/bytedance/sonic v1.12.0/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= | ||||
| github.com/bytedance/sonic v1.12.1 h1:jWl5Qz1fy7X1ioY74WqO0KjAMtAGQs4sYnjiEBiyX24= | ||||
| github.com/bytedance/sonic v1.12.1/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= | ||||
| github.com/bytedance/sonic v1.12.2 h1:oaMFuRTpMHYLpCntGca65YWt5ny+wAceDERTkT2L9lg= | ||||
| github.com/bytedance/sonic v1.12.2/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= | ||||
| github.com/bytedance/sonic/loader v0.1.0/go.mod h1:UmRT+IRTGKz/DAkzcEGzyVqQFJ7H9BqwBO3pm9H/+HY= | ||||
| github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= | ||||
| github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= | ||||
| @@ -93,6 +95,8 @@ github.com/go-playground/validator/v10 v10.21.0 h1:4fZA11ovvtkdgaeev9RGWPgc1uj3H | ||||
| github.com/go-playground/validator/v10 v10.21.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= | ||||
| github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao= | ||||
| github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= | ||||
| github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA= | ||||
| github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= | ||||
| github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= | ||||
| github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= | ||||
| github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= | ||||
| @@ -247,6 +251,8 @@ golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= | ||||
| golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= | ||||
| golang.org/x/arch v0.9.0 h1:ub9TgUInamJ8mrZIGlBG6/4TqWeMszd4N8lNorbrr6k= | ||||
| golang.org/x/arch v0.9.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= | ||||
| golang.org/x/arch v0.10.0 h1:S3huipmSclq3PJMNe76NGwkBR504WFkQ5dhzWzP8ZW8= | ||||
| golang.org/x/arch v0.10.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= | ||||
| golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||
| golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||
| golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= | ||||
| @@ -271,6 +277,8 @@ golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= | ||||
| golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= | ||||
| golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= | ||||
| golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= | ||||
| golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= | ||||
| golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= | ||||
| golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= | ||||
| golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U= | ||||
| golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= | ||||
| @@ -282,6 +290,8 @@ golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= | ||||
| golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= | ||||
| golang.org/x/image v0.19.0 h1:D9FX4QWkLfkeqaC62SonffIIuYdOk/UE2XKUBgRIBIQ= | ||||
| golang.org/x/image v0.19.0/go.mod h1:y0zrRqlQRWQ5PXaYCOMLTW2fpsxZ8Qh9I/ohnInJEys= | ||||
| golang.org/x/image v0.20.0 h1:7cVCUjQwfL18gyBJOmYvptfSHS8Fb3YUDtfLIZ7Nbpw= | ||||
| golang.org/x/image v0.20.0/go.mod h1:0a88To4CYVBAHp5FXJm8o7QbUl37Vd85ply1vyD8auM= | ||||
| golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= | ||||
| golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||
| golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| @@ -306,6 +316,8 @@ golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= | ||||
| golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= | ||||
| golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= | ||||
| golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= | ||||
| golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= | ||||
| golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= | ||||
| golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= | ||||
| @@ -343,6 +355,8 @@ golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= | ||||
| golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||||
| golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= | ||||
| golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||||
| golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= | ||||
| golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||||
| golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | ||||
| golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= | ||||
| golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= | ||||
| @@ -361,6 +375,8 @@ golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= | ||||
| golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= | ||||
| golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= | ||||
| golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= | ||||
| golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= | ||||
| golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= | ||||
| golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||
| golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||
| @@ -375,6 +391,8 @@ golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= | ||||
| golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= | ||||
| golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= | ||||
| golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= | ||||
| golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= | ||||
| golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= | ||||
| golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||||
| golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||
| golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| package goext | ||||
|  | ||||
| const GoextVersion = "0.0.507" | ||||
| const GoextVersion = "0.0.510" | ||||
|  | ||||
| const GoextVersionTimestamp = "2024-08-25T15:41:17+0200" | ||||
| const GoextVersionTimestamp = "2024-09-13T18:06:49+0200" | ||||
|   | ||||
							
								
								
									
										15
									
								
								langext/io.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								langext/io.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| package langext | ||||
|  | ||||
| import "io" | ||||
|  | ||||
| type nopCloser struct { | ||||
| 	io.Writer | ||||
| } | ||||
|  | ||||
| func (n nopCloser) Close() error { | ||||
| 	return nil // no op | ||||
| } | ||||
|  | ||||
| func WriteNopCloser(w io.Writer) io.WriteCloser { | ||||
| 	return nopCloser{w} | ||||
| } | ||||
| @@ -2,6 +2,9 @@ package timeext | ||||
|  | ||||
| import "time" | ||||
|  | ||||
| // YearDifference calculates the difference between two timestamps in years. | ||||
| // = t1 - t2 | ||||
| // returns a float value | ||||
| func YearDifference(t1 time.Time, t2 time.Time, tz *time.Location) float64 { | ||||
|  | ||||
| 	yDelta := float64(t1.Year() - t2.Year()) | ||||
| @@ -11,3 +14,31 @@ func YearDifference(t1 time.Time, t2 time.Time, tz *time.Location) float64 { | ||||
|  | ||||
| 	return yDelta + (processT1 - processT2) | ||||
| } | ||||
|  | ||||
| // MonthDifference calculates the difference between two timestamps in months. | ||||
| // = t1 - t2 | ||||
| // returns a float value | ||||
| func MonthDifference(t1 time.Time, t2 time.Time) float64 { | ||||
|  | ||||
| 	yDelta := float64(t1.Year() - t2.Year()) | ||||
| 	mDelta := float64(t1.Month() - t2.Month()) | ||||
|  | ||||
| 	dDelta := float64(0) | ||||
|  | ||||
| 	t1MonthDays := DaysInMonth(t1) | ||||
| 	t2MonthDays := DaysInMonth(t2) | ||||
|  | ||||
| 	if t2.Year() > t1.Year() || (t2.Year() == t1.Year() && t2.Month() > t1.Month()) { | ||||
| 		dDelta -= 1 | ||||
| 		dDelta += float64(t1MonthDays-t1.Day()) / float64(t1MonthDays) | ||||
| 		dDelta += float64(t2.Day()) / float64(t2MonthDays) | ||||
| 	} else if t2.Year() < t1.Year() || (t2.Year() == t1.Year() && t2.Month() < t1.Month()) { | ||||
| 		dDelta -= 1 | ||||
| 		dDelta += float64(t1.Day()) / float64(t1MonthDays) | ||||
| 		dDelta += float64(t2MonthDays-t2.Day()) / float64(t2MonthDays) | ||||
| 	} else { | ||||
| 		dDelta += float64(t1.Day()-t2.Day()) / float64(t1MonthDays) | ||||
| 	} | ||||
|  | ||||
| 	return yDelta*12 + mDelta + dDelta | ||||
| } | ||||
|   | ||||
| @@ -81,3 +81,63 @@ func epsilonEquals(a, b float64) bool { | ||||
| 	epsilon := 0.01 | ||||
| 	return math.Abs(a-b) < epsilon | ||||
| } | ||||
|  | ||||
| func TestMonthDifferenceSameDate(t *testing.T) { | ||||
| 	t1 := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC) | ||||
| 	t2 := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC) | ||||
| 	expected := 0.0 | ||||
| 	result := MonthDifference(t2, t1) | ||||
| 	if !epsilonEquals(result, expected) { | ||||
| 		t.Errorf("Expected %v, got %v", expected, result) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestMonthDifferenceSameMonth(t *testing.T) { | ||||
| 	t1 := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC) | ||||
| 	t2 := time.Date(2022, 1, 31, 0, 0, 0, 0, time.UTC) | ||||
| 	expected := 0.967741935483871 // Approximation of 30/31 days | ||||
| 	result := MonthDifference(t2, t1) | ||||
| 	if !epsilonEquals(result, expected) { | ||||
| 		t.Errorf("Expected %v, got %v", expected, result) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestMonthDifferenceDifferentMonthsSameYear(t *testing.T) { | ||||
| 	t1 := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC) | ||||
| 	t2 := time.Date(2022, 3, 1, 0, 0, 0, 0, time.UTC) | ||||
| 	expected := 2.0 | ||||
| 	result := MonthDifference(t2, t1) | ||||
| 	if !epsilonEquals(result, expected) { | ||||
| 		t.Errorf("Expected %v, got %v", expected, result) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestMonthDifferenceDifferentYears(t *testing.T) { | ||||
| 	t1 := time.Date(2021, 12, 1, 0, 0, 0, 0, time.UTC) | ||||
| 	t2 := time.Date(2022, 2, 1, 0, 0, 0, 0, time.UTC) | ||||
| 	expected := 2.0 | ||||
| 	result := MonthDifference(t2, t1) | ||||
| 	if !epsilonEquals(result, expected) { | ||||
| 		t.Errorf("Expected %v, got %v", expected, result) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestMonthDifferenceT1BeforeT2(t *testing.T) { | ||||
| 	t1 := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC) | ||||
| 	t2 := time.Date(2022, 6, 1, 0, 0, 0, 0, time.UTC) | ||||
| 	expected := 5.0 | ||||
| 	result := MonthDifference(t2, t1) | ||||
| 	if !epsilonEquals(result, expected) { | ||||
| 		t.Errorf("Expected %v, got %v", expected, result) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestMonthDifferenceT1AfterT2(t *testing.T) { | ||||
| 	t1 := time.Date(2022, 6, 1, 0, 0, 0, 0, time.UTC) | ||||
| 	t2 := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC) | ||||
| 	expected := -5.0 | ||||
| 	result := MonthDifference(t2, t1) | ||||
| 	if !epsilonEquals(result, expected) { | ||||
| 		t.Errorf("Expected %v, got %v", expected, result) | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -184,3 +184,10 @@ func AddYears(t time.Time, yearCount float64, tz *time.Location) time.Time { | ||||
|  | ||||
| 	return t.Add(time.Duration(float64(t1.Sub(t0)) * floatCount)) | ||||
| } | ||||
|  | ||||
| func DaysInMonth(t time.Time) int { | ||||
| 	// https://stackoverflow.com/a/73882035/1761622 | ||||
|  | ||||
| 	y, m, _ := t.Date() | ||||
| 	return time.Date(y, m+1, 0, 0, 0, 0, 0, time.UTC).Day() | ||||
| } | ||||
|   | ||||
| @@ -191,3 +191,39 @@ func TestCombineDateAndTime_CombineDifferentParts(t *testing.T) { | ||||
| 		t.Errorf("Expected %v, got %v", expected, result) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestDaysInMonth_31Days(t *testing.T) { | ||||
| 	date := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC) // January | ||||
| 	expected := 31 | ||||
| 	result := DaysInMonth(date) | ||||
| 	if result != expected { | ||||
| 		t.Errorf("Expected %d but got %d", expected, result) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestDaysInMonth_30Days(t *testing.T) { | ||||
| 	date := time.Date(2022, 4, 1, 0, 0, 0, 0, time.UTC) // April | ||||
| 	expected := 30 | ||||
| 	result := DaysInMonth(date) | ||||
| 	if result != expected { | ||||
| 		t.Errorf("Expected %d but got %d", expected, result) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestDaysInMonth_FebruaryLeapYear(t *testing.T) { | ||||
| 	date := time.Date(2020, 2, 1, 0, 0, 0, 0, time.UTC) // February in a leap year | ||||
| 	expected := 29 | ||||
| 	result := DaysInMonth(date) | ||||
| 	if result != expected { | ||||
| 		t.Errorf("Expected %d but got %d", expected, result) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestDaysInMonth_FebruaryNonLeapYear(t *testing.T) { | ||||
| 	date := time.Date(2021, 2, 1, 0, 0, 0, 0, time.UTC) // February in a non-leap year | ||||
| 	expected := 28 | ||||
| 	result := DaysInMonth(date) | ||||
| 	if result != expected { | ||||
| 		t.Errorf("Expected %d but got %d", expected, result) | ||||
| 	} | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user