You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

153 lines
3.4 KiB

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