Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Checking if current time is in a given interval, GOLANG

Tags:

time

go

I am trying to find a way to check if the current time is in a given interval, where start and end are given (eventually) by a user.

I have been trying by using the After and Before from the Time package after making sure all times are in UTC, but clearly I am doing something wrong.

The code looks similar to this example:

func inTimeSpan(start, end, check time.Time) bool {
    return check.After(start) && check.Before(end)
}

func main() {

    now := time.Now()
    newLayout := "15:04"
    ns, _ := time.Parse(newLayout, strconv.Itoa(now.Hour())+":"+strconv.Itoa(now.Minute()))
    srt, _ := time.Parse(newLayout, "23:00")
    end, _ := time.Parse(newLayout, "05:00")

    fmt.Println("1 : ", inTimeSpan(srt, end, ns))
}

Any help is appreciated.

like image 431
Francesco Avatar asked Mar 11 '19 00:03

Francesco


People also ask

How do you check time in Golang?

Now() function, we can get the current time in Golang by importing time module. Return: Return current date and time. Example #1: In this example, we can see that by using time. Now() function, we are able to get the current date and time.

What is the type of time now () in Golang?

The Now() function in Go language is used to find the current local time. Moreover, this function is defined under the time package. Here, you need to import the “time” package in order to use these functions. Return Value: It returns the current local time.

How do you store dates in go?

For example, a 4-digit year might be represented by %Y . In Go, though, these parts of a date or time are represented by characters that represent a specific date. To include a 4-digit year in a Go date format, you would actually include 2006 in the string itself.


4 Answers

EDIT: It was wrong initially, sorry about that. Fixed version, and also the range now is inclusive. Check also Mickael V. answer as adding time make sense too.

func inTimeSpan(start, end, check time.Time) bool {
    if start.Before(end) {
        return !check.Before(start) && !check.After(end)
    }
    if start.Equal(end) {
        return check.Equal(start)
    }
    return !start.After(check) || !end.Before(check)
}

Check it on playground https://play.golang.org/p/CGWqY2AQ-Th

package main

import (
    "fmt"
    "time"
)

func inTimeSpan(start, end, check time.Time) bool {
    if start.Before(end) {
        return !check.Before(start) && !check.After(end)
    }
    if start.Equal(end) {
        return check.Equal(start)
    }
    return !start.After(check) || !end.Before(check)
}

func main() {
    test := []struct {
        start string
        end   string
        check string
    }{
        {"23:00", "05:00", "04:00"},
        {"23:00", "05:00", "23:30"},
        {"23:00", "05:00", "20:00"},
        {"10:00", "21:00", "11:00"},
        {"10:00", "21:00", "22:00"},
        {"10:00", "21:00", "03:00"},
        // Additional checks.
        {"22:00", "02:00", "00:00"},
        {"10:00", "21:00", "10:00"},
        {"10:00", "21:00", "21:00"},
        {"23:00", "05:00", "06:00"},
        {"23:00", "05:00", "23:00"},
        {"23:00", "05:00", "05:00"},
        {"10:00", "21:00", "10:00"},
        {"10:00", "21:00", "21:00"},
        {"10:00", "10:00", "09:00"},
        {"10:00", "10:00", "11:00"},
        {"10:00", "10:00", "10:00"},
    }
    newLayout := "15:04"
    for _, t := range test {
        check, _ := time.Parse(newLayout, t.check)
        start, _ := time.Parse(newLayout, t.start)
        end, _ := time.Parse(newLayout, t.end)
        fmt.Println(t.start+"-"+t.end, t.check, inTimeSpan(start, end, check))
    }
}

Result:

23:00-05:00 04:00 true
23:00-05:00 23:30 true
23:00-05:00 20:00 false
10:00-21:00 11:00 true
10:00-21:00 22:00 false
10:00-21:00 03:00 false
22:00-02:00 00:00 true
10:00-21:00 10:00 true
10:00-21:00 21:00 true
23:00-05:00 06:00 false
23:00-05:00 23:00 true
23:00-05:00 05:00 true
10:00-21:00 10:00 true
10:00-21:00 21:00 true
10:00-10:00 09:00 false
10:00-10:00 11:00 false
10:00-10:00 10:00 true
like image 197
Kamil Dziedzic Avatar answered Oct 18 '22 18:10

Kamil Dziedzic


For example,

package main

import (
    "fmt"
    "strconv"
    "time"
)

func inTimeSpan(start, end, check time.Time) bool {
    start, end = start.UTC(), end.UTC()
    if start.After(end) {
        start, end = end, start
    }
    check = check.UTC()
    return !check.Before(start) && !check.After(end)
}

func main() {
    now := time.Now()
    newLayout := "15:04"
    ns, _ := time.Parse(newLayout, strconv.Itoa(now.Hour())+":"+strconv.Itoa(now.Minute()))
    srt, _ := time.Parse(newLayout, "23:00")
    end, _ := time.Parse(newLayout, "05:00")

    fmt.Println(srt, end)
    fmt.Println(ns)
    fmt.Println("1 : ", inTimeSpan(srt, end, ns))
}

Output:

0000-01-01 23:00:00 +0000 UTC 0000-01-01 05:00:00 +0000 UTC
0000-01-01 20:37:00 +0000 UTC
1 :  true
like image 32
peterSO Avatar answered Sep 24 '22 06:09

peterSO


Building on Kamil's answer, which is ultimately wrong as its own tests show, here is a solution that makes use only of Go's time interface. The trick, knowing that Go will make Before and After comparison using time AND date, is to just push the end time to the next day if its time component is "smaller" than the start one. Then the time to check needs to be done the same if it's before the start time.

I took the same test cases and added some significant ones like midnight.

package main

import (
    "fmt"
    "time"
)

func inTimeSpan(start, end, check time.Time) bool {
    _end := end
    _check := check
    if end.Before(start) {
        _end = end.Add(24 * time.Hour)
        if check.Before(start) {
            _check = check.Add(24 * time.Hour)
        }
    }
    return _check.After(start) && _check.Before(_end)
}

func main() {
    test := []struct {
        start string
        end   string
        check string
    }{
        {"23:00", "05:00", "04:00"},
        {"23:00", "05:00", "23:30"},
        {"23:00", "05:00", "20:00"},
        {"10:00", "21:00", "11:00"},
        {"10:00", "21:00", "22:00"},
        {"10:00", "21:00", "03:00"},
        {"22:00", "02:00", "00:00"},
        {"10:00", "21:00", "10:00"},
        {"10:00", "21:00", "21:00"},
    }
    newLayout := "15:04"
    for _, t := range test {
        check, _ := time.Parse(newLayout, t.check)
        start, _ := time.Parse(newLayout, t.start)
        end, _ := time.Parse(newLayout, t.end)
        fmt.Println(t.start+"-"+t.end, t.check, inTimeSpan(start, end, check))
    }
}

Output :

23:00-05:00 04:00 true
23:00-05:00 23:30 true
23:00-05:00 20:00 false
10:00-21:00 11:00 true
10:00-21:00 22:00 false
10:00-21:00 03:00 false
22:00-02:00 00:00 true
10:00-21:00 10:00 false
10:00-21:00 21:00 false

You will notice on the two last cases that the check is not inclusive for the start time, nor the end time. If you need to change that behavior, you can remediate this by changing the function a little :

func inTimeSpanEx(start, end, check time.Time, includeStart, includeEnd bool) bool {
    _start := start
    _end := end
    _check := check
    if end.Before(start) {
        _end = end.Add(24 * time.Hour)
        if check.Before(start) {
            _check = check.Add(24 * time.Hour)
        }
    }

    if includeStart {
    _start = _start.Add(-1 * time.Nanosecond)
    }
    if includeEnd {
    _end = _end.Add(1 * time.Nanosecond)
    }

    return _check.After(_start) && _check.Before(_end)
}

Extract of the output when calling with true for inclusion of start and end :

10:00-21:00 10:00 true
10:00-21:00 21:00 true

Complete playground here : https://play.golang.org/p/8ig34gmHl71

like image 2
Mickael V. Avatar answered Oct 18 '22 19:10

Mickael V.


Here is a clean solution which is immune from time zone issues. In my context, I run a function in a concurrent timer thread and it calls this function every 5 minutes to check the time against the given range.

Note that you should use whole minutes only for this to work. While I haven't tested it, I think the time.Kitchen layout will strip seconds during the time conversions. There is no need to specify seconds as the selector will take every time in the range except the exact end time.

There is no accounting for midnight but I didn't need that functionality. If you wanted that you could test

if end.Before(start) {

if timeNow.After(start) {
   return false 
}

if timeNow.After(end) {
   return true
}
return false
} else {

....
}

Here is my function as it is.

func stringToTime(str string) time.Time {
    tm, err := time.Parse(time.Kitchen, str)
    if err != nil {
        fmt.Println("Failed to decode time:", err)
    }
    fmt.Println("Time decoded:", tm)
    return tm
}

func isInTimeRange() bool {

    startTimeString := "06:00AM"  // "01:00PM"

    endTimeString := "06:05AM"

    t := time.Now()

    zone, offset := t.Zone()

    fmt.Println(t.Format(time.Kitchen), "Zone:", zone, "Offset UTC:", offset)

    timeNowString := t.Format(time.Kitchen)

    fmt.Println("String Time Now: ", timeNowString)

    timeNow := stringToTime(timeNowString)

    start := stringToTime(startTimeString)

    end := stringToTime(endTimeString)

    fmt.Println("Local Time Now: ", timeNow)

    if timeNow.Before(start) {
        return false
    }

    if timeNow.Before(end) {
        return true
    }

    return false
}

Output

What's the time, Mr Wolf?

10:58AM Zone: ACST Offset UTC: 34200

String Time Now: 10:58AM

Time decoded: 0000-01-01 10:58:00 +0000 UTC

Time decoded: 0000-01-01 11:00:00 +0000 UTC

Time decoded: 0000-01-01 11:05:00 +0000 UTC

Local Time Now: 0000-01-01 10:58:00 +0000 UTC

What's the time, Mr Wolf?

11:03AM Zone: ACST Offset UTC: 34200

String Time Now: 11:03AM

Time decoded: 0000-01-01 11:03:00 +0000 UTC

Time decoded: 0000-01-01 11:00:00 +0000 UTC

Time decoded: 0000-01-01 11:05:00 +0000 UTC

Local Time Now: 0000-01-01 11:03:00 +0000 UTC

Time to eat you!

like image 1
markhorrocks Avatar answered Oct 18 '22 18:10

markhorrocks