В зависимости от ваших потребностей, класс .NET DateTime имеет несколько статических методов для преобразования строк в DateTime
экземпляры, это DateTime.Parse
, DateTime.ParseExact
и DateTime.TryParse
и их несколько перегрузок. .
@ 7sharp9 продемонстрировал самый простой способ выполнения анализа даты с прямым вызовом метода DateTime.Parse
. Но в F # все становится интереснее с DateTime.TryParse
. Принимая во внимание, что DateTime.Parse
выдает исключение в случае сбоя синтаксического анализа, простейшая перегрузка DateTime.TryParse
имеет сигнатуру string * byref<DateTime> -> bool
, которая будет возвращать, удастся ли синтаксическому анализу установить аргумент byref
для проанализированной даты, если она истинна, или по умолчанию. значение (null
в данном случае) в противном случае. Тем не менее, синтаксис для использования этого в F # является громоздким (и на самом деле он не приятен для любого языка .NET), поэтому язык F # был разработан со специальной функцией, которая позволяет намного более приятное соглашение о вызовах для таких методов, как @Thomas Petricek указал вне.
Но даже шаблон возвращаемого типа F # (bool, result) здесь не идеален. В большинстве случаев вам не нужно значение по умолчанию, если анализ не выполняется. Лучшая подпись для DateTime.TryParse
будет string -> option<DateTime>
. К счастью, мы можем легко расширить DateTime
, как нам нравится:
type System.DateTime with
static member TryParseOption str =
match DateTime.TryParse str with
| true, r -> Some(r)
| _ -> None
Мы используем вышеуказанное расширение следующим образом:
match System.DateTime.TryParseOption "11/11/11" with
| Some r -> stdout.WriteLine r
| None -> stdout.WriteLine "none"
Что больше соответствует соглашениям F # (например, List.tryFind
). Но даже это может стать «лучше». Обратите внимание, как мы сопоставляем результат анализа try. Используя Partial Active Patterns (конечно!), Мы можем обернуть целый класс пробных разборов и переместить совпадение в регистр совпадений для большей гибкости Возьми следующее
open System
let (|DateTime|_|) str =
match DateTime.TryParse str with
| true, dt -> Some(dt)
| _ -> None
let (|Int|_|) str =
match Int32.TryParse str with
| true, num -> Some(num)
| _ -> None
let (|Float|_|) str =
match Double.TryParse str with
| true, num -> Some(num)
| _ -> None
Используя их, мы можем написать небольшое небольшое консольное приложение:
let rec loop() =
stdout.WriteLine "
Please select an option:
1) Parse something
2) Exit
"
match stdin.ReadLine() with
| Int 1 ->
stdout.WriteLine "Enter something to parse: "
match stdin.ReadLine() with
| Int num -> stdout.WriteLine("Successfully parsed int: {0}", num)
| Float num -> stdout.WriteLine("Successfully parsed float: {0}", num)
| DateTime dt -> stdout.WriteLine("Successfully parsed DateTime: {0}", dt)
| _ -> stdout.WriteLine "Parse Failed!"
loop()
| Int 2 ->
stdout.WriteLine "Now exiting"
| _ ->
stdout.WriteLine "Invalid option, please try again"
loop()
Ключевым моментом, на который следует обратить внимание, является вложенное совпадение, где Int
, Float
, DateTime
выполняют свои попытки анализа в пределах одного и того же выражения совпадения.
Есть и другие аккуратные приложения этих активных шаблонов, например, мы можем кратко одновременно фильтровать и отображать список строк дат
> ["11/23/2003"; "not a date"; "1/1/23 23:23pm"] |> Seq.choose(|DateTime|_|);;
val it : seq<DateTime> =
seq
[11/23/2003 12:00:00 AM {Date = 11/23/2003 12:00:00 AM;
Day = 23;
DayOfWeek = Sunday;
DayOfYear = 327;
Hour = 0;
Kind = Unspecified;
Millisecond = 0;
Minute = 0;
Month = 11;
Second = 0;
Ticks = 632051424000000000L;
TimeOfDay = 00:00:00;
Year = 2003;};
1/1/2023 11:23:00 PM {Date = 1/1/2023 12:00:00 AM;
Day = 1;
DayOfWeek = Sunday;
DayOfYear = 1;
Hour = 23;
Kind = Unspecified;
Millisecond = 0;
Minute = 23;
Month = 1;
Second = 0;
Ticks = 638082121800000000L;
TimeOfDay = 23:23:00;
Year = 2023;}]