You're on your way to the next level! Join the Kudos program to earn points and save your progress.
Level 1: Seed
25 / 150 points
Next: Root
1 badge earned
Challenges come and go, but your rewards stay with you. Do more to earn more!
What goes around comes around! Share the love by gifting kudos to your peers.
Keep earning points to reach the top of the leaderboard. It resets every quarter so you always have a chance!
Join now to unlock these features and more
Hello! I am new to Groove.
Please help me write the script Groove showing how long the task is in a specific status.
Only to display not the actual time - 24/7 A how working hours 5/8.
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.history.ChangeItemBean
def changeHistoryManager = ComponentAccessor.getChangeHistoryManager()
def inProgressName = "X"
List<Long> rt = [0L]
def changeItems = changeHistoryManager.getChangeItemsForField(issue, "status")
changeItems.reverse().each { ChangeItemBean item ->
def timeDiff = System.currentTimeMillis() - item.created.getTime()
if (item.fromString == inProgressName) {
rt << -timeDiff
}
if (item.toString == inProgressName) {
rt << timeDiff
}
}
def total = rt.sum() as Long
return (total / 1000 ) as long ?: 0L
PS. I am trying to configure the display of this functionality through (JMCF) Calculated (scripted) Duration Field
Hi @smorozov ,
you can use the following code, where you'll need to update the Status name (statusName), work hours (WORK_HOUR_START, WORK_HOUR_END), and time zone in which the work hours are defined (timeZone):
import com.atlassian.jira.issue.history.ChangeItemBean
import org.joda.time.DateTime
import org.joda.time.DateTimeZone
import java.sql.Timestamp
import java.time.*
import java.time.temporal.ChronoUnit
import java.util.stream.IntStream
final String statusName = "To Do" //adjust for the status you want
final List<ChangeItemBean> changeItems = issue.getFieldHistory("status")
if (changeItems.size() == 0) //issue status has never changed
if (issue.get("status").name == statusName)
return workingSecondsBetween(new Date().getTime(), issue.getCreated().getTime())
else
return null;
long totalTime = 0L;
Long startTime = null;
//iterate over changes
for (ChangeItemBean changeItemBean : changeItems) {
if (changeItemBean.getFromString() == statusName) {
if (startTime == null) //this must have been the starting status
startTime = issue.getCreated().getTime();
totalTime += workingSecondsBetween(changeItemBean.getCreated().getTime(), startTime);
startTime = null;
}
if (changeItemBean.getToString() == statusName) {
startTime = changeItemBean.getCreated().getTime();
}
}
if (startTime != null)
totalTime += workingSecondsBetween(new Date().getTime(), startTime); //we are currently in the requested status
return totalTime
public class WorkingMinutesCalculator {
private static final int WORK_HOUR_START = 8; //first working hour
private static final int WORK_HOUR_END = 16; //last working hour (work ends at the end of this hour)
private static final long SECONDS_PER_HOUR = 3600;
private static final long WORKING_HOURS_PER_DAY = WORK_HOUR_END - WORK_HOUR_START;
private static final long WORKING_SECONDS_PER_DAY = WORKING_HOURS_PER_DAY * SECONDS_PER_HOUR;
public int getWorkingSecondsSince(final Timestamp startTime) {
Timestamp now = Timestamp.from(Instant.now());
return getWorkingSeconds(startTime, now);
}
public int getWorkingSeconds(final Timestamp startTime, final Timestamp endTime) {
if (null == startTime || null == endTime) {
throw new IllegalStateException();
}
if (endTime.before(startTime)) {
return 0;
}
LocalDateTime from = startTime.toLocalDateTime();
LocalDateTime to = endTime.toLocalDateTime();
LocalDate fromDay = from.toLocalDate();
LocalDate toDay = to.toLocalDate();
int allDaysBetween = (int) (ChronoUnit.DAYS.between(fromDay, toDay) + 1);
long allWorkingSeconds = IntStream.range(0, allDaysBetween)
.filter{i -> isWorkingDay(from.plusDays(i))}
.count() * WORKING_SECONDS_PER_DAY ;
// from - working_day_from_start
long tailRedundantSeconds = 0;
if (isWorkingDay(from)) {
if (isWorkingHours(from)) {
tailRedundantSeconds = Duration.between(fromDay.atTime(WORK_HOUR_START, 0), from).toSeconds();
} else if (from.getHour() > WORK_HOUR_START) {
tailRedundantSeconds = WORKING_SECONDS_PER_DAY;
}
}
// working_day_end - to
long headRedundanSeconds = 0;
if (isWorkingDay(to)) {
if (isWorkingHours(to)) {
headRedundanSeconds = Duration.between(to, toDay.atTime(WORK_HOUR_END, 0)).toSeconds();
} else if (from.getHour() < WORK_HOUR_START) {
headRedundanSeconds = WORKING_SECONDS_PER_DAY;
}
}
return (int) (allWorkingSeconds - tailRedundantSeconds - headRedundanSeconds);
}
private boolean isWorkingDay(final LocalDateTime time) {
return time.getDayOfWeek().getValue() < DayOfWeek.SATURDAY.getValue();
}
private boolean isWorkingHours(final LocalDateTime time) {
int hour = time.getHour();
return WORK_HOUR_START <= hour && hour <= WORK_HOUR_END;
}
}
long workingSecondsBetween(long to, long from) {
//to and from are Unix time in the system time zone (not UTC)
final DateTimeZone timeZone = DateTimeZone.forID("America/Chicago") //adjust for the time zone you want
DateTime fromDt = new DateTime(from + (new Date(from)).getTimezoneOffset()*60000, timeZone)
DateTime toDt = new DateTime(to + (new Date(to)).getTimezoneOffset()*60000, timeZone)
return (new WorkingMinutesCalculator()).getWorkingSeconds(new Timestamp(fromDt.toLocalDateTime().getLocalMillis()), new Timestamp(toDt.toLocalDateTime().getLocalMillis()))
}
Hey @smorozov
You can check out this link, JMCF field rite ? there is a code in the answer for your case. You may have to check and modify if requried.
Regards,
Vishwas
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.