if I get a string from the command line and it looks like this:
'1-1-2011'
how can I convert that string to a DateTime object in F#?
Depending on your specific need, .NET's DateTime class has several static methods for converting strings to DateTime instances, these are DateTime.Parse, DateTime.ParseExact, and DateTime.TryParse and their several overloads.
#7sharp9 demonstrated the most basic way to perform date parsing, with a direct method call to DateTime.Parse. But where things get interesting in F# is with DateTime.TryParse. Whereas DateTime.Parse will throw an exception if the parse fails, the simplest overload of DateTime.TryParse has the signature string * byref<DateTime> -> bool which will return whether the parse succeeds setting the byref argument to the parsed date if true, or to it's default value (null in this case) otherwise. However, the syntax for using this in F# is cumbersome (and indeed it's not pleasant from any .NET language), so the F# language was designed with a special feature which allows a much nicer calling convention for methods like these as #Thomas Petricek pointed out.
But even F#'s (bool, result) return type pattern here is not ideal. Most of the time you don't need the default value if a parse fails. A nicer signature for DateTime.TryParse would be string -> option<DateTime>. Luckily, we can easily extend DateTime as we like:
type System.DateTime with
static member TryParseOption str =
match DateTime.TryParse str with
| true, r -> Some(r)
| _ -> None
We use the above extension like so:
match System.DateTime.TryParseOption "11/11/11" with
| Some r -> stdout.WriteLine r
| None -> stdout.WriteLine "none"
Which is more consistent with F# conventions (like List.tryFind, for example). But even this is can get "better". Notice how we are matching on the result of the try parse. Using Partial Active Patterns (of course!), we can wrap a whole class of try parses and move the match to the match case for greater flexibility. Take the following
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
Using these, we can write a neat little console application:
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()
The key thing to notice is the nested match, where Int, Float, DateTime perform their try parses within the same match expression.
There are other neat applications of these active patterns too, for example, we can succinctly simultaneously filter and map a list of date strings
> ["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;}]
To add one nice thing to what 7sharp9 wrote, if you also want to handle failures, you can write:
match System.DateTime.TryParse "1-1-2011" with
| true, date -> printfn "Success: %A" date
| false, _ -> printfn "Failed!"
This is not obvious, because the TryParse method has a byref<DateTime> as the last argument (and it is used using out in C#), but F# allows you to call the method like this.
You could do it as simply as this:
let dateTime = System.DateTime.Parse "1-1-2011"
Related
I have a String date in format Month-Day-4DigitYear that I want to convert to DateTime in Flutter. I'm a novice coder, and I'm struggling to understand the api.flutter.dev Parse method example.
Below is the example. I just have a few issues. Android Studio throws multiple errors when I just create a class and put in this function. I think I understand the non-nullable issue, so I delete the ! and ? marks everywhere.
My issues are: what are _parseFormat, _brokenDownDateToValue, _withValue ?
All give errors and just declaring the first two and deleting the _withValue doesn't seem to do the trick, although removes all errors. It's like they've left out a key portion that I'm missing or there is a package I need to import the neither I nor Android Studio knows about. Can anyone decrypt this? I get very frustrated with flutter's documentation, as it always seems to give 80% of required info, assuming you already are clairvoyant on all other topics except this single one they are discussing. Gotta be a pro before reading the manual.
// TODO(lrn): restrict incorrect values like 2003-02-29T50:70:80.
// Or not, that may be a breaking change.
static DateTime parse(String formattedString) {
var re = _parseFormat;
Match? match = re.firstMatch(formattedString);
if (match != null) {
int parseIntOrZero(String? matched) {
if (matched == null) return 0;
return int.parse(matched);
}
// Parses fractional second digits of '.(\d+)' into the combined
// microseconds. We only use the first 6 digits because of DateTime
// precision of 999 milliseconds and 999 microseconds.
int parseMilliAndMicroseconds(String? matched) {
if (matched == null) return 0;
int length = matched.length;
assert(length >= 1);
int result = 0;
for (int i = 0; i < 6; i++) {
result *= 10;
if (i < matched.length) {
result += matched.codeUnitAt(i) ^ 0x30;
}
}
return result;
}
int years = int.parse(match[1]!);
int month = int.parse(match[2]!);
int day = int.parse(match[3]!);
int hour = parseIntOrZero(match[4]);
int minute = parseIntOrZero(match[5]);
int second = parseIntOrZero(match[6]);
int milliAndMicroseconds = parseMilliAndMicroseconds(match[7]);
int millisecond =
milliAndMicroseconds ~/ Duration.microsecondsPerMillisecond;
int microsecond = milliAndMicroseconds
.remainder(Duration.microsecondsPerMillisecond) as int;
bool isUtc = false;
if (match[8] != null) {
// timezone part
isUtc = true;
String? tzSign = match[9];
if (tzSign != null) {
// timezone other than 'Z' and 'z'.
int sign = (tzSign == '-') ? -1 : 1;
int hourDifference = int.parse(match[10]!);
int minuteDifference = parseIntOrZero(match[11]);
minuteDifference += 60 * hourDifference;
minute -= sign * minuteDifference;
}
}
int? value = _brokenDownDateToValue(years, month, day, hour, minute,
second, millisecond, microsecond, isUtc);
if (value == null) {
throw FormatException("Time out of range", formattedString);
}
return DateTime._withValue(value, isUtc: isUtc);
} else {
throw FormatException("Invalid date format", formattedString);
}
}
My issues are: what are _parseFormat, _brokenDownDateToValue, _withValue ?
These are objects or functions declared elsewhere in the lib which are private (the _ as the first character declares objects and functions as private) and therefore not shown in the documentation.
_parseFormat seems to be a regular expression.
_brokenDownDateToValue seems to be a function.
_withValue is a named constructor.
I think what you want to use is the following if you want to parse your date String to a DateTime object.
var date = "11-28-2020"; // Month-Day-4DigitYear
var dateTime = DateTime.parse(date.split('-').reversed.join());
See https://api.flutter.dev/flutter/dart-core/DateTime/parse.html for the accepted strings to be parsed.
I did find the full code example here.
It didn't use the name _parseFormat, instead just RegExp? And has _withValue and _brokenDownDateToValue declarations.
As I see it, there isn't a proper way to decode their example. The example is insufficient. A dictionary should not create definitions using words that can't be found elsewhere in the dictionary.
I'm stuck on getting the current Timezone offset from the Date in Elm.
Date.now
This returns
<Thu Feb 22 2018 20:42:42 GMT+0530 (India Standard Time)> as string
As I have explored in Elm's core lib of date and time, and they don't provide any direct method to fetch the current timezone offset. So what should I do?
import Html as App
import Html exposing (..)
import Date exposing (Date)
import Task
type alias Model =
Maybe Date
type Msg =
SetDate (Maybe Date)
update : Msg -> Model -> (Model, Cmd Msg)
update (SetDate date) _ =
(date, Cmd.none)
view : Model -> Html Msg
view model =
div [] [ text <| dateString model ]
dateString : Model -> String
dateString model =
case model of
Nothing -> "No date here"
Just date ->
(toString <| date)
now : Cmd Msg
now =
Task.perform (Just >> SetDate) Date.now
main : Program Never Model Msg
main =
App.program
{ init = ( Nothing, now )
, view = view
, subscriptions = always Sub.none
, update = update
}
I need this +0530 as in the float 5.5.
Elm's DateTime functions are pretty sparse at the moment, but justinmimbs Date.Extra library is my go to for this type of problem. Check it out here
You can import it as such,
import Date.Extra exposing (offsetFromUtc)
And, then where you had toString <| date change your pipeline to
date
|> offsetFromUtc
|> toString
That'll give you your offset in minutes, if you want the float value, just divide the int by 60. Simple function here to do so:
divBy60 : Int -> Float
divBy60 t =
toFloat t / 60.0
then just change your pipeline again to
date
|> offsetFromUtc
|> divBy60
|> toString
Consider this function to build a string of random characters:
func makeToken(length: Int) -> String {
let chars: String = "abcdefghijklmnopqrstuvwxyz0123456789!?##$%ABCDEFGHIJKLMNOPQRSTUVWXYZ"
var result: String = ""
for _ in 0..<length {
let idx = Int(arc4random_uniform(UInt32(chars.characters.count)))
let idxEnd = idx + 1
let range: Range = idx..<idxEnd
let char = chars.substring(with: range)
result += char
}
return result
}
This throws an error on the substring method:
Cannot convert value of type 'Range<Int>' to expected argument
type 'Range<String.Index>' (aka 'Range<String.CharacterView.Index>')
I'm confused why I can't simply provide a Range with 2 integers, and why it's making me go the roundabout way of making a Range<String.Index>.
So I have to change the Range creation to this very over-complicated way:
let idx = Int(arc4random_uniform(UInt32(chars.characters.count)))
let start = chars.index(chars.startIndex, offsetBy: idx)
let end = chars.index(chars.startIndex, offsetBy: idx + 1)
let range: Range = start..<end
Why isn't it good enough for Swift for me to simply create a range with 2 integers and the half-open range operator? (..<)
Quite the contrast to "swift", in javascript I can simply do chars.substr(idx, 1)
I suggest converting your String to [Character] so that you can index it easily with Int:
func makeToken(length: Int) -> String {
let chars = Array("abcdefghijklmnopqrstuvwxyz0123456789!?##$%ABCDEFGHIJKLMNOPQRSTUVWXYZ".characters)
var result = ""
for _ in 0..<length {
let idx = Int(arc4random_uniform(UInt32(chars.count)))
result += String(chars[idx])
}
return result
}
Swift takes great care to provide a fully Unicode-compliant, type-safe, String abstraction.
Indexing a given Character, in an arbitrary Unicode string, is far from a trivial task. Each Character is a sequence of one or more Unicode scalars that (when combined) produce a single human-readable character. In particular, hiding all this complexity behind a simple Int based indexing scheme might result in the wrong performance mental model for programmers.
Having said that, you can always convert your string to a Array<Character> once for easy (and fast!) indexing. For instance:
let chars: String = "abcdefghijklmnop"
var charsArray = Array(chars.characters)
...
let resultingString = String(charsArray)
I'm trying to get a number from a user. That number is then used to
call another function randomList(n) which takes the given number and
uses it. I keep getting the error exception option. I've read that
adding SOME to the variable declaration or valOf can fix this issue,
but it is not working for me. What do I need to change?
fun getNumber() =
print "Please enter the number of integers: ";
let
val str = valOf (TextIO.inputLine TextIO.stdIn)
val i : int = valOf (Int.fromString str)
in
randomList(i)
end;
getNumber();
The issue here is that fun getNumber() only encompasses the print statement on the following line. You need to enclose the print and the let within parenthesis if you want them to both be a part of getNumber().
For example, the following code compiles and echos the input integer that is passed in via stdin:
fun getNumber() = (
print "Please enter the number of integers: ";
let
val str = valOf (TextIO.inputLine TextIO.stdIn)
val i : int = valOf (Int.fromString str)
in
print(Int.toString(i))
end
);
getNumber();
I am writing an app that needs to be quite accurate in dates and I wonder how can I compare LocalDate instances.. for now I was using something like:
LocalDate localdate1 = LocalDate().now();
LocalDate localdate2 = someService.getSomeDate();
localdate1.equals(localdate2);
But I noticed that my app is giving me some confusing results, and I think it is because of the date comparing.
I am thinking about obtaining the time from 1970' in long and compare those two, but I must be easier, I am sure of it
Using equals()
LocalDate does override equals:
int compareTo0(LocalDate otherDate) {
int cmp = (year - otherDate.year);
if (cmp == 0) {
cmp = (month - otherDate.month);
if (cmp == 0) {
cmp = (day - otherDate.day);
}
}
return cmp;
}
If you are not happy with the result of equals(), you are good using the predefined methods of LocalDate.
isAfter()
isBefore()
isEqual()
Notice that all of those method are using the compareTo0() method and just check the cmp value. if you are still getting weird result (which you shouldn't), please attach an example of input and output
LocalDate ld ....;
LocalDateTime ldtime ...;
ld.isEqual(LocalDate.from(ldtime));
I believe this snippet will also be helpful in a situation where the dates comparison spans more than two entries.
static final int COMPARE_EARLIEST = 0;
static final int COMPARE_MOST_RECENT = 1;
public LocalDate getTargetDate(List<LocalDate> datesList, int comparatorType) {
LocalDate refDate = null;
switch(comparatorType)
{
case COMPARE_EARLIEST:
//returns the most earliest of the date entries
refDate = (LocalDate) datesList.stream().min(Comparator.comparing(item ->
item.toDateTimeAtCurrentTime())).get();
break;
case COMPARE_MOST_RECENT:
//returns the most recent of the date entries
refDate = (LocalDate) datesList.stream().max(Comparator.comparing(item ->
item.toDateTimeAtCurrentTime())).get();
break;
}
return refDate;
}