Golang - Find month and day of month from YearDay int32 - go

I have a fixed data structure given to me that has the fields YearDay and TimeOfDay. YearDay is the number of days that have passed in the current year, TimeOfDay is the number of seconds that have passed in the current day (up to 86400). YearDay is an int32, while TimeOfDay is a float64.
I want to convert this to time.Now().UnixNano() form but am unsure how to convert it. The time module has a YearDay(), but no inverse function given an yearDay (int32) (and probably a year), to give me the month and day in the month.
Ideally I'd like to somehow parse
d := time.Date(time.Year(), month, day, hour, min, sec, ms, time.UTC)
where month, day, hour, min, sec, ms was somehow predetermined, or something of equivalent that I can convert easily to any form I'd like (but mainly UnixNano()).
My best imagination would be a complicated switch statement that subtracted 31, 28(29), 30, 31 ... and to see when the int is finally negative to find the month and day, but it would have to be two switch statements with a leap year check to choose which switch block to use, while doing several remainder calculations on TimeOfDay. Is there a simpler and cleaner way?
Edit: I ended up making the following function while playing around with it, but I'll definitely be using Icza's solution. Nice to know days can overflow. Thanks!
func findMonthAndDay(yearDay int32) (int32, int32) {
year := time.Now().Year()
isLeapYear := year%400 == 0 || year%4 == 0 && year%100 != 0 // Calculates if current year is leapyear
// Determines which array to send to for loop
var monthsOfYear [12]int32
if isLeapYear {
monthsOfYear = [12]int32{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
} else {
monthsOfYear = [12]int32{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
}
var currentMonth int32
var currentDayInMonth int32
// Loop through array of months
for i := range monthsOfYear {
// If yearDay - next month #OfDays positive, not correct month
if yearDay-monthsOfYear[i] > 0 {
// Subtract month #OfDays and continue
yearDay = yearDay - monthsOfYear[i]
} else {
currentMonth = int32(i + 1) // Month found (+1 due to index at 0)
currentDayInMonth = yearDay // Remainder of YearDay is day in month
break
}
}
return currentMonth, currentDayInMonth
}

You may use Time.AddDate() to add the number of days to a time.Time value. It is OK to add days greater than 31, the implementation normalizes the result.
And convert TimeOfDay to time.Duration and use Time.Add() to add it. When converting to time.Duration, we may multiply it by 1e9 to get number of nanoseconds, so fractional seconds will be retained.
Example:
t := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
fmt.Println(t)
var yearDay int32 = 100
var timeOfDay float64 = 70000.5
t = t.AddDate(0, 0, int(yearDay))
t = t.Add(time.Duration(timeOfDay * 1e9))
fmt.Println(t)
fmt.Println("Unix:", t.Unix())
fmt.Println("UnixNano:", t.UnixNano())
Output (try it on the Go Playground):
2020-01-01 00:00:00 +0000 UTC
2020-04-10 19:26:40.5 +0000 UTC
Unix: 1586546800
UnixNano: 1586546800500000000

Related

How to normalize elements of an array in a time range?

I'm trying to normalize an array of elements in a time range. Say you have 20 bank transactions that occur on Jan 1st, 2022
transaction 1 - 2022/01/01
transaction 2 - 2022/01/01
...
transaction 20 - 2022/01/01
we don't have other data than the day they occurred, but we still want to assign them an hour of the day, so they end as:
transaction 1 - 2022/01/01 00:00
transaction 2 - 2022/01/01 ??:??
...
transaction 20 - 2022/01/01 23:59
In Go I have this function that try to calculate the normalization of a time of day for an index in an array of elements:
func normal(start, end time.Time, arraySize, index float64) time.Time {
delta := end.Sub(start)
minutes := delta.Minutes()
duration := minutes * ((index+1) / arraySize)
return start.Add(time.Duration(duration) * time.Minute)
}
Howeve, I get an unexpected calculation of 2022/1/1 05:59 for index 0 in an array of 4 elements in a time range of 2022/1/1 00:00 to 2022/1/1 23:59, instead I would expect to see 2022/1/1 00:00. The only that works fine these conditions is index 3.
so, what am I doing wrong with my normalization?
EDIT:
Here is the function fixed thanks to #icza
func timeIndex(min, max time.Time, entries, position float64) time.Time {
delta := max.Sub(min)
minutes := delta.Minutes()
if position < 0 {
position = 0
}
duration := (minutes * (position / (entries - 1)))
return min.Add(time.Duration(duration) * time.Minute)
}
There is an example: Let's say our start and end date is 2022/01/01 00:00 - 2022/01/01 00:03, also we have 3 entries in our array of bank transactions and that we want to get the normalized time for the transaction nÂș 3 (2 in the array):
result := timeIndex(time.Date(2022, time.January, 1, 0, 0, 0, 0, time.UTC), time.Date(2022, time.January, 1, 0, 3, 0, 0, time.UTC), 3, 2)
since there is only 4 minutes between the starting and ending times (from 00:00 to 00:03) and want to find the normalized time for the last entry (index 2) in the array (size 3) the result should be:
fmt.Printf("%t", result.Equal(time.Date(2022, time.January, 1, 0, 3, 0, 0, time.UTC))
// prints "true"
or the last minute in the range, which is 00:03.
Here is a reproducible example: https://go.dev/play/p/EzwkqaNV1at
Between n points there are n-1 segments. This means if you want to include start and end in the interpolation, the number of time periods (being delta) is arraySize - 1.
Also if you add 1 to the index, you can't possibly have start as the result (you'll skip the 00:00).
So the correct algorithm is this:
func normal(start, end time.Time, arraySize, index float64) time.Time {
minutes := end.Sub(start).Minutes()
duration := minutes * (index / (arraySize - 1))
return start.Add(time.Duration(duration) * time.Minute)
}
Try it on the Go Playground.
Also note that if you have many transactions (in the order of the number of minutes in a day which is around a thousand), you may easily end up having multiple transactions having the same timestamp (same hour and minute). If you want to avoid this, use a smaller precision than minute, e.g. seconds or milliseconds:
func normal(start, end time.Time, arraySize, index float64) time.Time {
sec := end.Sub(start).Seconds()
duration := sec * (index / (arraySize - 1))
return start.Add(time.Duration(duration) * time.Second)
}
Yes, this will result in timestamps where the seconds is also not necessarily zero, but will ensure different, unique timestamps for higher transaction numbers.
If you have transactions in the order of magnitude that is close to the number of seconds in a day (which is 86400), then you can complete drop this "unit" and use time.Duration itself (which is the number of nanoseconds). This will guarantee timestamp uniqueness even for the highest number of transactions:
func normal(start, end time.Time, arraySize, index float64) time.Time {
delta := float64(end.Sub(start))
duration := delta * (index / (arraySize - 1))
return start.Add(time.Duration(duration))
}
Testing this with 1 million transactions, here are the first 15 time parts (they defer only in their sub-second part):
0 - 00:00:00.00000
1 - 00:00:00.08634
2 - 00:00:00.17268
3 - 00:00:00.25902
4 - 00:00:00.34536
5 - 00:00:00.43170
6 - 00:00:00.51804
7 - 00:00:00.60438
8 - 00:00:00.69072
9 - 00:00:00.77706
10 - 00:00:00.86340
11 - 00:00:00.94974
12 - 00:00:01.03608
13 - 00:00:01.12242
14 - 00:00:01.20876
15 - 00:00:01.29510
16 - 00:00:01.38144
17 - 00:00:01.46778
18 - 00:00:01.55412
19 - 00:00:01.64046
Try this one on the Go Playground.

How to replace order in pine script?

Is there an option to set on pine script such that when a new order command is submitted, it would cancel the previous unfulfilled one?
if secondbuycond and firstbuycond and (time >= testPeriodStart and time <= testPeriodStop)
strategy.entry("buy", strategy.long, stop=((valuewhen(firstbuycond,open,0))*x))
strategy.exit("Trailing Stop", "buy", trail_points= trailPoints, trail_offset= trailOffset, when= testType == 'Trail Points')
//#version=3
strategy("My Strategy", overlay=true)
limit_price = 0
ts = timestamp(2018, 11, 13, 0, 0)
if (time > ts)
limit_price := 999
ts2 = timestamp(2018, 11, 22, 0, 0)
// here new price will be set to replace an order
if time > ts2
limit_price := 988
strategy.entry("BUY", strategy.long, limit=limit_price)
It's possible to replace just making an entry again with new price, but both entries must differ only in price (so the order ID and direction must be the same)
I tested the stragegy above in CHMF, dayly resolution.

How to get the seconds of day [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
How to get seconds of day (1 - 86400) in Go?
Just like http://joda-time.sourceforge.net/apidocs/org/joda/time/base/AbstractDateTime.html#getSecondOfDay()
Update/Clarifications
Wanted equivalent of the joda/SecondOfDay in golang
Expected to start at 0 and 86400 at end of the day
Required when rewriting in golang a java opensource function which in turn is using joda/secondOfDay
Googled '24 hour to seconds' to get 86400
While asking question I could only think of now.Unix()-yesterdayMidnight.Unix() and the simple accepted answer didn't come to my mind
Obviously didn't think about Daylight Saving
Wanted to see if there is some built in function or popular/standard library
If we define "seconds of day" as the "elapsed seconds since midnight", then to get correct result even on days when daylight saving time happens we should subtract the time representing midnight from the given time. For that, we may use Time.Sub().
func daySeconds(t time.Time) int {
year, month, day := t.Date()
t2 := time.Date(year, month, day, 0, 0, 0, 0, t.Location())
return int(t.Sub(t2).Seconds())
}
Testing it:
for _, t := range []time.Time{
time.Date(2019, 1, 1, 0, 0, 30, 0, time.UTC),
time.Date(2019, 1, 1, 0, 1, 30, 0, time.UTC),
time.Date(2019, 1, 1, 0, 12, 30, 0, time.UTC),
time.Date(2019, 1, 1, 12, 12, 30, 0, time.UTC),
} {
fmt.Println(daySeconds(t))
}
Output (try it on the Go Playground):
30
90
750
43950
Let's see how this function gives correct result when daylight saving time happens. In Hungary, 25 March 2018 is a day when the clock was turned forward 1 hour at 02:00:00, from 2 am to 3 am.
loc, err := time.LoadLocation("CET")
if err != nil {
fmt.Println(err)
return
}
t := time.Date(2018, 3, 25, 0, 0, 30, 0, loc)
fmt.Println(t)
fmt.Println(daySeconds(t))
t = t.Add(2 * time.Hour)
fmt.Println(t)
fmt.Println(daySeconds(t))
This outputs (try it on the Go Playground):
2018-03-25 00:00:30 +0100 CET
30
2018-03-25 03:00:30 +0200 CEST
7230
We print the daySeconds of a time being 30 seconds after midnight, which is 30 of course. Then we add 2 hours to the time (2 hours = 2*3600 seconds = 7200), and the daySeconds of this new time will be properly 7200 + 30 = 7230, even though the time changed 3 hours.
Note:
This function returns the nominal number of seconds of day in the (0 - 86399) range. If you're looking for the "number of seconds elapsed since midnight", which may not be in (0 - 86399) range due to daylight saving time, please see #icza's answer.
Update:
Please note also that the question refers to Joda Time implementation, which, according to Joda Time getSecondOfDay on DST switch day, seems to correspond to the nominal number of seconds implementation (as in my answer below) as opposed to the "number of seconds elapsed since midnight" (as in #icza's answer).
package main
import (
"fmt"
"time"
)
func getSecondOfDay(t time.Time) int {
return 60*60*t.Hour() + 60*t.Minute() + t.Second()
}
func main() {
t := time.Now()
fmt.Println(getSecondOfDay(t))
}

Google Apps Script - if else statement for loop to allocate the value

I am new to GAS.
Need to write a script to execute the following calculation:
There are currently 87 employees who should be distributed for the week with a limit of 18 employees per day.
I have percentage allocation (col B). These percentages are based on other calculations and will change month over month.
What the team lead is currently doing - simple calculation in spreadsheet (multiplying % by 87). And then he manually adjusts numbers to have max of 18 per day (last column - is what needs to be at the end)
allocation number recount
Mon 0.21 18 18
Tue 0.08 7 7
Wed 0.22 19 18
Thu 0.12 10 11
Fri 0.25 22 18
Sat 0.07 6 10
Sun 0.05 4 4
Issues:
Total number of allocated staff after rounding is 86 (e.g. because 87*0.21 = 18.27 --> 18 employees)
If on some day it turns to be more than 18 employees (e.g. on Wed it is 22%*87=19 employees), only 18 people can be assigned to this day and remaining should be moved to the next "busiest" day (meaning day with the highest %). This is Friday, but on Friday we already have 22, so one from Wednesday should go to Thursday. And 4 extra from Friday go to Saturday.
Sounds complicated. But that's what I need - to automate adjustments, in other words to get column Recount from the column Number
I started the code, but have no idea how to proceed
Would be very grateful for any tips
function allocate() {
var ss=SpreadsheetApp.getActiveSpreadsheet();
var staff=ss.getSheetByName('staff');
var max = ss.getRange('B1').getValue();
for (var r=4; r<=10; r++)
{
var days=staff.getRange(r,2).getValue();
staff.getRange(r,3).setValue(days*max);
}
}
function round()
{
var ss=SpreadsheetApp.getActiveSpreadsheet();
var staff=ss.getSheetByName('staff');
for (var r=4; r<=10; r++)
{
var num=staff.getRange(r,3).getValue();
staff.getRange(r,3).setValue(Math.round(num));
}
}
You need to treat it on a week basis, not per day. Probably it is simplest to think of the task as removing marbles from a bag, until either your allocation quota is met, or the maximum draws are made. Then, advance to the next day.
E.g. (untested)
function allocate() {
const sheet = SpreadsheetApp.getActive().getSheetByName("weekly roster");
const numStaff = 50; // sheet.getRange("whatever cell you store this in").getValue();
const percentages = sheet.getRange("B4:B10").getValues();
const allotmentRange = sheet.getRange("C4:C10");
const maxPerDay = 5;
const allocated = [
[0],
[0],
[0],
[0],
[0],
[0],
[0]
];
var remaining = numStaff;
var day = 0;
while (remaining > 0 && day < allocated.length) {
if (allocated[day][0] < maxPerDay && allocated[day][0] / numStaff < percentages[day][0]) {
--remaining;
++allocated[day][0];
} else {
++day;
}
}
/* Add code to go back through allocated and add any remaining unused staff if the number is less than maxPerDay (i.e. ignoring percentage this time) */
// Write the worker allotment for the week.
allotmentRange.setValues(allocated);
}

Linq duplicate removal with a twist

I got a list that contains al the status items of each order.
The problem that i have is that i need to remove all the items of which the status -> logdate combination is not the highest.
e.g
var inputs = new List<StatusItem>();
//note that the 3th id is simply a modifier that adds that amount of secs
//to the current datetime, to make testing easier
inputs.Add(new StatusItem(123, 30, 1));
inputs.Add(new StatusItem(123, 40, 2));
inputs.Add(new StatusItem(123, 50, 3));
inputs.Add(new StatusItem(123, 40, 4));
inputs.Add(new StatusItem(123, 50, 5));
inputs.Add(new StatusItem(100, 20, 6));
inputs.Add(new StatusItem(100, 30, 7));
inputs.Add(new StatusItem(100, 20, 8));
inputs.Add(new StatusItem(100, 30, 9));
inputs.Add(new StatusItem(100, 40, 10));
inputs.Add(new StatusItem(100, 50, 11));
inputs.Add(new StatusItem(100, 40, 12));
var l = from i in inputs
group i by i.internalId
into cg
select
from s in cg
group s by s.statusId
into sg
select sg.OrderByDescending(n => n.date).First()
;
edit: for convenience im adding the class definition as well.
public class StatusItem
{
public int internalId;
public int statusId;
public DateTime date;
public StatusItem(int internalId, int statusId, int secMod)
{
this.internalId = internalId;
this.statusId = statusId;
date = DateTime.Now.AddSeconds(secMod);
}
}
This creates a list that returnes me the following:
order 123 status 30 date 4/9/2010 6:44:21 PM
order 123 status 40 date 4/9/2010 6:44:24 PM
order 123 status 50 date 4/9/2010 6:44:25 PM
order 100 status 20 date 4/9/2010 6:44:28 PM
order 100 status 30 date 4/9/2010 6:44:29 PM
order 100 status 40 date 4/9/2010 6:44:32 PM
order 100 status 50 date 4/9/2010 6:44:31 PM
This is ALMOST correct. However that last line which has status 50 needs to be filtered out as well because it was overruled by status 40 in the historylist. U can tell by the fact that its date is lower then the "last" status-item with the status 40.
I was hoping someone could give me some pointers because im stuck.
Edit: Final complete solution:
var k = from sg in
from i in inputs
group i by i.internalId
into cg
select
from s in cg
group s by s.statusId
into sg
select sg.OrderByDescending(n => n.date).First()
from s in sg
where s.date >= sg.Where(n => n.statusId <= s.statusId).Max(n => n.date)
group s by s.internalId
into si
from x in si
select x;
Looks like you don't currently have anything performing the filtering you need for the date, so you'd need to do something about that.
Off hand, something like this would perform the additional filtering:
var k = from sg in l
from s in sg
where s.date >= sg.Where(n => n.statusId <= s.statusId).Max(n => n.date)
group s by s.internalId;
Haven't tested it, so the grouping may not be what you want, and the comparisons may be reversed, but something like that should filter. >= and <= instead of > or < should mean that the status will always be compared to itself and not have to deal with empty set in aggregate issues.
It's not exactly in the same form you have, but it does give the correct result. I made a status item class with i, j, and k properties. Not sure what names you used for them.
var keys = inputs.Select(
input =>
new { i = input.i, j = input.j })
.Distinct();
var maxes = keys.Select(
ints =>
inputs.First(
input =>
input.i == ints.i
&& input.j == ints.j
&& input.k == inputs.Where(
i =>
i.i == ints.i
&& i.j == ints.j
).Select(i => i.k).Max()));

Resources