Skip to content

Instantly share code, notes, and snippets.

@choonkeat
Last active March 17, 2021 07:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save choonkeat/e90eb5fc9e7f016cd8550ac9f5614f55 to your computer and use it in GitHub Desktop.
Save choonkeat/e90eb5fc9e7f016cd8550ac9f5614f55 to your computer and use it in GitHub Desktop.
module ICal exposing
( VAlarmAction(..)
, VAlarmTrigger(..)
, VCalendar(..)
, VEvent(..)
, VEventProp(..)
, VEventStatusValue(..)
, eventPropString
, eventString
, string
)
{-| Build VCalendar values and generate string out of it
Refer to <https://www.ietf.org/rfc/rfc2445.txt>
<https://www.kanzaki.com/docs/ical/>
Example:
import Time
import String.Extra
startTime : Time.Posix
startTime =
Time.millisToPosix 0
endTime : Time.Posix
endTime =
Time.millisToPosix 1615966974517
given : VCalendar
given =
VCalendar
[ VEvent
[ VEventUID "unique123"
, VEventSummary "Title"
, VEventDescription "Lorem ipsum"
, VEventDTStart startTime
, VEventDTEnd endTime
, VEventURL "https://www.kanzaki.com/docs/ical/"
, VEventStatus VEventStatusConfirmed
, VAlarm (VAlarmTrigger "-PT1H") "Remember the milk!" VAlarmActionDisplay
]
]
expected : String
expected =
"""
BEGIN:VCALENDAR
BEGIN:VEVENT
UID:unique123
SUMMARY:Title
DESCRIPTION:Lorem ipsum
DTSTART:19700101T000000Z
DTEND:20210317T074254Z
URL:https://www.kanzaki.com/docs/ical/
STATUS:CONFIRMED
BEGIN:VALARM
TRIGGER:-PT1H
DESCRIPTION:Remember the milk!
ACTION:DISPLAY
END:VALARM
END:VEVENT
END:VCALENDAR
"""
|> String.Extra.unindent
|> String.trim
ICal.string given
--> expected
-}
import DateFormat
import Time
type VCalendar
= VCalendar (List VEvent)
type VEvent
= VEvent (List VEventProp)
type VEventProp
= VEventUID String
| VEventSummary String
| VEventDescription String
| VEventDTStart Time.Posix
| VEventDTEnd Time.Posix
| VEventURL String
| VEventStatus VEventStatusValue
| VAlarm VAlarmTrigger String VAlarmAction
type VEventStatusValue
= VEventStatusTentative
| VEventStatusConfirmed
| VEventStatusCancelled
type VAlarmTrigger
= VAlarmTrigger String
type VAlarmAction
= VAlarmActionAudio
| VAlarmActionDisplay
| VAlarmActionEmail
| VAlarmActionProcedure
string : VCalendar -> String
string (VCalendar events) =
String.join "\n" ("BEGIN:VCALENDAR" :: List.map eventString events) ++ "\nEND:VCALENDAR"
eventString : VEvent -> String
eventString (VEvent props) =
String.join "\n" ("BEGIN:VEVENT" :: List.map eventPropString props) ++ "\nEND:VEVENT"
eventPropString : VEventProp -> String
eventPropString prop =
case prop of
VEventUID s ->
"UID:" ++ s
VEventSummary s ->
"SUMMARY:" ++ s
VEventDescription s ->
"DESCRIPTION:" ++ s
VEventDTStart posixTime ->
"DTSTART:" ++ dtString posixTime
VEventDTEnd posixTime ->
"DTEND:" ++ dtString posixTime
VEventURL s ->
"URL:" ++ s
VEventStatus s ->
"STATUS:" ++ statusString s
VAlarm (VAlarmTrigger ts) s action ->
String.join "\n"
[ "BEGIN:VALARM"
, "TRIGGER:" ++ ts
, "DESCRIPTION:" ++ s
, "ACTION:" ++ actionString action
, "END:VALARM"
]
--
dtString : Time.Posix -> String
dtString posixTime =
DateFormat.format
[ DateFormat.yearNumber
, DateFormat.monthFixed
, DateFormat.dayOfMonthFixed
, DateFormat.text "T"
, DateFormat.hourMilitaryFixed
, DateFormat.minuteFixed
, DateFormat.text "00Z" --forcing `00` seconds instead of DateFormat.secondFixed
]
Time.utc
posixTime
statusString : VEventStatusValue -> String
statusString status =
case status of
VEventStatusTentative ->
"TENTATIVE"
VEventStatusConfirmed ->
"CONFIRMED"
VEventStatusCancelled ->
"CANCELLED"
actionString : VAlarmAction -> String
actionString action =
case action of
VAlarmActionAudio ->
"AUDIO"
VAlarmActionDisplay ->
"DISPLAY"
VAlarmActionEmail ->
"EMAIL"
VAlarmActionProcedure ->
"PROCEDURE"
@choonkeat
Copy link
Author

could probably do better for VAlarmTrigger "-PT1H" to be a data structure instead of VAlarmTrigger String

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment