Rounding timestamp to the nearest 30 seconds - kdb

My table is as follows:
t: ([]dt: 2021.10.25T09:30:28 2021.10.25T09:30:32;price:9.99 10.00)
I wish to round the timestamp to the nearest 30sec mark.
I tried using xbar like so:
update roundedDt: 30 xbar dt.second from t
However it seems to have floored the results.
The desired result should be 09:30:30 for both rows.
How can one round to the nearest 30 second mark?

Jonathon's answer is the most flexible for modifying the rounding for not just seconds specifically but an alternative simple solution for just seconds would be to offset by 15:
q)update roundedDt:30 xbar 15+dt.second from t
dt price roundedDt
---------------------------------------
2021.10.25T09:30:28.000 9.99 09:30:30
2021.10.25T09:30:32.000 10 09:30:30
Edit: If you want the full dateTime rounded, I would convert it to timestamp as easy to work with and adjust my offset/xbar to match.
q)update roundedDt:30000000000 xbar 15000000000 + `timestamp$dt from t
dt price roundedDt
-----------------------------------------------------------
2021.10.25T09:30:28.000 9.99 2021.10.25D09:30:30.000000000
2021.10.25T09:30:32.000 10 2021.10.25D09:30:30.000000000
2020.10.25T23:59:59.000 9.99 2020.10.26D00:00:00.000000000
2020.10.26T00:00:01.000 10 2020.10.26D00:00:00.000000000

You can try something like this:
update roundedDt:?[(`ss$dt)within(0;14);`time$(`int$`time$dt)-1000*`ss$dt;
?[(`ss$dt)within(15;44);`time$30000+(`int$`time$dt)-1000*`ss$dt;`time$60000+(`int$`time$dt)-1000*`ss$dt]] from t

You could use a modified version of xbar that rounds to nearest int instead of flooring:
q)xbar2:{type[y]$x*"j"$y%x:$[16h=abs type x;"j"$x;x]}
q)update roundedDt:xbar2[30;dt.second] from t
dt price roundedDt
---------------------------------------
2021.10.25T09:30:28.000 9.99 09:30:30
2021.10.25T09:30:32.000 10 09:30:30
Note that because this function is defined in root namespace you must use bracket notation (xbar2[30;dt.second]). If you wish to use infix notation (30 xbar2 dt.second), you'll need to define the function in .q namespace i.e. .q.xbar2:{type[y]$x*"j"$y%x:$[16h=abs type x;"j"$x;x]}.
xbar2 is based on the original xbar, but where xbar uses div which has the effect of flooring the result, here % is used which will produce a float output and this is then cast to a long int which will round to the nearest integer.

What about this solutions:
/ x is your timestamp
/ y is the timebucket (in seconds)
.time.round:{
:"z"$+[`date$x;`time$1e3*y*`int$%[`time$x;y*1e3]];
};
As example, if you want to round at the nearest 30 seconds, you need to use this as follows:
ts1:2020.10.30T10:32:35
.time.round[ts1;30]
In your case, simply type:
t[`round_time]:{.time.round[x;30]} each t[`dt]
As a side note, some of the proposed solutions would round timestamps like 2020.10.25T23:59:59 and 2020.10.26T00:00:01 to 24:00:00 and 00:00:00 respectively, which is not what we would like I suppose.

Related

How do I round a datetime to the nearest minute in KDB?

I have an array of timestamps of the form:
2022.05.23T14:31:04.222
I'm trying to round them to the nearest date-minute, such that the output would be 2022.05.23T14:31:00.000.
I've various casts (e.g. `minute$t), and also 00:01:00 xbar t, but none of these worked.
How should it be done?
A few suggestions:
Don't use datetime datatype (aka .z.Z, aka type 15, aka "z"$) use timestamp datatype (aka .z.P, aka type 12, aka "p"$). The former is underpinned by a float (not good for bar'ing) while the latter is underpinned by long.
Your 00:01:00 xbar t approach almost works, you just need to use timestamps for t and datespan (aka .z.N, aka type 16, aka "n"$) for the xbar:
q)t:"p"$2022.05.23T14:31:04.222 2022.05.23T14:32:04.222;
q)0D00:01:00 xbar t
2022.05.23D14:31:00.000000000 2022.05.23D14:32:00.000000000
Add 30 seconds; cast to year and minute; sum.
q)t
2022.05.23T14:31:04.222
q)f:sum "du"$ ("v"$30)+ / composition
q)f t
2022.05.23D14:31:00.000000000
q)f t+"v"$40
2022.05.23D14:32:00.000000000
Here's one method (this will only round down though):
q){`datetime$(`date$x)+`minute$x}2022.05.23T14:31:04.222
2022.05.23T14:31:00.000
This should round up & down:
q){"z"$("d"$x)+{$[30=`ss$x;00:00:30+;]x}30 xbar"v"$x}2022.05.23T14:31:04.222
2022.05.23T14:31:00.000
q){"z"$("d"$x)+{$[30=`ss$x;00:00:30+;]x}30 xbar"v"$x}2022.05.23T14:31:34.222
2022.05.23T14:32:00.000

Make a list with the quarter and year based on a date range of quarters KDB+/Q

I have a list of date ranges for the past 8 quarters given by the below function
q) findLastYQuarters:{reverse("d"$(-3*til y)+m),'-1+"d"$(-3*-1+til y)+m:3 bar"m"$x}[currentDate;8]
q) findLastYQuarters
2020.01.01 2020.03.31
2020.04.01 2020.06.30
2020.07.01 2020.09.30
2020.10.01 2020.12.31
2021.01.01 2021.03.31
2021.04.01 2021.06.30
2021.07.01 2021.09.30
2021.10.01 2021.12.31
I need to produce a separate list that labels each item in this list by a specific format; the second list would need to be
1Q20,2Q20,3Q20,4Q20,1Q21,2Q21,3Q21,4Q21
This code needs to be able to run on it's own, so how can I take the first list as an input and produce the second list? I thought about casting the latter date in the range as a month and dividing it by 3 to get the quarter and extracting the year, but I couldn't figure out how to actually implement that. Any advice would be much appreciated!
I'm sure there are many ways to solve this, a function like f defined below would do the trick:
q)f:{`$string[1+mod[`month$d;12]%3],'"Q",/:string[`year$d:x[;0]][;2 3]}
q)lyq
2020.01.01 2020.03.31
2020.04.01 2020.06.30
2020.07.01 2020.09.30
2020.10.01 2020.12.31
2021.01.01 2021.03.31
2021.04.01 2021.06.30
2021.07.01 2021.09.30
2021.10.01 2021.12.31
q)f lyq
`1Q20`2Q20`3Q20`4Q20`1Q21`2Q21`3Q21`4Q21
Figured it out.
crop:findLastYQuarters;
crop[0]:crop[0][1];
crop[1]:crop[1][1];
crop[2]:crop[2][1];
crop[3]:crop[3][1];
crop[4]:crop[4][1];
crop[5]:crop[5][1];
crop[6]:crop[6][1];
crop[7]:crop[7][1];
labels:()
labelingFunc:{[r] temp:("." vs string["m"$r]); labels,((string(("J"$temp[1])%3)),"Q",(temp[0][2,3])};
leblingFunc each crop;
labels

Rounding to two decimal places is not working

I am trying to reduce the decimal places of my number to two. Unfortunately is not possible. For this reason I added some of my code, maybe you will see the mistake...
Update [dbo].[company$Line] SET
Amount = ROUND((SELECT RAND(1) * Amount),2),
...
SELECT * FROM [dbo].[company$Line]
Amount in db which I want to change:
0.00000000000000000000
1914.65000000000010000000
376.81999999999999000000
289.23000000000002000000
Result I get after executing the code:
0.00000000000000000000
1366.28000000000000000000
268.89999999999998000000
206.38999999999999000000
Result I want to get (or something like this):
0.00000000000000000000 or 0.00
1366.30000000000000000000 or 1366.30
268.99000000000000000000 or 268.99
206.49000000000000000000 or 206.49
RAND() returns float.
According to data type precedence the result of multiplying decimal and float is float, try:
ROUND(CAST(RAND(1) as decimal(28,12)) * Amount, 2)
this should do the trick.

53 * .01 = .531250

I'm converting a string date/time to a numerical time value. In my case I'm only using it to determine if something is newer/older than something else, so this little decimal problem is not a real problem. It doesn't need to be seconds precise. But still it has me scratching my head and I'd like to know why..
My date comes in a string format of #"2010-09-08T17:33:53+0000". So I wrote this little method to return a time value. Before anyone jumps on how many seconds there are in months with 28 days or 31 days I don't care. In my math it's fine to assume all months have 31 days and years have 31*12 days because I don't need the difference between two points in time, only to know if one point in time is later than another.
-(float) uniqueTimeFromCreatedTime: (NSString *)created_time {
float time;
if ([created_time length]>19) {
time = ([[created_time substringWithRange:NSMakeRange(2, 2)]floatValue]-10) * 535680; // max for 12 months is 535680.. uh oh y2100 bug!
time=time + [[created_time substringWithRange:NSMakeRange(5, 2)]floatValue] * 44640; // to make it easy and since it doesn't matter we assume 31 days
time=time + [[created_time substringWithRange:NSMakeRange(8, 2)]floatValue] * 1440;
time=time + [[created_time substringWithRange:NSMakeRange(11, 2)]floatValue] * 60;
time=time + [[created_time substringWithRange:NSMakeRange(14, 2)]floatValue];
time = time + [[created_time substringWithRange:NSMakeRange(17, 2)]floatValue] * .01;
return time;
}
else {
//NSLog(#"error - time string not long enough");
return 0.0;
}
}
When passed that very string listed above the result should be 414333.53, but instead it is returning 414333.531250.
When I toss an NSLog in between each time= to track where it goes off I get this result:
time 0.000000
time 401760.000000
time 413280.000000
time 414300.000000
time 414333.000000
floatvalue 53.000000
time 414333.531250
Created Time: 2010-09-08T17:33:53+0000 414333.531250
So that last floatValue returned 53.0000 but when I multiply it by .01 it turns into .53125. I also tried intValue and it did the same thing.
Welcome to floating point rounding errors. If you want accuracy two a fixed number of decimal points, multiply by 100 (for 2 decimal points) then round() it and divide it by 100. So long as the number isn't obscenely large (occupies more than I think 57 bits) then you should be fine and not have any rounding problems on the division back down.
EDIT: My note about 57 bits should be noted I was assuming double, floats have far less precision. Do as another reader suggests and switch to double if possible.
IEEE floats only have 24 effective bits of mantissa (roughly between 7 and 8 decimal digits). 0.00125 is the 24th bit rounding error between 414333.53 and the nearest float representation, since the exact number 414333.53 requires 8 decimal digits. 53 * 0.01 by itself will come out a lot more accurately before you add it to the bigger number and lose precision in the resulting sum. (This shows why addition/subtraction between numbers of very different sizes in not a good thing from a numerical point of view when calculating with floating point arithmetic.)
This is from a classic floating point error resulting from how the number is represented in bits. First, use double instead of float, as it is quite fast to use on modern machines. When the result really really matters, use the decimal type, which is 20x slower but 100% accurate.
You can create NSDate instances form those NSString dates using the +dateWithString: method. It takes strings formatted as YYYY-MM-DD HH:MM:SS ±HHMM, which is what you're dealing with. Once you have two NSDates, you can use the -compare: method to see which one is later in time.
You could try multiplying all your constants by by 100 so you don't have to divide. The division is what's causing the problem because dividing by 100 produces a repeating pattern in binary.

how do I round numbers with NSNumberFormatter

I've got a calculation for example 57 / 30 so the solution will be 1,766666667..
How do i first of all get the 1,766666667 i only get 1 or 1.00 and then how do i round the solution (to be 2)?
thanks a lot!
57/30 performs integer division. To obtain a float (or double) result you should make 1 of the operands a floating point value:
result = 57.0/30;
To round result have a look at standard floor and ceil functions.