🏡 index : quine-global/quintodrome.git

package utils

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"time"
)

func TimeNewest(times ...time.Time) time.Time {
	newest := time.Time{}
	for _, t := range times {
		if t.After(newest) {
			newest = t
		}
	}
	return newest
}

var durationDayWeekRe = regexp.MustCompile(`-?\d+(?:\.\d+)?[dw]`)

// ParseDuration is time.ParseDuration extended with d (24h) and w (168h) units.
// Negative durations are rejected.
func ParseDuration(s string) (time.Duration, error) {
	expanded := durationDayWeekRe.ReplaceAllStringFunc(s, func(match string) string {
		value, err := strconv.ParseFloat(match[:len(match)-1], 64)
		if err != nil {
			return match
		}
		hours := value * 24
		if match[len(match)-1] == 'w' {
			hours = value * 24 * 7
		}
		return strconv.FormatFloat(hours, 'f', -1, 64) + "h"
	})
	d, err := time.ParseDuration(expanded)
	if err != nil {
		return 0, fmt.Errorf("invalid duration %q: %w", s, err)
	}
	if d < 0 {
		return 0, fmt.Errorf("negative duration not allowed: %q", s)
	}
	return d, nil
}

// FormatDuration renders whole w/d multiples with those units, falling back to
// time.Duration.String for the sub-day remainder, so ParseDuration round-trips.
func FormatDuration(d time.Duration) string {
	if d < 24*time.Hour {
		return formatSubDay(d)
	}
	var b strings.Builder
	weekDuration := 7 * 24 * time.Hour
	if weeks := d / weekDuration; weeks > 0 {
		b.WriteString(strconv.Itoa(int(weeks)) + "w")
		d %= weekDuration
	}
	dayDuration := 24 * time.Hour
	if days := d / dayDuration; days > 0 {
		b.WriteString(strconv.Itoa(int(days)) + "d")
		d %= dayDuration
	}
	if d > 0 {
		b.WriteString(formatSubDay(d))
	}
	return b.String()
}

func formatSubDay(d time.Duration) string {
	if d >= time.Hour && d%time.Hour == 0 {
		return strconv.Itoa(int(d/time.Hour)) + "h"
	}
	return d.String()
}