Skip to content

Commit cc644dc

Browse files
authored
Adjust parse / tryParse (#558)
1 parent 3962979 commit cc644dc

File tree

2 files changed

+106
-18
lines changed

2 files changed

+106
-18
lines changed

src/FSharpPlus/Control/Converter.fs

Lines changed: 81 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -111,26 +111,47 @@ type TryParse =
111111
static member TryParse (_: string , _: TryParse) = fun x -> Some x : option<string>
112112
static member TryParse (_: StringBuilder , _: TryParse) = fun x -> Some (new StringBuilder (x: string)) : option<StringBuilder>
113113
#if !FABLE_COMPILER
114-
static member TryParse (_: DateTime , _: TryParse) = fun (x:string) -> DateTime.TryParseExact (x, [|"yyyy-MM-ddTHH:mm:ss.fffZ"; "yyyy-MM-ddTHH:mm:ssZ"|], null, DateTimeStyles.RoundtripKind) |> tupleToOption : option<DateTime>
115-
static member TryParse (_: DateTimeOffset, _: TryParse) = fun (x:string) -> DateTimeOffset.TryParseExact (x, [|"yyyy-MM-ddTHH:mm:ss.fffK"; "yyyy-MM-ddTHH:mm:ssK"|], null, DateTimeStyles.RoundtripKind) |> tupleToOption : option<DateTimeOffset>
114+
115+
static member TryParse (_: DateTime , _: TryParse) = fun (x:string) ->
116+
match DateTime.TryParseExact (x, [|"yyyy-MM-ddTHH:mm:ss.fffZ"; "yyyy-MM-ddTHH:mm:ssZ"|], null, DateTimeStyles.RoundtripKind) with
117+
| true, x -> Some x
118+
| _ ->
119+
match DateTime.TryParse (x, CultureInfo.InvariantCulture, DateTimeStyles.None) with
120+
| true, x -> Some x
121+
| _ -> None
122+
123+
static member TryParse (_: DateTimeOffset, _: TryParse) = fun (x:string) ->
124+
match DateTimeOffset.TryParseExact (x, [|"yyyy-MM-ddTHH:mm:ss.fffK"; "yyyy-MM-ddTHH:mm:ssK"|], null, DateTimeStyles.AssumeUniversal) with
125+
| true, x -> Some x
126+
| _ ->
127+
match DateTimeOffset.TryParse (x, CultureInfo.InvariantCulture, DateTimeStyles.None) with
128+
| true, x -> Some x
129+
| _ -> None
116130
#endif
117131

118132
static member inline Invoke (value: string) =
119133
let inline call_2 (a: ^a, b: ^b) = ((^a or ^b) : (static member TryParse : _*_ -> _) b, a)
120134
let inline call (a: 'a) = fun (x: 'x) -> call_2 (a, Unchecked.defaultof<'r>) x : 'r option
121135
call Unchecked.defaultof<TryParse> value
122136

123-
type TryParse with
124-
static member inline TryParse (_: 'R, _: Default2) = fun x ->
137+
/// The F# signature
138+
static member inline InvokeOnInstance (value: string) = (^R: (static member TryParse : string -> 'R option) value)
139+
140+
/// The .Net signature
141+
static member inline InvokeOnConvention (value: string) =
125142
let mutable r = Unchecked.defaultof< ^R>
126-
if (^R: (static member TryParse : _ * _ -> _) (x, &r)) then Some r else None
143+
if (^R: (static member TryParse : _ * _ -> _) (value, &r)) then Some r else None
144+
145+
#if NET7_0
146+
/// IParsable<'T>
147+
static member InvokeOnInterface<'T when 'T :> IParsable<'T>> (value: string) =
148+
let mutable r = Unchecked.defaultof<'T>
149+
if ('T.TryParse(value, CultureInfo.InvariantCulture, &r)) then Some r else None
150+
#endif
127151

128-
static member inline TryParse (_: ^t when ^t: null and ^t: struct, _: Default1) = id
129-
static member inline TryParse (_: 'R, _: Default1) = fun x -> (^R: (static member TryParse : string -> 'R option) x)
130152

131153
type Parse =
132154
inherit Default1
133-
static member inline Parse (_: ^R , _: Default1) = fun (x:string) -> (^R: (static member Parse : _ -> ^R) x)
134155
static member inline Parse (_: ^R , _: Parse ) = fun (x:string) -> (^R: (static member Parse : _ * _ -> ^R) (x, CultureInfo.InvariantCulture))
135156

136157
static member inline Parse (_: 'T when 'T : enum<_>, _: Parse ) = fun (x:string) ->
@@ -139,6 +160,16 @@ type Parse =
139160
| _ -> invalidArg "value" ("Requested value '" + x + "' was not found.")
140161
) : 'enum
141162

163+
#if !FABLE_COMPILER
164+
static member Parse (_: DateTime , _: Parse) = fun (x:string) ->
165+
match DateTime.TryParseExact (x, [|"yyyy-MM-ddTHH:mm:ss.fffZ"; "yyyy-MM-ddTHH:mm:ssZ"|], null, DateTimeStyles.RoundtripKind) with
166+
| true, x -> x
167+
| _ -> DateTime.Parse (x, CultureInfo.InvariantCulture)
168+
169+
static member Parse (_: DateTimeOffset, _: Parse) = fun (x:string) ->
170+
try DateTimeOffset.ParseExact (x, [|"yyyy-MM-ddTHH:mm:ss.fffK"; "yyyy-MM-ddTHH:mm:ssK"|], null, DateTimeStyles.AssumeUniversal)
171+
with _ -> DateTimeOffset.Parse (x, CultureInfo.InvariantCulture)
172+
#endif
142173

143174
static member Parse (_: bool , _: Parse) = fun (x:string) -> Boolean.Parse (x)
144175

@@ -151,4 +182,46 @@ type Parse =
151182
let inline call (a: 'a) = fun (x: 'x) -> call_2 (a, Unchecked.defaultof<'r>) x : 'r
152183
call Unchecked.defaultof<Parse> value
153184

185+
static member inline InvokeOnInstance (value: string) = (^R: (static member Parse : _ -> ^R) value)
186+
187+
188+
type Parse with
189+
190+
static member inline Parse (_: ^R , _: Default4) = fun (value: string) ->
191+
match TryParse.InvokeOnConvention value with
192+
| Some x -> x : ^R
193+
| None -> invalidArg "value" ("Error parsing value '" + value + "'.")
194+
195+
static member inline Parse (_: ^R , _: Default3) = fun (value: string) ->
196+
match TryParse.InvokeOnInstance value with
197+
| Some x -> x : ^R
198+
| None -> invalidArg "value" ("Error parsing value '" + value + "'.")
199+
200+
static member inline Parse (_: ^R , _: Default2) : string -> ^R = Parse.InvokeOnInstance
201+
202+
#if NET7_0
203+
static member Parse<'T when 'T :> IParsable<'T>> (_: 'T, _: Default1) = fun (x: string) -> 'T.Parse (x, CultureInfo.InvariantCulture)
204+
static member inline Parse (_: ^t when ^t: null and ^t: struct, _: Default1) = id
205+
#else
206+
static member inline Parse (_: ^t when ^t: null and ^t: struct, _: Default2) = id
207+
#endif
208+
209+
type TryParse with
210+
211+
static member inline TryParse (_: 'R, _: Default4) : string -> 'R option = fun (value: string) ->
212+
try Some (Parse.InvokeOnInstance value) with
213+
| :? ArgumentNullException | :? FormatException -> None
214+
| _ -> reraise ()
215+
216+
static member inline TryParse (_: 'R, _: Default3) : string -> 'R option = TryParse.InvokeOnConvention
217+
218+
static member inline TryParse (_: 'R, _: Default2) : string -> 'R option = TryParse.InvokeOnInstance
219+
220+
#if NET7_0
221+
static member inline TryParse (_: 'R, _: Default1) : string -> 'R option = TryParse.InvokeOnInterface
222+
static member inline TryParse (_: ^t when ^t: null and ^t: struct, _: Default1) = id
223+
#else
224+
static member inline TryParse (_: ^t when ^t: null and ^t: struct, _: Default2) = id
225+
#endif
226+
154227
#endif

tests/FSharpPlus.Tests/Parsing.fs

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,33 @@ module Parsing =
2020

2121
[<Test>]
2222
let parseDateTime () =
23-
#if MONO
24-
let v1 : DateTime = parse "2011-03-04T15:42:19+03:00"
25-
Assert.IsTrue((v1 = DateTime(2011,3,4,12,42,19)))
26-
#else
27-
Assert.Ignore ("Depends on how it's executed...")
28-
#endif
23+
24+
let t = DateTime(2011,3,14,12,42,19)
25+
let u = DateTimeOffset(2011,3,14,15,42,19, TimeSpan.FromHours 3.)
26+
let u0 = DateTimeOffset(2011,3,14,15,42,19, TimeSpan.FromHours 0.)
27+
28+
let t1 = parse<DateTime> "2011-03-14T15:42:19+03:00" in Assert.AreEqual( t, t1, nameof t1)
29+
let t2 = tryParse<DateTime> "2011-03-14T15:42:19+03:00" in Assert.AreEqual(Some t, t2, nameof t2)
30+
31+
let u1 = parse<DateTimeOffset> "2011-03-14T15:42:19+03:00" in Assert.AreEqual( u, u1, nameof u1)
32+
let u2 = tryParse<DateTimeOffset> "2011-03-14T15:42:19+03:00" in Assert.AreEqual(Some u, u2, nameof u2)
33+
34+
let t3 = parse<DateTime> "Mon, 14 Mar 2011 12:42:19 GMT" in Assert.AreEqual( t, t3, nameof t3)
35+
let t4 = tryParse<DateTime> "Mon, 14 Mar 2011 12:42:19 GMT" in Assert.AreEqual(Some t, t4, nameof t4)
36+
37+
let u3 = parse<DateTimeOffset> "Mon, 14 Mar 2011 15:42:19 GMT" in Assert.AreEqual( u0, u3, nameof u3)
38+
let u4 = tryParse<DateTimeOffset> "Mon, 14 Mar 2011 15:42:19 GMT" in Assert.AreEqual(Some u0, u4, nameof u4)
39+
40+
let u5 = parse<DateTimeOffset> "2011-03-14T15:42:19" in Assert.AreEqual( u0, u5, nameof u5)
41+
let u6 = tryParse<DateTimeOffset> "2011-03-14T15:42:19" in Assert.AreEqual(Some u0, u6, nameof u6)
42+
43+
let u7 = parse<DateTimeOffset> "2011-03-14T15:42:19Z" in Assert.AreEqual( u0, u7, nameof u7)
44+
let u8 = tryParse<DateTimeOffset> "2011-03-14T15:42:19Z" in Assert.AreEqual(Some u0, u8, nameof u8)
45+
46+
2947

3048
[<Test>]
3149
let parse () =
32-
let v2 : DateTimeOffset = parse "2011-03-04T15:42:19+03:00"
33-
34-
Assert.IsTrue((v2 = DateTimeOffset(2011,3,4,15,42,19, TimeSpan.FromHours 3.)))
3550

3651
let _101 = tryParse "10.1.0.1" : Net.IPAddress option
3752
let _102 = tryParse "102" : string option
@@ -50,7 +65,7 @@ module Parsing =
5065
areStEqual r66 (Some 66.0)
5166

5267
let r123: WrappedListA<int> option = tryParse "[1;2;3]"
53-
areStEqual r123 (Some (WrappedListA [1; 2; 3]))
68+
areStEqual r123 (Some (WrappedListA [1; 2; 3]))
5469

5570
[<Test>]
5671
let parseCustomType () =

0 commit comments

Comments
 (0)