Round time in Xpath - datetime

What is the simplest and correct way to round the time and dateTime in XPath?
For example, how to define a function local:round-time-to-minutes in such way that the following test-case:
let $t1 := xs:time( "12:58:37" )
let $t2 := local:round-time-to-minutes( $t1 )
return format-time( $t2, '[H01]:[m01]:[s01]' )
will return "12:59:00".
Don't sure what is better in case of "23:59:31" — to return "00:00:00" or to raise a dynamic error.
And similar function local:round-datetime-to-minutes to handle dateTime?
(it doesn't have such edge case as above)
Let these functions use "round half towards positive infinity" rule, where half is 30.0 seconds.

This is how the solution proposed by #michael.hor257k would look in XQuery:
declare variable $ONE_MIN := xs:dayTimeDuration("PT1M");
declare variable $MIDNIGHT := xs:time("00:00:00");
declare function local:round-time-to-minutes($time) {
$MIDNIGHT + round(($time - $MIDNIGHT) div $ONE_MIN) * $ONE_MIN
};

Another solution is to subtract the number of second from the given dateTime and add one minute (60 seconds) if the number of seconds is not less than 30.
To convert a number of seconds into duration we multiple it on 1S duration (actually, this operation can be eliminated by a compiler).
declare function local:round-time-to-minutes ( $time as xs:time ) {
let $s := seconds-from-time( $time )
return $time - xs:dayTimeDuration('PT1S') * ( $s - 60 * ($s idiv 30) )
};
declare function local:round-dateTime-to-minutes ( $dt as xs:dateTime ) {
let $s := seconds-from-dateTime( $dt )
return $dt - xs:dayTimeDuration('PT1S') * ( $s - 60 * ($s idiv 30) )
};
This solution is uniform for the cases of xs:time and xs:dateTime types.

Related

Recursive TCL proc without explicit return

I am writting a simple "proc" to calculate the factorial. I would like to understand why my function does not work without the return statement.
According to TCL docs, functions that are defined without explicit "return",
return the value of the last executed command in its body.
proc fac { n } {
if { $n == 1 } {
return 1
}
puts $n
set n [expr {$n - 1}]
return [expr {[fac $n ] * $n}]
}
puts [fac 5] # ans 24
When the "return" is removed, I get the following error message:
invalid command name "1"
while executing
"[expr {[fac $n ] * $n}] "
(procedure "fac" line 7)
invoked from within
I expected that without the explicit "return", the function should return 24 as well.
Your expectation is correct. But you have square brackets around expr procedure in the last line. It is:
[expr {[fac $n] * $n}]
This means for the interpreter: 1) execute expr procedure with given argument; 2) execute the result of expr procedure. Because of this, the interpreter tries to execute procedure 1 that doesn't exist and you receive an error.
To fix this error - just remove square brackets from the last line:
proc fac { n } {
if { $n == 1 } {
return 1
}
puts $n
set n [expr {$n - 1}]
expr {[fac $n ] * $n}
}

Delphi Calculate age of person from TDateEdit component

I have a very simple form with a TDateEdit, TButton and TLabel component.
What is the best way to get the age of someone if the date given is their birthdate? How would someone do this in Delphi from the TDateEdit component and then display the age within the label?
Is there maybe a built in function or something I can use to get the age of someone from their date of birth in the component? I'm looking for the simplest, easiest and best way to do this.
Here is a function to calculate someone's age.
It is distinctly different from the RTL function YearsBetween, as that calculates the number of years between two dates; and is fundamentally not the same as someone's age.
function GetAge(const BirthDate, CurrentDate: TDateTime): Integer;
var
y1, m1, d1: Word; //born
y2, m2, d2: Word; //today
begin
Result := 0;
if CurrentDate < BirthDate then
Exit;
DecodeDate(BirthDate, y1, m1, d1);
DecodeDate(CurrentDate, y2, m2, d2);
//Fudge someone born on the leap-day to Feb 28th of the same year
//strictly for the purposes of this calculation
if ( (m1=2) and (d1=29) )
and
( not IsLeapYear(y2) ) then
begin
d1 := 28;
end;
Result := y2-y1; //rough count of years
//Take away a year of the month/day is before their birth month/day
if (m2 < m1) or
((m2=m1) and (d2<d1)) then
Dec(Result);
end;
You can use the function YearsBetween() from the System.DateUtils unit to calculate the number of years between today (which you get from the function now) and the date in your TDateEdit component, which you get from the date property.
Label1.Text:= Trunc(YearsBetween(Now,DateEdit1.Date)).toString;
I prefer Ian Boyd's answer, but for those that insist that YearsBetween is the way to go, here's an alternative.
If Y is the result from YearsBetween, then the proper result is either Y or Y+1. This routine calls YearsBetween and returns Y+1. Then it uses IncYear to see if the answer is too big. If it is, it returns Y.
This might seem like it's simpler, but behind the scenes there is a lot more work going on. Ian Boyd's answer is the one you should use.
function AgeInCompleteYears ( const nBirthDate : tDateTime;
const nCurrentDate : tDateTime ) : integer;
begin
Result := 1 + DateUtils.YearsBetween ( nCurrentDate, nBirthDate );
if DateUtils.IncYear ( nBirthDate, Result ) > nCurrentDate then
dec ( Result );
end;
function GetAge(BirthDate:TDateTime; RefDate:TDateTime=0):Integer;
begin
if RefDate=0 then RefDate:= Today;
Result := Trunc((RefDate- BirthDate) / 365.25)
end;

XQuery difference between same function different implementation

Return the number of cycles:
let $bd := doc("document")
return count ( for $c in $bd//cycle
where $c[#id]
return $c
)
Every cycle has an ID, not important here but it is a must to specify it.
What is the difference between the above use of count and the below use of count?
let $bd := doc("document")
let $c := $bd//cycle[#id]
return count($c)
I dont know the difference between these 2 XQueries return same result but following the same pattern the next 2 queries should work but the 2nd one doesnt... Here they are:
The total of hours of modules which is above 100.
*Working query*
let $bd:=doc("document")
return sum (
for $m in $bd//module[#id]
where $m/hours>100
return $m/hours
)
*Not working query*
let $bd := doc("document")
for $c in $bd//module[#id]
where $c/hours>100
return sum($c/hours)
Id like to know why following the same "pattern" the second query is not working.
The output of the not working query is this one:
160 160 256 224 192 160
Its not the result i need, I want the sum of all them.
The first two expressions are functionally equivalent. The difference is the use of FLWOR vs. XPath to select your sequence.
In the second example, you are calling sum() on each item of the sequence ($c/hours), instead of on the sequence itself:
let $bd := doc("document")
return sum(
for $c in $bd//module[#id]
where $c/hours>100
return $c/hours)
You could also use XPath:
let $bd := doc("document")
let $c := $bd//module[#id][hours>100]
return sum($c/hours)
Or similarly assign the result of the FLWOR to a variable and sum that:
let $bd := doc("document")
let $c :=
for $m in $bd//module[#id]
where $m/hours>100
return $m/hours
return sum($c)

How to do date/time comparison

Is there any options in doing date comparison in Go? I have to sort data based on date and time - independently. So I might allow an object that occurs within a range of dates so long as it also occurs within a range of times. In this model, I could not simply just select the oldest date, youngest time/latest date, latest time and Unix() seconds compare them. I'd really appreciate any suggestions.
Ultimately, I wrote a time parsing string compare module to check if a time is within a range. However, this is not faring to well; I've got some gaping issues. I'll post that here just for fun, but I'm hoping there's a better way to time compare.
package main
import (
"strconv"
"strings"
)
func tryIndex(arr []string, index int, def string) string {
if index <= len(arr)-1 {
return arr[index]
}
return def
}
/*
* Takes two strings of format "hh:mm:ss" and compares them.
* Takes a function to compare individual sections (split by ":").
* Note: strings can actually be formatted like "h", "hh", "hh:m",
* "hh:mm", etc. Any missing parts will be added lazily.
*/
func timeCompare(a, b string, compare func(int, int) (bool, bool)) bool {
aArr := strings.Split(a, ":")
bArr := strings.Split(b, ":")
// Catches margins.
if (b == a) {
return true
}
for i := range aArr {
aI, _ := strconv.Atoi(tryIndex(aArr, i, "00"))
bI, _ := strconv.Atoi(tryIndex(bArr, i, "00"))
res, flag := compare(aI, bI)
if res {
return true
} else if flag { // Needed to catch case where a > b and a is the lower limit
return false
}
}
return false
}
func timeGreaterEqual(a, b int) (bool, bool) {return a > b, a < b}
func timeLesserEqual(a, b int) (bool, bool) {return a < b, a > b}
/*
* Returns true for two strings formmated "hh:mm:ss".
* Note: strings can actually be formatted like "h", "hh", "hh:m",
* "hh:mm", etc. Any missing parts will be added lazily.
*/
func withinTime(timeRange, time string) bool {
rArr := strings.Split(timeRange, "-")
if timeCompare(rArr[0], rArr[1], timeLesserEqual) {
afterStart := timeCompare(rArr[0], time, timeLesserEqual)
beforeEnd := timeCompare(rArr[1], time, timeGreaterEqual)
return afterStart && beforeEnd
}
// Catch things like `timeRange := "22:00:00-04:59:59"` which will happen
// with UTC conversions from local time.
// THIS IS THE BROKEN PART I BELIEVE
afterStart := timeCompare(rArr[0], time, timeLesserEqual)
beforeEnd := timeCompare(rArr[1], time, timeGreaterEqual)
return afterStart || beforeEnd
}
So TLDR, I wrote a withinTimeRange(range, time) function but it's not working totally correctly. (In fact, mostly just the second case, where a time range crosses over days is broken. The original part worked, I just realized I'd need to account for that when making conversions to UTC from local.)
If there's a better (preferably built in) way, I'd love to hear about it!
NOTE:
Just as an example, I solved this issue in Javascript with this function:
function withinTime(start, end, time) {
var s = Date.parse("01/01/2011 "+start);
var e = Date.parse("01/0"+(end=="24:00:00"?"2":"1")+"/2011 "+(end=="24:00:00"?"00:00:00":end));
var t = Date.parse("01/01/2011 "+time);
return s <= t && e >= t;
}
However I really want to do this filter server-side.
Use the time package to work with time information in Go.
Time instants can be compared using the Before, After, and Equal
methods. The Sub method subtracts two instants, producing a Duration.
The Add method adds a Time and a Duration, producing a Time.
Play example:
package main
import (
"fmt"
"time"
)
func inTimeSpan(start, end, check time.Time) bool {
return check.After(start) && check.Before(end)
}
func main() {
start, _ := time.Parse(time.RFC822, "01 Jan 15 10:00 UTC")
end, _ := time.Parse(time.RFC822, "01 Jan 16 10:00 UTC")
in, _ := time.Parse(time.RFC822, "01 Jan 15 20:00 UTC")
out, _ := time.Parse(time.RFC822, "01 Jan 17 10:00 UTC")
if inTimeSpan(start, end, in) {
fmt.Println(in, "is between", start, "and", end, ".")
}
if !inTimeSpan(start, end, out) {
fmt.Println(out, "is not between", start, "and", end, ".")
}
}
For comparison between two times use time.Sub()
// utc life
loc, _ := time.LoadLocation("UTC")
// setup a start and end time
createdAt := time.Now().In(loc).Add(1 * time.Hour)
expiresAt := time.Now().In(loc).Add(4 * time.Hour)
// get the diff
diff := expiresAt.Sub(createdAt)
fmt.Printf("Lifespan is %+v", diff)
The program outputs:
Lifespan is 3h0m0s
http://play.golang.org/p/bbxeTtd4L6
For case when your interval's end date doesn't contains hours like
"from 2017-01-01 to whole day of 2017-01-16" it's better to adjust interval's end to midnight of the next day to include all milliseconds like this:
if now.After(start) && now.Before(end.Add(24 * time.Hour).Truncate(24 * time.Hour)) {
...
}
It's possible to compare date using int64 of Unix epoch with seconds granularity. If you need more exact comparison like milisecons or microseconds etc. I guess that
#Oleg Neumyvakin's answer is perfect.
if expirationDate.Unix() > time.Now().Unix() {
...
}
If you're interested in comparing whether a time is close to another for test purposes, you can use testify assert.WithinDuration for this. For example:
expectedTime := time.Now()
actualTime := expectedTime.Add(100*time.Millisecond)
assert.WithinDuration(t, expectedTime, actualTime, 1*time.Second) // pass
assert.WithinDuration(t, expectedTime, actualTime, 1*time.Millisecond) // fail
Otherwise the implementation of assert.WithinDuration can be re-used in your code to determine how close two times are (subtracting one date from the other gives the time difference):
func WithinDuration(expected, actual time.Time, delta time.Duration) bool {
dt := expected.Sub(actual)
return dt >= -delta && dt <= delta
}
Recent protocols prefer usage of RFC3339 per golang time package documentation.
In general RFC1123Z should be used instead of RFC1123 for servers that insist on that format, and RFC3339 should be preferred for new protocols. RFC822, RFC822Z, RFC1123, and RFC1123Z are useful for formatting; when used with time.Parse they do not accept all the time formats permitted by the RFCs.
cutOffTime, _ := time.Parse(time.RFC3339, "2017-08-30T13:35:00Z")
// POSTDATE is a date time field in DB (datastore)
query := datastore.NewQuery("db").Filter("POSTDATE >=", cutOffTime).
As explained in the theread we could use github.com/google/go-cmp/cmp package for dates comparison in tests.
func TestDates(t *testing.T) {
date, _ := time.Parse(time.RFC3339, "2021-11-05T12:00:00+02:00")
dateEqual, _ := time.Parse(time.RFC3339, "2021-11-05T11:00:00+01:00")
dateNotEqual, _ := time.Parse(time.RFC3339, "2021-11-05T12:00:01+02:00")
assertDates(t, date, dateEqual) //pass
assertDates(t, date, dateNotEqual) //fail
}
func assertDates(t *testing.T, expected, actual time.Time) {
t.Helper()
if diff := cmp.Diff(expected, actual); diff != "" {
t.Errorf("mismatch (-expected +actual):\n%s", diff)
}
}
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("Hello World")
maxRep := 5
repPeroid := 6
expiry := maxRep * repPeroid
fmt.Println("Expiry: ", expiry)
fmt.Println(time.Now())
CorrIdtime := time.Now().Add(time.Second * time.Duration(expiry)).Format(time.RFC3339)
Notifytime := time.Now().Add(2 * time.Second * time.Duration(expiry)).Format(time.RFC3339)
fmt.Println(CorrIdtime)
fmt.Println(Notifytime)
if CorrIdtime < Notifytime {
fmt.Println("Discarded")
} else {
fmt.Println("Accepted")
}
}
Per proposal time: add Time.Compare and related commit, time.Compare will be added in the new release (Go 1.20)
// Compare compares the time instant t with u. If t is before u, it returns -1;
// if t is after u, it returns +1; if they're the same, it returns 0.
func (t Time) Compare(u Time) int {
Sample
var t1, t2 Time
result := t1.Compare(t2)

How does one iterate over a sequence in xquery by twos?

I want to iterate over a sequence in xquery and grab 2 elements at a time. What is the easiest way to do this?
XQuery 3.0 Solution
For this and more complex paging use cases the Window Clause has been created in XQuery 3.0. But, it is not yet supported by many XQuery processors.
Windowing example
Here is a working example that you could execute for example on try.zorba :
for tumbling window $pair in (2, 4, 6, 8, 10, 12, 14)
start at $s when fn:true()
end at $e when $e - $s eq 1
return <window>{ $pair }</window>
Result
<window>2 4</window><window>6 8</window><window>10 12</window><window>14</window>
One option is to iterate over all items and just take the items once the items reach the divisor, in this case 2. The one downside is that you won't reach the last group of items if the items aren't even multiples of the divisor. For instance, the last element of a sequence with an odd number of elements will not be returned with this approach.
for $item at $index in $items
return
if ($item mod 2 = 0) then
($items[$index - 1], $items[$index])
else
()
Another option is to use mod and the index of the item. Using this approach you can make certain to include all elements in the $items sequence by adding one less than the number of items in your group to the count.
let $group-size := 2
return
for $index in (1 to fn:count($items)+($group-size - 1))[. mod $group-size = 0]
return
($items[$index - 1] , $items[$index])
let $s := ("a","b","c","d","e","f")
for $i in 1 to xs:integer(count($s) div 2)
return
<pair>
{($s[$i*2 - 1],$s[$i*2])}
</pair>
returns
<pair>a b</pair>
<pair>c d</pair>
<pair>e f</pair>
for $item at $index in $items
return
(
if ($index mod 2 eq 0) then
(
$items[xs:integer(xs:integer($index) - 1)], $items[xs:integer($index)]
)
else
()
)

Resources