152 lines
3.4 KiB
Go
152 lines
3.4 KiB
Go
// Copyright 2016 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package currency
|
|
|
|
import (
|
|
"sort"
|
|
"time"
|
|
|
|
"golang.org/x/text/language"
|
|
)
|
|
|
|
// QueryIter represents a set of Units. The default set includes all Units that
|
|
// are currently in use as legal tender in any Region.
|
|
type QueryIter interface {
|
|
// Next returns true if there is a next element available.
|
|
// It must be called before any of the other methods are called.
|
|
Next() bool
|
|
|
|
// Unit returns the unit of the current iteration.
|
|
Unit() Unit
|
|
|
|
// Region returns the Region for the current iteration.
|
|
Region() language.Region
|
|
|
|
// From returns the date from which the unit was used in the region.
|
|
// It returns false if this date is unknown.
|
|
From() (time.Time, bool)
|
|
|
|
// To returns the date up till which the unit was used in the region.
|
|
// It returns false if this date is unknown or if the unit is still in use.
|
|
To() (time.Time, bool)
|
|
|
|
// IsTender reports whether the unit is a legal tender in the region during
|
|
// the specified date range.
|
|
IsTender() bool
|
|
}
|
|
|
|
// Query represents a set of Units. The default set includes all Units that are
|
|
// currently in use as legal tender in any Region.
|
|
func Query(options ...QueryOption) QueryIter {
|
|
it := &iter{
|
|
end: len(regionData),
|
|
date: 0xFFFFFFFF,
|
|
}
|
|
for _, fn := range options {
|
|
fn(it)
|
|
}
|
|
return it
|
|
}
|
|
|
|
// NonTender returns a new query that also includes matching Units that are not
|
|
// legal tender.
|
|
var NonTender QueryOption = nonTender
|
|
|
|
func nonTender(i *iter) {
|
|
i.nonTender = true
|
|
}
|
|
|
|
// Historical selects the units for all dates.
|
|
var Historical QueryOption = historical
|
|
|
|
func historical(i *iter) {
|
|
i.date = hist
|
|
}
|
|
|
|
// A QueryOption can be used to change the set of unit information returned by
|
|
// a query.
|
|
type QueryOption func(*iter)
|
|
|
|
// Date queries the units that were in use at the given point in history.
|
|
func Date(t time.Time) QueryOption {
|
|
d := toDate(t)
|
|
return func(i *iter) {
|
|
i.date = d
|
|
}
|
|
}
|
|
|
|
// Region limits the query to only return entries for the given region.
|
|
func Region(r language.Region) QueryOption {
|
|
p, end := len(regionData), len(regionData)
|
|
x := regionToCode(r)
|
|
i := sort.Search(len(regionData), func(i int) bool {
|
|
return regionData[i].region >= x
|
|
})
|
|
if i < len(regionData) && regionData[i].region == x {
|
|
p = i
|
|
for i++; i < len(regionData) && regionData[i].region == x; i++ {
|
|
}
|
|
end = i
|
|
}
|
|
return func(i *iter) {
|
|
i.p, i.end = p, end
|
|
}
|
|
}
|
|
|
|
const (
|
|
hist = 0x00
|
|
now = 0xFFFFFFFF
|
|
)
|
|
|
|
type iter struct {
|
|
*regionInfo
|
|
p, end int
|
|
date uint32
|
|
nonTender bool
|
|
}
|
|
|
|
func (i *iter) Next() bool {
|
|
for ; i.p < i.end; i.p++ {
|
|
i.regionInfo = ®ionData[i.p]
|
|
if !i.nonTender && !i.IsTender() {
|
|
continue
|
|
}
|
|
if i.date == hist || (i.from <= i.date && (i.to == 0 || i.date <= i.to)) {
|
|
i.p++
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (r *regionInfo) Region() language.Region {
|
|
// TODO: this could be much faster.
|
|
var buf [2]byte
|
|
buf[0] = uint8(r.region >> 8)
|
|
buf[1] = uint8(r.region)
|
|
return language.MustParseRegion(string(buf[:]))
|
|
}
|
|
|
|
func (r *regionInfo) Unit() Unit {
|
|
return Unit{r.code &^ nonTenderBit}
|
|
}
|
|
|
|
func (r *regionInfo) IsTender() bool {
|
|
return r.code&nonTenderBit == 0
|
|
}
|
|
|
|
func (r *regionInfo) From() (time.Time, bool) {
|
|
if r.from == 0 {
|
|
return time.Time{}, false
|
|
}
|
|
return fromDate(r.from), true
|
|
}
|
|
|
|
func (r *regionInfo) To() (time.Time, bool) {
|
|
if r.to == 0 {
|
|
return time.Time{}, false
|
|
}
|
|
return fromDate(r.to), true
|
|
}
|