// Package julianday provides Time to Julian day conversions.
package julianday import ( ) const secs_per_day = 86_400 const nsec_per_sec = 1_000_000_000 const nsec_per_day = nsec_per_sec * secs_per_day const epoch_days = 2_440_587 const epoch_secs = secs_per_day / 2 func jd( time.Time) (, int64) { := .Unix() // guaranteed not to overflow , = /secs_per_day+epoch_days, %secs_per_day+epoch_secs return , *nsec_per_sec + int64(.Nanosecond()) } // Date returns the Julian day number for t, // and the nanosecond offset within that day, // in the range [0, 86399999999999]. func ( time.Time) (, int64) { , = jd() switch { case < 0: -= 1 += nsec_per_day case >= nsec_per_day: += 1 -= nsec_per_day } return , } // Float returns the Julian date for t as a float64. // // In the XXI century, this has submillisecond precision. func ( time.Time) float64 { , := jd() // converting day and nsec to float64 is exact return float64() + float64()/nsec_per_day } // Format returns the Julian date for t as a string. // // This has nanosecond precision. func ( time.Time) string { var [32]byte return string(AppendFormat([:0], )) } // AppendFormat is like Format but appends the textual representation to dst // and returns the extended buffer. func ( []byte, time.Time) []byte { , := Date() if < 0 && != 0 { = append(, '-') = ^ = nsec_per_day - } var [20]byte = strconv.AppendInt(, , 10) := strconv.AppendFloat([:0], float64()/nsec_per_day, 'f', 15, 64) return append(, bytes.TrimRight([1:], ".0")...) } // Time returns the UTC Time corresponding to the Julian day number // and nanosecond offset within that day. // Not all day values have a corresponding time value. func (, int64) time.Time { return time.Unix((-epoch_days)*secs_per_day-epoch_secs, ).UTC() } // FloatTime returns the UTC Time corresponding to a Julian date. // Not all date values have a corresponding time value. // // In the XXI century, this has submillisecond precision. func ( float64) time.Time { , := math.Modf() := math.Floor( * nsec_per_day) return Time(int64(), int64()) } // Parse parses a formatted Julian date and returns the UTC Time it represents. // // This has nanosecond precision. func ( string) (time.Time, error) { := 0 := len() for , := range []byte() { if '0' <= && <= '9' { ++ continue } if == '.' && < { = continue } if ( == '+' || == '-') && == 0 { continue } return time.Time{}, errors.New("julianday: invalid syntax") } if == 0 { return time.Time{}, errors.New("julianday: invalid syntax") } , := strconv.ParseInt([:], 10, 64) if != nil && > 0 { return time.Time{}, errors.New("julianday: value out of range") } , := strconv.ParseFloat([:], 64) := int64(math.Round( * nsec_per_day)) if [0] == '-' { = - } return Time(, ), nil }