├── manual
├── images
│ └── .empty
├── scalajs-support.md
├── datetime-formatting.md
├── scheduler.md
├── support.md
├── type-safe-dsl.md
├── introduction.md
├── development.md
└── src
│ └── main
│ └── scala
│ └── pl
│ └── metastack
│ └── metatime
│ └── manual
│ ├── Deploy.scala
│ ├── Shared.scala
│ ├── Examples.scala
│ └── Manual.scala
├── project
├── build.properties
├── plugins.sbt
└── Build.scala
├── .travis.yml
├── version.sbt
├── shared
└── src
│ ├── main
│ └── scala
│ │ └── pl
│ │ └── metastack
│ │ └── metatime
│ │ ├── Timezone.scala
│ │ ├── Range.scala
│ │ ├── Timestamp.scala
│ │ ├── Locale.scala
│ │ ├── Implicits.scala
│ │ ├── Pattern.scala
│ │ ├── Constants.scala
│ │ ├── Offset.scala
│ │ ├── PatternTime.scala
│ │ ├── Scheduler.scala
│ │ ├── PatternDateTime.scala
│ │ ├── DateTime.scala
│ │ ├── Time.scala
│ │ ├── Date.scala
│ │ └── Component.scala
│ └── test
│ └── scala
│ └── pl
│ └── metastack
│ └── metatime
│ ├── PeriodTest.scala
│ ├── TimestampTest.scala
│ ├── LocaleTest.scala
│ ├── PatternTest.scala
│ ├── DateTest.scala
│ ├── DateTimeTest.scala
│ ├── TimeTest.scala
│ └── SchedulerTest.scala
├── js
└── src
│ └── main
│ └── scala
│ └── pl
│ └── metastack
│ └── metatime
│ ├── Platform.scala
│ └── AsyncScheduler.scala
├── .gitignore
├── jvm
└── src
│ └── main
│ └── scala
│ └── pl
│ └── metastack
│ └── metatime
│ ├── Platform.scala
│ └── AsyncScheduler.scala
└── README.md
/manual/images/.empty:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=0.13.8
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: scala
2 | scala:
3 | - 2.11.7
4 |
--------------------------------------------------------------------------------
/version.sbt:
--------------------------------------------------------------------------------
1 | version in ThisBuild := "0.1.0-SNAPSHOT"
2 |
--------------------------------------------------------------------------------
/shared/src/main/scala/pl/metastack/metatime/Timezone.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime
2 |
3 | class Timezone {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/shared/src/test/scala/pl/metastack/metatime/PeriodTest.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime
2 |
3 | class PeriodTest {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/manual/scalajs-support.md:
--------------------------------------------------------------------------------
1 | # Scala.js support
2 |
3 | Library offers support for Scala.js which enables users to use all these features in Scala.js also
--------------------------------------------------------------------------------
/shared/src/main/scala/pl/metastack/metatime/Range.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime
2 |
3 | case class Range(lowerBound: Long, upperBound: Long)
4 |
--------------------------------------------------------------------------------
/js/src/main/scala/pl/metastack/metatime/Platform.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime
2 |
3 | object Platform {
4 | implicit lazy val DefaultScheduler: Scheduler = new AsyncScheduler
5 | }
--------------------------------------------------------------------------------
/shared/src/main/scala/pl/metastack/metatime/Timestamp.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime
2 |
3 | case class Unix(value: Long) extends Timestamp
4 |
5 | trait Timestamp {
6 | def time: Time = ???
7 | def date: Date = ???
8 | def dateTime: DateTime = ???
9 | }
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.class
2 | *.log
3 |
4 | # sbt specific
5 | .cache/
6 | .history/
7 | .lib/
8 | dist/*
9 | target/
10 | lib_managed/
11 | src_managed/
12 | project/boot/
13 | project/plugins/project/
14 |
15 | # Scala-IDE specific
16 | .scala_dependencies
17 | .worksheet
18 | .idea
19 |
--------------------------------------------------------------------------------
/shared/src/test/scala/pl/metastack/metatime/TimestampTest.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime
2 |
3 | import org.scalatest.FunSuite
4 |
5 | class TimestampTest extends FunSuite {
6 | ignore("construct") {
7 | assert(Unix(???).date == ???)
8 | assert(Unix(???).dateTime == ???)
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/shared/src/test/scala/pl/metastack/metatime/LocaleTest.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime
2 |
3 | import org.scalatest.FunSuite
4 |
5 | class LocaleTest extends FunSuite {
6 | ignore("construct") {
7 | assert(Locale("en_GB") == Locale.English.GreatBritain)
8 | assert(Locale("pl_PL") == Locale.Polish)
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | logLevel := Level.Warn
2 |
3 | addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.6")
4 |
5 | addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.5.0")
6 |
7 | addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "0.5.0")
8 |
9 | addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0")
10 |
11 | addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.0")
12 |
--------------------------------------------------------------------------------
/jvm/src/main/scala/pl/metastack/metatime/Platform.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime
2 |
3 | import java.util.concurrent.Executors
4 | import scala.concurrent.ExecutionContext
5 |
6 | object Platform {
7 | implicit lazy val DefaultScheduler: Scheduler = new AsyncScheduler(
8 | Executors.newSingleThreadScheduledExecutor(),
9 | ExecutionContext.Implicits.global)
10 | }
11 |
--------------------------------------------------------------------------------
/manual/datetime-formatting.md:
--------------------------------------------------------------------------------
1 | # DateTime Formatting
2 |
3 | MetaTime provides various ways to customize the format of the Time, Date and DateTime components. You may wish to use the default patterns or you can create your own, its as easy as describing component next to each other.
4 |
5 | For example below is usage of default format:
6 | [scala type="section" value="formatting" file="Examples"]
7 |
--------------------------------------------------------------------------------
/manual/scheduler.md:
--------------------------------------------------------------------------------
1 | # Scheduler
2 |
3 | One of the most powerful feature offered by MetaTime is the scheduler, user may schedule different tasks / activities by defining the relative time in future from now or absolute Time/DateTime components. One simple example of scheduler usage looks like this:
4 |
5 | [scala type="section" value="scheduler" file="Examples"]
6 | Above example will schedule the task for 01 May 2016 midnight.
7 |
8 |
--------------------------------------------------------------------------------
/manual/support.md:
--------------------------------------------------------------------------------
1 | # Support
2 | ## Bugs/feature requests
3 | Please use our [issue tracker](https://github.com/MetaStack-pl/MetaTime/issues) to file bugs and feature requests.
4 |
5 | ## Discussions
6 | For discussions and general questions, please join our Gitter channel instead: [](https://gitter.im/MetaStack-pl/MetaTime?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
7 |
--------------------------------------------------------------------------------
/manual/type-safe-dsl.md:
--------------------------------------------------------------------------------
1 | # Type-safe DSL
2 |
3 | MetaTime is a type-safe DSL which offers to prevent a lot of type errors and gives users opportunity to focus on how best library can be used in their projects.
4 |
5 | Library provides various ways to perform addition of DateTime components, for example:
6 |
7 | For example below is usage of addition of date and day:
8 | [scala type="section" value="addition" file="Examples"]
9 |
10 | For example below is usage of subtraction of date and day:
11 | [scala type="section" value="subtraction" file="Examples"]
--------------------------------------------------------------------------------
/manual/introduction.md:
--------------------------------------------------------------------------------
1 | [package value="pl.metastack.metatime.manual"]
2 | # Introduction
3 | MetaTime is a type-safe library for working with times and dates in Scala and Scala.js.
4 |
5 | ## Installation
6 | Add the following dependencies to your build configuration:
7 |
8 | ```scala
9 | libraryDependencies += "pl.metastack" %% "metatime" % "%version%" // Scala
10 | libraryDependencies += "pl.metastack" %%% "metatime" % "%version%" // Scala.js
11 | ```
12 |
13 | ## Key Features
14 | * Type-safe DSL
15 | * Scheduler
16 | * Date / Time formatting
17 | * Scala.js support
18 | * Duration
19 | * Timezones
20 |
21 |
--------------------------------------------------------------------------------
/shared/src/main/scala/pl/metastack/metatime/Locale.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime
2 |
3 | object Locale {
4 | object English {
5 | case object America extends Locale
6 | case object GreatBritain extends Locale
7 | }
8 |
9 | object German {
10 | case object Germany extends Locale
11 | case object Switzerland extends Locale
12 | case object Austria extends Locale
13 | }
14 |
15 | case object Polish extends Locale
16 |
17 | def apply(isoCode: String): Locale = ???
18 | }
19 |
20 | trait Locale {
21 | def isoCode: String = ???
22 | def languageCode: String = ???
23 | def countryCode: String = ???
24 | }
25 |
--------------------------------------------------------------------------------
/shared/src/main/scala/pl/metastack/metatime/Implicits.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime
2 |
3 | object Implicits {
4 | implicit class IntExtensions(value: Int) {
5 | def year: Year = Year(value)
6 | def years: Year = Year(value)
7 | def day: Day = Day(value)
8 | def days: Day = Day(value)
9 | def week: Day = Day(7 * value)
10 | def weeks: Day = Day(7 * value)
11 | def hour: Hour = Hour(value)
12 | def hours: Hour = Hour(value)
13 | def minute: Minute = Minute(value)
14 | def minutes: Minute = Minute(value)
15 | def second: Second = Second(value)
16 | def seconds: Second = Second(value)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/manual/development.md:
--------------------------------------------------------------------------------
1 | # Development
2 | [](https://travis-ci.org/MetaStack-pl/MetaTime)
3 |
4 | ## Tests
5 | The proper functioning of each operation is backed by [test cases](https://github.com/MetaStack-pl/MetaTime/tree/master/shared/src/test/scala/pl/metastack/metatime). These also provide complementary documentation.
6 |
7 | ## Manual
8 | Run the following command to generate the manual:
9 |
10 | ```bash
11 | $ sbt manual/runMain pl.metastack.metatime.manual.Manual
12 | ```
13 |
14 | Deploy it with:
15 |
16 | ```bash
17 | $ sbt manual/runMain pl.metastack.metatime.manual.Deploy
18 | ```
19 |
--------------------------------------------------------------------------------
/manual/src/main/scala/pl/metastack/metatime/manual/Deploy.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime.manual
2 |
3 | import java.io.File
4 |
5 | import org.eclipse.jgit.api.Git
6 | import org.eclipse.jgit.storage.file.FileRepositoryBuilder
7 |
8 | import pl.metastack.metatime.BuildInfo
9 |
10 | object Deploy extends App with Shared {
11 | val repo = FileRepositoryBuilder.create(new File(projectPath, ".git"))
12 | val git = new Git(repo)
13 | git.add().addFilepattern(projectName).call()
14 | if (!git.status().call().isClean) {
15 | git.commit()
16 | .setAll(true)
17 | .setMessage(s"Update $projectName v${BuildInfo.version}").call()
18 | git.push().call()
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/shared/src/main/scala/pl/metastack/metatime/Pattern.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime
2 |
3 | trait Pattern[+T,ItemType <: Component]{
4 |
5 | def string(s: String): T = add(_ => s)
6 | def newLine: T = add(_ => "\n")
7 | def dash : T = string("-")
8 | def space: T = string(" ")
9 | def colon: T = string(":")
10 | def comma: T = string(",")
11 | def dot : T = string(".")
12 |
13 | def add(item: ItemType => String): T
14 |
15 | val months = Seq("January", "February", "March",
16 | "April", "May", "June", "July", "August",
17 | "September", "October", "November", "December")
18 |
19 | val weekDays = Seq("Thursday", "Friday", "Saturday",
20 | "Sunday", "Monday", "Tuesday", "Wednesday")
21 | }
--------------------------------------------------------------------------------
/manual/src/main/scala/pl/metastack/metatime/manual/Shared.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime.manual
2 |
3 | import java.io.File
4 |
5 | import pl.metastack.metatime.BuildInfo
6 |
7 | trait Shared {
8 | val organisation = "MetaStack-pl"
9 | val repoName = s"$organisation.github.io"
10 | val projectName = "metatime"
11 | val projectPath = new File("..", repoName)
12 | val manualPath = new File(projectPath, projectName)
13 | val manualPathStr = manualPath.getPath
14 | val manualVersionPath = new File(manualPath, "v" + BuildInfo.version)
15 | val manualVersionPathStr = manualVersionPath.getPath
16 | val imagesPath = new File(manualVersionPath, "images")
17 | val isSnapshot = BuildInfo.version.endsWith("SNAPSHOT")
18 | }
19 |
--------------------------------------------------------------------------------
/manual/src/main/scala/pl/metastack/metatime/manual/Examples.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime.manual
2 |
3 | import pl.metastack.metadocs.SectionSupport
4 | import pl.metastack.metatime._
5 |
6 | import scala.concurrent.Promise
7 |
8 | object Examples extends SectionSupport {
9 | section("introduction") {
10 | DateTime(2015, 1, 1).format(PatternDateTime.DefaultDate)
11 | }
12 |
13 | section("formatting") {
14 | DateTime(2015, 1, 1).format(PatternDateTime.DefaultDate)
15 | }
16 | section("subtraction") {
17 | Date(2015, 1, 20) - Day(1)
18 | }
19 |
20 | section("addition") {
21 | Date(2015, 1, 20) + Day(1)
22 | }
23 |
24 | section("scheduler") {
25 | val scheduler: Scheduler = Platform.DefaultScheduler
26 | scheduler.at(DateTime(2016, 5, 1, 0, 0, 0, 0)) {
27 | //TASK
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MetaTime
2 | [](https://travis-ci.org/MetaStack-pl/MetaTime)
3 | [](https://gitter.im/MetaStack-pl/MetaTime?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
4 |
5 | MetaTime is a time formatting library for Scala and Scala.js.
6 |
7 | ## Links
8 | * [Single-page manual](http://metastack.pl/metatime/latest.html)
9 | * [Multi-page manual](http://metastack.pl/metatime/latest/index.html)
10 | * [Versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22pl.metastack%22%20AND%20a%3A%22metatime_2.11%22)
11 |
12 | ## License
13 | MetaTime is licensed under the terms of the Apache v2.0 license.
14 |
15 | ## Authors
16 | * Tim Nieradzik
17 | * Syed Waqar Abbas
18 |
--------------------------------------------------------------------------------
/shared/src/main/scala/pl/metastack/metatime/Constants.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime
2 |
3 | object Constants {
4 | final val YearIndex = 1
5 | final val MonthIndex = 2
6 | final val DayIndex = 3
7 | final val HourIndex = 4
8 | final val MinuteIndex = 5
9 | final val SecondIndex = 6
10 | final val MillisecondIndex = 7
11 |
12 | final val MillisInSecond = 1000
13 | final val SecondsInMinute = 60
14 | final val MillisInMinute = SecondsInMinute * MillisInSecond
15 | final val MinutesInHour = 60
16 | final val SecondsInHour = MinutesInHour * SecondsInMinute
17 | final val MillisInHour = MinutesInHour * MillisInMinute
18 |
19 | final val HoursInDay = 24
20 | final val MinutesInDay = HoursInDay * MinutesInHour
21 | final val SecondsInDay = HoursInDay * SecondsInHour
22 | final val MillisInDay = HoursInDay * MillisInHour
23 | final val DefaultYear = 1970
24 | final val DaysInMonth = 30
25 | final val DaysInYear = 365
26 |
27 | final val In = "in "
28 | final val Ago = " ago"
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/jvm/src/main/scala/pl/metastack/metatime/AsyncScheduler.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime
2 |
3 | import java.util.concurrent.{ScheduledExecutorService, TimeUnit}
4 | import scala.concurrent.duration._
5 | import scala.concurrent.ExecutionContext
6 |
7 | class AsyncScheduler(s: ScheduledExecutorService,
8 | ec: ExecutionContext) extends Scheduler {
9 | def schedule(interval: FiniteDuration, r: Runnable): Cancelable =
10 | schedule(interval.length, interval.unit, r)
11 |
12 | def schedule(interval: Long, unit: TimeUnit, r: Runnable): Cancelable = {
13 | require(interval > 0)
14 | val initialDelay = interval
15 | val task = s.scheduleAtFixedRate(r, initialDelay, interval, unit)
16 | Cancelable(task.cancel(true))
17 | }
18 |
19 | def scheduleOnce(initialDelay: FiniteDuration, r: Runnable): Cancelable =
20 | scheduleOnce(initialDelay.length, initialDelay.unit, r)
21 |
22 | def scheduleOnce(initialDelay: Long, unit: TimeUnit, r: Runnable): Cancelable = {
23 | if (initialDelay <= 0) {
24 | ec.execute(r)
25 | Cancelable()
26 | } else {
27 | val task = s.schedule(r, initialDelay, unit)
28 | Cancelable(task.cancel(true))
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/js/src/main/scala/pl/metastack/metatime/AsyncScheduler.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime
2 |
3 | import scala.concurrent.duration._
4 |
5 | import scala.scalajs.js
6 |
7 | class AsyncScheduler extends Scheduler {
8 | type Timeout = js.Dynamic
9 | type Interval = js.Dynamic
10 |
11 | def setTimeout(delayMillis: Long, r: Runnable): Timeout = {
12 | val lambda: js.Function = () => r.run()
13 | js.Dynamic.global.setTimeout(lambda, delayMillis)
14 | }
15 |
16 | def setInterval(intervalMillis: Long, r: Runnable): Interval = {
17 | val lambda: js.Function = () => r.run()
18 | js.Dynamic.global.setInterval(lambda, intervalMillis)
19 | }
20 |
21 | def clearTimeout(task: Timeout): Unit =
22 | js.Dynamic.global.clearTimeout(task)
23 |
24 | def clearInterval(task: Interval): Unit =
25 | js.Dynamic.global.clearInterval(task)
26 |
27 | def schedule(interval: FiniteDuration, r: Runnable): Cancelable = {
28 | val task = setInterval(interval.toMillis, r)
29 | Cancelable(clearInterval(task))
30 | }
31 |
32 | def scheduleOnce(initialDelay: FiniteDuration, r: Runnable): Cancelable = {
33 | val task = setTimeout(initialDelay.toMillis, r)
34 | Cancelable(clearTimeout(task))
35 | }
36 | }
--------------------------------------------------------------------------------
/shared/src/main/scala/pl/metastack/metatime/Offset.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime
2 |
3 | trait Offset[+T <: Component] {
4 | val component: T
5 |
6 | def format: String =
7 | if (component.unix().value <= 0) Constants.In + absoluteVal
8 | else absoluteVal + Constants.Ago
9 |
10 | def absoluteVal: String =
11 | component match {
12 | case date: Date => math.abs(date.year) + " year(s), " + math.abs(date.month) +
13 | " month(s) and " + math.abs(date.day)
14 | case year: Year => math.abs(year.year) + " year(s)"
15 | case month: Month => math.abs(month.month) + " month(s)"
16 | case day: Day => math.abs(day.day) + " day(s)"
17 | case time: Time => math.abs(time.h) + " hour(s), " + math.abs(time.m) + " minute(s), " +
18 | math.abs(time.s) + " second(s) and " + math.abs(time.ms) + " millisecond(s)"
19 | case hour: Hour => math.abs(hour.h) + " hour(s)"
20 | case minute: Minute => math.abs(minute.m) + " minute(s)"
21 | case second: Second => math.abs(second.s) + " second(s)"
22 | case milliseconds: Millisecond => math.abs(milliseconds.ms) + " millisecond(s)"
23 | }
24 | }
25 |
26 | object Offset {
27 | def apply[T <: Component](value: T): Offset[T] =
28 | new Offset[T] { override val component = value }
29 | }
30 |
--------------------------------------------------------------------------------
/shared/src/test/scala/pl/metastack/metatime/PatternTest.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime
2 |
3 | import org.scalatest.FunSuite
4 |
5 | class PatternTest extends FunSuite {
6 | import Implicits._
7 |
8 | test("FormatDateTime") {
9 | val dateTime = DateTime(2015, 1, 1)
10 | assert(dateTime.format(PatternDateTime.DefaultDate) == "Thursday, 1 January 2015")
11 | }
12 |
13 | test("FormatTime") {
14 | val time = Time(11, 1, 0, 0)
15 | assert(time.format(PatternTime.DefaultTime) == "11:01 AM")
16 | }
17 |
18 | ignore("Format from now") {
19 | val offset = (DateTime.now() + 1.day).fromNow
20 | assert(offset.format == s"1 day(s) from now")
21 | }
22 |
23 | test("Custom pattern") {
24 | val dateTime = DateTime(2016, 6, 1, 0, 0, 0, 0)
25 | val formatter = PatternDateTime.branch(Seq(
26 | (0.seconds to 1.minute) -> PatternDateTime.JustNow,
27 | (1.minute to 59.minute ) -> PatternDateTime.MinutesAgo,
28 | (59.minute to 1.day ) -> PatternDateTime.HoursAgo,
29 | (1.day to 1.week ) -> PatternDateTime.DaysAgo
30 | ), PatternDateTime.DefaultDateTime)
31 |
32 | val dt = DateTime(2015, 1, 1)
33 |
34 | val now = DateTime.now()
35 | assert(formatter.format((now - 5.minutes).asInstanceOf[DateTime]) == "5 minute(s) ago")
36 | assert(formatter.format((now - 60.minutes).asInstanceOf[DateTime]) == "1 hour(s) ago")
37 |
38 | val twoWeeks = (now - 2.weeks).asInstanceOf[DateTime]
39 | assert(formatter.format(twoWeeks) == PatternDateTime.DefaultDateTime.format(twoWeeks))
40 | }
41 | }
--------------------------------------------------------------------------------
/shared/src/test/scala/pl/metastack/metatime/DateTest.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime
2 |
3 | import org.scalatest.FunSuite
4 |
5 | class DateTest extends FunSuite {
6 | import Implicits._
7 |
8 | test("construct") {
9 | val date = Date(2015, 12, 5)
10 | assert(date.year == 2015)
11 | assert(date.month == 12)
12 | assert(date.day == 5)
13 | }
14 |
15 | test("implicits") {
16 | assert(Year(2015) == 2015.year)
17 | }
18 |
19 | test("dayUnix") {
20 | assert(Day(1 + 1.0 / 24).unix().value == 90000000)
21 | }
22 |
23 | test("date") {
24 | assert(Year(2015).date == Date(2015, 1, 1))
25 | assert(Day(1) == Day(1))
26 | }
27 |
28 | test("add") {
29 | val dt = (Date(2015) + 1.day).asInstanceOf[Date]
30 | assert(Year(2015) + 1.day == Date(2015, 1, 1))
31 | }
32 | //TODO forbid addition of absolute dates in future
33 | ignore("datePlus") {
34 | val date = (Date(2015, 1, 1) + Date(2015, 1, 1)).asInstanceOf[Date]
35 | assert(Date(2015, 1, 1) + Date(2015, 1, 1) == Date(2060, 1, 2))
36 | }
37 |
38 | ignore("until") {
39 | assert((Year(2015) until Year(2016)) == ???)
40 | }
41 |
42 | ignore("now") {
43 | /** Current time */
44 | assert(DateTime.now() == ???)
45 |
46 | /** Unix timestamp */
47 | assert(DateTime.now().timestamp == ???)
48 | }
49 |
50 | test("fromNow") {
51 | assert(Date(2012, 1, 1).fromNow.format == "4 year(s) ago")
52 | assert(Date(2015, 8, 8).fromNow.format == "8 month(s) ago")
53 | assert(Date(2018, 10, 1).fromNow.format == "in 2 year(s)")
54 | }
55 | }
--------------------------------------------------------------------------------
/shared/src/test/scala/pl/metastack/metatime/DateTimeTest.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime
2 |
3 | import org.scalatest.FunSuite
4 | import scala.concurrent.Promise
5 | import scala.concurrent.ExecutionContext.Implicits.global
6 |
7 | class DateTimeTest extends FunSuite {
8 | test("construct") {
9 | val dateTime = DateTime(2016, 1, 1, 10, 10, 50, 500)
10 | assert(dateTime.year == 2016)
11 | assert(dateTime.month == 1)
12 | assert(dateTime.day == 1)
13 | assert(dateTime.h == 10)
14 | assert(dateTime.m == 10)
15 | assert(dateTime.s == 50)
16 | assert(dateTime.ms == 500)
17 | }
18 |
19 | test("date") {
20 | assert(Year(2015).dateTime == DateTime(2015, 1, 0, 0, 0, 0))
21 | }
22 |
23 | test("Add day") {
24 | assert(
25 | DateTime(2016, 1, 1, 1, 1, 1, 1) + Day(1) ==
26 | DateTime(2016, 1, 2, 1, 1, 1, 1))
27 | }
28 |
29 | test("Add second") {
30 | val dt = DateTime.now()
31 | val dtAfter = dt + Second(1)
32 | assert(dtAfter.unix().value == dt.unix().value + 1000)
33 | }
34 |
35 | test("now") {
36 | val scheduler: Scheduler = Platform.DefaultScheduler
37 | val dt = DateTime.now()
38 | val dtAfter = (dt + Second(5)).asInstanceOf[DateTime]
39 | val p = Promise[Boolean]()
40 | scheduler.at(dtAfter) {
41 | p.success((DateTime.now() - dt).unix().value < 10000)
42 | }
43 | p.future.map(assert(_))
44 | }
45 |
46 | ignore("fromNow") {
47 | assert(DateTime(2018, 10, 1, 1, 1, 1, 1).fromNow.format == "in 2 year(s)")
48 | assert(DateTime(2013, 2, 1, 1, 1, 1, 1).fromNow.format == "3 year(s) ago")
49 | }
50 | }
--------------------------------------------------------------------------------
/shared/src/test/scala/pl/metastack/metatime/TimeTest.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime
2 |
3 | import org.scalatest.FunSuite
4 | import scala.concurrent.Promise
5 | import scala.concurrent.ExecutionContext.Implicits.global
6 |
7 | class TimeTest extends FunSuite {
8 | import Implicits._
9 |
10 | test("construct") {
11 | val time = Time(23, 10, 9, 555)
12 | assert(time.h == 23)
13 | assert(time.m == 10)
14 | assert(time.s == 9)
15 | assert(time.ms == 555)
16 | }
17 |
18 | test("implicits") {
19 | assert(Hour(23) == 23.hour)
20 | }
21 |
22 | ignore("hour") {
23 | assert(Hour(25).days == Day(1 + 1.0 / 24))
24 | }
25 |
26 | test("operators") {
27 | assert(Time(23, 10) > Time(22, 0))
28 | assert(Time(23, 10) > Hour(22))
29 | assert(Hour(2) > Minute(80))
30 | }
31 |
32 | test("plusTime") {
33 | assert(Time(Hour(8) + Hour(2)) == Time(hour = 10))
34 | assert(Time(Minute(20) + Second(40)) != Time(hour = 10, minute = 20))
35 | assert(Time(Hour(2) + Minute(60)) == Time(hour = 3))
36 | }
37 |
38 | test("minusTime") {
39 | assert(Time(Minute(50) - Minute(10)) == Time(minute = 40))
40 | assert(Time(Second(1) - Millisecond(100)) == Time(milliseconds = 900))
41 | assert(Time(Hour(20) - Minute(100)) == Time(hour = 18, minute = 20))
42 | }
43 |
44 | test("now") {
45 | val scheduler: Scheduler = Platform.DefaultScheduler
46 | val timeBefore = Time.now()
47 | val p = Promise[Boolean]()
48 | scheduler.at(Time(0, 0, 1, 0)) {
49 | p.success((Time.now() - timeBefore).unix().value >= 1000)
50 | }
51 | p.future.map(assert(_))
52 | }
53 |
54 | ignore("locale") {
55 | implicit val locale = Locale.English.America
56 | //assert(Time(23).format == "11 pm")
57 | }
58 |
59 | test("fromNow") {
60 | assert(Minute(120).fromNow.format == "2 hour(s) ago")
61 | }
62 |
63 | test("millis") {
64 | assert(Minute(2).milliseconds() == 120000)
65 | assert(Time(1, 0, 0, 0).milliseconds() == 3600000)
66 | }
67 | }
--------------------------------------------------------------------------------
/shared/src/main/scala/pl/metastack/metatime/PatternTime.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime
2 |
3 | case class PatternTime(parts: Seq[Time => String] = Nil) extends Pattern[PatternTime, Time] {
4 |
5 | def amPm: PatternTime =
6 | add(time =>
7 | if (time.h > 12) "PM"
8 | else "AM")
9 |
10 | def hour(amPm: Boolean = false): PatternTime = add(_.h.toString)
11 | def minute: PatternTime = add(time =>
12 | f"${time.m}%02d".toString)
13 |
14 | def minuteAgo: PatternTime = add(time =>
15 | time.fromNow.component.dateTime.m.toString)
16 |
17 | def hourAgo: PatternTime = add(time =>
18 | time.fromNow.component.dateTime.h.toString)
19 |
20 | def second: PatternTime = add(_.s.toString)
21 |
22 | def secondsFract(digits: Int): PatternTime = {
23 | add(_.ms.toString)
24 | }
25 |
26 | //def newLine: PatternTime = add(_ => "\n")
27 |
28 | def add(item: Time => String): PatternTime = copy(parts = parts ++ Seq(item))
29 |
30 | def format(time: Time): String =
31 | parts.foldLeft("") { case (string, format) => string + format(time) }
32 |
33 | def concat(pattern: PatternTime): PatternTime = PatternTime(parts ++ pattern.parts)
34 |
35 | def ++(pattern: PatternTime): PatternTime = concat(pattern)
36 | }
37 |
38 | object PatternTime {
39 |
40 | val DefaultTime = PatternTime().hour(amPm = true).colon.minute.space.amPm
41 |
42 | val JustNow = PatternTime().string("just now")
43 |
44 | def ago(pattern: PatternTime): PatternTime = pattern.space.string("ago")
45 |
46 | val MinutesAgo: PatternTime = ago(PatternTime().minuteAgo.space.string("minute(s)"))
47 | val HoursAgo: PatternTime = ago(PatternTime().hourAgo.space.string("hour(s)"))
48 | val DaysAgo: PatternTime = ago(PatternTime().minute.space.string("day(s)"))
49 |
50 | def branch(ranges: Seq[(Range, PatternTime)], fallback: PatternTime): PatternTime = {
51 | PatternTime().add { time =>
52 | val unixVal = time.fromNow.component.unix().value
53 | val x =ranges.find { case (r, p) =>
54 | (r.lowerBound to r.upperBound).contains(unixVal)
55 | }
56 | x.fold(fallback.format(time)) { case (r, p) => p.format(time) }
57 | }
58 | }
59 | }
--------------------------------------------------------------------------------
/shared/src/test/scala/pl/metastack/metatime/SchedulerTest.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime
2 |
3 | import org.scalatest.AsyncFunSuite
4 | import scala.concurrent.Promise
5 |
6 | class SchedulerTest extends AsyncFunSuite {
7 | val scheduler: Scheduler = Platform.DefaultScheduler
8 |
9 | test("Schedule once at Time") {
10 | val timeBefore = Time.now()
11 | val p = Promise[Boolean]()
12 | scheduler.at(Time(0, 0, 1, 0)) {
13 | p.success((Time.now() - timeBefore).unix().value >= 1000)
14 | }
15 | p.future.map(assert(_))
16 | }
17 |
18 | test("Schedule once at DateTime") {
19 | val dt = DateTime.now()
20 | val dtAfter = (dt + Second(1)).asInstanceOf[DateTime]
21 | assert(dtAfter.unix().value == dt.unix().value + 1000)
22 | val p = Promise[Boolean]()
23 | scheduler.at(dtAfter) {
24 | p.success((DateTime.now() - dt).unix().value >= 1000)
25 | }
26 | p.future.map(assert(_))
27 | }
28 |
29 | test("Schedule once at Offset") {
30 | val timeBefore = Time.now()
31 | val offset = Offset(Second(1))
32 | val p = Promise[Boolean]()
33 | scheduler.at(offset) {
34 | p.success((Time.now() - timeBefore).unix().value >= 1000)
35 | }
36 | p.future.map(assert(_))
37 | }
38 |
39 | ignore("Schedule repeatedly at Time") {
40 | val timeBefore = Time.now()
41 | val p = Promise[Boolean]()
42 | scheduler.every(Time(0, 0, 1, 0)) {
43 | // TODO Check whether every() is called more than once in the same intervals
44 | p.success(Math.abs((Time.now() - timeBefore).unix().value) >= 1000)
45 | }
46 | p.future.map(assert(_))
47 | }
48 |
49 | ignore("Schedule repeatedly at DateTime") {
50 | val dt = DateTime.now()
51 | val dtAfter = (dt + Second(1)).asInstanceOf[DateTime]
52 | val p = Promise[Boolean]()
53 | scheduler.every(dtAfter) {
54 | p.success((DateTime.now() - dt).unix().value >= 1000)
55 | }
56 | p.future.map(assert(_))
57 | }
58 |
59 | ignore("Schedule repeatedly at Offset") {
60 | val timeBefore = Time.now()
61 | val offset = Offset(Second(1))
62 | val p = Promise[Boolean]()
63 | scheduler.every(offset) {
64 | p.success(Math.abs((Time.now() - timeBefore).unix().value) >= 1000)
65 | }
66 | p.future.map(assert(_))
67 | }
68 | }
--------------------------------------------------------------------------------
/shared/src/main/scala/pl/metastack/metatime/Scheduler.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime
2 |
3 | import scala.concurrent.duration._
4 |
5 | /**
6 | * Inspired from Monifu's scheduling code.
7 | */
8 | trait Scheduler {
9 | def schedule(interval: FiniteDuration, r: Runnable): Cancelable
10 | def scheduleOnce(initialDelay: FiniteDuration, action: Runnable): Cancelable
11 |
12 | def schedule(interval: FiniteDuration)(action: => Unit): Cancelable =
13 | schedule(interval, new Runnable {
14 | def run(): Unit = action
15 | })
16 |
17 | def scheduleOnce(initialDelay: FiniteDuration)(action: => Unit): Cancelable =
18 | scheduleOnce(initialDelay, new Runnable {
19 | def run(): Unit = action
20 | })
21 |
22 | def currentTimeMillis(): Long = System.currentTimeMillis()
23 |
24 | def at(time: Time)(f: => Unit): Cancelable =
25 | at(time.fromNow)(f)
26 | def at(dateTime: DateTime)(f: => Unit): Cancelable =
27 | at(dateTime.fromNow)(f)
28 | def at(time: Offset[_])(f: => Unit): Cancelable =
29 | scheduleOnce(Math.abs(time.component.asInstanceOf[Component].milliseconds()).millis)(f)
30 |
31 | def in(time: Time)(f: => Unit): Cancelable =
32 | scheduleOnce(time.milliseconds.millis)(f)
33 | def in(date: Date)(f: => Unit): Cancelable =
34 | scheduleOnce(date.milliseconds.millis)(f)
35 | def in(dateTime: DateTime)(f: => Unit): Cancelable =
36 | scheduleOnce(dateTime.milliseconds.millis)(f)
37 |
38 | def every(time: Time)(f: => Unit): Cancelable =
39 | schedule(time.milliseconds.millis)(f)
40 | def every(dateTime: DateTime)(f: => Unit): Cancelable =
41 | schedule(dateTime.milliseconds.millis)(f)
42 | def every(time: Offset[Component])(f: => Unit): Cancelable =
43 | schedule(Math.abs(time.component.milliseconds()).millis)(f)
44 | }
45 |
46 | trait Cancelable {
47 | def cancel(): Boolean
48 | }
49 |
50 | object Cancelable {
51 | def apply(): Cancelable = apply({})
52 |
53 | def apply(callback: => Unit): Cancelable =
54 | new Cancelable {
55 | var isCanceled = false
56 |
57 | def cancel(): Boolean =
58 | if (isCanceled) false
59 | else {
60 | isCanceled = true
61 | callback
62 | true
63 | }
64 | }
65 | }
66 |
67 | trait Task {
68 | def cancel(): Unit = ???
69 | }
70 |
--------------------------------------------------------------------------------
/project/Build.scala:
--------------------------------------------------------------------------------
1 | import sbt._
2 | import sbt.Keys._
3 | import xerial.sbt.Sonatype.sonatypeSettings
4 | import org.scalajs.sbtplugin.ScalaJSPlugin.autoImport._
5 | import sbtbuildinfo.BuildInfoPlugin
6 | import sbtbuildinfo.BuildInfoPlugin.autoImport._
7 |
8 | object Build extends sbt.Build {
9 | object Dependencies {
10 | val ScalaTest = "3.0.0-M14"
11 | val MetaDocs = "0.1.1"
12 | val MetaRx = "0.1.6"
13 | }
14 |
15 | val SharedSettings = Seq(
16 | name := "MetaTime",
17 | organization := "pl.metastack",
18 | scalaVersion := "2.11.7",
19 | scalacOptions := Seq(
20 | "-unchecked",
21 | "-deprecation",
22 | "-encoding", "utf8"
23 | )
24 | )
25 |
26 | lazy val root = project.in(file("."))
27 | .aggregate(js, jvm)
28 | .settings(SharedSettings: _*)
29 | .settings(publishArtifact := false)
30 |
31 | lazy val metaTime = crossProject.in(file("."))
32 | .settings(SharedSettings: _*)
33 | .settings(sonatypeSettings: _*)
34 | .settings(
35 | pomExtra :=
36 | https://github.com/MetaStack-pl/MetaTime
37 |
38 |
39 | Apache-2.0
40 | https://www.apache.org/licenses/LICENSE-2.0.html
41 |
42 |
43 |
44 | git@github.com:MetaStack-pl/MetaTime.git
45 |
46 |
47 |
48 | tindzk
49 | Tim Nieradzik
50 | http://github.com/tindzk/
51 |
52 | ,
53 |
54 | autoAPIMappings := true,
55 | apiMappings +=
56 | (scalaInstance.value.libraryJar -> url(s"http://www.scala-lang.org/api/${scalaVersion.value}/"))
57 | )
58 | .jsSettings(
59 | libraryDependencies +=
60 | "org.scalatest" %%% "scalatest" % Dependencies.ScalaTest % "test",
61 | libraryDependencies +=
62 | "pl.metastack" %%% "metarx" % Dependencies.MetaRx % "test",
63 |
64 | /* Use io.js for faster compilation of test cases */
65 | scalaJSStage in Global := FastOptStage
66 | )
67 | .jvmSettings(
68 | libraryDependencies +=
69 | "org.scalatest" %% "scalatest" % Dependencies.ScalaTest % "test",
70 | libraryDependencies +=
71 | "pl.metastack" %% "metarx" % Dependencies.MetaRx % "test"
72 | )
73 |
74 | lazy val js = metaTime.js
75 | lazy val jvm = metaTime.jvm
76 |
77 | lazy val manual = project.in(file("manual"))
78 | .dependsOn(jvm)
79 | .enablePlugins(BuildInfoPlugin)
80 | .settings(SharedSettings: _*)
81 | .settings(
82 | publishArtifact := false,
83 | libraryDependencies ++= Seq(
84 | "pl.metastack" %% "metadocs" % Dependencies.MetaDocs,
85 | "org.eclipse.jgit" % "org.eclipse.jgit" % "4.1.1.201511131810-r"),
86 | buildInfoKeys := Seq[BuildInfoKey](name, version, scalaVersion, sbtVersion),
87 | buildInfoPackage := "pl.metastack.metatime",
88 | name := "MetaTime manual")
89 | }
90 |
--------------------------------------------------------------------------------
/shared/src/main/scala/pl/metastack/metatime/PatternDateTime.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime
2 |
3 | case class PatternDateTime(parts: Seq[DateTime => String] = Nil) extends Pattern[PatternDateTime, DateTime] {
4 |
5 | def year(short: Boolean = false): PatternDateTime =
6 | add(dt =>
7 | if (!short) dt.year.toString
8 | else dt.year.toString.drop(2))
9 |
10 | def month: PatternDateTime = add(_.month.toString)
11 |
12 | def monthName(short: Boolean = false): PatternDateTime = {
13 | add(dateTime =>
14 | months(dateTime.month - 1))
15 | }
16 |
17 | def day: PatternDateTime = add(_.day.toInt.toString)
18 |
19 | def amPm: PatternDateTime =
20 | add(dt =>
21 | if (dt.h > 12) "PM"
22 | else "AM")
23 |
24 | def hour(amPm: Boolean = false): PatternDateTime = add(_.h.toString)
25 | def minute: PatternDateTime = add(dt =>
26 | f"${dt.asInstanceOf[Time].m}%02d".toString)
27 |
28 | def minuteAgo: PatternDateTime = add(dt =>
29 | dt.asInstanceOf[Time].fromNow.component.dateTime.m.toString)
30 |
31 | def hourAgo: PatternDateTime = add(dt =>
32 | dt.asInstanceOf[Time].fromNow.component.dateTime.h.toString)
33 |
34 | def second: PatternDateTime = add(_.asInstanceOf[Time].s.toString)
35 |
36 | /** Fractional seconds padded to `digits` */
37 | def secondsFract(digits: Int): PatternDateTime = {
38 | add(_.asInstanceOf[Time].ms.toString)
39 | }
40 |
41 | /**
42 | * Day of the week such as Monday, Tuesday, Wednesday etc.
43 | * @param short Short version of weekdays (Mon, Tue, Wed)
44 | */
45 | def weekDay(short: Boolean = false): PatternDateTime = {
46 | add(dateTime =>
47 | weekDays(dateTime.weekDay() - 1))
48 | }
49 |
50 | def add(item: DateTime => String): PatternDateTime = copy(parts = parts ++ Seq(item))
51 |
52 | def format(dateTime: DateTime): String =
53 | parts.foldLeft("") { case (string, format) => string + format(dateTime) }
54 |
55 | def concat(pattern: PatternDateTime): PatternDateTime = PatternDateTime(parts ++ pattern.parts)
56 |
57 | def ++(pattern: PatternDateTime): PatternDateTime = concat(pattern)
58 | }
59 |
60 | object PatternDateTime {
61 | val Iso8601 = PatternDateTime()
62 | .year().dash.month.dash.day.space
63 | .hour().colon.minute.colon.second.dot.secondsFract(3)
64 | .string("Z")
65 |
66 | // Tuesday, 1 March 2016
67 | val DefaultDate = PatternDateTime().weekDay().comma.space.day.space.monthName().space.year()
68 |
69 | // 6:57 PM
70 | val DefaultTime = PatternDateTime().hour(amPm = true).colon.minute.space.amPm
71 |
72 | val DefaultDateTime = DefaultDate ++ DefaultTime
73 |
74 | val JustNow = PatternDateTime().string("just now")
75 |
76 | def ago(pattern: PatternDateTime): PatternDateTime = pattern.space.string("ago")
77 |
78 | val MinutesAgo: PatternDateTime = ago(PatternDateTime().minuteAgo.space.string("minute(s)"))
79 | val HoursAgo: PatternDateTime = ago(PatternDateTime().hourAgo.space.string("hour(s)"))
80 | val DaysAgo: PatternDateTime = ago(PatternDateTime().minute.space.string("day(s)"))
81 |
82 | def branch(ranges: Seq[(Range, PatternDateTime)], fallback: PatternDateTime): PatternDateTime = {
83 | PatternDateTime().add { dt =>
84 | val unixVal = dt.fromNow.component.unix().value
85 | val x =ranges.find { case (r, p) =>
86 | (r.lowerBound to r.upperBound).contains(unixVal)
87 | }
88 | x.fold(fallback.format(dt)) { case (r, p) => p.format(dt) }
89 | }
90 | }
91 | }
--------------------------------------------------------------------------------
/shared/src/main/scala/pl/metastack/metatime/DateTime.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime
2 |
3 | trait DateTime extends Time with Date {
4 | def format(pattern: PatternDateTime): String = pattern.format(this)
5 |
6 | def timestamp: Timestamp = ???
7 |
8 | override def equals(that: Any): Boolean =
9 | that match {
10 | case other: DateTime => other.year == year && other.month == month && other.day == day &&
11 | h == other.h && m == other.m && s == other.s && ms == other.ms
12 | case _ => false
13 | }
14 |
15 | override def toString: String = {
16 | year + "-" + month + "-" + day.toInt + " " +
17 | h + ":" + m + ":" + s + ":" + ms
18 | }
19 |
20 | override def unix(): Unix = Unix(
21 | Year(year).unix().value +
22 | Month(month).unix().value +
23 | Day(day).unix().value +
24 | Hour(h).unix().value +
25 | Minute(m).unix().value +
26 | Second(s).unix().value +
27 | Millisecond(ms).unix().value)
28 | }
29 |
30 | object DateTime {
31 |
32 | def calculateDateTime(milliDate: Long, milliTime: Long): DateTime = {
33 | val date = Date(milliDate)
34 | val time = Time(milliTime)
35 | DateTime(date.year, date.month, date.day, time.h, time.m, time.s, time.ms)
36 | }
37 |
38 | def now(): DateTime = calculateDateTime(Date.now().unix().value, Time.now.unix().value)
39 | def apply(millisecond: Long): DateTime = calculateDateTime(millisecond, millisecond)
40 |
41 |
42 | def apply(yr: Int, mo: Int, dy: Int,
43 | hour: Int, minute: Int, second: Int, millisecond: Int): DateTime =
44 | new DateTime {
45 | override val year: Int = yr
46 | override val month: Int = mo
47 | override val day: Double = dy
48 | override val h: Int = hour
49 | override val m: Int = minute
50 | override val s: Int = second
51 | override val ms: Int = millisecond
52 | }
53 |
54 | def apply(yr: Int = 0, mo: Int = 1, dy: Double = 0.0,
55 | hr: Int = 0, min: Int = 0, sec: Int = 0,
56 | milli: Int = 0): DateTime = new DateTime {
57 | override val year: Int = yr
58 | override val month: Int = mo
59 | override val day: Double = dy
60 | override val h: Int = hr
61 | override val m: Int = min
62 | override val s: Int = sec
63 | override val ms: Int = milli
64 | }
65 |
66 | def apply(): DateTime = new DateTime {
67 | override val year: Int = 0
68 | override val month: Int = 1
69 | override val day: Double = 0.0
70 | override val h: Int = 0
71 | override val m: Int = 0
72 | override val s: Int = 0
73 | override val ms: Int = 0
74 | }
75 |
76 | def apply(comp : Component) : DateTime = {
77 | comp match {
78 | case dT: DateTime =>
79 | DateTime(dT.year, dT.month, dT.day, dT.h, dT.m, dT.s, dT.ms)
80 |
81 | case tempYear: Year =>
82 | DateTime(tempYear.year, 1, 0, 0, 0, 0, 0)
83 |
84 | case tempMonth: Month =>
85 | DateTime(0, tempMonth.month, 0, 0, 0, 0, 0)
86 |
87 | case tempDay: Day =>
88 | DateTime(0, 1, tempDay.day, 0, 0, 0, 0)
89 |
90 | case tempHour: Hour =>
91 | DateTime(0, 1, 0, tempHour.h, 0, 0, 0)
92 |
93 | case tempMinute: Minute =>
94 | DateTime(0, 1, 0, 0, tempMinute.m, 0, 0)
95 |
96 | case tempSecond: Second =>
97 | DateTime(0, 1, 0, 0, 0, tempSecond.s, 0)
98 |
99 | case tempMilliSecond: Millisecond =>
100 | DateTime(0, 1, 0, 0, 0, 0, tempMilliSecond.ms)
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/shared/src/main/scala/pl/metastack/metatime/Time.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime
2 |
3 | trait Hour extends Component {
4 | val h: Int
5 |
6 | override def unix(): Unix = Unix(h * Minute(60).unix().value)
7 |
8 | override def equals(that: Any): Boolean =
9 | that match {
10 | case other: Hour => other.h == h
11 | case _ => false
12 | }
13 | }
14 |
15 | object Hour {
16 | def apply(value: Int): Hour = new Hour {
17 | override val h = value
18 | }
19 | }
20 |
21 | trait Minute extends Component {
22 | val m: Int
23 |
24 | override def unix(): Unix = Unix(m * Second(60).unix().value)
25 |
26 | override def equals(that: Any): Boolean =
27 | that match {
28 | case other: Minute => other.m == m
29 | case _ => false
30 | }
31 | }
32 |
33 | object Minute {
34 | def apply(value: Int): Minute = new Minute {
35 | override val m = value
36 | }
37 | }
38 |
39 | trait Second extends Component {
40 | val s: Int
41 |
42 | override def unix(): Unix = Unix(s * Millisecond(1000).unix().value)
43 |
44 | override def equals(that: Any): Boolean =
45 | that match {
46 | case other: Second => other.s == s
47 | case _ => false
48 | }
49 | }
50 |
51 | object Second {
52 | def apply(value: Int): Second = new Second {
53 | override val s = value
54 | }
55 | }
56 |
57 | trait Millisecond extends Component {
58 | val ms: Int
59 |
60 | override def unix(): Unix = Unix(ms)
61 |
62 | override def equals(that: Any): Boolean =
63 | that match {
64 | case other: Millisecond => other.ms == ms
65 | case _ => false
66 | }
67 | }
68 |
69 | object Millisecond {
70 | def apply(value: Int): Millisecond =
71 | new Millisecond {
72 | override val ms = value
73 | }
74 | }
75 |
76 | trait Time extends Hour with Minute with Second with Millisecond {
77 |
78 | def format(pattern: PatternTime): String = pattern.format(this)
79 |
80 | override def equals(that: Any): Boolean =
81 | that match {
82 | case other: Time => other.h == h && other.m == m && other.s == s && other.ms == ms
83 | case _ => false
84 | }
85 |
86 | override def toString: String = h + ":" + m + ":" + s + ":" + ms
87 |
88 | override def unix(): Unix = Unix(
89 | Hour(h).unix().value +
90 | Minute(m).unix().value +
91 | Second(s).unix().value +
92 | Millisecond(ms).unix().value)
93 | }
94 |
95 | object Time {
96 | def apply(hour: Int = 0,
97 | minute: Int = 0,
98 | second: Int = 0,
99 | millisecond: Int = 0): Time = new Time {
100 | override val h: Int = hour
101 | override val m: Int = minute
102 | override val s: Int = second
103 | override val ms: Int = millisecond
104 | }
105 |
106 | def apply(comp : Component) : Time = {
107 | comp match {
108 | case t: Time =>
109 | Time(hour = t.h,
110 | minute = t.m,
111 | second = t.s,
112 | millisecond = t.ms)
113 | case hr: Hour => Time(hour = hr.h)
114 | case m: Minute => Time(minute = m.m)
115 | case s: Second => Time(second = s.s)
116 | case ms: Millisecond => Time(millisecond = ms.ms)
117 | }
118 | }
119 |
120 | def apply(milliseconds: Long): Time = {
121 | val hour = ((milliseconds /
122 | (Hour(1).unix().value)).toInt) % 24
123 | val minute = ((milliseconds %
124 | (Hour(1).unix().value)) /
125 | (Minute(1).unix().value)).toInt
126 | val second = (((milliseconds %
127 | (Hour(1).unix().value)) %
128 | (Minute(1).unix().value)) /
129 | Second(1).unix().value).toInt
130 | val millisecond = ((((milliseconds %
131 | (Hour(1).unix().value)) %
132 | (Minute(1).unix().value)) %
133 | Second(1).unix().value) /
134 | Millisecond(1).unix().value).toInt
135 | Time(hour, minute, second, millisecond)
136 | }
137 |
138 | /** Current time */
139 | def now(): Time = {
140 | val currTimeInMilliSec: Long = System.currentTimeMillis()
141 | new Time {
142 | override val h: Int = (((currTimeInMilliSec / 1000) / 3600) % 24).toInt
143 | override val m: Int = (((currTimeInMilliSec / 1000) / 60) % 60).toInt
144 | override val s: Int = ((currTimeInMilliSec / 1000) % 60).toInt
145 | override val ms: Int = (currTimeInMilliSec % 1000).toInt
146 | }
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/manual/src/main/scala/pl/metastack/metatime/manual/Manual.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime.manual
2 |
3 | import java.io.File
4 | import java.nio.file.{Files, Paths}
5 |
6 | import org.eclipse.jgit.api.Git
7 | import org.joda.time.DateTime
8 |
9 | import pl.metastack.metadocs.document._
10 | import pl.metastack.metadocs.input._
11 | import pl.metastack.metadocs.input.metadocs._
12 | import pl.metastack.metadocs.output.html.Components
13 | import pl.metastack.metadocs.output.html.document.{Book, SinglePage}
14 |
15 | import pl.metastack.metatime.BuildInfo
16 |
17 | object Manual extends App with Shared {
18 | if (!projectPath.exists())
19 | Git.cloneRepository()
20 | .setURI(s"git@github.com:$organisation/$repoName.git")
21 | .setDirectory(projectPath)
22 | .call()
23 |
24 | manualPath.mkdir()
25 | manualVersionPath.mkdirs()
26 | manualVersionPath.listFiles().foreach(_.delete())
27 |
28 | imagesPath.mkdirs()
29 | imagesPath.listFiles().foreach(_.delete())
30 |
31 | val meta = Meta(
32 | date = DateTime.now(),
33 | title = "MetaTime User Manual v" + BuildInfo.version,
34 | author = "Tim Nieradzik",
35 | affiliation = "MetaStack Sp. z o.o.",
36 | `abstract` = "Time formatting library for Scala and Scala.js",
37 | language = "en-GB",
38 | url = "",
39 | editSourceURL = Some("https://github.com/MetaStack-pl/MetaTime/edit/master/"))
40 |
41 | val instructionSet = DefaultInstructionSet
42 | .inherit(BookInstructionSet)
43 | .inherit(CodeInstructionSet)
44 | .inherit(DraftInstructionSet)
45 | .withAliases(
46 | "b" -> Bold,
47 | "i" -> Italic,
48 | "li" -> ListItem)
49 |
50 | val rawTrees = Seq(
51 | "introduction", "type-safe-dsl", "scheduler",
52 | "datetime-formatting", "scalajs-support", "development", "support"
53 | ).map(chapter => s"manual/$chapter.md")
54 | .map(file =>
55 | Markdown.loadFileWithExtensions(file,
56 | instructionSet,
57 | constants = Map("version" -> BuildInfo.version),
58 | generateId = caption => Some(caption.collect {
59 | case c if c.isLetterOrDigit => c
60 | case c if c.isSpaceChar => '-'
61 | }.toLowerCase)
62 | ).get)
63 |
64 | val docTree = Document.mergeTrees(rawTrees)
65 |
66 | // Explicitly print out all chapters/sections which is useful when
67 | // restructuring the document
68 | println("Document tree:")
69 | println(Extractors.references(docTree))
70 | println()
71 |
72 | Document.printTodos(docTree)
73 |
74 | val pipeline =
75 | Document.pipeline
76 | .andThen(CodeProcessor.embedListings("manual") _)
77 | .andThen(CodeProcessor.embedOutput _)
78 | val docTreeWithCode = pipeline(docTree)
79 |
80 | val skeleton = Components.pageSkeleton(
81 | cssPaths = Seq(
82 | "css/kult.css",
83 | "css/default.min.css"
84 | ),
85 | jsPaths = Seq(
86 | "//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js",
87 | "js/main.js",
88 | "js/highlight.pack.js"
89 | ),
90 | script = Some("hljs.initHighlightingOnLoad();"),
91 | favicon = Some("favicon.ico")
92 | )(_, _, _)
93 |
94 | // TODO Copy scaladocs as well
95 |
96 | SinglePage.write(docTreeWithCode,
97 | skeleton,
98 | s"$manualPathStr/v${BuildInfo.version}.html",
99 | meta = Some(meta),
100 | toc = true,
101 | tocDepth = 2) // Don't include subsections
102 |
103 | Book.write(docTreeWithCode,
104 | skeleton,
105 | s"$manualPathStr/v${BuildInfo.version}",
106 | meta = Some(meta),
107 | tocDepth = 2)
108 |
109 | val links =
110 | Set("css", "js", "favicon.ico").flatMap { folder =>
111 | Seq(
112 | s"$manualPathStr/$folder" -> s"../$folder",
113 | s"$manualVersionPathStr/$folder" -> s"../../$folder")
114 | }.toMap ++
115 | Map(s"$manualPathStr/images" -> s"v${BuildInfo.version}/images") ++ (
116 | if (isSnapshot) Map.empty
117 | else Map(
118 | s"$manualPathStr/latest" -> s"v${BuildInfo.version}",
119 | s"$manualPathStr/latest.html" -> s"v${BuildInfo.version}.html"))
120 |
121 | links.map { case (from, to) =>
122 | Paths.get(from) -> Paths.get(to)
123 | }.foreach { case (from, to) =>
124 | if (Files.exists(from)) Files.delete(from)
125 | Files.createSymbolicLink(from, to)
126 | }
127 |
128 | /* Replace images */
129 | new File("manual", "images")
130 | .listFiles()
131 | .filter(_.getName.endsWith(".png"))
132 | .foreach { image =>
133 | Files.copy(image.toPath, new File(imagesPath, image.getName).toPath)
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/shared/src/main/scala/pl/metastack/metatime/Date.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime
2 |
3 | trait Year extends Component {
4 | val year: Int
5 |
6 | override def unix(): Unix = Unix(daysOf(this).toLong * 24 * 3600 * 1000)
7 |
8 | override def equals(that: Any): Boolean =
9 | that match {
10 | case other: Year => other.year == year
11 | case _ => false
12 | }
13 | }
14 |
15 | object Year {
16 | def apply(value: Int): Year = new Year {
17 | override val year = value
18 | }
19 | }
20 |
21 | trait Month extends Component {
22 | val month: Int
23 |
24 | override def unix(): Unix = Unix(daysOf(this).toLong * 24 * 3600 * 1000)
25 |
26 | override def equals(that: Any): Boolean =
27 | that match {
28 | case other: Month => other.month == month
29 | case _ => false
30 | }
31 | }
32 |
33 | object Month {
34 | def apply(value: Int): Month = new Month {
35 | override val month = value
36 | }
37 | }
38 |
39 | trait Day extends Component {
40 | val day: Double
41 |
42 | override def unix(): Unix = Unix((day * 24 * 3600 * 1000).toLong)
43 |
44 | override def equals(that: Any): Boolean =
45 | that match {
46 | case other: Day => other.day == day
47 | case _ => false
48 | }
49 |
50 | def truncate: Day = Day(Math.round(day))
51 | }
52 |
53 | object Day {
54 | def apply(value: Double): Day = new Day {
55 | override val day = value
56 | }
57 | }
58 |
59 | trait Date extends Year with Month with Day {
60 |
61 | override def equals(that: Any): Boolean =
62 | that match {
63 | case other: Date => other.year == year && other.month == month && other.day == day
64 | case _ => false
65 | }
66 |
67 | override def toString: String = year + "-" + month + "-" + day.toInt
68 |
69 |
70 | override def unix(): Unix = Unix(
71 | Year(year).unix().value +
72 | Month(month).unix().value +
73 | Day(day).unix().value)
74 | }
75 |
76 | object Date {
77 | def apply(y: Int): Date = new Date {
78 | override val year: Int = y
79 | override val month: Int = 1
80 | override val day: Double = 1.0
81 | }
82 | def apply(year: Int, month: Int): Date = ???
83 | def parse(format: String, timezone: Timezone): Date = ???
84 |
85 | val YearsSequence = Seq(365, 365, 366, 365)
86 | val DaysInMonths = Seq(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
87 | val LeapDaysInMonths = Seq(31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
88 |
89 | def isLeapYear(year: Int): Boolean = (year % 4) == 0
90 | def accumulate(f: Int => Seq[Int], y: Int): Seq[Int] = f(y)
91 | def accuPrevElements(seq: Seq[Int]) = seq.scanLeft(0)(_ + _).tail
92 |
93 | def accuYears(totalDays: Int) : Seq[Int] = {
94 | val yearLength = Stream.continually(YearsSequence).flatten.take((totalDays / 365) + 1).toList
95 | accuPrevElements(yearLength)
96 | }
97 |
98 | def yearsPassed(totalDays: Int) : Int = {
99 | accumulate(accuYears, totalDays).zipWithIndex.indexWhere( {
100 | case (accDays, year) => totalDays <= accDays
101 | }) + 1970
102 | }
103 |
104 | def daysPassed(totalDays: Int) : Int = {
105 | val accumulatedDaysList = accumulate(accuYears, totalDays).filter(x => x <= totalDays)
106 | accumulatedDaysList.length match {
107 | case 0 => totalDays
108 | case n => totalDays - accumulatedDaysList(n - 1)
109 | }
110 | }
111 |
112 | def findMonth(days: Int, year: Int): Int = {
113 | accumulate(accuDays, year).filter(x => x <= days).length + 1
114 | }
115 |
116 | def accuDays(year: Int): Seq[Int] = {
117 | if(isLeapYear(year))
118 | accuPrevElements(LeapDaysInMonths)
119 | else
120 | accuPrevElements(DaysInMonths)
121 | }
122 |
123 | def findDay(days: Int, year: Int): Int = {
124 | val accumulatedDays = accumulate(accuDays, year).filter(x => x <= days)
125 | accumulatedDays.length match {
126 | case 0 => days
127 | case n => days - accumulatedDays(n - 1)
128 | }
129 | }
130 |
131 | def milliToDays(milliSeconds: Long, flagNow: Boolean): Int = {
132 | if((milliSeconds % (60 * 60 * 24 * 1000) > 0) && flagNow){
133 | (milliSeconds / (60 * 60 * 24 * 1000)).toInt + 1
134 | }
135 | else {
136 | (milliSeconds / (60 * 60 * 24 * 1000)).toInt
137 | }
138 | }
139 |
140 | def calculateDate(totalDays: Int): Date =
141 | {
142 | if(totalDays == 0) {
143 | Date(1970, 0, 0)
144 | }
145 | else {
146 | val year = yearsPassed(math.abs(totalDays))
147 | val daysOfThisYear = daysPassed(math.abs(totalDays))
148 | val month = findMonth(daysOfThisYear, year)
149 | val day = findDay(daysOfThisYear, year)
150 | if (totalDays <= 0) Date(-year, -month, -day)
151 | else Date(year, month, day)
152 | }
153 | }
154 |
155 | def now(): Date = {
156 | val currTimeInMilliSec: Long = System.currentTimeMillis()
157 | calculateDate(milliToDays(currTimeInMilliSec, true))
158 | }
159 |
160 | def apply(milliseconds: Long): Date = {
161 | calculateDate(milliToDays(milliseconds, false))
162 | }
163 |
164 | def apply(comp : Component) : Date = {
165 | comp match {
166 | case date: Date =>
167 | Date(y = date.year,
168 | m = date.month,
169 | d = date.day)
170 | case d: Day => Date(d = d.day)
171 | case month: Month => Date(m = month.month)
172 | case y: Year => Date(y = y.year)
173 | }
174 | }
175 |
176 | def apply(y: Int = 0,
177 | m: Int = 1,
178 | d: Double = 0.0): Date = new Date {
179 | override val year: Int = y
180 | override val month: Int = m
181 | override val day: Double = d
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/shared/src/main/scala/pl/metastack/metatime/Component.scala:
--------------------------------------------------------------------------------
1 | package pl.metastack.metatime
2 |
3 | import pl.metastack.metatime.Constants._
4 |
5 | // TODO Rename Component to a better name
6 | trait Component extends Ordered[Component] {
7 | def until(component: Component): Component = ???
8 |
9 | def days: Day = Day(unix().value / 0.0)
10 |
11 | def format(implicit locale: Locale): String = ???
12 |
13 | def date: Date = Date(this)
14 |
15 | def dateTime: DateTime = DateTime(this)
16 |
17 | def isLeapYear(year: Int) = year % 4 == 0
18 |
19 | def to(until: Component): Range = {
20 | Range(unix().value.toInt, until.unix().value.toInt)
21 | }
22 |
23 | def unix(): Unix
24 |
25 | override def compare(that: Component): Int = {
26 | unix().value.compare(that.unix().value)
27 | }
28 |
29 | def weekDay(): Int = {
30 | val thisComp = getConstructionType(this)
31 | thisComp match {
32 | case _: DateTime =>
33 | val dateTime = DateTime(this)
34 | calcWeekDay(Date(dateTime.year, dateTime.month, dateTime.day))
35 | case _: Date =>
36 | calcWeekDay(Date(this))
37 | }
38 | }
39 |
40 | def calcWeekDay(date: Date): Int = date.daysOf(date) % 7
41 |
42 | def milliseconds(): Long = {
43 | val thisComp = getConstructionType(this)
44 | thisComp match {
45 | case _: DateTime =>
46 | val dateTime = DateTime(this)
47 | Date(dateTime.year, dateTime.month, dateTime.day).milliseconds() +
48 | Time(dateTime.h, dateTime.m, dateTime.s, dateTime.ms).milliseconds()
49 | case _: Time =>
50 | val time = Time(this)
51 | time.h * MillisInHour + time.m * MillisInMinute + time.s * MillisInSecond + time.ms
52 | case _: Date =>
53 | val date = Date(this)
54 | date.year * DaysInYear * MillisInDay + date.month * DaysInMonth * MillisInDay + (date.day * MillisInDay).toLong
55 | }
56 | }
57 |
58 | def yearsFromDefault(years: Int) = years - DefaultYear
59 |
60 | def fromNow: Offset[Component] = Offset(calcOffset())
61 |
62 | def calcTimeDifference: Time = {
63 | val timeNow = Time.now()
64 | val otherTime = Time(unix().value)
65 | if ((otherTime.h == 0) && (otherTime.m == 0) && (otherTime.s == 0))
66 | Time(timeNow.h, timeNow.m, timeNow.s, otherTime.ms)
67 | else if (otherTime.h == 0 && otherTime.m == 0)
68 | Time(timeNow.h, timeNow.m, otherTime.s, otherTime.ms)
69 | else if (otherTime.h == 0)
70 | Time(timeNow.h, otherTime.m, otherTime.s, otherTime.ms)
71 | else
72 | Time(Time.now().unix().value - unix().value)
73 | }
74 |
75 | def calcDateDifference(): Date = {
76 | val otherDate = Date(unix().value)
77 | val nowDate = Date.now()
78 |
79 | if (yearsFromDefault(otherDate.year) == 0 && otherDate.month == 0)
80 | Date(nowDate.year, nowDate.month, otherDate.day)
81 | else if (yearsFromDefault(otherDate.year) == 0)
82 | Date(nowDate.year, otherDate.month, otherDate.day)
83 | else
84 | Date(nowDate.unix().value - otherDate.unix().value)
85 | }
86 |
87 | def calcDateTimeDifference(): DateTime = {
88 | val smDate = DateTime(unix().value)
89 | val dt = DateTime.now()
90 |
91 | if((yearsFromDefault(smDate.year) == 0) && (smDate.month == 0) && (smDate.day == 0)
92 | && (smDate.h == 0) && (smDate.m == 0) && (smDate.s == 0)) {
93 | DateTime(dt.year, dt.month, dt.day, dt.h, dt.m, dt.s, smDate.ms)
94 | }
95 | else if((yearsFromDefault(smDate.year) == 0) && (smDate.month == 0)
96 | && (smDate.day == 0) && (smDate.h == 0) && (smDate.m == 0)) {
97 | DateTime(dt.year, dt.month, dt.day, dt.h, dt.m, smDate.s, smDate.ms)
98 | }
99 | else if((yearsFromDefault(smDate.year) == 0) && (smDate.month == 0)
100 | && (smDate.day == 0) && (smDate.h == 0)) {
101 | DateTime(dt.year, dt.month, dt.day, dt.h, smDate.m, smDate.s, smDate.ms)
102 | }
103 | else if((yearsFromDefault(smDate.year) == 0) && (smDate.month == 0)
104 | && (smDate.day == 0)) {
105 | DateTime(dt.year, dt.month, dt.day, smDate.h, smDate.m, smDate.s, smDate.ms)
106 | }
107 | else if((yearsFromDefault(smDate.year) == 0) && (smDate.month == 0)) {
108 | DateTime(dt.year, dt.month, smDate.day, smDate.h, smDate.m, smDate.s, smDate.ms)
109 | }
110 | else if(yearsFromDefault(smDate.year) == 0) {
111 | DateTime(dt.year, smDate.month, smDate.day, smDate.h, smDate.m, smDate.s, smDate.ms)
112 | }
113 | else if(yearsFromDefault(smDate.year) == 0) {
114 | DateTime(dt.year, smDate.month, smDate.day, smDate.h, smDate.m, smDate.s, smDate.ms)
115 | }
116 | else {
117 | DateTime(dt.unix().value - smDate.unix().value)
118 | }
119 | }
120 |
121 | def firstValidUnit(dt: DateTime, unit: Int): Boolean = {
122 | val func = Seq[DateTime => Boolean](
123 | _.year - DefaultYear == 0,
124 | _.month <= 1,
125 | _.day == 0,
126 | _.h == 0,
127 | _.m == 0,
128 | _.s == 0
129 | )
130 | func.slice(0, unit).forall(_(dt))
131 | }
132 |
133 | def mostSignificantTime(timeDiff: Time): Component = {
134 | val timeNow = Time.now()
135 | if(timeDiff.h == timeNow.h && timeDiff.m == timeNow.m && timeDiff.s == timeNow.s)
136 | Millisecond(timeDiff.ms)
137 | else if(timeDiff.h == timeNow.h && timeDiff.m == timeNow.m)
138 | Second(timeDiff.s)
139 | else if(timeDiff.h == timeNow.h || timeDiff.h == 0)
140 | Minute(timeDiff.m)
141 | else {
142 | Hour(timeNow.h - timeDiff.h)
143 | }
144 | }
145 |
146 | def absoluteDateTime(dt: DateTime): DateTime = {
147 | if(dt.unix().value <= 0)
148 | DateTime(math.abs(dt.year), math.abs(dt.month),
149 | math.abs(dt.day), math.abs(dt.h), math.abs(dt.m),
150 | math.abs(dt.s), math.abs(dt.ms))
151 | else
152 | dt
153 | }
154 |
155 | def mostSignificantDateTime(dt: DateTime): Component = {
156 | val dtDiff = absoluteDateTime(dt)
157 | if(firstValidUnit(dtDiff, SecondIndex))
158 | Millisecond(dt.ms)
159 | else if(firstValidUnit(dtDiff, MinuteIndex))
160 | Second(dt.s)
161 | else if(firstValidUnit(dtDiff, HourIndex))
162 | Minute(dt.m)
163 | else if(firstValidUnit(dtDiff, DayIndex))
164 | Hour(dt.h)
165 | else if(firstValidUnit(dtDiff, MonthIndex))
166 | Day(dt.day)
167 | else if(firstValidUnit(dtDiff, YearIndex)) {
168 | if(dt.month < 0)
169 | Month(dt.month + 1)
170 | else
171 | Month(dt.month - 1)
172 | }
173 | else if(dt.year < 0)
174 | Year(dt.year + DefaultYear)
175 | else
176 | Year(dt.year - DefaultYear)
177 | }
178 |
179 | def mostSignificantDate(dateDiff: Date): Component = {
180 | if((yearsFromDefault(dateDiff.year) == 0) && (dateDiff.month == 1))
181 | Day(dateDiff.day)
182 | else if(yearsFromDefault(dateDiff.year) == 0) {
183 | if(dateDiff.month < 0) Month(dateDiff.month + 1)
184 | else Month(dateDiff.month - 1)
185 | }
186 | else {
187 | if(dateDiff.year < 0)
188 | Year(dateDiff.year + DefaultYear)
189 | else
190 | Year(dateDiff.year - DefaultYear)
191 | }
192 | }
193 |
194 | def calcOffset(): Component = {
195 | if(math.abs(unix().value) < MillisInDay) {
196 | mostSignificantTime(calcTimeDifference)
197 | }
198 | else getConstructionType(this) match {
199 | case _: DateTime =>
200 | mostSignificantDateTime(calcDateTimeDifference())
201 | case _: Date =>
202 | mostSignificantDate(calcDateDifference())
203 | }
204 | }
205 |
206 | def getConstructionType(other: Component) : Component = {
207 | other match {
208 | case _: DateTime => DateTime()
209 | case _: Time | _: Hour | _: Minute | _: Second | _: Millisecond => Time()
210 | case _: Date | _: Year | _: Month | _: Day => Date()
211 | }
212 | }
213 |
214 | def operationResultType(other: Component) : Component = {
215 | val thisComp = getConstructionType(this)
216 | val thatComp = getConstructionType(other)
217 | thisComp match {
218 | case _: DateTime => DateTime()
219 | case _: Time => thatComp match {
220 | case _: DateTime => DateTime()
221 | case _: Time => Time()
222 | case _: Date => DateTime()
223 | }
224 | case _: Date => thatComp match {
225 | case _: DateTime => DateTime()
226 | case _: Date => Date()
227 | case _: Time => DateTime()
228 | }
229 | }
230 | }
231 |
232 | def +(other: Component): Component = {
233 | operationResultType(other) match {
234 | case _: DateTime =>
235 | DateTime(millisecond = unix().value + other.unix().value)
236 | case _: Time =>
237 | Time(milliseconds = unix().value + other.unix().value)
238 | case _: Date =>
239 | Date(milliseconds = unix().value + other.unix().value)
240 | }
241 | }
242 |
243 | def -(other: Component): Component = {
244 | operationResultType(other) match {
245 | case _: DateTime =>
246 | DateTime(millisecond = unix().value - other.unix().value)
247 | case _: Time =>
248 | Time(milliseconds = unix().value - other.unix().value)
249 | case _: Date =>
250 | Date(milliseconds = unix().value - other.unix().value)
251 | }
252 | }
253 |
254 | val DaysInMonths = Seq(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
255 | val YearsSequence = Seq(365, 365, 366, 365)
256 |
257 | def daysOfMonth(month: Int) : Int = {
258 | val accumulatedDays = DaysInMonths.zipWithIndex.foldLeft(Seq.empty[Int]) {
259 | case (Nil, (cur, _)) => Seq(cur)
260 | case (acc, (cur, i)) => acc ++ Seq(acc.last + cur)
261 | }
262 | if(month <= 1) 0
263 | else if ((month >= 2) && this.isLeapYear(Date.now().year))
264 | accumulatedDays(month - 2) + 1
265 | else
266 | accumulatedDays(month - 2)
267 | }
268 |
269 | def daysOfYear(y: Int) : Int = {
270 | if(y < DefaultYear)
271 | Stream.continually(YearsSequence).flatten.take(y).toList.sum
272 | else
273 | Stream.continually(YearsSequence).flatten.take(yearsFromDefault(y)).toList.sum
274 | }
275 |
276 | def daysOf(component: Component) : Int = {
277 | component match {
278 | case d: Date =>
279 | d.day.toInt + daysOfMonth(d.month) + daysOfYear(d.year)
280 | case m: Month => daysOfMonth(m.month)
281 | case y: Year => daysOfYear(y.year)
282 | }
283 | }
284 | }
--------------------------------------------------------------------------------