Description
Small Scala library for parsing systemd.time like calendar event expressions. It is available for Scala 2.12 and 2.13. The core module has no dependencies.
calev alternatives and similar packages
Based on the "Misc" category.
Alternatively, view calev alternatives based on common mentions on social networks and blogs.
-
BootZooka
Simple project to quickly start developing a Scala-based microservice or web application, without the need to write login, user registration etc. -
ScalaSTM
A library-based Software Transactional Memory (STM) for Scala, coupled with transactional sets and maps -
diff
Visually compare Scala data structures with out of the box support for arbitrary case classes. -
scala-debugger
Scala libraries and tooling utilizing the Java Debugger Interface. -
Miniboxing
Miniboxing is a program transformation that improves the performance of Scala generics when used with primitive types. It can speed up generic collections by factors between 1.5x and 22x, while maintaining bytecode duplication to a minimum. You can easily add miniboxing to your sbt project: -
Scalan
Generic framework for development of domain-specific compilers in Scala -
aws4s
Non-blocking AWS SDK for Scala exposing strongly-typed APIs built on top of http4s, fs2 and cats -
Ostinato
A chess library that runs on the server (Scala) and on the browser (ScalaJS). -
media4s
Scala command-line wrapper around ffmpeg, ffprobe, ImageMagick, and other tools relating to media. -
powerscala
Powerful framework providing many useful utilities and features on top of the Scala language. -
GoogleApiScala
This API is a wrapper for the google java libraries. Currently mapping Admin Directory, Drive, and Calendar. -
Easy Config
Easy Config makes Scala application configuration extremely easy. It reads configuration from the environment or command line arguments. -
service-chassis
A scala chassis to get your applications and services bootstrapped quickly
Static code analysis for 29 languages.
* Code Quality Rankings and insights are calculated and provided by Lumnify.
They vary from L1 to L5 with "L5" being the highest.
Do you think we are missing an alternative of calev or a related project?
Popular Comparisons
README
calev
Small Scala library for parsing systemd.time like calendar event expressions. It is available for Scala 2.12 and 2.13. The core module has no dependencies.
What are calendar events?
It serves the same purpose as cron expressions, but uses a different
syntax: a "normal" timestamp where each part is a pattern. A pattern
is a list of values, a range or *
meaning every value. Some
examples:
Expression | Meaning |
---|---|
*-*-* 12:15:00 |
every day at 12:15 |
2020-1,5,9-* 10:00:00 |
every day on Jan, May and Sept of 2020 at 10:00 |
Mon *-*-* 09:00:00 |
every monday at 9:00 |
Mon..Fri *-*-1/7 15:00:00 |
on 1.,8.,15. etc of every month at 15:00 but not on weekends |
The 1/7
means value 1
and all multiples of 7
added to it. A
range with repetition, like 1..12/2
means 1
and all multiples of
2
addet to it within the range 1..12
.
For more information see
man systemd.time
or
https://man.cx/systemd.time#heading7
Limitations
This library has some limitations when parsing calendar events compared to systemd:
- The
~
in the date part for refering last days of a month is not supported. - No parts except weekdays may be absent. Date and time parts must all be specified, except seconds are optional.
Modules
- The core module has zero dependencies and implements the parser
and generator for calendar events. With sbt, use:
sbt libraryDependencies += "com.github.eikek" %% "calev-core" % "0.3.1"
- The fs2 module contains utilities to work with
FS2 streams.
These were taken, thankfully and slightly modified to exchange cron expressions
for calendar events, from the
fs2-cron library. With sbt, use
sbt libraryDependencies += "com.github.eikek" %% "calev-fs2" % "0.3.1"
- The doobie module contains
Meta
,Read
andWrite
instances forCalEvent
to use with doobie.sbt libraryDependencies += "com.github.eikek" %% "calev-doobie" % "0.3.1"
- The circe module defines a json decoder and encoder for
CalEvent
instances to use with circe.sbt libraryDependencies += "com.github.eikek" %% "calev-circe" % "0.3.1"
Examples
Core
Calendar events can be read from a string:
import com.github.eikek.calev._
CalEvent.parse("Mon..Fri *-*-* 6,14:0:0")
// res0: Either[String, CalEvent] = Right(
// CalEvent(
// List(Vector(Range(WeekdayRange(Mon, Fri)))),
// DateEvent(All, All, All),
// TimeEvent(
// List(Vector(Single(6, None), Single(14, None))),
// List(Vector(Single(0, None))),
// List(Vector(Single(0, None)))
// ),
// None
// )
// )
CalEvent.parse("Mon *-*-* 6,88:0:0")
// res1: Either[String, CalEvent] = Left("Value 88 not in range [0,23]")
There is an unsafe
way that throws exceptions:
CalEvent.unsafe("*-*-* 0/2:0:0")
// res2: CalEvent = CalEvent(
// All,
// DateEvent(All, All, All),
// TimeEvent(
// List(Vector(Single(0, Some(2)))),
// List(Vector(Single(0, None))),
// List(Vector(Single(0, None)))
// ),
// None
// )
There is a tiny dsl for more conveniently defining events in code:
import com.github.eikek.calev.Dsl._
val ce = CalEvent(AllWeekdays, DateEvent.All, time(0 #/ 2, 0.c, 0.c))
// ce: CalEvent = CalEvent(
// All,
// DateEvent(All, All, All),
// TimeEvent(
// List(List(Single(0, Some(2)))),
// List(List(Single(0, None))),
// List(List(Single(0, None)))
// ),
// None
// )
ce.asString
// res3: String = "*-*-* 00/2:00:00"
Once there is a calendar event, the times it will elapse next can be generated:
import java.time._
ce.asString
// res4: String = "*-*-* 00/2:00:00"
val now = LocalDateTime.now
// now: LocalDateTime = 2020-04-26T11:34:34.584
ce.nextElapse(now)
// res5: Option[LocalDateTime] = Some(2020-04-26T12:00)
ce.nextElapses(now, 5)
// res6: List[LocalDateTime] = List(
// 2020-04-26T12:00,
// 2020-04-26T14:00,
// 2020-04-26T16:00,
// 2020-04-26T18:00,
// 2020-04-26T20:00
// )
If an event is in the past, the nextElapsed
returns a None
:
CalEvent.unsafe("1900-01-* 12,14:0:0").nextElapse(LocalDateTime.now)
// res7: Option[LocalDateTime] = None
FS2
The fs2 utilities allow to schedule things based on calendar events. This is the same as fs2-cron provides, only adopted to use calendar events instead of cron expressions. The example is also from there.
import cats.effect.{IO, Timer}
import fs2.Stream
import com.github.eikek.fs2calev._
import java.time.LocalTime
import scala.concurrent.ExecutionContext
implicit val timer: Timer[IO] = IO.timer(ExecutionContext.global)
// timer: Timer[IO] = cats.effect.internals.IOTimer@405b6ebb
val printTime = IO(println(LocalTime.now))
// printTime: IO[Unit] = Delay(<function0>)
val event = CalEvent.unsafe("*-*-* *:*:0/2")
// event: CalEvent = CalEvent(
// All,
// DateEvent(All, All, All),
// TimeEvent(All, All, List(Vector(Single(0, Some(2))))),
// None
// )
val task = CalevFs2.awakeEvery[IO](event).evalMap(_ => printTime)
// task: Stream[IO[x], Unit] = Stream(..)
task.take(3).compile.drain.unsafeRunSync
// 11:34:36.021
// 11:34:38.001
// 11:34:40.001
Doobie
When using doobie, this module contains instances to write and read calendar event expressions through SQL.
import com.github.eikek.calev._
import com.github.eikek.calev.doobie.CalevDoobieMeta._
import _root_.doobie._
import _root_.doobie.implicits._
case class Record(event: CalEvent)
val r = Record(CalEvent.unsafe("Mon *-*-* 0/2:15"))
// r: Record = Record(
// CalEvent(
// List(Vector(Single(Mon))),
// DateEvent(All, All, All),
// TimeEvent(
// List(Vector(Single(0, Some(2)))),
// List(Vector(Single(15, None))),
// List(List(Single(0, None)))
// ),
// None
// )
// )
val insert =
sql"INSERT INTO mytable (event) VALUES (${r.event})".update.run
// insert: ConnectionIO[Int] = Suspend(
// BracketCase(
// Suspend(PrepareStatement("INSERT INTO mytable (event) VALUES (?)")),
// doobie.hi.connection$$$Lambda$7825/109277604@31054516,
// cats.effect.Bracket$$Lambda$7827/1917232790@5d5c1682
// )
// )
val select =
sql"SELECT event FROM mytable WHERE id = 1".query[Record].unique
// select: ConnectionIO[Record] = Suspend(
// BracketCase(
// Suspend(PrepareStatement("SELECT event FROM mytable WHERE id = 1")),
// doobie.hi.connection$$$Lambda$7825/109277604@46e99524,
// cats.effect.Bracket$$Lambda$7827/1917232790@6d189071
// )
// )
Circe
The defined encoders/decoders can be put in scope to use calendar event expressions in json.
import com.github.eikek.calev._
import com.github.eikek.calev.circe.CalevCirceCodec._
import io.circe._
import io.circe.generic.semiauto._
import io.circe.syntax._
case class Meeting(name: String, event: CalEvent)
object Meeting {
implicit val jsonDecoder = deriveDecoder[Meeting]
implicit val jsonEncoder = deriveEncoder[Meeting]
}
val meeting = Meeting("trash can", CalEvent.unsafe("Mon..Fri *-*-* 14,18:0"))
// meeting: Meeting = Meeting(
// "trash can",
// CalEvent(
// List(Vector(Range(WeekdayRange(Mon, Fri)))),
// DateEvent(All, All, All),
// TimeEvent(
// List(Vector(Single(14, None), Single(18, None))),
// List(Vector(Single(0, None))),
// List(List(Single(0, None)))
// ),
// None
// )
// )
val json = meeting.asJson.noSpaces
// json: String = "{\"name\":\"trash can\",\"event\":\"Mon..Fri *-*-* 14,18:00:00\"}"
val read = for {
parsed <- parser.parse(json)
value <- parsed.as[Meeting]
} yield value
// read: Either[Error, Meeting] = Right(
// Meeting(
// "trash can",
// CalEvent(
// List(Vector(Range(WeekdayRange(Mon, Fri)))),
// DateEvent(All, All, All),
// TimeEvent(
// List(Vector(Single(14, None), Single(18, None))),
// List(Vector(Single(0, None))),
// List(Vector(Single(0, None)))
// ),
// None
// )
// )
// )