ijt / go-anytime

Parse natural and standardized dates/times and ranges in Go without knowing the format in advance
MIT License
23 stars 2 forks source link

Return Precision (of range)? #68

Open oschrenk opened 9 months ago

oschrenk commented 9 months ago

Prerequisites

Description

My notetaking app distinguishes daily and weekly notes (fwiw also monthly, quarterly, and yearly).

go-anytime works great for targeting notes of a specific day ./notes "last friday". Now I'm trying to think of a way to target the weekly notes eg. ./notes "last week"

This of course gives me last week's day back (relative to today).

parsed_, err := anytime.Parse(args[0], time.Now())
parsedRange, _ := anytime.ParseRange(args[0], time.Now())
[...]
2024-01-21 00:00:00 -0600 CST
{time: 2024-01-21 00:00:00 -0600 CST, duration: 167h59m59s}

That is great for most uses cases, but I "lost" the precision that I wss asking for a week (or maybe a month, or a specific hour).

Would you have an idea of how to target the intended precision?

Steps to Reproduce

-

ijt commented 9 months ago

What you're describing here is the original motivation for including the ParseRange function. The duration that you see there is 1 week (168h/(24h/d) = 7 days) minus 1 second.

Is there a reason why you can't use that duration as the precision you're seeking? You can add a second to the duration to compensate for my admittedly stupid design choice there.

oschrenk commented 9 months ago

I see. For some reason I assumed that the range was computed between the parsed range and the relative now. I didn't see that it was already a "precision".

In the end, I use this to convert it to a useful type (for me):

import (
    "fmt"
    "time"
)

type TimePrecision int64

const (
    Day TimePrecision = iota
    Week
    Month
    Year
)

func (tp TimePrecision) hours() int64 {
    switch tp {
    case Day:
        duration, _ := time.ParseDuration("24h")
        return int64(duration.Hours())
    case Week:
        duration, _ := time.ParseDuration("168h")
        return int64(duration.Hours())
    case Month:
        duration, _ := time.ParseDuration("744h")
        return int64(duration.Hours())
    case Year:
        duration, _ := time.ParseDuration("8760h")
        return int64(duration.Hours())
    }
    return -1
}

func isInt(val float64) bool {
    return val == float64(int(val))
}

func BuildTimePrecision(d time.Duration) (TimePrecision, error) {
    fullDurationInHours := (d + time.Second).Hours()
    if !isInt(fullDurationInHours) {
        return -1, fmt.Errorf("Unsupported duration %s. Must be full hour.", d)
    }
    hours := int64(fullDurationInHours)

    if int64(hours/Year.hours()) == 1 {
        return Year, nil
    }

    if int64(hours/Month.hours()) == 1 {
        return Month, nil
    }

    if int64(hours/Week.hours()) == 1 {
        return Week, nil
    }

    if int64(hours/Day.hours()) == 1 {
        return Day, nil
    }

    return -1, fmt.Errorf("Unsupported duration %s. Unknown precision.", d)

}

Am I right to assume that a month is always 31 days, and a year always 365 days as part of go-anytime's understanding of a range?

ijt commented 9 months ago

If I recall correctly, that assumption is not correct. If you specify January 2024 then you'll get a range with 31 days, whereas if you specify February 2024 then you'll get 29 days. Also, you'll get different numbers of days for leap years than for ordinary years.

On Sun, Feb 4, 2024 at 6:51 PM Oliver Schrenk @.***> wrote:

I see. For some reason I assumed that the range was computed between the parsed range and the relative now. I didn't see that it was already a "precisions".

In the end I use this to convert it to a useful type (for me):

import ( "fmt" "time" )

type TimePrecision int64

const ( Day TimePrecision = iota Week Month Year )

func (tp TimePrecision) hours() int64 { switch tp { case Day: duration, := time.ParseDuration("24h") return int64(duration.Hours()) case Week: duration, := time.ParseDuration("168h") return int64(duration.Hours()) case Month: duration, := time.ParseDuration("744h") return int64(duration.Hours()) case Year: duration, := time.ParseDuration("8760h") return int64(duration.Hours()) } return -1 }

func isInt(val float64) bool { return val == float64(int(val)) }

func BuildTimePrecision(d time.Duration) (TimePrecision, error) { fullDurationInHours := (d + time.Second).Hours() if !isInt(fullDurationInHours) { return -1, fmt.Errorf("Unsupported duration %s. Must be full hour.", d) } hours := int64(fullDurationInHours)

if int64(hours/Year.hours()) == 1 { return Year, nil }

if int64(hours/Month.hours()) == 1 { return Month, nil }

if int64(hours/Week.hours()) == 1 { return Week, nil }

if int64(hours/Day.hours()) == 1 { return Day, nil }

return -1, fmt.Errorf("Unsupported duration %s. Unknown precision.", d)

}

Am I right to assume that a month is always 31 days, and a year always 365 days as part of go-anytime's understanding of a range?

— Reply to this email directly, view it on GitHub https://github.com/ijt/go-anytime/issues/68#issuecomment-1926083152, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAADZKQ2TE4KEWXEHR7TU4DYSA3KJAVCNFSM6AAAAABCRLDHE2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSMRWGA4DGMJVGI . You are receiving this because you commented.Message ID: @.***>