0

I would like to change only the date and/or time parts of a System.DateTime object in F#, while maintaining the existing fields. When I say "change", I mean to maintain immutability, so I want to receive a new DateTime object after the operation.

I have illustrated how I would think I would change the time, but this appears to be incorrect.

open System

type State = {
  When: DateTime
}

// How would I actually do the following?
// let setTime h m state = { state with When = { state.When with Hour = h; Minute = m } }

Ideally the solution will work on both .NET Core and Framework.

Edit

As mentioned in a comment, there is a separate question on how to change a DateTime, but this question appears to be focused on C# (rather than F#) and also on adding a TimeSpan. I want to set the field values (rather than add to them). There are answers that give copying the fields and calling the constructor, but I was hoping for a more elegant solution.

MattMS
  • 1,106
  • 1
  • 16
  • 32
  • Possible duplicate of [How to change time in DateTime?](https://stackoverflow.com/questions/1859248/how-to-change-time-in-datetime) – glennsl Oct 05 '19 at 10:13

3 Answers3

2

The standard System.DateTime type is a struct (rather than an F# record), which makes it somewhat unsuitable for immutable programming in F# - you can solve that using helpers like the ones you suggested.

Another approach would be to define an F# record with mapping to and from System.DateTime:

type ImmutableDateTime = 
  { Day : int
    Month : int
    Year : int
    Hour : int 
    Minute : int
    Second : int }
  member x.DateTime = 
    System.DateTime(x.Year, x.Month, x.Day, x.Hour, x.Minute, x.Second)
  static member FromDateTime(dt:System.DateTime) =
    { Day = dt.Day; Month = dt.Month; Year = dt.Year
      Hour = dt.Hour; Minute = dt.Minute; Second = dt.Second }

Now you can take System.DateTime, turn it into ImmutableDateTime, do nice functional programming with the record and then convert it back:

let dt1 = ImmutableDateTime.FromDateTime(System.DateTime.Now)
let dt2 = { dt1 with Year = 2018 }
dt2.DateTime.ToShortDateString()

That said, in practice, I would probably just accept the fact that System.DateTime is a struct and use it in that way.

Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
2

This is the top answer from How to change time in DateTime? translated to F#:

open System

let x = DateTime.Now
let y = x.Date + TimeSpan(10, 30, 0)

where

printfn "%A" x
printfn "%A" y

prints

10/5/2019 1:49:58 PM
10/5/2019 10:30:00 AM

You can set the Date part in a similar manner:

let x = DateTime.Now
let y = DateTime(2015, 03, 15) + x.TimeOfDay
glennsl
  • 28,186
  • 12
  • 57
  • 75
  • I misread the use of the `Date` property in the original answer, and the answer did not contain how to set the date but keep the time, which I gather would be done with the `TimeOfDay` property. Thank you for the clarification. – MattMS Oct 05 '19 at 14:04
  • 1
    Ah, sorry, I missed that you wanted to set the date independently as well. I've updated the answer with a similar approach for doing that. – glennsl Oct 05 '19 at 14:25
1

This could possibly be solved with the following:

let setTime h m (t : DateTime) = DateTime(t.Year, t.Month, t.Day, h, m, 0)

let setDate y m d (t : DateTime) = DateTime(y, m, d, t.Hour, t.Minute, t.Second)

let setTimeInState h m state = { state with When = (setTime h m state.When) }
MattMS
  • 1,106
  • 1
  • 16
  • 32