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