diff --git a/gradle.properties b/gradle.properties index 85dd964..3321ea6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ MORNY_ARCHIVE_NAME = morny-coeur MORNY_CODE_STORE = https://github.com/Eyre-S/Coeur-Morny-Cono MORNY_COMMIT_PATH = https://github.com/Eyre-S/Coeur-Morny-Cono/commit/%s -VERSION = 1.1.1-alpha1 +VERSION = 1.1.1-alpha2 USE_DELTA = false VERSION_DELTA = diff --git a/src/main/scala/cc/sukazyo/cono/morny/daemon/MedicationTimer.scala b/src/main/scala/cc/sukazyo/cono/morny/daemon/MedicationTimer.scala index 3c61872..25c181d 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/daemon/MedicationTimer.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/daemon/MedicationTimer.scala @@ -87,7 +87,7 @@ class MedicationTimer (using coeur: MornyCoeur) extends Thread { object MedicationTimer { @throws[IllegalArgumentException] - private[daemon] def calcNextRoutineTimestamp (baseTimeMillis: Long, zone: ZoneOffset, notifyAt: Set[Int]): Long = { + def calcNextRoutineTimestamp (baseTimeMillis: Long, zone: ZoneOffset, notifyAt: Set[Int]): Long = { if (notifyAt isEmpty) throw new IllegalArgumentException("notify time is not set") var time = LocalDateTime.ofEpochSecond( baseTimeMillis / 1000, ((baseTimeMillis % 1000) * 1000 * 1000) toInt, diff --git a/src/main/scala/cc/sukazyo/cono/morny/util/EpochDateTime.scala b/src/main/scala/cc/sukazyo/cono/morny/util/EpochDateTime.scala new file mode 100644 index 0000000..af102f3 --- /dev/null +++ b/src/main/scala/cc/sukazyo/cono/morny/util/EpochDateTime.scala @@ -0,0 +1,29 @@ +package cc.sukazyo.cono.morny.util + +import java.time.{LocalDateTime, ZoneOffset} +import java.time.format.DateTimeFormatter + +object EpochDateTime { + + type EpochMillis = Long + + object EpochMillis: + /** convert a localtime with timezone to epoch milliseconds + * + * @param time the local time in that timezone, should be formatted + * in [[DateTimeFormatter.ISO_DATE_TIME]] + * @param zone timezone of the localtime. + * cannot be "UTC" or "GMT" (use "Z" instead) + * @return the epoch millisecond the local time means. + */ + def apply (time: String, zone: String): EpochMillis = { + val formatter = DateTimeFormatter.ISO_DATE_TIME + val innerTime = LocalDateTime.parse(time, formatter) + val instant = innerTime.toInstant(ZoneOffset of zone) + instant.toEpochMilli + } + def apply (time_zone: (String, String)): EpochMillis = + time_zone match + case (time, zone) => apply(time, zone) + +} diff --git a/src/test/scala/cc/sukazyo/cono/morny/test/cc/sukazyo/cono/morny/event/OnQuestionMarkReplyTest.scala b/src/test/scala/cc/sukazyo/cono/morny/test/cc/sukazyo/cono/morny/bot/event/OnQuestionMarkReplyTest.scala similarity index 92% rename from src/test/scala/cc/sukazyo/cono/morny/test/cc/sukazyo/cono/morny/event/OnQuestionMarkReplyTest.scala rename to src/test/scala/cc/sukazyo/cono/morny/test/cc/sukazyo/cono/morny/bot/event/OnQuestionMarkReplyTest.scala index 119e1ab..3e23d57 100644 --- a/src/test/scala/cc/sukazyo/cono/morny/test/cc/sukazyo/cono/morny/event/OnQuestionMarkReplyTest.scala +++ b/src/test/scala/cc/sukazyo/cono/morny/test/cc/sukazyo/cono/morny/bot/event/OnQuestionMarkReplyTest.scala @@ -1,4 +1,4 @@ -package cc.sukazyo.cono.morny.test.cc.sukazyo.cono.morny.event +package cc.sukazyo.cono.morny.test.cc.sukazyo.cono.morny.bot.event import cc.sukazyo.cono.morny.bot.event.OnQuestionMarkReply import cc.sukazyo.cono.morny.test.MornyTests diff --git a/src/test/scala/cc/sukazyo/cono/morny/test/cc/sukazyo/cono/morny/daemon/MedicationTimerTest.scala b/src/test/scala/cc/sukazyo/cono/morny/test/cc/sukazyo/cono/morny/daemon/MedicationTimerTest.scala new file mode 100644 index 0000000..b6057b9 --- /dev/null +++ b/src/test/scala/cc/sukazyo/cono/morny/test/cc/sukazyo/cono/morny/daemon/MedicationTimerTest.scala @@ -0,0 +1,42 @@ +package cc.sukazyo.cono.morny.test.cc.sukazyo.cono.morny.daemon + +import cc.sukazyo.cono.morny.daemon.MedicationTimer +import cc.sukazyo.cono.morny.test.MornyTests +import cc.sukazyo.cono.morny.util.EpochDateTime.EpochMillis +import org.scalatest.prop.TableDrivenPropertyChecks + +import java.time.ZoneOffset + +class MedicationTimerTest extends MornyTests with TableDrivenPropertyChecks { + + "on calculating next notify time :" - { + + val examples = Table( + ("current" , "notifyAt" , "useTimezone", "nextNotifyTime"), + (("2023-10-09T23:54:10.000","+8"), Set(1, 5, 19) , "+8" , ("2023-10-10T01:00:00.000","+8")), + (("2022-11-13T13:14:15.000","+2"), Set(7, 19, 21) , "+2" , ("2022-11-13T19:00:00.000","+2")), + (("2022-11-13T13:14:35.000","+8"), Set(7, 19, 21) , "+8" , ("2022-11-13T19:00:00" ,"+8")), + (("2022-11-13T13:14:35.174","+2"), Set(7, 19, 21) , "+2" , ("2022-11-13T19:00:00" ,"+2")), + (("1998-02-01T08:14:35.871","+8"), Set(7, 19, 21) , "+8" , ("1998-02-01T19:00:00" ,"+8")), + (("2022-11-13T00:00:00.000","-1"), Set(7, 19, 21) , "-1" , ("2022-11-13T07:00:00" ,"-1")), + (("2022-11-21T19:00:00.000","+0"), Set(7, 19, 21) , "+0" , ("2022-11-21T21:00:00" ,"+0")), + (("2022-12-31T21:00:00.000","+0"), Set(7, 19, 21) , "+0" , ("2023-01-01T07:00:00" ,"+0")), + (("2125-11-18T23:45:27.062","+0"), Set(7, 19, 21) , "+0" , ("2125-11-19T07:00:00" ,"+0")) + ) + + forAll (examples) { (current, notifyAt, useTimezone, nextNotifyTime) => + val _curr = EpochMillis(current) + val _tz = ZoneOffset of useTimezone + val _next = EpochMillis(nextNotifyTime) + + s"at time [$_curr], and need to be notify at hours ${notifyAt.mkString(",")} with $_tz :" - { + s"next notify should at time [$_curr]" in { + MedicationTimer.calcNextRoutineTimestamp(_curr, _tz, notifyAt) shouldEqual _next + } + } + + } + + } + +} diff --git a/src/test/scala/cc/sukazyo/cono/morny/test/utils/EpochDateTimeTest.scala b/src/test/scala/cc/sukazyo/cono/morny/test/utils/EpochDateTimeTest.scala new file mode 100644 index 0000000..a0d218a --- /dev/null +++ b/src/test/scala/cc/sukazyo/cono/morny/test/utils/EpochDateTimeTest.scala @@ -0,0 +1,31 @@ +package cc.sukazyo.cono.morny.test.utils + +import cc.sukazyo.cono.morny.test.MornyTests +import cc.sukazyo.cono.morny.util.EpochDateTime.EpochMillis +import org.scalatest.prop.TableDrivenPropertyChecks + +class EpochDateTimeTest extends MornyTests with TableDrivenPropertyChecks { + + "while converting date-time string to time-millis : " - { + + "while using ISO-Offset-Date-Time : " - { + + val examples = Table( + ("str" , "zone" , "millis"), + ("2011-12-03T10:15:30" , "+01:00", 1322903730000L), + ("2023-10-10T06:12:44.857", "Z" , 1696918364857L), + ("2023-10-10T02:12:44.857", "-04:00", 1696918364857L), + ("2023-10-10T14:12:44.857", "+8" , 1696918364857L), + ("1938-04-24T22:13:20" , "Z" ,-1000000000000L), + ) + + forAll(examples) { (str, zone, millis) => + s"$str should be epoch time millis $millis" in: + EpochMillis(str, zone) shouldEqual millis + } + + } + + } + +}