2024-11-06 10:11:16 +00:00
|
|
|
package utils
|
2024-11-01 15:31:34 +00:00
|
|
|
|
2024-11-01 16:03:10 +00:00
|
|
|
import (
|
|
|
|
"fmt"
|
2024-11-06 10:34:41 +00:00
|
|
|
"math"
|
2024-11-01 16:03:10 +00:00
|
|
|
"strconv"
|
|
|
|
"time"
|
|
|
|
)
|
2024-11-01 15:31:34 +00:00
|
|
|
|
2024-11-01 16:03:10 +00:00
|
|
|
// TimeSince converts an int64 timestamp to a relative time string
|
2024-11-06 10:11:16 +00:00
|
|
|
func TimeSince(timestamp int64) string {
|
2024-11-01 16:03:10 +00:00
|
|
|
var duration time.Duration
|
|
|
|
var suffix string
|
|
|
|
|
|
|
|
t := time.Unix(timestamp, 0)
|
|
|
|
|
|
|
|
if t.After(time.Now()) {
|
|
|
|
duration = time.Until(t)
|
|
|
|
suffix = "from now"
|
|
|
|
} else {
|
|
|
|
duration = time.Since(t)
|
|
|
|
suffix = "ago"
|
|
|
|
}
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case duration < time.Minute:
|
|
|
|
return fmt.Sprintf("%ds %s", int(duration.Seconds()), suffix)
|
|
|
|
case duration < time.Hour:
|
|
|
|
return fmt.Sprintf("%dm %s", int(duration.Minutes()), suffix)
|
|
|
|
case duration < time.Hour*24:
|
|
|
|
return fmt.Sprintf("%dh %s", int(duration.Hours()), suffix)
|
|
|
|
case duration < time.Hour*24*7:
|
|
|
|
return fmt.Sprintf("%dd %s", int(duration.Hours()/24), suffix)
|
|
|
|
case duration < time.Hour*24*30:
|
|
|
|
return fmt.Sprintf("%dw %s", int(duration.Hours()/(24*7)), suffix)
|
|
|
|
default:
|
|
|
|
months := int(duration.Hours() / (24 * 30))
|
|
|
|
if months == 1 {
|
|
|
|
return fmt.Sprintf("1 month %s", suffix)
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("%d months %s", months, suffix)
|
|
|
|
}
|
|
|
|
}
|
2024-11-06 10:34:41 +00:00
|
|
|
|
|
|
|
// Convert the float to a string, trimming unnecessary zeros
|
|
|
|
func FormatFloat(f float64) string {
|
|
|
|
return strconv.FormatFloat(f, 'f', -1, 64)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Formats bytes as a human-readable string with the specified number of decimal places.
|
|
|
|
func FormatBytes(bytes, decimals int) string {
|
|
|
|
if bytes == 0 {
|
|
|
|
return "0 Bytes"
|
|
|
|
}
|
|
|
|
|
|
|
|
const k float64 = 1024
|
|
|
|
sizes := []string{"Bytes", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"}
|
|
|
|
|
|
|
|
i := int(math.Floor(math.Log(float64(bytes)) / math.Log(k)))
|
|
|
|
dm := decimals
|
|
|
|
if dm < 0 {
|
|
|
|
dm = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
value := float64(bytes) / math.Pow(k, float64(i))
|
|
|
|
return fmt.Sprintf("%.*f %s", dm, value, sizes[i])
|
|
|
|
}
|
|
|
|
|
|
|
|
// Formats a hash value (h) into human readable format.
|
|
|
|
//
|
|
|
|
// This function was adapted from jtgrassie/monero-pool project.
|
|
|
|
// Source: https://github.com/jtgrassie/monero-pool/blob/master/src/webui-embed.html
|
|
|
|
//
|
|
|
|
// Copyright (c) 2018, The Monero Project
|
|
|
|
func FormatHashes(h float64) string {
|
|
|
|
switch {
|
|
|
|
case h < 1e-12:
|
|
|
|
return "0 H"
|
|
|
|
case h < 1e-9:
|
|
|
|
return fmt.Sprintf("%.0f pH", maxPrecision(h*1e12, 0))
|
|
|
|
case h < 1e-6:
|
|
|
|
return fmt.Sprintf("%.0f nH", maxPrecision(h*1e9, 0))
|
|
|
|
case h < 1e-3:
|
|
|
|
return fmt.Sprintf("%.0f μH", maxPrecision(h*1e6, 0))
|
|
|
|
case h < 1:
|
|
|
|
return fmt.Sprintf("%.0f mH", maxPrecision(h*1e3, 0))
|
|
|
|
case h < 1e3:
|
|
|
|
return fmt.Sprintf("%.0f H", h)
|
|
|
|
case h < 1e6:
|
|
|
|
return fmt.Sprintf("%.2f KH", maxPrecision(h*1e-3, 2))
|
|
|
|
case h < 1e9:
|
|
|
|
return fmt.Sprintf("%.2f MH", maxPrecision(h*1e-6, 2))
|
|
|
|
default:
|
|
|
|
return fmt.Sprintf("%.2f GH", maxPrecision(h*1e-9, 2))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns a number with a maximum precision.
|
|
|
|
//
|
|
|
|
// This function was adapted from jtgrassie/monero-pool project.
|
|
|
|
// Source: https://github.com/jtgrassie/monero-pool/blob/master/src/webui-embed.html
|
|
|
|
//
|
|
|
|
// Copyright (c) 2018, The Monero Project
|
|
|
|
func maxPrecision(n float64, p int) float64 {
|
|
|
|
format := "%." + strconv.Itoa(p) + "f"
|
|
|
|
result, _ := strconv.ParseFloat(fmt.Sprintf(format, n), 64)
|
|
|
|
return result
|
|
|
|
}
|