diff --git a/src/PrayerTracker.Data/DataAccess.fs b/src/PrayerTracker.Data/DataAccess.fs index 2abcde8..ef7169e 100644 --- a/src/PrayerTracker.Data/DataAccess.fs +++ b/src/PrayerTracker.Data/DataAccess.fs @@ -2,6 +2,7 @@ module PrayerTracker.DataAccess open System.Linq +open NodaTime open PrayerTracker.Entities [] @@ -24,7 +25,6 @@ module private Helpers = if pageNbr > 0 then q.Skip((pageNbr - 1) * pageSize).Take pageSize else q -open System open Microsoft.EntityFrameworkCore open Microsoft.FSharpLu @@ -90,12 +90,14 @@ type AppDbContext with /// Get all (or active) requests for a small group as of now or the specified date member this.AllRequestsForSmallGroup (grp : SmallGroup) clock listDate activeOnly pageNbr = backgroundTask { - let theDate = match listDate with Some dt -> dt | _ -> grp.LocalDateNow clock + let theDate = match listDate with Some dt -> dt | _ -> SmallGroup.localDateNow clock grp let query = this.PrayerRequests.Where(fun req -> req.SmallGroupId = grp.Id) |> function | q when activeOnly -> - let asOf = DateTime (theDate.AddDays(-(float grp.Preferences.DaysToExpire)).Date.Ticks, DateTimeKind.Utc) + let asOf = + (theDate.AtStartOfDayInZone(SmallGroup.timeZone grp) - Duration.FromDays grp.Preferences.DaysToExpire) + .ToInstant () q.Where(fun req -> ( req.UpdatedDate > asOf || req.Expiration = Manual diff --git a/src/PrayerTracker.Data/Entities.fs b/src/PrayerTracker.Data/Entities.fs index 62418fc..b38d84f 100644 --- a/src/PrayerTracker.Data/Entities.fs +++ b/src/PrayerTracker.Data/Entities.fs @@ -1,7 +1,5 @@ namespace PrayerTracker.Entities -// fsharplint:disable RecordFieldNames MemberNames - (*-- SUPPORT TYPES --*) /// How as-of dates should (or should not) be displayed with requests @@ -633,10 +631,10 @@ and [] PrayerRequest = SmallGroupId : SmallGroupId /// The date/time on which this request was entered - EnteredDate : DateTime + EnteredDate : Instant /// The date/time this request was last updated - UpdatedDate : DateTime + UpdatedDate : Instant /// The name of the requestor or subject, or title of announcement Requestor : string option @@ -664,8 +662,8 @@ with RequestType = CurrentRequest UserId = UserId Guid.Empty SmallGroupId = SmallGroupId Guid.Empty - EnteredDate = DateTime.MinValue - UpdatedDate = DateTime.MinValue + EnteredDate = Instant.MinValue + UpdatedDate = Instant.MinValue Requestor = None Text = "" NotifyChaplain = false @@ -674,20 +672,6 @@ with Expiration = Automatic } - /// Is this request expired? - member this.IsExpired (curr : DateTime) expDays = - match this.Expiration, this.RequestType with - | Forced, _ -> true - | Manual, _ - | Automatic, LongTermRequest - | Automatic, Expecting -> false - | Automatic, _ -> curr.AddDays(-(float expDays)).Date > this.UpdatedDate.Date // Automatic expiration - - /// Is an update required for this long-term request? - member this.UpdateRequired curr expDays updWeeks = - if this.IsExpired curr expDays then false - else curr.AddDays(-(float (updWeeks * 7))).Date > this.UpdatedDate.Date - /// Configure EF for this entity static member internal ConfigureEF (mb : ModelBuilder) = mb.Entity (fun it -> @@ -759,19 +743,6 @@ with Users = ResizeArray () } - /// Get the local date for this group - member this.LocalTimeNow (clock : IClock) = - if isNull clock then nullArg (nameof clock) - let tzId = TimeZoneId.toString this.Preferences.TimeZoneId - let tz = - if DateTimeZoneProviders.Tzdb.Ids.Contains tzId then DateTimeZoneProviders.Tzdb[tzId] - else DateTimeZone.Utc - clock.GetCurrentInstant().InZone(tz).ToDateTimeUnspecified () - - /// Get the local date for this group - member this.LocalDateNow clock = - (this.LocalTimeNow clock).Date - /// Configure EF for this entity static member internal ConfigureEF (mb : ModelBuilder) = mb.Entity (fun it -> @@ -855,7 +826,7 @@ and [] User = Salt : Guid option /// The last time the user was seen (set whenever the user is loaded into a session) - LastSeen : DateTime option + LastSeen : Instant option /// The small groups which this user is authorized SmallGroups : ResizeArray @@ -900,7 +871,7 @@ with mb.Model.FindEntityType(typeof).FindProperty(nameof User.empty.Salt) .SetValueConverter (OptionConverter ()) mb.Model.FindEntityType(typeof).FindProperty(nameof User.empty.LastSeen) - .SetValueConverter (OptionConverter ()) + .SetValueConverter (OptionConverter ()) /// Cross-reference between user and small group @@ -947,4 +918,44 @@ with .SetValueConverter (Converters.UserIdConverter ()) mb.Model.FindEntityType(typeof).FindProperty(nameof UserSmallGroup.empty.SmallGroupId) .SetValueConverter (Converters.SmallGroupIdConverter ()) - \ No newline at end of file + + +/// Support functions for small groups +module SmallGroup = + + /// The DateTimeZone for the time zone ID for this small group + let timeZone group = + let tzId = TimeZoneId.toString group.Preferences.TimeZoneId + if DateTimeZoneProviders.Tzdb.Ids.Contains tzId then DateTimeZoneProviders.Tzdb[tzId] + else DateTimeZone.Utc + + /// Get the local date/time for this group + let localTimeNow (clock : IClock) group = + if isNull clock then nullArg (nameof clock) + clock.GetCurrentInstant().InZone(timeZone group).LocalDateTime + + /// Get the local date for this group + let localDateNow clock group = + (localTimeNow clock group).Date + + + +/// Support functions for prayer requests +module PrayerRequest = + + /// Is this request expired? + let isExpired (asOf : LocalDate) group req = + match req.Expiration, req.RequestType with + | Forced, _ -> true + | Manual, _ + | Automatic, LongTermRequest + | Automatic, Expecting -> false + | Automatic, _ -> + // Automatic expiration + asOf.PlusDays -group.Preferences.DaysToExpire > req.UpdatedDate.InZone(SmallGroup.timeZone group).Date + + /// Is an update required for this long-term request? + let updateRequired asOf group req = + if isExpired asOf group req then false + else asOf.PlusWeeks -group.Preferences.LongTermUpdateWeeks + >= req.UpdatedDate.InZone(SmallGroup.timeZone group).Date diff --git a/src/PrayerTracker.Data/PrayerTracker.Data.fsproj b/src/PrayerTracker.Data/PrayerTracker.Data.fsproj index 0fbe868..09642d4 100644 --- a/src/PrayerTracker.Data/PrayerTracker.Data.fsproj +++ b/src/PrayerTracker.Data/PrayerTracker.Data.fsproj @@ -17,7 +17,7 @@ - + diff --git a/src/PrayerTracker.UI/Layout.fs b/src/PrayerTracker.UI/Layout.fs index 24b2a93..a9e6277 100644 --- a/src/PrayerTracker.UI/Layout.fs +++ b/src/PrayerTracker.UI/Layout.fs @@ -255,13 +255,13 @@ let private messages viewInfo = |> List.singleton -open System +open NodaTime /// Render the