Allow the user to set time zone

Fix timezone handling for periodic tasks
This commit is contained in:
eikek 2022-03-01 21:40:23 +01:00
parent 6f7eafcebc
commit 9545431d59
21 changed files with 186 additions and 72 deletions

View File

@ -33,7 +33,7 @@ object EmptyTrashArgs {
val taskName = Ident.unsafe("empty-trash")
val defaultSchedule = CalEvent.unsafe("*-*-1/7 03:00:00")
val defaultSchedule = CalEvent.unsafe("*-*-1/7 03:00:00 UTC")
def periodicTaskId(coll: Ident): Ident =
Ident.unsafe(s"docspell") / taskName / coll

View File

@ -6,10 +6,8 @@
package docspell.common
import java.time.LocalDateTime
import java.time.ZonedDateTime
import java.time.temporal.ChronoUnit
import java.time.{Instant, LocalDate, ZoneId}
import java.time.{Duration => _, _}
import cats.effect.Sync
@ -57,7 +55,7 @@ case class Timestamp(value: Instant) {
}
object Timestamp {
val UTC = ZoneId.of("UTC")
val UTC: ZoneId = ZoneOffset.UTC
val Epoch = Timestamp(Instant.EPOCH)

View File

@ -144,7 +144,7 @@ docspell.joex {
# When the house keeping tasks execute. Default is to run every
# week.
schedule = "Sun *-*-* 00:00:00"
schedule = "Sun *-*-* 00:00:00 UTC"
# This task removes invitation keys that have been created but not
# used. The timespan here must be greater than the `invite-time'
@ -209,8 +209,9 @@ docspell.joex {
test-run = false
# When the update check should execute. Default is to run every
# week.
schedule = "Sun *-*-* 00:00:00"
# week. You can specify a time zone identifier, like
# 'Europe/Berlin' at the end.
schedule = "Sun *-*-* 00:00:00 UTC"
# An account id in form of `collective/user` (or just `user` if
# collective and user name are the same). This user account must

View File

@ -566,7 +566,7 @@ updateCollSettings texts lmsg model =
let
( lm, lc, ls ) =
Page.CollectiveSettings.Update.update texts.collectiveSettings
model.flags
(modelEnv model)
lmsg
model.collSettingsModel
in

View File

@ -18,6 +18,7 @@ import Api.Model.CalEventCheck exposing (CalEventCheck)
import Api.Model.CalEventCheckResult exposing (CalEventCheckResult)
import Data.CalEvent exposing (CalEvent)
import Data.Flags exposing (Flags)
import Data.TimeZone exposing (TimeZone)
import Data.Validated exposing (Validated(..))
import Html exposing (..)
import Html.Attributes exposing (..)
@ -109,8 +110,8 @@ isCheckError model =
False
update : Flags -> Maybe CalEvent -> Msg -> Model -> ( Model, Cmd Msg, Maybe CalEvent )
update flags mev msg model =
update : Flags -> TimeZone -> Maybe CalEvent -> Msg -> Model -> ( Model, Cmd Msg, Maybe CalEvent )
update flags tz mev msg model =
let
ev =
Maybe.withDefault model.inner mev
@ -153,7 +154,7 @@ update flags mev msg model =
( m
, Cmd.none
, if res.success then
Just event
Just { event | timeZone = tz }
else
Nothing

View File

@ -26,6 +26,7 @@ import Data.DropdownStyle as DS
import Data.Flags exposing (Flags)
import Data.ListType exposing (ListType)
import Data.TagOrder
import Data.TimeZone exposing (TimeZone)
import Data.UiSettings exposing (UiSettings)
import Html exposing (..)
import Html.Attributes exposing (..)
@ -111,8 +112,8 @@ getSettings model =
model.schedule
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
update flags msg model =
update : Flags -> TimeZone -> Msg -> Model -> ( Model, Cmd Msg )
update flags tz msg model =
case msg of
GetTagsResp (Ok tl) ->
let
@ -123,7 +124,7 @@ update flags msg model =
lm =
Comp.Dropdown.SetOptions categories
in
update flags (CategoryListMsg lm) model
update flags tz (CategoryListMsg lm) model
GetTagsResp (Err _) ->
( model, Cmd.none )
@ -133,6 +134,7 @@ update flags msg model =
( cm, cc, ce ) =
Comp.CalEventInput.update
flags
tz
model.schedule
lmsg
model.scheduleModel

View File

@ -26,6 +26,7 @@ import Comp.StringListInput
import Data.DropdownStyle as DS
import Data.Flags exposing (Flags)
import Data.Language exposing (Language)
import Data.TimeZone exposing (TimeZone)
import Data.UiSettings exposing (UiSettings)
import Html exposing (..)
import Html.Attributes exposing (..)
@ -143,8 +144,8 @@ type Msg
| PasswordMsg Comp.StringListInput.Msg
update : Flags -> Msg -> Model -> ( Model, Cmd Msg, Maybe CollectiveSettings )
update flags msg model =
update : Flags -> TimeZone -> Msg -> Model -> ( Model, Cmd Msg, Maybe CollectiveSettings )
update flags tz msg model =
case msg of
LangDropdownMsg m ->
let
@ -207,7 +208,7 @@ update flags msg model =
ClassifierSettingMsg lmsg ->
let
( cm, cc ) =
Comp.ClassifierSettingsForm.update flags lmsg model.classifierModel
Comp.ClassifierSettingsForm.update flags tz lmsg model.classifierModel
in
( { model
| classifierModel = cm
@ -219,7 +220,7 @@ update flags msg model =
EmptyTrashMsg lmsg ->
let
( cm, cc ) =
Comp.EmptyTrashForm.update flags lmsg model.emptyTrashModel
Comp.EmptyTrashForm.update flags tz lmsg model.emptyTrashModel
in
( { model
| emptyTrashModel = cm

View File

@ -29,6 +29,7 @@ import Data.CalEvent exposing (CalEvent)
import Data.DropdownStyle as DS
import Data.Flags exposing (Flags)
import Data.TagOrder
import Data.TimeZone exposing (TimeZone)
import Data.UiSettings exposing (UiSettings)
import Data.Validated exposing (Validated(..))
import Html exposing (..)
@ -237,8 +238,8 @@ withValidSettings mkcmd model =
)
update : Flags -> Msg -> Model -> ( Model, Action, Cmd Msg )
update flags msg model =
update : Flags -> TimeZone -> Msg -> Model -> ( Model, Action, Cmd Msg )
update flags tz msg model =
case msg of
ChannelMsg lm ->
let
@ -254,6 +255,7 @@ update flags msg model =
let
( cm, cc, cs ) =
Comp.CalEventInput.update flags
tz
model.schedule
lmsg
model.scheduleModel

View File

@ -20,6 +20,7 @@ import Comp.DueItemsTaskForm
import Comp.DueItemsTaskList
import Comp.MenuBar as MB
import Data.Flags exposing (Flags)
import Data.TimeZone exposing (TimeZone)
import Data.UiSettings exposing (UiSettings)
import Html exposing (..)
import Html.Attributes exposing (..)
@ -81,8 +82,8 @@ init flags =
--- Update
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
update flags msg model =
update : Flags -> TimeZone -> Msg -> Model -> ( Model, Cmd Msg )
update flags tz msg model =
case msg of
GetDataResp (Ok items) ->
( { model
@ -126,7 +127,7 @@ update flags msg model =
Just dm ->
let
( mm, action, mc ) =
Comp.DueItemsTaskForm.update flags lm dm
Comp.DueItemsTaskForm.update flags tz lm dm
( model_, cmd_ ) =
case action of

View File

@ -19,6 +19,7 @@ import Comp.CalEventInput
import Comp.IntField
import Data.CalEvent exposing (CalEvent)
import Data.Flags exposing (Flags)
import Data.TimeZone exposing (TimeZone)
import Data.UiSettings exposing (UiSettings)
import Html exposing (..)
import Html.Attributes exposing (..)
@ -81,14 +82,15 @@ getSettings model =
model.minAgeDays
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
update flags msg model =
update : Flags -> TimeZone -> Msg -> Model -> ( Model, Cmd Msg )
update flags tz msg model =
case msg of
ScheduleMsg lmsg ->
let
( cm, cc, ce ) =
Comp.CalEventInput.update
flags
tz
model.schedule
lmsg
model.scheduleModel

View File

@ -25,6 +25,7 @@ import Comp.MenuBar as MB
import Comp.PowerSearchInput
import Data.CalEvent exposing (CalEvent)
import Data.Flags exposing (Flags)
import Data.TimeZone exposing (TimeZone)
import Data.UiSettings exposing (UiSettings)
import Data.Validated exposing (Validated(..))
import Html exposing (..)
@ -254,13 +255,14 @@ withValidSettings mkcmd model =
}
update : Flags -> Msg -> Model -> UpdateResult
update flags msg model =
update : Flags -> TimeZone -> Msg -> Model -> UpdateResult
update flags tz msg model =
case msg of
CalEventMsg lmsg ->
let
( cm, cc, cs ) =
Comp.CalEventInput.update flags
tz
model.schedule
lmsg
model.scheduleModel

View File

@ -20,6 +20,7 @@ import Comp.MenuBar as MB
import Comp.PeriodicQueryTaskForm
import Comp.PeriodicQueryTaskList
import Data.Flags exposing (Flags)
import Data.TimeZone exposing (TimeZone)
import Data.UiSettings exposing (UiSettings)
import Html exposing (..)
import Html.Attributes exposing (..)
@ -81,8 +82,8 @@ init flags =
--- Update
update : Flags -> Msg -> Model -> ( Model, Cmd Msg, Sub Msg )
update flags msg model =
update : Flags -> TimeZone -> Msg -> Model -> ( Model, Cmd Msg, Sub Msg )
update flags tz msg model =
case msg of
GetDataResp (Ok items) ->
( { model
@ -130,7 +131,7 @@ update flags msg model =
let
--( mm, action, mc ) =
result =
Comp.PeriodicQueryTaskForm.update flags lm dm
Comp.PeriodicQueryTaskForm.update flags tz lm dm
( model_, cmd_ ) =
case result.action of

View File

@ -40,6 +40,7 @@ import Data.Flags exposing (Flags)
import Data.FolderOrder
import Data.Language exposing (Language)
import Data.TagOrder
import Data.TimeZone exposing (TimeZone)
import Data.UiSettings exposing (UiSettings)
import Data.Validated exposing (Validated(..))
import Html exposing (..)
@ -183,7 +184,7 @@ initWith flags s =
|> Maybe.withDefault []
( nm, _, nc ) =
update flags (ConnMsg (Comp.Dropdown.SetSelection imap)) im
update flags Data.TimeZone.utc (ConnMsg (Comp.Dropdown.SetSelection imap)) im
newSchedule =
Data.CalEvent.fromEvent s.schedule
@ -354,13 +355,14 @@ withValidSettings mkAction model =
)
update : Flags -> Msg -> Model -> ( Model, Action, Cmd Msg )
update flags msg model =
update : Flags -> TimeZone -> Msg -> Model -> ( Model, Action, Cmd Msg )
update flags tz msg model =
case msg of
CalEventMsg lmsg ->
let
( cm, cc, cs ) =
Comp.CalEventInput.update flags
tz
model.schedule
lmsg
model.scheduleModel
@ -585,8 +587,8 @@ update flags msg model =
( a, NoAction, b )
in
Util.Update.andThen1
[ update flags (FolderDropdownMsg opts) >> removeAction
, update flags (FolderDropdownMsg (Comp.Dropdown.SetSelection sel)) >> removeAction
[ update flags tz (FolderDropdownMsg opts) >> removeAction
, update flags tz (FolderDropdownMsg (Comp.Dropdown.SetSelection sel)) >> removeAction
]
model_
|> addNoAction

View File

@ -21,6 +21,7 @@ import Comp.MenuBar as MB
import Comp.ScanMailboxForm
import Comp.ScanMailboxList
import Data.Flags exposing (Flags)
import Data.TimeZone exposing (TimeZone)
import Data.UiSettings exposing (UiSettings)
import Html exposing (..)
import Html.Attributes exposing (..)
@ -82,8 +83,8 @@ init flags =
--- Update
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
update flags msg model =
update : Flags -> TimeZone -> Msg -> Model -> ( Model, Cmd Msg )
update flags tz msg model =
case msg of
GetDataResp (Ok res) ->
( { model
@ -127,7 +128,7 @@ update flags msg model =
Just dm ->
let
( mm, action, mc ) =
Comp.ScanMailboxForm.update flags lm dm
Comp.ScanMailboxForm.update flags tz lm dm
( model_, cmd_ ) =
case action of

View File

@ -19,6 +19,7 @@ import Api
import Api.Model.TagList exposing (TagList)
import Comp.BasicSizeField
import Comp.ColorTagger
import Comp.Dropdown
import Comp.FieldListSelect
import Comp.FixedDropdown
import Comp.IntField
@ -32,7 +33,7 @@ import Data.Flags exposing (Flags)
import Data.ItemTemplate as IT exposing (ItemTemplate)
import Data.Pdf exposing (PdfMode)
import Data.TagOrder
import Data.TimeZone
import Data.TimeZone exposing (TimeZone)
import Data.UiSettings exposing (ItemPattern, StoredUiSettings, UiSettings)
import Dict exposing (Dict)
import Html exposing (..)
@ -75,6 +76,7 @@ type alias Model =
, powerSearchEnabled : Bool
, uiLangModel : Comp.FixedDropdown.Model UiLanguage
, uiLang : UiLanguage
, timezoneDropdown : Comp.Dropdown.Model String
, openTabs : Set String
, defaults : UiSettings
}
@ -170,6 +172,11 @@ initModel flags storedSettings defaults =
, uiLang = settings.uiLang
, uiLangModel =
Comp.FixedDropdown.init Messages.UiLanguage.all
, timezoneDropdown =
Comp.Dropdown.makeSingleList
{ options = Data.TimeZone.listAll
, selected = Just (Data.TimeZone.toName settings.timeZone)
}
, openTabs = Set.empty
, defaults = defaults
}
@ -209,6 +216,7 @@ type Msg
| TogglePowerSearch
| UiLangMsg (Comp.FixedDropdown.Msg UiLanguage)
| PdfModeMsg (Comp.FixedDropdown.Msg PdfMode)
| TimeZoneMsg (Comp.Dropdown.Msg String)
| ToggleAllTabs
| ResetTab AkkordionTab
@ -599,6 +607,27 @@ update flags sett msg model =
in
( { nm | openTabs = model.openTabs }, Just newSettings )
TimeZoneMsg lm ->
let
( tm, tcmd ) =
Comp.Dropdown.update lm model.timezoneDropdown
tz =
if Comp.Dropdown.isDropdownChangeMsg lm then
Comp.Dropdown.getSelected tm |> List.head
else
sett.timeZone
newSett =
if sett.timeZone == tz then
Nothing
else
Just { sett | timeZone = tz }
in
( { model | timezoneDropdown = tm }, newSett )
--- View2
@ -670,6 +699,9 @@ settingFormTabs texts flags _ model =
[ i [ class "fa fa-eraser mr-1" ] []
, text texts.resetLabel
]
uiSettings =
Data.UiSettings.defaults
in
[ { name = akkordionTabName GeneralTab
, title = texts.general
@ -695,6 +727,22 @@ settingFormTabs texts flags _ model =
model.uiLangModel
)
]
, div [ class "mb-4" ]
[ label [ class S.inputLabel ] [ text "Timezone" ]
, Html.map TimeZoneMsg
(Comp.Dropdown.view2
{ makeOption = \s -> { text = s, additional = "" }
, placeholder = ""
, labelColor = \_ -> \_ -> ""
, style = DS.mainStyle
}
uiSettings
model.timezoneDropdown
)
, span [ class "opacity-75 text-sm" ]
[ text "Used to format date-time values."
]
]
]
}
, { name = akkordionTabName SearchTab

View File

@ -12,6 +12,7 @@ module Data.CalEvent exposing
, makeEvent
)
import Data.TimeZone exposing (TimeZone)
import Util.Maybe
@ -22,12 +23,13 @@ type alias CalEvent =
, day : String
, hour : String
, minute : String
, timeZone : TimeZone
}
everyMonth : CalEvent
everyMonth =
CalEvent Nothing "*" "*" "01" "00" "00"
CalEvent Nothing "*" "*" "01" "00" "00" Data.TimeZone.utc
makeEvent : CalEvent -> String
@ -43,6 +45,8 @@ makeEvent ev =
++ ev.hour
++ ":"
++ ev.minute
++ " "
++ Data.TimeZone.toName ev.timeZone
in
case ev.weekday of
Just wd ->
@ -60,18 +64,40 @@ fromEvent str =
parts =
String.split " " str
foldChanges : List (CalEvent -> Maybe CalEvent) -> Maybe CalEvent
foldChanges list =
List.foldl (\fmc -> \c -> Maybe.andThen fmc c) (Just init) list
in
case parts of
wd :: date :: time :: [] ->
Maybe.andThen
(fromDate date)
(fromTime time init)
|> Maybe.map (withWeekday wd)
wd :: date :: time :: tz :: [] ->
foldChanges
[ fromWeekDays wd
, fromDate date
, fromTime time
, fromTimeZone tz
]
a :: b :: c :: [] ->
if startsWithWeekday a then
foldChanges
[ fromWeekDays a
, fromDate b
, fromTime c
]
else
foldChanges
[ fromDate a
, fromTime b
, fromTimeZone c
]
date :: time :: [] ->
Maybe.andThen
(fromDate date)
(fromTime time init)
foldChanges
[ fromDate date
, fromTime time
]
_ ->
Nothing
@ -109,6 +135,31 @@ fromTime time ev =
Nothing
fromTimeZone : String -> CalEvent -> Maybe CalEvent
fromTimeZone tzStr ev =
Data.TimeZone.get tzStr
|> Maybe.map (\tz -> { ev | timeZone = tz })
fromWeekDays : String -> CalEvent -> Maybe CalEvent
fromWeekDays str ce =
if startsWithWeekday str then
Just (withWeekday str ce)
else
Nothing
withWeekday : String -> CalEvent -> CalEvent
withWeekday wd ev =
{ ev | weekday = Util.Maybe.fromString wd }
weekDays : List String
weekDays =
[ "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" ]
startsWithWeekday : String -> Bool
startsWithWeekday str =
List.any (\a -> String.startsWith a str) weekDays

View File

@ -12,13 +12,14 @@ import Comp.CollectiveSettingsForm
import Comp.ShareManage
import Comp.SourceManage
import Comp.UserManage
import Data.Flags exposing (Flags)
import Data.Environment as Env
import Data.Flags
import Messages.Page.CollectiveSettings exposing (Texts)
import Page.CollectiveSettings.Data exposing (..)
update : Texts -> Flags -> Msg -> Model -> ( Model, Cmd Msg, Sub Msg )
update texts flags msg model =
update : Texts -> Env.Update -> Msg -> Model -> ( Model, Cmd Msg, Sub Msg )
update texts env msg model =
case msg of
SetTab t ->
let
@ -27,45 +28,45 @@ update texts flags msg model =
in
case t of
SourceTab ->
update texts flags (SourceMsg Comp.SourceManage.LoadSources) m
update texts env (SourceMsg Comp.SourceManage.LoadSources) m
UserTab ->
update texts flags (UserMsg Comp.UserManage.LoadUsers) m
update texts env (UserMsg Comp.UserManage.LoadUsers) m
InsightsTab ->
update texts flags Init m
update texts env Init m
SettingsTab ->
update texts flags Init m
update texts env Init m
ShareTab ->
update texts flags (ShareMsg Comp.ShareManage.loadShares) m
update texts env (ShareMsg Comp.ShareManage.loadShares) m
SourceMsg m ->
let
( m2, c2 ) =
Comp.SourceManage.update flags m model.sourceModel
Comp.SourceManage.update env.flags m model.sourceModel
in
( { model | sourceModel = m2 }, Cmd.map SourceMsg c2, Sub.none )
ShareMsg lm ->
let
( sm, sc, ss ) =
Comp.ShareManage.update texts.shareManage flags lm model.shareModel
Comp.ShareManage.update texts.shareManage env.flags lm model.shareModel
in
( { model | shareModel = sm }, Cmd.map ShareMsg sc, Sub.map ShareMsg ss )
UserMsg m ->
let
( m2, c2 ) =
Comp.UserManage.update flags m model.userModel
Comp.UserManage.update env.flags m model.userModel
in
( { model | userModel = m2 }, Cmd.map UserMsg c2, Sub.none )
SettingsFormMsg m ->
let
( m2, c2, msett ) =
Comp.CollectiveSettingsForm.update flags m model.settingsModel
Comp.CollectiveSettingsForm.update env.flags env.settings.timeZone m model.settingsModel
cmd =
case msett of
@ -73,7 +74,7 @@ update texts flags msg model =
Cmd.none
Just sett ->
Api.setCollectiveSettings flags sett SubmitResp
Api.setCollectiveSettings env.flags sett SubmitResp
in
( { model | settingsModel = m2, formState = InitialState }
, Cmd.batch [ cmd, Cmd.map SettingsFormMsg c2 ]
@ -83,8 +84,8 @@ update texts flags msg model =
Init ->
( { model | formState = InitialState }
, Cmd.batch
[ Api.getInsights flags GetInsightsResp
, Api.getCollectiveSettings flags CollectiveSettingsResp
[ Api.getInsights env.flags GetInsightsResp
, Api.getCollectiveSettings env.flags CollectiveSettingsResp
]
, Sub.none
)
@ -98,7 +99,7 @@ update texts flags msg model =
CollectiveSettingsResp (Ok data) ->
let
( cm, cc ) =
Comp.CollectiveSettingsForm.init flags data
Comp.CollectiveSettingsForm.init env.flags data
in
( { model | settingsModel = cm }
, Cmd.map SettingsFormMsg cc

View File

@ -270,7 +270,7 @@ update texts settings navKey flags msg model =
PeriodicQuery pqm ->
let
( pqm_, pqc, pqs ) =
Comp.PeriodicQueryTaskManage.update flags lm pqm
Comp.PeriodicQueryTaskManage.update flags settings.timeZone lm pqm
in
( { model | content = PeriodicQuery pqm_ }
, Cmd.map PeriodicQueryMsg pqc

View File

@ -161,7 +161,7 @@ update flags settings msg model =
NotificationMsg lm ->
let
( m2, c2 ) =
Comp.DueItemsTaskManage.update flags lm model.notificationModel
Comp.DueItemsTaskManage.update flags settings.timeZone lm model.notificationModel
in
{ model = { model | notificationModel = m2 }
, cmd = Cmd.map NotificationMsg c2
@ -172,7 +172,7 @@ update flags settings msg model =
ScanMailboxMsg lm ->
let
( m2, c2 ) =
Comp.ScanMailboxManage.update flags lm model.scanMailboxModel
Comp.ScanMailboxManage.update flags settings.timeZone lm model.scanMailboxModel
in
{ model = { model | scanMailboxModel = m2 }
, cmd = Cmd.map ScanMailboxMsg c2
@ -227,7 +227,7 @@ update flags settings msg model =
PeriodicQueryMsg lm ->
let
( pqm, pqc, pqs ) =
Comp.PeriodicQueryTaskManage.update flags lm model.periodicQueryModel
Comp.PeriodicQueryTaskManage.update flags settings.timeZone lm model.periodicQueryModel
in
{ model = { model | periodicQueryModel = pqm }
, cmd = Cmd.map PeriodicQueryMsg pqc

View File

@ -48,7 +48,7 @@ let
wakeup-period = "10 minutes";
};
house-keeping = {
schedule = "Sun *-*-* 00:00:00";
schedule = "Sun *-*-* 00:00:00 UTC";
cleanup-invites = {
enabled = true;
older-than = "30 days";
@ -70,7 +70,7 @@ let
update-check = {
enabled = false;
test-run = false;
schedule = "Sun *-*-* 00:00:00";
schedule = "Sun *-*-* 00:00:00 UTC";
sender-account = "";
smtp-id = "";
recipients = [];

View File

@ -114,7 +114,7 @@ docspell.joex {
retry-delay = "10 seconds"
}
house-keeping {
schedule = "*-*-* 01:00:00"
schedule = "*-*-* 01:00:00 UTC"
cleanup-invites = {
older-than = "10 days"
}