summaryrefslogtreecommitdiff
path: root/vendor/github.com/ncruces/julianday/julianday.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/ncruces/julianday/julianday.go')
-rw-r--r--vendor/github.com/ncruces/julianday/julianday.go124
1 files changed, 124 insertions, 0 deletions
diff --git a/vendor/github.com/ncruces/julianday/julianday.go b/vendor/github.com/ncruces/julianday/julianday.go
new file mode 100644
index 000000000..d7d0e1960
--- /dev/null
+++ b/vendor/github.com/ncruces/julianday/julianday.go
@@ -0,0 +1,124 @@
+// Package julianday provides Time to Julian day conversions.
+package julianday
+
+import (
+ "bytes"
+ "errors"
+ "math"
+ "strconv"
+ "time"
+)
+
+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(t time.Time) (day, nsec int64) {
+ sec := t.Unix()
+ // guaranteed not to overflow
+ day, sec = sec/secs_per_day+epoch_days, sec%secs_per_day+epoch_secs
+ return day, sec*nsec_per_sec + int64(t.Nanosecond())
+}
+
+// Date returns the Julian day number for t,
+// and the nanosecond offset within that day,
+// in the range [0, 86399999999999].
+func Date(t time.Time) (day, nsec int64) {
+ day, nsec = jd(t)
+ switch {
+ case nsec < 0:
+ day -= 1
+ nsec += nsec_per_day
+ case nsec >= nsec_per_day:
+ day += 1
+ nsec -= nsec_per_day
+ }
+ return day, nsec
+}
+
+// Float returns the Julian date for t as a float64.
+//
+// In the XXI century, this has submillisecond precision.
+func Float(t time.Time) float64 {
+ day, nsec := jd(t)
+ // converting day and nsec to float64 is exact
+ return float64(day) + float64(nsec)/nsec_per_day
+}
+
+// Format returns the Julian date for t as a string.
+//
+// This has nanosecond precision.
+func Format(t time.Time) string {
+ var buf [32]byte
+ return string(AppendFormat(buf[:0], t))
+}
+
+// AppendFormat is like Format but appends the textual representation to dst
+// and returns the extended buffer.
+func AppendFormat(dst []byte, t time.Time) []byte {
+ day, nsec := Date(t)
+ if day < 0 && nsec != 0 {
+ dst = append(dst, '-')
+ day = ^day
+ nsec = nsec_per_day - nsec
+ }
+ var buf [20]byte
+ dst = strconv.AppendInt(dst, day, 10)
+ frac := strconv.AppendFloat(buf[:0], float64(nsec)/nsec_per_day, 'f', 15, 64)
+ return append(dst, bytes.TrimRight(frac[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 Time(day, nsec int64) time.Time {
+ return time.Unix((day-epoch_days)*secs_per_day-epoch_secs, nsec).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 FloatTime(date float64) time.Time {
+ day, frac := math.Modf(date)
+ nsec := math.Floor(frac * nsec_per_day)
+ return Time(int64(day), int64(nsec))
+}
+
+// Parse parses a formatted Julian date and returns the UTC Time it represents.
+//
+// This has nanosecond precision.
+func Parse(s string) (time.Time, error) {
+ digits := 0
+ dot := len(s)
+ for i, b := range []byte(s) {
+ if '0' <= b && b <= '9' {
+ digits++
+ continue
+ }
+ if b == '.' && i < dot {
+ dot = i
+ continue
+ }
+ if (b == '+' || b == '-') && i == 0 {
+ continue
+ }
+ return time.Time{}, errors.New("julianday: invalid syntax")
+ }
+ if digits == 0 {
+ return time.Time{}, errors.New("julianday: invalid syntax")
+ }
+
+ day, err := strconv.ParseInt(s[:dot], 10, 64)
+ if err != nil && dot > 0 {
+ return time.Time{}, errors.New("julianday: value out of range")
+ }
+ frac, _ := strconv.ParseFloat(s[dot:], 64)
+ nsec := int64(math.Round(frac * nsec_per_day))
+ if s[0] == '-' {
+ nsec = -nsec
+ }
+ return Time(day, nsec), nil
+}