1// Copyright 2016 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package currency 6 7import ( 8 "sort" 9 "time" 10 11 "golang.org/x/text/language" 12) 13 14// QueryIter represents a set of Units. The default set includes all Units that 15// are currently in use as legal tender in any Region. 16type QueryIter interface { 17 // Next returns true if there is a next element available. 18 // It must be called before any of the other methods are called. 19 Next() bool 20 21 // Unit returns the unit of the current iteration. 22 Unit() Unit 23 24 // Region returns the Region for the current iteration. 25 Region() language.Region 26 27 // From returns the date from which the unit was used in the region. 28 // It returns false if this date is unknown. 29 From() (time.Time, bool) 30 31 // To returns the date up till which the unit was used in the region. 32 // It returns false if this date is unknown or if the unit is still in use. 33 To() (time.Time, bool) 34 35 // IsTender reports whether the unit is a legal tender in the region during 36 // the specified date range. 37 IsTender() bool 38} 39 40// Query represents a set of Units. The default set includes all Units that are 41// currently in use as legal tender in any Region. 42func Query(options ...QueryOption) QueryIter { 43 it := &iter{ 44 end: len(regionData), 45 date: 0xFFFFFFFF, 46 } 47 for _, fn := range options { 48 fn(it) 49 } 50 return it 51} 52 53// NonTender returns a new query that also includes matching Units that are not 54// legal tender. 55var NonTender QueryOption = nonTender 56 57func nonTender(i *iter) { 58 i.nonTender = true 59} 60 61// Historical selects the units for all dates. 62var Historical QueryOption = historical 63 64func historical(i *iter) { 65 i.date = hist 66} 67 68// A QueryOption can be used to change the set of unit information returned by 69// a query. 70type QueryOption func(*iter) 71 72// Date queries the units that were in use at the given point in history. 73func Date(t time.Time) QueryOption { 74 d := toDate(t) 75 return func(i *iter) { 76 i.date = d 77 } 78} 79 80// Region limits the query to only return entries for the given region. 81func Region(r language.Region) QueryOption { 82 p, end := len(regionData), len(regionData) 83 x := regionToCode(r) 84 i := sort.Search(len(regionData), func(i int) bool { 85 return regionData[i].region >= x 86 }) 87 if i < len(regionData) && regionData[i].region == x { 88 p = i 89 for i++; i < len(regionData) && regionData[i].region == x; i++ { 90 } 91 end = i 92 } 93 return func(i *iter) { 94 i.p, i.end = p, end 95 } 96} 97 98const ( 99 hist = 0x00 100 now = 0xFFFFFFFF 101) 102 103type iter struct { 104 *regionInfo 105 p, end int 106 date uint32 107 nonTender bool 108} 109 110func (i *iter) Next() bool { 111 for ; i.p < i.end; i.p++ { 112 i.regionInfo = ®ionData[i.p] 113 if !i.nonTender && !i.IsTender() { 114 continue 115 } 116 if i.date == hist || (i.from <= i.date && (i.to == 0 || i.date <= i.to)) { 117 i.p++ 118 return true 119 } 120 } 121 return false 122} 123 124func (r *regionInfo) Region() language.Region { 125 // TODO: this could be much faster. 126 var buf [2]byte 127 buf[0] = uint8(r.region >> 8) 128 buf[1] = uint8(r.region) 129 return language.MustParseRegion(string(buf[:])) 130} 131 132func (r *regionInfo) Unit() Unit { 133 return Unit{r.code &^ nonTenderBit} 134} 135 136func (r *regionInfo) IsTender() bool { 137 return r.code&nonTenderBit == 0 138} 139 140func (r *regionInfo) From() (time.Time, bool) { 141 if r.from == 0 { 142 return time.Time{}, false 143 } 144 return fromDate(r.from), true 145} 146 147func (r *regionInfo) To() (time.Time, bool) { 148 if r.to == 0 { 149 return time.Time{}, false 150 } 151 return fromDate(r.to), true 152} 153