Groovy TimeCategory bad millseconds addition - groovy

When try this sample code:
use(groovy.time.TimeCategory) {
800.millisecond + 300.millisecond
}
in groovy web console, I get a funny result:
0.1100 seconds
Does any one know why this happens or how to fix it?

That looks like a bug, the TimeDuration contains 1100 milliseconds, but when it prints it out, it converts it wrongly to seconds.
I've added it to the Groovy JIRA as a bug EDIT It's now marked as FIXED for versions 2.0.6, 1.8.9 and 2.1.0
In the mean time, I guess you'll need to do your own converter from TimeDuration to String :-/
Edit
You could do something like this (and there is probably a neater way of doing it)
groovy.time.TimeDuration.metaClass.normalize = { ->
def newdmap = ['days','hours','minutes','seconds','millis'].collectEntries {
[ (it):delegate."$it" ]
}.with { dmap ->
[millis:1000,seconds:60,minutes:60,hours:24,days:-1].inject( [ dur:[ days:0, hours:0, minutes:0, seconds:0, millis:0 ], roll:0 ] ) { val, field ->
val.dur."$field.key" = dmap."$field.key" + val.roll
val.roll = val.dur."$field.key".intdiv( field.value )
val.dur."$field.key" = field.value < 0 ?
val.dur."$field.key" :
val.dur."$field.key" % field.value
val
}.dur
}
new TimeDuration( newdmap.days, newdmap.hours, newdmap.minutes, newdmap.seconds, newdmap.millis )
}
That adds a normalize method to TimeDuration, so then doing:
use(groovy.time.TimeCategory) {
800.millisecond + 300.millisecond
}.normalize()
Shows 1.100 seconds
I haven't done a huge amount of testing on that method, so be warned it could do with some unit tests to make sure it doesn't fall over with other situations.

Related

Why does this String→List→Map conversion doesn't work in Groovy

I have input data of type
abc 12d
uy 76d
ce 12a
with the lines being separated by \n and the values by \t.
The data comes from a shell command:
brlist = 'mycommand'.execute().text
Then I want to get this into a map:
brmap = brlist.split("\n").collectEntries {
tkns = it.tokenize("\t")
[ (tkns[0]): tkns[1] ]
}
I also tried
brmap = brlist.split("\n").collectEntries {
it.tokenize("\t").with { [ (it[0]): it[1] ] }
}
Both ways gave the same result, which is a map with a single entry:
brmap.toString()
# prints "[abc:12d]"
Why does only the first line of the input data end up being in the map?
Your code works, which means the input String brlist isn't what you say it is...
Are you sure that's what you have? Try printing brlist, and then it inside collectEntries
As an aside, this does the same thing as your code:
brlist.split('\n')*.split('\t')*.toList().collectEntries()
Or you could try (incase it's spaces not tabs, this will expect both)
brlist.split('\n')*.split(/\s+/)*.toList().collectEntries()
This code works
// I use 4 spaces as tab.
def text = 'sh abc.sh'.execute().text.replaceAll(" " * 4, "\t")
brmap = text.split("\n").collectEntries {
tkns = it.tokenize("\t")
[(tkns[0]) : tkns[1]]
}
assert[abc:"12d", uy:"76d", ce:"12a"] == brmap
abc.sh
#!/bin/sh
echo "abc 12d"
echo "uy 76d"
echo "ce 12a
Also, I think your groovy code is correct. maybe your mycommand has some problem.
Ok, thanks for the hints, it is a bug in Jenkins: https://issues.jenkins-ci.org/browse/JENKINS-26481.
And it has been mentioned here before: Groovy .each only iterates one time

How do I subtract minutes from current time

I am trying to subtract 10 minutes from the current time, but I cant find the proper syntax in the documentation I have looked up
I have this so far
def deltaMinutes = 10
use(TimeCategory) {
def nowTime = new Date() - deltaMinutes.minutes
log.debug nowTime
}
and get this in the SmartThings IDE
java.lang.NullPointerException # line 53
Perhaps the IDE doesnt support this library? What would be the next best method for calculating this?
10.minutes.ago should give what you are looking for
use( groovy.time.TimeCategory ) {
println 10.minutes.ago
}

object has been rewritten In loop

I have code like this:
def updateSensor(List<String> boardIds, SensorShort sensor) {
for (String boardId : boardIds) {
println("Working on ${boardId} for ${sensor.sensorId}")
pool.submit({
println("[${Thread.currentThread().name}] Working on ${boardId} for ${sensor.sensorId}")
})
}
}
result of this code is:
Working on 400 for 11
Working on 100 for 11
Working on 101 for 11
Working on 300 for 11
[pool-4-thread-4] Working on 300 for 11
[pool-4-thread-1] Working on 300 for 11
[pool-4-thread-3] Working on 300 for 11
[pool-3-thread-1] Working on 300 for 11
but is wrong. It seams boardId object has been rewritten
You're submitting a job based on a non-final local variable, instead try:
def updateSensor(List<String> boardIds, SensorShort sensor) {
boardIds.each { String boardId ->
println("Working on ${boardId} for ${sensor.sensorId}")
pool.submit {
println("[${Thread.currentThread().name}] Working on ${boardId} for ${sensor.sensorId}")
}
}
}
The problem with the original code is that by the time the Closure is evaluated on a separate thread, the loop has finished, and the local boardId variable has the value of the last item in the list. So each job runs with the last element, rather than the element you required.
In Java, you would declare (indeed, Java would force you to declare the variable as final):
for( final String boardId : boardIds ) {
However, groovy does not have local final variables :-/
By doing it with boardIds.each, the local boardId variable inside the each closure has the value you need...
Hope that explains it?

groovy date: build list of all Fridays in 2011

How would one build a list of all Fridays in 2011, and allow for different date output, e.g. mm/dd/yyyy and yyyymmdd?
An alternative to ataylor's correct answer, you could do:
import static java.util.Calendar.*
def s = Date.parse("MM/dd/yyyy", "01/01/2011")
def e = Date.parse("MM/dd/yyyy", "12/31/2011")
(s..e).findAll {
it[ DAY_OF_WEEK ] == FRIDAY
}.each {
println it.format("MM/dd/yyyy")
}
I'd go with something like this:
use (groovy.time.TimeCategory) {
def d = Date.parse("MM/dd/yyyy", "01/01/2011")
while (d[Calendar.DAY_OF_WEEK] != Calendar.FRIDAY) {
d = d + 1.day
}
while (d[Calendar.YEAR] == 2011) {
println d.format("MM/dd/yyyy")
d = d + 1.week
}
}
Both ataylor's answer and Tim's answer are correct and very informative -- see comments if you're coming up to speed on Groovy.
I hate these situations where I'm supposed to pick one as the answer over the other. They are both right answers. Stackoverflow won't change their stuff so I can select both as correct, so I will merely point to both as being correct.

Groovy 1.7 changes "final"?

Just started learning Groovy, got the PragProg book "Programming Groovy" and had a problem compiling one of the sample scripts:
class GCar2 {
final miles = 0
def getMiles() {
println "getMiles called"
miles
}
def drive(dist) {
if (dist > 0) {
miles += dist
}
}
}
def car = new GCar2()
println "Miles: $car.miles"
println 'Driving'
car.drive(10)
println "Miles: $car.miles"
try {
print 'Can I see the miles? '
car.miles = 12
} catch (groovy.lang.ReadOnlyPropertyException ex) {
println ex.message
GroovyCar2.groovy: 20: cannnot access final field or property outside of constructor.
# line 20, column 35.
def drive(dist) { if (dist > 0) miles += dist }
^
Groovy versions prior to 1.7 do not give an error. I looked through whatever documentation I could find and did not see the issue discussed. What is going on here?
Aaron
I don't know much about Groovy 1.7, but it looks like a bug in earlier versions which has now been fixed - if a variable is final, you shouldn't be able to assign to it outside the constructor (or its declaration). If you can, what's the point of making it final?
I doubt that it'll stop you from reading it outside the constructor though...
You shouldn't be able to assign to a final variable in a normal method. It was a bug in groovy, fixed in 1.7.

Resources