@@ -2,6 +2,7 @@ module FSharp.Data.Tests.Conversions
22
33open NUnit.Framework
44open FsUnit
5+ open System
56open System.Globalization
67open FSharp.Data
78
@@ -19,13 +20,126 @@ let ``Boolean conversions``() =
1920
2021 asBoolean " rubbish" |> should equal None
2122
23+ [<Test>]
24+ let ``Boolean conversions with whitespace`` () =
25+ let asBoolean = TextConversions.AsBoolean
26+
27+ asBoolean " yes " |> should equal ( Some true )
28+ asBoolean " \t true\t " |> should equal ( Some true )
29+ asBoolean " \n 1\n " |> should equal ( Some true )
30+
31+ asBoolean " no " |> should equal ( Some false )
32+ asBoolean " \t false\t " |> should equal ( Some false )
33+ asBoolean " \n 0\n " |> should equal ( Some false )
34+
35+ [<Test>]
36+ let ``Boolean conversions with additional valid values`` () =
37+ let asBoolean = TextConversions.AsBoolean
38+
39+ asBoolean " YES" |> should equal ( Some true )
40+ asBoolean " True" |> should equal ( Some true )
41+ asBoolean " TRUE" |> should equal ( Some true )
42+
43+ asBoolean " NO" |> should equal ( Some false )
44+ asBoolean " False" |> should equal ( Some false )
45+ asBoolean " FALSE" |> should equal ( Some false )
46+
47+ [<Test>]
48+ let ``Integer conversions`` () =
49+ let culture = CultureInfo.InvariantCulture
50+
51+ TextConversions.AsInteger culture " 123" |> should equal ( Some 123 )
52+ TextConversions.AsInteger culture " -456" |> should equal ( Some - 456 )
53+ TextConversions.AsInteger culture " 0" |> should equal ( Some 0 )
54+ TextConversions.AsInteger culture " 789 " |> should equal ( Some 789 )
55+ TextConversions.AsInteger culture " 1000" |> should equal ( Some 1000 )
56+
57+ // Currency adorners should be removed
58+ TextConversions.AsInteger culture " $100" |> should equal ( Some 100 )
59+ TextConversions.AsInteger culture " €200" |> should equal ( Some 200 )
60+ TextConversions.AsInteger culture " ¥300" |> should equal ( Some 300 )
61+
62+ // Non-currency adorners should be removed
63+ TextConversions.AsInteger culture " 50%" |> should equal ( Some 50 )
64+ TextConversions.AsInteger culture " 25‰" |> should equal ( Some 25 )
65+
66+ // Invalid values
67+ TextConversions.AsInteger culture " abc" |> should equal None
68+ TextConversions.AsInteger culture " 12.34" |> should equal None
69+ TextConversions.AsInteger culture " " |> should equal None
70+
71+ [<Test>]
72+ let ``Integer64 conversions`` () =
73+ let culture = CultureInfo.InvariantCulture
74+
75+ TextConversions.AsInteger64 culture " 9223372036854775807" |> should equal ( Some 9223372036854775807 L)
76+ TextConversions.AsInteger64 culture " -9223372036854775808" |> should equal ( Some - 9223372036854775808 L)
77+ TextConversions.AsInteger64 culture " 0" |> should equal ( Some 0 L)
78+ TextConversions.AsInteger64 culture " 12345678901234 " |> should equal ( Some 12345678901234 L)
79+ TextConversions.AsInteger64 culture " 1000000000" |> should equal ( Some 1000000000 L)
80+
81+ // Currency adorners
82+ TextConversions.AsInteger64 culture " $1000000" |> should equal ( Some 1000000 L)
83+
84+ // Invalid values
85+ TextConversions.AsInteger64 culture " abc" |> should equal None
86+ TextConversions.AsInteger64 culture " 12.34" |> should equal None
87+
2288[<Test>]
2389let ``Decimal conversions`` () =
2490 TextConversions.AsDecimal CultureInfo.InvariantCulture " ¤50" |> should equal ( Some 50 M)
2591 TextConversions.AsDecimal ( CultureInfo " en-GB" ) " £50" |> should equal ( Some 50 M)
2692 TextConversions.AsDecimal ( CultureInfo " en-GB" ) " $50" |> should equal ( Some 50 M)
2793 TextConversions.AsDecimal CultureInfo.InvariantCulture " (10,000,000.99)" |> should equal ( Some - 10000000.99 M)
2894
95+ [<Test>]
96+ let ``Decimal conversions with various formats`` () =
97+ let culture = CultureInfo.InvariantCulture
98+
99+ TextConversions.AsDecimal culture " 123.45" |> should equal ( Some 123.45 M)
100+ TextConversions.AsDecimal culture " -67.89" |> should equal ( Some - 67.89 M)
101+ TextConversions.AsDecimal culture " 0.0" |> should equal ( Some 0 M)
102+ TextConversions.AsDecimal culture " .5" |> should equal ( Some 0.5 M)
103+ TextConversions.AsDecimal culture " 1000.00" |> should equal ( Some 1000 M)
104+ TextConversions.AsDecimal culture " 123.456 " |> should equal ( Some 123.456 M)
105+
106+ // Percentage adorner
107+ TextConversions.AsDecimal culture " 25.5%" |> should equal ( Some 25.5 M)
108+
109+ // Invalid values
110+ TextConversions.AsDecimal culture " abc" |> should equal None
111+ TextConversions.AsDecimal culture " 12.34.56" |> should equal None
112+
113+ [<Test>]
114+ let ``Float conversions with missing values`` () =
115+ let culture = CultureInfo.InvariantCulture
116+ let missingValues = [| " NaN" ; " NA" ; " N/A" ; " #N/A" ; " :" ; " -" ; " TBA" ; " TBD" |]
117+
118+ // Normal values
119+ TextConversions.AsFloat missingValues false culture " 123.45" |> should equal ( Some 123.45 )
120+ TextConversions.AsFloat missingValues false culture " -67.89" |> should equal ( Some - 67.89 )
121+ TextConversions.AsFloat missingValues false culture " 0.0" |> should equal ( Some 0.0 )
122+
123+ // Scientific notation
124+ TextConversions.AsFloat missingValues false culture " 1.23e10" |> should equal ( Some 1.23e10 )
125+ TextConversions.AsFloat missingValues false culture " 1.23e-5" |> should equal ( Some 1.23e-5 )
126+ TextConversions.AsFloat missingValues false culture " 1.23E+3" |> should equal ( Some 1230.0 )
127+
128+ // Special values
129+ TextConversions.AsFloat missingValues false culture " Infinity" |> should equal ( Some Double.PositiveInfinity)
130+ TextConversions.AsFloat missingValues false culture " -Infinity" |> should equal ( Some Double.NegativeInfinity)
131+
132+ // Missing values with useNoneForMissingValues = true
133+ for missingValue in missingValues do
134+ TextConversions.AsFloat missingValues true culture missingValue |> should equal None
135+
136+ // Missing values with useNoneForMissingValues = false
137+ for missingValue in missingValues do
138+ TextConversions.AsFloat missingValues false culture missingValue |> should equal ( Some Double.NaN)
139+
140+ // Invalid values
141+ TextConversions.AsFloat missingValues false culture " abc" |> should equal None
142+
29143[<Test>]
30144let ``DateTime conversions`` () =
31145 let case sample result =
@@ -36,6 +150,23 @@ let ``DateTime conversions`` () =
36150 case " 2016-11-21T10:29:05" <| System.DateTime( 2016 , 11 , 21 , 10 , 29 , 05 , System.DateTimeKind.Local)
37151 case " 2016-11-21T13:29:05+03:00" <| System.DateTime( 2016 , 11 , 21 , 10 , 29 , 05 , System.DateTimeKind.Utc) .ToLocalTime()
38152
153+ [<Test>]
154+ let ``DateTime conversions with various formats`` () =
155+ let culture = CultureInfo.InvariantCulture
156+
157+ // Basic date formats
158+ TextConversions.AsDateTime culture " 2020-01-01" |> Option.isSome |> should equal true
159+ TextConversions.AsDateTime culture " 01/01/2020" |> Option.isSome |> should equal true
160+ TextConversions.AsDateTime culture " Jan 1, 2020" |> Option.isSome |> should equal true
161+
162+ // Microsoft JSON date format
163+ TextConversions.AsDateTime culture " /Date(1577836800000)/" |> Option.isSome |> should equal true
164+ TextConversions.AsDateTime culture " /Date(1577836800000+0000)/" |> Option.isSome |> should equal true
165+
166+ // Invalid values
167+ TextConversions.AsDateTime culture " not-a-date" |> should equal None
168+ TextConversions.AsDateTime culture " " |> should equal None
169+
39170[<Test>]
40171let ``DateTimeOffset conversions`` () =
41172 let shouldBe expected actual =
@@ -51,3 +182,97 @@ let ``DateTimeOffset conversions`` () =
51182 " 2018-04-25T00:00:00Z" |> shouldBe ( System.DateTimeOffset( 2018 , 4 , 25 , 0 , 0 , 0 , System.TimeSpan.FromHours( 0. )))
52183 " garbage" |> shouldFail
53184
185+ [<Test>]
186+ let ``DateTimeOffset conversions with additional formats`` () =
187+ let culture = CultureInfo.InvariantCulture
188+
189+ // More timezone formats
190+ TextConversions.AsDateTimeOffset culture " 2020-01-01T12:00:00+05:30" |> Option.isSome |> should equal true
191+ TextConversions.AsDateTimeOffset culture " 2020-01-01T12:00:00-08:00" |> Option.isSome |> should equal true
192+
193+ // Invalid values
194+ TextConversions.AsDateTimeOffset culture " 2020-01-01" |> should equal None
195+ TextConversions.AsDateTimeOffset culture " invalid" |> should equal None
196+
197+ [<Test>]
198+ let ``TimeSpan conversions`` () =
199+ let culture = CultureInfo.InvariantCulture
200+
201+ TextConversions.AsTimeSpan culture " 12:30:45" |> should equal ( Some ( TimeSpan( 12 , 30 , 45 )))
202+ TextConversions.AsTimeSpan culture " 01:05:30" |> should equal ( Some ( TimeSpan( 1 , 5 , 30 )))
203+ TextConversions.AsTimeSpan culture " 00:00:00" |> should equal ( Some TimeSpan.Zero)
204+ TextConversions.AsTimeSpan culture " 1.12:30:45" |> should equal ( Some ( TimeSpan( 1 , 12 , 30 , 45 )))
205+ TextConversions.AsTimeSpan culture " 05:15:00 " |> should equal ( Some ( TimeSpan( 5 , 15 , 0 )))
206+
207+ // Invalid values
208+ TextConversions.AsTimeSpan culture " invalid" |> should equal None
209+ TextConversions.AsTimeSpan culture " invalid:00:00" |> should equal None
210+ TextConversions.AsTimeSpan culture " " |> should equal None
211+
212+ [<Test>]
213+ let ``Guid conversions`` () =
214+ let validGuid = Guid.NewGuid()
215+ let guidString = validGuid.ToString()
216+
217+ TextConversions.AsGuid guidString |> should equal ( Some validGuid)
218+ TextConversions.AsGuid ( guidString.ToUpper()) |> should equal ( Some validGuid)
219+ TextConversions.AsGuid ( " " + guidString + " " ) |> should equal ( Some validGuid)
220+
221+ // Different GUID formats
222+ TextConversions.AsGuid " 6F9619FF-8B86-D011-B42D-00C04FC964FF" |> Option.isSome |> should equal true
223+ TextConversions.AsGuid " {6F9619FF-8B86-D011-B42D-00C04FC964FF}" |> Option.isSome |> should equal true
224+ TextConversions.AsGuid " (6F9619FF-8B86-D011-B42D-00C04FC964FF)" |> Option.isSome |> should equal true
225+ TextConversions.AsGuid " 6F9619FF8B86D011B42D00C04FC964FF" |> Option.isSome |> should equal true
226+
227+ // Invalid values
228+ TextConversions.AsGuid " invalid-guid" |> should equal None
229+ TextConversions.AsGuid " " |> should equal None
230+ TextConversions.AsGuid " 6F9619FF-8B86-D011-B42D" |> should equal None
231+
232+ [<Test>]
233+ let ``String conversions`` () =
234+ TextConversions.AsString " hello" |> should equal ( Some " hello" )
235+ TextConversions.AsString " " |> should equal None
236+ TextConversions.AsString " test " |> should equal ( Some " test " )
237+ TextConversions.AsString " " |> should equal None
238+
239+ [<Test>]
240+ let ``Currency adorner removal`` () =
241+ let culture = CultureInfo.InvariantCulture
242+
243+ // Various currency symbols
244+ TextConversions.AsDecimal culture " $123.45" |> should equal ( Some 123.45 M)
245+ TextConversions.AsDecimal culture " €123.45" |> should equal ( Some 123.45 M)
246+ TextConversions.AsDecimal culture " £123.45" |> should equal ( Some 123.45 M)
247+ TextConversions.AsDecimal culture " ¥123" |> should equal ( Some 123 M)
248+ TextConversions.AsDecimal culture " ₹123.45" |> should equal ( Some 123.45 M)
249+
250+ // Non-currency adorners
251+ TextConversions.AsDecimal culture " 25%" |> should equal ( Some 25 M)
252+ TextConversions.AsDecimal culture " 15‰" |> should equal ( Some 15 M)
253+ TextConversions.AsDecimal culture " 5‱" |> should equal ( Some 5 M)
254+
255+ [<Test>]
256+ let ``Missing values handling`` () =
257+ let culture = CultureInfo.InvariantCulture
258+ let defaultMissingValues = TextConversions.DefaultMissingValues
259+
260+ // All default missing values should be handled
261+ for missingValue in defaultMissingValues do
262+ TextConversions.AsFloat defaultMissingValues true culture missingValue |> should equal None
263+ TextConversions.AsFloat defaultMissingValues false culture missingValue |> should equal ( Some Double.NaN)
264+
265+ [<Test>]
266+ let ``Edge cases with whitespace and special characters`` () =
267+ let culture = CultureInfo.InvariantCulture
268+
269+ // Whitespace handling
270+ TextConversions.AsInteger culture " 123 " |> should equal ( Some 123 )
271+ TextConversions.AsDecimal culture " \t 45.67\t " |> should equal ( Some 45.67 M)
272+ TextConversions.AsBoolean " \n true \n " |> should equal ( Some true )
273+
274+ // Empty and null strings
275+ TextConversions.AsInteger culture " " |> should equal None
276+ TextConversions.AsDecimal culture " " |> should equal None
277+ TextConversions.AsBoolean " " |> should equal None
278+
0 commit comments