@@ -139,6 +139,140 @@ public static function forNumericCode(int $numericCode): array
139
139
return self ::readEntry (['NumericToAlpha3 ' , (string ) $ numericCode ], 'meta ' );
140
140
}
141
141
142
+ /**
143
+ * @param string $country e.g. 'FR'
144
+ * @param ?bool $legalTender If the currency must be a legal tender; null to not filter anything
145
+ * @param ?bool $active Indicates whether the currency should always be active for the given $date; null to not filter anything
146
+ * @param \DateTimeInterface $date The date on which the check will be performed
147
+ *
148
+ * @return list<string> a list of unique currencies
149
+ *
150
+ * @throws MissingResourceException if the given $country does not exist
151
+ */
152
+ public static function forCountry (string $ country , ?bool $ legalTender = true , ?bool $ active = true , \DateTimeInterface $ date = new \DateTimeImmutable ('today ' , new \DateTimeZone ('Etc/UTC ' ))): array
153
+ {
154
+ $ currencies = [];
155
+
156
+ foreach (self ::readEntry (['Map ' , $ country ], 'meta ' ) as $ currency => $ currencyMetadata ) {
157
+ if (null !== $ legalTender && $ legalTender !== self ::isLegalTender ($ currencyMetadata )) {
158
+ continue ;
159
+ }
160
+
161
+ if (null === $ active ) {
162
+ $ currencies [] = $ currency ;
163
+
164
+ continue ;
165
+ }
166
+
167
+ if (self ::isDateActive ($ country , $ currency , $ currencyMetadata , $ date ) !== $ active ) {
168
+ continue ;
169
+ }
170
+
171
+ $ currencies [] = $ currency ;
172
+ }
173
+
174
+ return $ currencies ;
175
+ }
176
+
177
+ /**
178
+ * @param string $country e.g. 'FR'
179
+ * @param string $currency e.g. 'USD'
180
+ * @param ?bool $legalTender If the currency must be a legal tender; null to not filter anything
181
+ * @param ?bool $active Indicates whether the currency should always be active for the given $date; null to not filter anything
182
+ * @param \DateTimeInterface $date The date that will be checked when $active is set to true
183
+ */
184
+ public static function isValidInCountry (string $ country , string $ currency , ?bool $ legalTender = true , ?bool $ active = true , \DateTimeInterface $ date = new \DateTimeImmutable ('today ' , new \DateTimeZone ('Etc/UTC ' ))): bool
185
+ {
186
+ if (!self ::exists ($ currency )) {
187
+ throw new \InvalidArgumentException ("The currency $ currency does not exist. " );
188
+ }
189
+
190
+ try {
191
+ $ currencyMetadata = self ::readEntry (['Map ' , $ country , $ currency ], 'meta ' );
192
+ } catch (MissingResourceException ) {
193
+ return false ;
194
+ }
195
+
196
+ if (null !== $ legalTender && $ legalTender !== self ::isLegalTender ($ currencyMetadata )) {
197
+ return false ;
198
+ }
199
+
200
+ if (null === $ active ) {
201
+ return true ;
202
+ }
203
+
204
+ return self ::isDateActive ($ country , $ currency , $ currencyMetadata , $ date ) === $ active ;
205
+ }
206
+
207
+ /**
208
+ * @param array{tender?: bool} $currencyMetadata When the `tender` property does not exist, it means it is a legal tender
209
+ */
210
+ private static function isLegalTender (array $ currencyMetadata ): bool
211
+ {
212
+ return !\array_key_exists ('tender ' , $ currencyMetadata ) || false !== $ currencyMetadata ['tender ' ];
213
+ }
214
+
215
+ /**
216
+ * @param string $country e.g. 'FR'
217
+ * @param string $currency e.g. 'USD'
218
+ * @param array{from?: string, to?: string} $currencyMetadata
219
+ * @param \DateTimeInterface $date The date on which the check will be performed
220
+ */
221
+ private static function isDateActive (string $ country , string $ currency , array $ currencyMetadata , \DateTimeInterface $ date ): bool
222
+ {
223
+ if (!\array_key_exists ('from ' , $ currencyMetadata )) {
224
+ // Note: currencies that are not legal tender don't have often validity dates.
225
+ throw new \RuntimeException ("Cannot check whether the currency $ currency is active or not in $ country because they are no validity dates available. " );
226
+ }
227
+
228
+ $ from = \DateTimeImmutable::createFromFormat ('Y-m-d ' , $ currencyMetadata ['from ' ]);
229
+
230
+ if (\array_key_exists ('to ' , $ currencyMetadata )) {
231
+ $ to = \DateTimeImmutable::createFromFormat ('Y-m-d ' , $ currencyMetadata ['to ' ]);
232
+ } else {
233
+ $ to = null ;
234
+ }
235
+
236
+ return $ from <= $ date && (null === $ to || $ to >= $ date );
237
+ }
238
+
239
+ /**
240
+ * @param string $currency e.g. 'USD'
241
+ * @param ?bool $legalTender If the currency must be a legal tender; null to not filter anything
242
+ * @param ?bool $active Indicates whether the currency should always be active for the given $date; null to not filter anything
243
+ * @param \DateTimeInterface $date the date on which the check will be performed if $active is set to true
244
+ */
245
+ public static function isValidInAnyCountry (string $ currency , ?bool $ legalTender = true , ?bool $ active = true , \DateTimeInterface $ date = new \DateTimeImmutable ('today ' , new \DateTimeZone ('Etc/UTC ' ))): bool
246
+ {
247
+ if (!self ::exists ($ currency )) {
248
+ throw new \InvalidArgumentException ("The currency $ currency does not exist. " );
249
+ }
250
+
251
+ foreach (self ::readEntry (['Map ' ], 'meta ' ) as $ countryCode => $ country ) {
252
+ foreach ($ country as $ currencyCode => $ currencyMetadata ) {
253
+ if ($ currencyCode !== $ currency ) {
254
+ continue ;
255
+ }
256
+
257
+ if (null !== $ legalTender && $ legalTender !== self ::isLegalTender ($ currencyMetadata )) {
258
+ continue ;
259
+ }
260
+
261
+ if (null === $ active ) {
262
+ return true ;
263
+ }
264
+
265
+ if (self ::isDateActive ($ countryCode , $ currencyCode , $ currencyMetadata , $ date ) !== $ active ) {
266
+ continue ;
267
+ }
268
+
269
+ return true ;
270
+ }
271
+ }
272
+
273
+ return false ;
274
+ }
275
+
142
276
protected static function getPath (): string
143
277
{
144
278
return Intl::getDataDirectory ().'/ ' .Intl::CURRENCY_DIR ;
0 commit comments