package common import ( `math` `time` ) /* CurrentTimeStep returns the current time step using the given [time.Duration] `period`. If `period` is equal to time.Duration(0), it will be set to [DefaultTimePeriod]. */ func CurrentTimeStep(period time.Duration) (ts int64) { return TimeStepFromTime(time.Now(), period) } /* OffsetTimeStep is like [CurrentTimeStep] but returns the time step next/previous to the current specified by `offset` (`offset` may be negative or positive). */ func OffsetTimeStep(period time.Duration, offset int64) (ts int64) { var offsetDuration time.Duration period = normalizePeriod(period) offsetDuration = period * time.Duration(offset) ts = TimeStepFromTime(time.Now().Add(offsetDuration), period) return } /* OffsetTimeStepToTime is like [TimeStepToTime] but returns the next/previous time range in relation to time step `ts` specified by `offset` (`offset` may be negative or positive). */ func OffsetTimeStepToTime(ts int64, period time.Duration, offset int64) (start, end time.Time) { return TimeStepToTime(ts+offset, period) } /* TimeStepFromTime returns a time step (see [RFC 6238 § 1.2], [RFC 6238 § 5.2]) from a provided generation time `t` as a [time.Time] and a life length `period` as a [time.Duration]. If `period` is equal to time.Duration(0), it will be set to [DefaultTimePeriod]. [RFC 6238 § 1.2]: https://datatracker.ietf.org/doc/html/rfc6238#section-1.2 [RFC 6238 § 5.2]: https://datatracker.ietf.org/doc/html/rfc6238#section-5.2 */ func TimeStepFromTime(t time.Time, period time.Duration) (ts int64) { period = normalizePeriod(period) ts = int64(math.Floor(float64(t.Unix()) / period.Seconds())) return } /* TimeStepToTime returns a [time.Time] `start` and `end` from a time step `ts` and a lifetime period `period` (as a [time.Duration]). If `period` is equal to time.Duration(0), it will be set to [DefaultTimePeriod]. */ func TimeStepToTime(ts int64, period time.Duration) (start, end time.Time) { var fromEpoch time.Duration period = normalizePeriod(period) fromEpoch = (time.Second * time.Duration(ts*int64(period.Seconds()))).Truncate(time.Second) start = time.Unix(0, 0).Add(fromEpoch).Truncate(time.Second) end = start.Add(period).Truncate(time.Second) return } // VaultEscape is used to normalize a string to a Vault-safe name (for e.g. secrets). func VaultEscape(s *string) { var c rune var idx int var last rune var norm []rune var reduced []rune = make([]rune, 0) if s == nil { return } norm = make([]rune, 0, len(*s)) // https://github.com/hashicorp/vault/issues/5645#issuecomment-434404750 // It seems they relaxed this a bit since then, as hyphens and periods are also allowed. // https://github.com/hashicorp/vault/blob/main/sdk/framework/path.go#L26 // At time of writing, the regex is: (?P\\w(([\\w-.@]+)?\\w)?) // Per comments: "alphanumeric characters along with -, . and @." for _, c = range *s { // If it's "safe" chars, it's fine if (c == '-' || c == '.') || // 0x2d, 0x2e (c >= '0' && c <= '9') || // 0x30 to 0x39 (c == '@') || // 0x40 (c >= 'A' && c <= 'Z') || // 0x41 to 0x5a (c == '_') || // 0x5f (c >= 'a' && c <= 'z') { // 0x61 to 0x7a norm = append(norm, c) continue } // Otherwise normalize it to a safe char norm = append(norm, VaultRepl) } // And remove repeating sequential replacers. for idx, c = range norm[:] { if idx == 0 { last = c reduced = append(reduced, c) continue } if c == last && last == VaultRepl { continue } reduced = append(reduced, c) last = c } *s = string(reduced) return } // normalizedPeriod returns `period` if non-zero, otherwise [DefaultTimePeriod]. func normalizePeriod(period time.Duration) (normalized time.Duration) { if period != time.Duration(0) { normalized = period return } normalized = DefaultTimePeriod return }