Cron timer testing with psuedo clock in Drools Fusion? - drools

I am trying to test the firing of a rule based on a cron timer using the pseudo clock in Drools Fusion 5.5. I want the rule to fire everyday at 1am:
rule "CALCULATING DATE FOR NEXT DAY"
timer (cron:00 00 01 * * ?)
no-loop
when
$summary: FxSummary(sameDayCcyFlag == false)
then
BusinessDayUtil b = new BusinessDayUtil();
modify($summary) {
setSettlementDate(b);
}
end
I then do the following in my test case:
PseudoClockScheduler timeService = ( PseudoClockScheduler ) ksession.getSessionClock();
DateFormat df = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSSZ" );
Date date = df.parse( "2014-01-16T01:00:00.000-0000" );
Summary sum = new Summary("YEN").setSameDayCcyFlag(false);
ksession.fireAllRules();
timeService.advanceTime( date.getTime(), TimeUnit.MILLISECONDS );
ksession.fireAllRules();
It doesn't seem to do anything...no indication that the timer fired or anything. I've also tried to insert a date at say 12:59:50 and advanced the clock 10sec. Also, fireUntilHalt to have the engine running, etc. Nothing seems to work. Am I using this correctly? Does the pseudo clock work with timers? Also, does it fire "missed" timers if I advance the clock past a timer that was supposed to fire?

Think about how cron can be implemented. The basic function is timer, and this works like ye olde kitchen's egg-timer: at one point in time you wind it up, and then it'll ring 4 or 5 minutes later. Thus, for the next cron ring of the bell, the Cook will have to look at the clock and calculate the interval to the indicated point in time.
You'll have to let the Cook look at the clock some time before the next 1:00am, say, around midnight. The code goes something like this, with advance() overloaded with Date and long to advance the pseudo-clock:
date = df.parse( "2014-01-15T00:00:00.000-0000" ); // Note: midnight
advance( date );
kSession.fireAllRules(); // (Ah, ring in one hour!)
advance( 1000*60*60 );
kSession.fireAllRules(); // Ring!
advance( 24*1000*60*60 );
kSession.fireAllRules(); // Ring!
The postman only rings twice ;-)

Related

How to calculate the next minute and next 5 minute intevals given a ZonedDateTime

I have a instance of a ZonedDatetime.
ZonedDateTime.now(ZoneId.of("America/New_York"))
I basically need a function that will take an instance of a ZonedDateTime and return the next 1 minute and 5 minute values.
So if the current time is:
2021-10-24T19:46:10.649817
The next minute will be 19:47:00 and the next 5 minute will be 19:50:00
The next 5 minute interval is always like:
1:00
1:05
1:10
1:15
1:20
1:25
...
1:50
1:55
2:00
i.e. the next 5 minute interval is not based on exactly 5 minutes from now, but rather the next 5 minutes based on starting from the beginning of the hour. Same goes for the next 1 minute interval in the future.
def nextIntervals(zdt: ZonedDateTime): (ZonedDateTime, ZonedDateTime) = {
???
}
It is fairly simple to do so without hardcoding the values. Unfortunately I'm not familiar with scala so I'll give you some pseudo code, I believe you'll be able to easily translate it.
nextIntervals(zdt) {
timestamp = zdt.toUnixTimestamp();
return [
new ZonedDateTime(timestamp + (60 - timestamp % 60)),
new ZonedDateTime(timestamp + (300 - timestamp % 300))
]
}
The above code assumes that ZonedDateTime can be instantiated by giving it a unix timestamp, measured in seconds. And also that it can be converted to a unix timestamp.
The idea is pretty simple: the remainder of the modulus will be the time that has elapsed since the last required period (in your case 1 minute or 5 minutes). Take that away from the period itself and you have the time that's left until the next period. Add that to the current time and you have the exact datetime.
Edit:
Here's a working javascript example
function nextIntervals(date) {
let t = date.getTime();
return [
60e3,
300e3,
].map(i => new Date(t + i - t % i));
}
console.log(nextIntervals(new Date));
You can use the following functions to meet your requirements:
ZonedDateTime#plusMinutes
ZonedDateTime#minusMinutes
ZonedDateTime#truncatedTo
Demo:
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
public class Main {
public static void main(String[] args) {
ZonedDateTime now = ZonedDateTime.now(ZoneId.of("America/New_York"));
ZonedDateTime nextMinute = now.plusMinutes(1).truncatedTo(ChronoUnit.MINUTES);
ZonedDateTime nextMultipleOfFiveMin = now.truncatedTo(ChronoUnit.MINUTES)
.minusMinutes(now.getMinute() % 5)
.plusMinutes(5);
System.out.println(now);
System.out.println(nextMinute);
System.out.println(nextMultipleOfFiveMin);
}
}
Output from a sample run:
2021-10-25T16:59:22.662943-04:00[America/New_York]
2021-10-25T17:00-04:00[America/New_York]
2021-10-25T17:00-04:00[America/New_York]
Output from another sample run after a while:
2021-10-25T17:05:09.596952-04:00[America/New_York]
2021-10-25T17:06-04:00[America/New_York]
2021-10-25T17:10-04:00[America/New_York]
ONLINE DEMO
Learn more about the modern Date-Time API from Trail: Date Time. Check this answer and this answer to learn how to use java.time API with JDBC.
Note: The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*.
* If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring. Note that Android 8.0 Oreo already provides support for java.time.
We do need a little bit of hand-coded math to handle the 5-minute interval case. Excuse my Java syntax.
ZonedDateTime now = ZonedDateTime.now(ZoneId.of("America/New_York"));
System.out.println("Now: " + now);
// Truncate to the previous 5 minutes
ZonedDateTime zdt = now.truncatedTo(ChronoUnit.MINUTES);
zdt = zdt.withMinute(zdt.getMinute() / 5 * 5);
for (int i = 0; i <= 12; i++) {
zdt = zdt.plusMinutes(5);
System.out.println(zdt);
}
Example output:
Now: 2021-10-25T15:23:31.357567-04:00[America/New_York]
2021-10-25T15:25-04:00[America/New_York]
2021-10-25T15:30-04:00[America/New_York]
2021-10-25T15:35-04:00[America/New_York]
2021-10-25T15:40-04:00[America/New_York]
2021-10-25T15:45-04:00[America/New_York]
2021-10-25T15:50-04:00[America/New_York]
2021-10-25T15:55-04:00[America/New_York]
2021-10-25T16:00-04:00[America/New_York]
2021-10-25T16:05-04:00[America/New_York]
2021-10-25T16:10-04:00[America/New_York]
2021-10-25T16:15-04:00[America/New_York]
2021-10-25T16:20-04:00[America/New_York]
2021-10-25T16:25-04:00[America/New_York]
The trick to truncate to a whole multiple of 5 minutes is to divide by 5, obtain a whole number and discard any remainder, and multiply by 5 again.
The 1-minute interval is similar, only a bit simpler: we don’t need to do any math ourselves, java.time takes care of it all.

Timer drags a bit when too much is happening

I'm working on a timer that needs to do some calculations and run some functions at a certain interval. I'm trying to keep the interval as big as possible, but I need it to be kind of fine grained.
Here is the periodic timer with some of the stuff that needs to happen.
So as you can see, every second (the milliseconds passed % 1000 == 0) it will do some stuff if some conditions are met. But also every 10 milliseconds I need to check some stuff.
It seems this is a bit too much, and after running the timer for 2 minutes it already drags 1 second behind. I guess I'm approaching this the wrong way. Could/should I somehow put all that logic in a function that just runs async so the timer can just keep going.
It's not the end of the world if the timer display drags for a few milliseconds every now and then, if it catches up later. But now the whole timer just drags.
_timer = Timer.periodic(Duration(milliseconds: 10), (timer) {
passedMilliseconds = passedMilliseconds + 10;
// METRONOME BEEP AND BLINK
if (passedMilliseconds % currentTimerSettingsObject.milliSecondDivider == 0) {
_playMetronomeBeep();
_startMetronomeBlink();
}
// ONE SECOND
if (passedMilliseconds % 1000 == 0) {
secondsDuration--;
// COUNTDOWN
if (secondsDuration < currentTimerSettingsObject.countDown + 1) {
_player.play('sounds/beep.mp3');
}
// SET COUNTDOWN START VALUES
if (secondsDuration == currentTimerSettingsObject.countDown) {
isCountdown = true;
}
notifyListeners();
}
// TIME IS UP
if (secondsDuration < 0) {
switchToNextTimer();
notifyListeners();
}
});
}
You cannot rely on a timer to deliver events exactly on time. You need to use a more exact method than simply incrementing a counter by 10 on every tick. One example would be to start a Stopwatch before the timer and then (knowing that your ticks will only be on approximately 10ms intervals) read stopwatch.elapsedMilliseconds and base your decisions on that.
You will need to change your logic a bit. For example, you want to know when you pass a 1 second boundary. Previously, with your exact increments of 10 you knew you would eventually reach a round 1000. Now, you might see 995 followed by 1006, and need to deduce that you've crossed a second boundary to run your per second logic.

Coroutine Countdown Timer being extremely slow

Hey guys so just as a disclaimer I'm relatively new to programming so if I'm making some super obvious mistake please go easy on me
So I'm trying to create a higher customizable Countdown timer for my game, and I want it to be able to be accurate to 0.01 Seconds. I decided I would use the Coroutine method for creating my timer instead of the delta-time one I have seen a couple of times, thinking that this would be a more efficient approach. My game is not very intensive and thus easily runs on hundreds of frames per second, so I thought that using Waitforseconds(0.01) is going to work better because it only needs to be called 100 times every second rather than multiple hundreds. however, I have come into a major issue with my timer. It is EXTREMELY slow. I ran the countdown timer on google and mine side by side starting at 25 seconds and it beat mine out by ten seconds. I even tried adding a artifical delay thinking the waitforseconds function was overshooting, so I would have the time tick down 0.01 seconds when a bit less then that had passed, but my results ended up being sort of inconsistent. Here is my code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class TimerScript : MonoBehaviour
{
public Text textDisplay;
private double secondsLeft = 30;
public bool takingAway = false;
private string Texttodisplay;
public int Milisecondsdigits = 2;
void Start()
{
textDisplay = GetComponent<Text>();
Texttodisplay = "00:" + secondsLeft;
if(Milisecondsdigits == 0)
{
Milisecondsdigits = -1;
}
}
void Update()
{
if (takingAway == false && secondsLeft > 0)
{
StopAllCoroutines();
StartCoroutine(TimerTake());
}
if(Texttodisplay.Length > 8 - (Mathf.Abs(Milisecondsdigits -2)))
{
Texttodisplay = Texttodisplay.Substring(0,8- (Mathf.Abs(Milisecondsdigits -2)));
}
textDisplay.text = Texttodisplay;
}
IEnumerator TimerTake()
{
takingAway = true;
yield return new WaitForSeconds(0.01f);
secondsLeft -= 0.01;
if(secondsLeft < 10)
{
Texttodisplay = "00:0" + secondsLeft;
}
else
{
Texttodisplay = "00:" + secondsLeft;
}
takingAway = false;
}
}
could somebody please let me know how I could cause this to become more accurate or why it's acting extremely inaccurate currently :/
Coroutine events like WaitForSeconds trigger at a defined point in Unity's event cycle, which takes place after Update() is processed (see
https://docs.unity3d.com/Manual/ExecutionOrder.html). This defined execution point might not line up exactly with the timer delay. This means it may wait longer than you want it to.
In your example, you tell it to wait for 0.01 seconds. Let's say that you are running a game at 30 frames per second. The frame time for 30 frames per second is 1/30 seconds, or approximately 0.0333 seconds. WaitForSeconds will then wait for the next frame, and 0.0333 seconds passes until the next frame. Then the next WaitForSeconds event cycle, it sees that the delay has passed and triggers, but you actually waited over 3 times as long as you wanted to because of the delay between event cycles. Since your code assumes that WaitForSeconds had only waited for 0.01 seconds, you will end up waiting longer than you intended to. This normally doesn't matter in a lot of applications, but when accumulating with frequent short delays it certainly does.
To solve this, you have two choices:
Accumulate time manually using Time.deltaTime in Update().
Coroutines likely check their completion status per frame in a
similar way in the yield WaitForSeconds event. If coroutines
check if they need to continue every frame, doing this manually with
Time.deltaTime might not be any less efficient at all than a
coroutine. You will have to benchmark to find out, because coroutines being more efficient isn't a safe assumption.
Use Time.time (or Time.realtimeSinceStartup if you want it unscaled) to measure the actual span of time that elapsed after the WaitForSeconds trigger, and use that as what you subtract from your remaining time.
There is also an additional consideration here that if you want your
text display to update at specific regular intervals, you will want
to dynamically adjust what value you pass into WaitForSeconds to
compensate for its drift.
Do you try to do it without Corroutine in a fixed update? Fixed update refresh every 0.02 seconds by default but you can settup to run in 0.01 seconds in Edit > Settings > Time > Fixed Timestep.
Replace the corroutine with a function in FixedUpdate
There is a link with better explation how works fixedupdate.
FixedUpdate Unity

Getting time range between the first day of current week and current time JDK 8

I can easilly calculate time period between the first day of month and current time:
/**
* Returns the time range between the first day of month and current time in milliseconds.
*
* #param zoneId time zone ID.
* #return a {#code long} array, where at index: 0 - the first day of month midnight time; 1 - current time.
*/
public static long[] monthDateRange(ZoneId zoneId) {
long[] toReturn = new long[2];
ZonedDateTime nowZdt = LocalDateTime.now().atZone(zoneId);
ZonedDateTime startZdt = nowZdt.withDayOfMonth(1);
toReturn[0] = startZdt.toInstant().toEpochMilli();
toReturn[1] = nowZdt.toInstant().toEpochMilli();
return toReturn;
}
But how to start counting at the first day (midnight) of current week?
tl;dr
ZonedDateTime
.now( ZoneId.of( "Asia/Kolkata" ) ) // Current moment in a particular time zone.
.toLocalDate() // Extract date-only value, losing the time-of-day and time zone components.
.with( TemporalAdjusters.previousOrSame( DayOfWeek.SUNDAY ) ) // Move to another day-of-week, or same date if this is the desired day-of-week.
.atStartOfDay( ZoneId.of( "Asia/Kolkata" ) ) // Determine the first moment of the day. Do *not* assume this time-of-day is 00:00:00 as anomalies such as Daylight Saving Time (DST) may mean otherwise such as 01:00:00.
.toInstant() // Adjust into UTC, same moment, same point on the timeline, but viewed through the lens of UTC time zone.
.toEpochMilli() // Extract a count-from-epoch in milliseconds. I do *not* recommend tracking date-time this way, but the Question requires this number.
Details
The Answer by Gruodis is good, but here's an alternative that is a bit more direct and flexible.
Get current moment as a ZonedDateTime.
ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime now = ZonedDateTime.now( z ) ;
TemporalAdjuster
The TemporalAdjuster interface lets you manipulate a date-time value to get a fresh date-time value. The TemporalAdjusters class (note plural s) provides several handy implementations. Use the DayOfWeek enum to specify what day you consider to be the first day of the week.
DayOfWeek dowStartOfWeek = DayOfWeek.MONDAY ;
LocalDate weekStartDate = now.toLocalDate().with( TemporalAdjusters.previousOrSame( DayOfWeek.MONDAY ) ) ;
ZonedDateTime start = weekStartDate.atStartOfDay( z ) ; // Determine first moment of the day. Note: *not* always 00:00:00.
See this code run live at IdeOne.com.
2017-08-21T00:00+12:00[Pacific/Auckland]
2017-08-21T08:44:46.439+12:00[Pacific/Auckland]
Span of time
To report your span of time, pou could indeed extract a count-from-epoch of whole seconds, if required.
long epochSeconds = start.toEpochSecond() ;
Or extract milliseconds via Instant.
long epochMillis = start.toInstant().toEpochMilli() ;
But keep in mind that both those numbers truncate any further fractional second, as the java.time types resolve to nanoseconds.
Besides truncation, there are other reasons to avoid tracking date-time as a count-from-epoch. Since such values are meaningless to the human eye, debugging is much more difficult and faulty data may escape your notice. Also, you may assume the epoch is 1970-01-01T00:00:00Z, but there are at least another couple dozen epochs is use by common software systems. Yet another problem is ambiguity over the granularity of the count, where some systems use whole seconds, others use milliseconds, others use microseconds, others nanoseconds, and still others use other resolutions.
Interval
So instead of returning mere long integer numbers, I suggest returning an object. A pair of Instant objects work, which is what is used by the Interval class in the ThreeTen-Extra project. That class has several very handy methods I expect the calling code may find useful such as contains, encloses, abuts, overlaps, span, isEmpty, and more.
org.threeten.extra.Interval interval = Interval.of( start.toInstant() , now.toInstant() ) ;
You can apply a time zone to view either the beginning or ending through the lens of a region’s own wall-clock time.
ZonedDateTime zdtStart = interval.getStart().atZone( z ); // Or `getEnd()`.
The solution:
/**
* Returns the time range between the first day of current week midnight and current time in milliseconds.
*
* #param zoneId time zone ID.
* #return a {#code long} array, where at index: 0 - the first day of current week midnight time; 1 - current time.
*/
public static long[] monthDateRange(ZoneId zoneId) {
long[] toReturn = new long[2];
//ZonedDateTime nowZdt = LocalDateTime.now().atZone(zoneId);
ZonedDateTime nowZdt = ZonedDateTime.now(zoneId);//As suggested by Basil Bourque (tested).
//ZonedDateTime startZdt = nowZdt.with(ChronoField.DAY_OF_WEEK, 1);
ZonedDateTime startZdt = nowZdt.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));//As suggested by Basil Bourque (tested).
startZdt = startZdt.toLocalDate ().atStartOfDay(zoneId);
toReturn[0] = startZdt.toInstant().toEpochMilli();
toReturn[1] = nowZdt.toInstant().toEpochMilli();
return toReturn;
}
See this code run live at IdeOne.com.

Duration to TemporalUnits

How do I convert a duration to a set of temporal units? I have a duration, and I want to divide that into for instance whole years, whole days and fractional seconds.
I have found PeriodFormatterBuilder for JodaTime, but it seems to facilitate printing as well as division into units, i would just like the division.
I am not sure if you speak about the new java.time-package of Java-8 or about Joda-Time so I try to present solutions for both libraries.
Most important aspect however is that you cannot divide a Duration defined in seconds into years, months etc. in a self-consistent manner because month-based units vary in length of seconds and days. At least not possible without any trick.
Best you can do in this case is to use a reference timestamp in order to recalculate the duration you have. That means you add your duration to the reference timestamp and then evaluate the new duration between the old reference timestamp and the result in years, months, days etc. This is also called normalization and has nothing to do with printing/formatting.
Java-8:
Duration dur = Duration.ofSeconds(5000001); // example
LocalDateTime ref = LocalDateTime.now(); // reference timestamp
LocalDateTime end = ref.plus(dur);
System.out.println(ref);
System.out.println(end);
// normalize first the calendrical part
LocalDateTime ldt = ref;
long years = ChronoUnit.YEARS.between(ldt, end);
// find the months part
ldt = ldt.plus(years, ChronoUnit.YEARS);
long months = ChronoUnit.MONTHS.between(ldt, end);
// find the days part
ldt = ldt.plus(months, ChronoUnit.MONTHS);
long days = ChronoUnit.DAYS.between(ldt, end);
// find the hours part
ldt = ldt.plus(days, ChronoUnit.DAYS);
long hours = ChronoUnit.HOURS.between(ldt, end);
// find the minutes part
ldt = ldt.plus(hours, ChronoUnit.HOURS);
long minutes = ChronoUnit.MINUTES.between(ldt, end);
// find the seconds part
ldt = ldt.plus(minutes, ChronoUnit.MINUTES);
long seconds = ChronoUnit.SECONDS.between(ldt, end);
// print the new normalized duration in ISO-8601-format
System.out.println(
String.format("P%1$dY%2$dM%3$dDT%4$dH%5$dM%6$dS", years, months, days, hours, minutes, seconds));
// example output
// 2015-03-17T12:54:07.943
// 2015-05-14T09:47:28.943
// P0Y1M26DT20H53M21S
Compared with old JDK pre 8 this can be considered as much better because at least elementary methods for calculation of a duration in one given unit are offered. But a general duration type for handling all units spanning from years to seconds is completely missing. And the best duration formatter I could find is just java.util.Formatter.
Joda-Time
That is the second-best Java library when duration handling is needed, in most details better than Java-8 on this area. Joda-Time indeed offers a duration type spanning from years to seconds (and millis) called Period. See here the much simpler solution:
Duration dur = new Duration(5000001 * 1000L); // in milliseconds
LocalDateTime ref = new LocalDateTime(); // reference timestamp
LocalDateTime end = ref.plus(dur);
// construct normalized duration
PeriodType type = PeriodType.yearMonthDayTime().withMillisRemoved();
Period p = new Period(ref, end, type);
// print the new normalized duration
System.out.println(p); // P1M26DT20H53M21S
Small note: I have left out fractional seconds (in Joda-Time limited to milliseconds, in Java-8 up to nanoseconds) in given examples. It is easy to enhance the examples if you really have need for this precision.