summaryrefslogtreecommitdiffstats
path: root/time/time.go
blob: 89847ab8e2b3d15e66097fe83d9ad0a26f9c3c52 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
// Copyright © 2022 siddharth ravikumar <s@ricketyspace.net>
// SPDX-License-Identifier: ISC

// ISO 8601 utility functions
package time

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

// ISO 8601 Duration regex for matching duration in PT3H4M60S format.
var durationRegex = regexp.MustCompile(`PT(([0-9]{0,2})?H)?(([0-9]{0,2})?M)?(([0-9]{0,2}?)S)?`)

// Converts ISO 8601 duration[1] to time.Duration
//
// Recognizes durations in this format: PT3H4M60S
//
// [1]: https://en.wikipedia.org/wiki/ISO_8601#Durations
func Duration(duration string) (time.Duration, error) {
	m := durationRegex.FindStringSubmatch(duration)
	if m == nil || len(m) == 0 {
		return 0, fmt.Errorf("duration invalid: %v", duration)
	}
	hours, err := strconv.Atoi(m[2])
	if err != nil {
		hours = 0
	}
	mins, err := strconv.Atoi(m[4])
	if err != nil {
		mins = 0
	}
	secs, err := strconv.Atoi(m[6])
	if err != nil {
		secs = 0
	}

	// Add 'em all together.
	secs += hours * 3600
	secs += mins * 60

	// Convert seconds to time.Duration.
	d, err := time.ParseDuration(fmt.Sprintf("%ds", secs))

	return d, nil
}

// Checks if the given ISO 8601 time is within the current time
// period.
//
// `t` must be in 2022-08-07T02:00:00+00:00/PT1H format
//
// Returns true if the time `t` is the current time period; false
// otherwise.
func IsCurrent(t string) (bool, error) {
	parts := strings.Split(t, "/")
	if len(parts) != 2 {
		return false, fmt.Errorf("time invalid")
	}

	// Parse time `t` into time intervals t1 and t2.
	t1, err := time.Parse(time.RFC3339, parts[0])
	if err != nil {
		return false, fmt.Errorf("time invalid: %s", err)
	}
	d, err := Duration(parts[1])
	if err != nil {
		return false, fmt.Errorf("time invalid: %s", err)
	}
	t2 := t1.Add(d)

	// Time `t` is in the current time period if current time is
	// within the interval t1 and t2.
	now := time.Now()
	if t1.Before(now) && now.Before(t2) {
		return true, nil
	}
	if t1.Equal(now) || t2.Equal(now) {
		return true, nil
	}
	return false, nil
}