KRL: Comparing two timestamps - krl

I have two timestamps created with time:now() (one stored in an app variable from the past, one the current time). I need to find the difference between them (preferably in minutes). How do I do that?
I've tried this syntax, but the parser didn't like it:
diff = time:now() - original_time;
time:compare() doesn't give me enough information, and time:add() is the opposite of what I need. There don't seem to be any other applicable time functions documented.

The time functions return a time string, not a time object. To calculate time elapsed, you will have to convert your time string into epoch time (seconds since 1970..). Fortunately, epoch time is one of the formats supported by strftime.
foo = time:now();
efoo = time:strftime(foo,"%s);
The minus operator is actually sensitive to a leading whitespace. It's on the list of things to work out of the parser, but I just haven't had time to get to it. Here is a working rule:
rule first_rule {
select when pageview ".*" setting ()
pre {
foo = time:now();
bar = time:add(foo,{"minutes": -5});
ebar = time:strftime(bar,"%s");
efoo = time:strftime(foo,"%s");
diff = efoo-ebar;
}
notify("-5 minutes in seconds", diff) with sticky = true;
}

Related

How to format the Duration object in Swift

Swift released a new Duration object that is "a representation of high precision time."
I'm using it like this:
let clock = ContinuousClock()
let duration = clock.measure {
// Code or function call to measure here
}
print("Duration: \(duration)")
If the duration really short it prints out something like this:
8.2584e-05 seconds
Instead of scientific notation, I would like to always display as seconds: 0.000082584 seconds
Does anyone know how to always keep the format in seconds?
Just like Dates, Durations support the formatted method. You can give it either the TimeFormatStyle (time) or UnitsFormatStyle (units). For your desired format, it looks like the latter is more suitable. You basically want a fractionalPart that has a very large allowed length.
Though from my experiments, it still rounds everything to nanosecond-precision, even though Duration can support higher precisions. This is perhaps because nanoseconds is the smallest supported unit in Duration.UnitsFormatStyle.Unit.
For example:
let duration: Duration = .nanoseconds(1234)
print(
duration.formatted(.units(
width: .wide,
fractionalPart: .init(lengthLimits: 1...1000)
))
)
Output:
0.000001234 seconds
By default, this will also include hours and minutes if the duration is long enough. If you don't want that, pass allowed: [.seconds] as the first parameter:
duration.formatted(.units(
allowed: [.seconds],
width: .wide,
fractionalPart: .init(lengthLimits: 1...1000)
))

Powershell cast string to time only

I have created a Powershell/XAML app, that on button press makes a RESTAPI call, parses the JSON response into fields in the app front end. All fine so far.
These fields will be populated with a string representing a time, so "1800" or "2000" etc.
The user can then change this from 1800 to 1900 for example.
This is all fine, and in the background the app will use 1900 to update a setting to be used in a POST back.
However there are other settings that are offset by 90 mins of the time above. I don't want the user to have update each one, which is why I am trying to programmatically.
But try as I might, I cannot take a string of 1800, add 90 mins to it and make the value 1930 (not 1890).
You could parse the input as a DateTime object (ignoring the date part) and then use the AddMinutes method.
$input = '1800'
$hour = $input.Substring(0,2)
$minute = $input.Substring(2,2)
$dateInputStr = "0001-01-01,${hour}:${minute}:00"
[datetime]$dateInput = ([datetime]$dateInputStr)
$dateInput = $dateInput.AddMinutes(90)
$dateInput.ToString("HHmm")
Using [timespan] instances is another option:
$time = '1800'
([timespan] ($time -replace '(?<=^..)', ':') + '01:30').ToString('hhmm') #->'1930'
$time -replace '(?<=^..)', ':' uses the regex-based -replace operator to insert : after the first two characters - see this regex101.com page for an explanation of the regex and the ability to experiment with it.
Due to expressing the results only in terms of hours and minutes, the calculation wraps around at midnight, so that adding '05:30', for instance, would yield '0030'
The RHS operand needn't be cast to [timespan] directly, because the data type of the LHS - with its explicit [timespan] cast - implicitly converts the RHS to [timespan] too, with '01:30' representing 1 hour and 30 minutes, i.e. 90 minutes.
If you want to define the duration to add in terms of 90 minutes, use the following instead (there are analogous static methods for other units, such as ::FromSeconds():
[timespan]::FromMinutes(90)
Alternatively, you can cast a number to [timespan], which is interpreted as ticks, which are 100-nanosecond units; there are 1e9 (10 to the power of 9) nanoseconds in a second, and therefore 1e7 100-nanosecond units in a second. Thus, multiplying with 1e7 gives you seconds, and multiplying that with 60 minutes.
# 90 minutes expressed as ticks
[timespan] 90 * (60 * 1e7)
When I read this question I wanted to solve it with minimal string manipulation, leaning on time related objects and methods instead. datetime was the first object I thought of, but it expects a date (year, month, day). Things actually simplify if we use timespan. Its static method, ParseExact, can parse the string directly.
$offsetTimeSpan = [timespan]::FromMinutes(90)
$timeField = '830'
$timeStr = $timeField.PadLeft(4, '0')
$timeSpan = [timespan]::ParseExact($timeStr, 'hhmm', [CultureInfo]::InvariantCulture)
$offsetTime = $timeSpan.Add($offsetTimeSpan)
$offsetTime.ToString('hhmm')
$timeField is used to represent the time you get from the RESTAPI. PadLeft is only needed if it's possible for a leading 0 to be missing. ParseExact does the heavy lifting of converting the string to a time type. Because timespan doesn't have an AddMinutes member, we use the Add method passing in a timespan of 90 minutes, $offsetTimeSpan.
You don't mention anything about overflowing past midnight. You can test for overflow using $offsetTime.Days, if any special processing is required.

Pine scripting: how to find the price X days ago

In Pine Script, how do I find the price based on a certain number of days ago? I've tried something like this...
// Find the price 90 days ago
target = time - 90 * 60 * 60 * 24 * 1000
valuewhen(time < target, close, 1)
...however time < target never seems to return true – presumably because the current bar's time cannot also be in the past at the same time. Perhaps valuewhen() wasn't designed to be used with dynamic values that change on every bar?
Do I need to use a loop instead, and scan through every past bar until I find the date I'm looking for?
Perhaps there's a better way, but the workaround I'm using currently using is a function with a for loop, scanning backwards until the appropriate date is found. Here is my function:
priceXDaysAgo(numDays) =>
targetTimestamp = time - numDays*60*60*24*1000
// Declare a result variable with a "void" value
float result = if false
1
// We'll scan backwards through the preceding bars to find the first bar
// earlier than X days ago (it might be a little greater than X days if
// there was a break in trading: weekend, public holiday, etc.)
for i = 1 to 1000
if time[i] < targetTimestamp
result := close[i]
break
result
You can then call the function anywhere in your script:
priceXDaysAgo(90)

Result of adding second to date is one minute off; workaround

I'm adding a second to an instance of Foundation's date, but the result is off by an entire minute.
var calendar = Calendar(identifier: .iso8601)
calendar.locale = Locale(identifier: "en")
calendar.timeZone = TimeZone(identifier: "GMT")!
let date1 = Date(timeIntervalSinceReferenceDate: -62544967141.9)
let date2 = calendar.date(byAdding: DateComponents(second: 1),
to: date1,
wrappingComponents: true)!
ISO8601DateFormatter().string(from: date1) // => 0019-01-11T22:00:58Z
ISO8601DateFormatter().string(from: date2) // => 0019-01-11T21:59:59Z
Interestingly, one of the following makes the error go away:
round time interval since reference date
don't add time zone to calendar
set wrappingComponents to false (even though it shouldn't wrap in this case)
I don't really need sub-second precision in my code, so I created this extension that allows me to discard it.
extension Date {
func roundedToSeconds() -> Date {
return Date(timeIntervalSinceReferenceDate: round(timeIntervalSinceReferenceDate))
}
}
I want to know this:
Why does this error happen?
Am I doing something wrong?
Is there any issue with my workaround?
Why does this error happen?
I would say this is a bug in Core Foundation (CF).
Calendar.date(byAdding:to:wrappingComponents:) calls down to the internal Core Foundation function _CFCalendarAddComponentsV, which in turn uses the ICU Calendar C API. ICU represents a time as an floating-point number of milliseconds since the Unix epoch, while CF uses a floating-point number of seconds since the NeXT reference date. So CF has to convert its representation to ICU's representation before calling into ICU, and convert back to return the result to you.
Here's how it converts from a CF timestamp to an ICU timestamp:
double startingInt;
double startingFrac = modf(*atp, &startingInt);
UDate udate = (startingInt + kCFAbsoluteTimeIntervalSince1970) * 1000.0;
The modf function splits a floating-point number into its integer and fractional parts. Let's plug in your example date:
var startingInt: Double = 0
var startingFrac: Double = modf(date1.timeIntervalSinceReferenceDate, &startingInt)
print(startingInt, startingFrac)
// Output:
-62544967141.0 -0.9000015258789062
Next, CF calls __CFCalendarAdd to add one second to -62544967141. Note that -62544967141 lies in the round one-minute interval -62544967200 ..< -62544967140.0. So when CF adds one second to -62544967141, it gets -62544967140, which would be in the next round one-minute interval. Since you specified wrapping components, CF isn't allowed to change the minute part of the date, so it wraps back to the beginning of the original round one-minute interval, -62544967200.
Finally, CF converts the ICU time back to a CF time, adding in the fractional part of the original time:
*atp = (udate / 1000.0) - kCFAbsoluteTimeIntervalSince1970 + startingFrac + (nanosecond * 1.0e-9);
So it returns -62544967200 + -0.9000015258789062 = -62544967200.9, exactly 59 seconds earlier than the input time.
Am I doing something wrong?
No, the bug is in CF, not in your code.
Is there any issue with my workaround?
If you don't need sub-second precision, your workaround should be fine.
I can reproduce it with more recent dates but so far only with negative reference dates, e.g. Date(timeIntervalSinceReferenceDate: -1008899941.9), which is 1969-01-11T22:00:58Z.
Any negative timeIntervalSinceReferenceDate in the last second of a minute interval should cause the problem. The bug effectively makes the first round whole minute prior to time 0 span from -60.99999999999999 through -1.0, but it should span from -60.0 through -5e324. All more-negative round minute intervals are similarly offset.

Carbon Difference in Time between two Dates in hh:mm:ss format

I'm trying to figure out how I can take two date time strings that are stored in our database and convert it to a difference in time format of hh:mm:ss.
I looked at diffForHumans, but that does give the format I'd like and returns things like after, ago, etc; which is useful, but not for what I'm trying to do.
The duration will never span days, only a max of a couple hours.
$startTime = Carbon::parse($this->start_time);
$finishTime = Carbon::parse($this->finish_time);
$totalDuration = $finishTime->diffForHumans($startTime);
dd($totalDuration);
// Have: "21 seconds after"
// Want: 00:00:21
I ended up grabbing the total seconds difference using Carbon:
$totalDuration = $finishTime->diffInSeconds($startTime);
// 21
Then used gmdate:
gmdate('H:i:s', $totalDuration);
// 00:00:21
If anyone has a better way I'd be interested. Otherwise this works.
$finishTime->diff($startTime)->format('%H:%I:%S');
// 00:00:21
$start = new Carbon('2018-10-04 15:00:03');
$end = new Carbon('2018-10-05 17:00:09');
You may use
$start->diff($end)->format('%H:%I:%S');
which gives the difference modulo 24h
02:00:06
If you want to have the difference with more than 24h, you may use :
$start->diffInHours($end) . ':' . $start->diff($end)->format('%I:%S');
which gives :
26:00:06
I know this is an old question, but it still tops Google results when searching for this sort of thing and Carbon has added a lot of flexibility on this, so wanted to drop a 2022 solution here as well.
TL;DR - check out the documentation for different/more verbose versions of the diffForHumans method, and greater control over options.
As an example, we needed to show the difference between two Carbon instances (start and end) in hours & minutes, but it's possible they're more than 24 hours apart—in which case the minutes become less valuable/important. We want to exclude the "ago" or Also wanted to join the strings with a comma.
We can accomplish all of that, with the $options passed into diffForHumans, like this:
use Carbon\CarbonInterface;
$options = [
'join' => ', ',
'parts' => 2,
'syntax' => CarbonInterface::DIFF_ABSOLUTE,
];
return $end->diffForHumans($start, $options);
Which will result in values like what's seen in the Duration column:
Hope that's helpful for someone!
You can do it using the Carbon package this way to get the time difference:
$start_time = new Carbon('14:53:00');
$end_time = new Carbon('15:00:00');
$time_difference_in_minutes = $end_time->diffInMinutes($start_time);//you also find difference in hours using diffInHours()