Powershell 通貨に変換するときにCultureinfoを使う方法

# 確認したバージョン
Powershell 5.1
# Stackoverflowにヒントあり
[Convert number to currency](https://stackoverflow.com/questions/57061395/convert-number-to-currency)
>Beware that the resulting formatted string will depend on the current locale. You can pass a [cultureinfo] object to the ToString() method of the target object instead if you want a specific locale enforced. Here shown with en-US, de-DE and da-DK:

>```powershell
PS C:\> 'en-US','de-DE','da-DK' |ForEach-Object { $dollars.ToString('C',[cultureinfo]$_) }
$1,234.56
1.234,56 €
1.234,56 kr.
```

# 日本語 ja-JP環境CultureInfoを指定してドルにする場合

## ToStringを使えば指定できる

```powershell
PS >$(789).ToString('C2',[cultureinfo]'en-US')
$789.00
PS >$(789).ToString('C',[cultureinfo]'en-US')
$789.00
```

[日付の英語表記を取得する（Excel, VBA, PowerShell）](https://qiita.com/nukie_53/items/5cbc7197e507b2602e75)
こちらの日付についての書き方を参考にすると、こうも書けます。

```Powershell
PS >$(1532.445).ToString('C', [Globalization.CultureInfo]::GetCultureInfo('en-US'))
$1,532.45
PS >$(1532.445).ToString('C', [Globalization.CultureInfo]::GetCultureInfo('en-US').numberformat)
$1,532.45

PS >[Globalization.CultureInfo]::GetCultureInfo('en-US').numberformat
CurrencyDecimalDigits    : 2
CurrencyDecimalSeparator : .
IsReadOnly               : True
CurrencyGroupSizes       : {3}
NumberGroupSizes         : {3}
PercentGroupSizes        : {3}
CurrencyGroupSeparator   : ,
CurrencySymbol           : $
NaNSymbol                : NaN
CurrencyNegativePattern  : 0
NumberNegativePattern    : 1
PercentPositivePattern   : 1
PercentNegativePattern   : 1
NegativeInfinitySymbol   : -∞
NegativeSign             : -
NumberDecimalDigits      : 2
NumberDecimalSeparator   : .
NumberGroupSeparator     : ,
CurrencyPositivePattern  : 0
PositiveInfinitySymbol   : ∞
PositiveSign             : +
PercentDecimalDigits     : 2
PercentDecimalSeparator  : .
PercentGroupSeparator    : ,
PercentSymbol            : %
PerMilleSymbol           : ‰
NativeDigits             : {0, 1, 2, 3...}
DigitSubstitution        : None
```

Cを指定することで、通貨に設定されている桁区切り、桁区切り文字、通貨単位が設定されるようです。

## 米ドルはC2にしなくても自動的に小数点2桁になる
米ドルの場合、自動的に小数点2桁までになるためCだけでも設定できます。
これはCultureInfoで決まっているためでしょう。
円はja-JPです。円はコンマがつくところまで入っているようです。
コンビニで円を小数点2桁まで表示するということが報道されていますが、そんなときはC2を使います。

```powershell
$(1789.11).tostring('C2',[cultureinfo]'ja-JP')
```

## Tostringの場合シングル(ダブル)クォーテーションで囲むとエラーになる

```Powershell
$('1789').ToString('C',[cultureinfo]'en-US')
"tostring" のオーバーロードで、引数の数が "2" であるものが見つかりません。
発生場所 行:1 文字:3
+ $('1789').tostring('C',[cultureinfo]'en-US')
+   ~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodCountCouldNotFindBest
    +  $("1789").ToString('C',[cultureinfo]'en-US')
"tostring" のオーバーロードで、引数の数が "2" であるものが見つかりません。
発生場所 行:1 文字:3
+ $("1789").ToString('C',[cultureinfo]'en-US')
+   ~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodCountCouldNotFindBest
```

ドル記号の次に数字をカッコで囲むと数字として扱われるためだと思われます。
型指定をすると回避できるようです。

## Decimalで指定（キャスト）すると回避

```powershell
$([System.Decimal]"1789").tostring('C',[cultureinfo]'en-US')
$1,789.00
```
### Decimalでアメリカドルだと3桁目が四捨五入される
C2もCultureInfoも自動的に四捨五入されます。

```powershell
$([System.Decimal]"1789.33").tostring('C',[cultureinfo]'en-US')
$1,789.33
$([System.Decimal]"1789.345").tostring('C',[cultureinfo]'en-US')
$1,789.35
```


# -f Operaterを使った場合はCaltureInfoは使えない
直接は使えないようですが、一時的に変更してフォーマット演算子を置くとできます。しかし不安定です。

`-f`はPowerShellではフォーマット演算子(Format Operator )になります。
`変換パターン -f 変換される値、変数`
という関係になります。コンマで区切られた値の場合には順番を入れ替えることができます。
正規表現の`{\1}`に似ています。しかし、`ToString`と異なり、`ja-JP`などというものを入れる余地がありません。

```powershell
$newNumber = "{0:c}" -f $rawNumber
```

この`"{0:c}" -f `はサンプルが多いのですが、cultureinfoは基本的にCurrentしか使えません。
この場合ja-JPを前提とすると、

```powershell
"{0:C}円" -f 1234.56
¥1,235円
'{0:C}円' -f 1234.56
¥1,235円
"{0:C}円" -f "1234.56"
1234.56円
'{0:C}円' -f "1234.56"
1234.56円
'{0:C2}円' -f 1234.56
¥1,234.56円
'{0:C}円' -f 1,234.56
¥1円
```

<UL>
<li>`{}`0：Cを囲んでいるのは中括弧です。大カッコはエラーです
<li>囲むのはシングルクォーテーションかダブルクォーテーションのペアです。どちらも使えます。
<li> -f のあとには数字だと認識できる数字の列が必要です。このためなにも囲まないほうがいいです。
<li> {0:C}の場合、小数点以下は四捨五入されます。
<li> 数字にコンマがついていると失敗します。
<li> `'{0:C}円'`とすると、¥1,235円のようになります。
<li> `'{0:C2}円'`とすると、¥1,234.56円のようになります。
<li> `'{0:C}'` だけだと、常に &Yen; とコンマがつくのみです。
</UL>

## [cultureinfo]::CurrentCultureに並べると変換される

```powershell
[cultureinfo]::CurrentCulture = 'en-US' ; 'val {0:C} ' -f  $(125.35)
val $125.35
```

### ただしこの方法ではja-JPに戻さないと、アメリカのままになったりする

```powershell
[cultureinfo]::CurrentCulture = 'en-US' ; 'val {0:C} ' -f  $(125.35)
val $125.35
[cultureinfo]::currentculture

LCID             Name             DisplayName
----             ----             -----------
1033             en-US            英語 (米国)

[cultureinfo]::currentculture = 'ja-JP'
[cultureinfo]::currentculture

LCID             Name             DisplayName
----             ----             -----------
1041             ja-JP            日本語 (日本)
```



```powershell
[cultureinfo]::currentculture = 'da-DE' ; 'val {0:C} ' -f  $(1200.568)
val 1.200,57 €
[cultureinfo]::CurrentCulture

LCID             Name             DisplayName
----             ----             -----------
1041             ja-JP            日本語 (日本)
```


同じ命令のはずなんですが。。。
[powershell : changing the culture of current session](https://stackoverflow.com/questions/4105224/how-to-set-culture-in-powershell/4106821)
とりあえず安定しているかどうかは別として`[cultureinfo]::currentculture = 'da-DE' ;`と同じラインにある限りは有効になるとしています。
しかし、長い上になぜか変な挙動をします。


```powershell
[System.Threading.Thread]::CurrentThread.CurrentCulture = 'da-DE' ; 'val {0:C} ' -f  $(1200.568)
val 1.200,57 €
[cultureinfo]::CurrentCulture

LCID             Name             DisplayName
----             ----             -----------
1041             ja-JP            日本語 (日本)
```

Powershell5.1では古い命令も使えました。

```powershell
[System.Threading.Thread]::CurrentThread.CurrentCulture = 'da-DE' ; 'val {0:C} ' -f  $(1200.568)
val 1.200,57 €
[cultureinfo]::CurrentCulture


LCID             Name             DisplayName
----             ----             -----------
4096             da-DE            不明なロケール (da-DE)
```

Powershell7.1でも使えましたが、やはり戻りません。そもそもドイツ語は1031です。

## この場合番号も使える

```Powershell
[cultureinfo]::CurrentCulture = 1031

[cultureinfo]::CurrentCulture

LCID             Name             DisplayName
----             ----             -----------
1031             de-DE            ドイツ語 (ドイツ)
[cultureinfo]::CurrentCulture = 1041
```

Powershell7.1やはり戻らないですね。


## フォーマット演算子は円やドルにするとき書式指定文字は使える

```powershell
'{0:#,##0円}' -f 1234.56
1,235円
'{0:#,#}円' -f 1234.568
1,235円
'{0:#,#円}' -f 1234.568
1,235円
'\{0:#,#0円}' -f 1234.568
\1,235円
```

ものすごく強引な感じですが、円記号を追加することができます。あとで使うNと同じように円マークを前置できます。
このため、ドルに変えるといいわけです。


```powershell
'${0:#,#0.00USD}' -f 1234.568
$1,234.57USD
'${0:#,#0.00USD}' -f 0
$0.00USD
'${0:#,#.##USD}' -f 0
$USD
```

この場合はドルの前にエスケープ記号が必要なように見えていりません。
`#`と0の違いは0だと何も表示しないのが`#`で0が表示されるのが`0`の場合です

```powershell
'${0:#,#0.##USD}' -f 0
$0USD

'${0:#,#0.00USD}' -f 0
$0.00USD
```

このように0のときの表示が違ってきます。

### さらに千円、百万円

```Powershell
[System.Threading.Thread]::CurrentThread.CurrentCulture = 'ja-jp' ; '{0:#,#,}千円' -f  [int64]$(123456789012)
123,456,789千円
[System.Threading.Thread]::CurrentThread.CurrentCulture = 'ja-jp' ; '{0:#,#,,}百万円' -f  [int64]$(123456789012)
123,457百万円
```

[カスタム数値形式文字列](https://docs.microsoft.com/ja-jp/dotnet/standard/base-types/custom-numeric-format-strings)
>数値位取り指定子: 明示的または暗黙的な小数点のすぐ左側に 1 つ以上のコンマが指定されている場合は、コンマごとに書式設定対象の数値が 1000 で除算されます。 たとえば、"0,," 文字列を使用して数値 1 億が書式設定された場合、出力は "100" となります。

これはExcelでもあります。


## Nを使って円を追加する
Nを使うと、自動的に1000円単位で桁区切り文字が追加されます。
これと円を追加することができます。
小数点以下の桁数も指定できます。
なお、桁区切り文字は現在のCultureinfoで決定されます。


```powershell
'{0:N2}円' -f 1234.568
1,234.57円
'{0:N2}円' -f 0 
0.00円
```

N2などと、小数点を指定すると、必ず指定した小数点まで表記されます。


### Nだけでもあくまでも文字列になる

```powershell
$i = '{0:N2}' -f 1234.568
$i.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object

[int32]$i = '{0:N2}' -f 1234.568
MetadataError: Cannot convert value "1.234,57" to type "System.Int32". Error: "Input string was not in a correct format."


```

仮に、シンプルにNだけを使ったとしてもあくまでも文字列になります。



[しばたテック](https://blog.shibata.tech/entry/2015/12/03/000000)
>文字列のフォーマットを行う演算子です。
書式の指定は.NETの書式と同じになります。

とはいうものの、Format演算子の場合はCultureを使うと安定しません。なぜかGet-Dateと異なり、もとに戻す作業が必要だったりします。

## &yen; 記号は前置する

```powershell
'\{0:N2}円' -f 1234.568
\1,234.57円
```

ネットではスラッシュですが、Powershellの画面では円マークになります。

```powershell
"{0:# degrees}" -f 132
132 degrees
```

# まとめ
CultureInfoはTostirngのときに有効
ただし、常に&Yen;記号が前置され、コンマ形式になる。
これをCultureinfoで変更することができる。
フォーマット演算子はカレントカルチャーのみで、変更する方法はあったが、なぜか安定しない。見つかりはしたが、とても長い。
ただし、表示形式を工夫したりNを使うと、円表示ができる。こちらかTostringを使うほうが早い。
これを使ってUSDにもできる。

# 参考文献
[日本語 POWERSHELLについてよくある質問](https://powershell-guru.com/faq-powershell-in-japanese/)
ここも翻訳しているのでドルで表示されています。
[.NET カスタム数値書式](https://docs.microsoft.com/ja-jp/dotnet/standard/base-types/custom-numeric-format-strings?redirectedfrom=MSDN)
[型演算子](https://docs.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_type_operators?view=powershell-5.1)
この型演算子は.NET型としている。
また、as演算子はDateTime型の場合、現在のカルチャーに依存するとしている。

```powershell
[cultureinfo]::CurrentCulture = 'fr-FR'
```

またこの記事のこの部分は現在のカルチャーを示しているだけで、これでフランス語に終局的に変更できることはない。同じラインの中であれば変更される。

```Powershell
[cultureinfo]::CurrentCulture = 'de-DE'; Get-Date '2017-01-01'
```

ただし常に無効ではなく、Get-Dateは有効。この場合でも一時的であり、完全に変更することはない。同じラインであればフォーマット演算子でも一時的に変更されるはずだが、もとに戻らない場合もある。

[PowerShell, formatting values in another culture](https://stackoverflow.com/questions/2379514/powershell-formatting-values-in-another-culture)
この記事が重要だが、このなかでinVariantCultureが紹介されている。
日本語でこれを指定すると通貨は &yen;がつく。
[How-to: The -f Format operator](https://ss64.com/ps/syntax-f-operator.html)
>:c 	Currency format (for the current culture)

ここではこのように説明されている。

## 補足
### Cultureinfoの一覧の出力
前記StackOverflow

```powershell
[System.Globalization.CultureInfo]::GetCultures( [System.Globalization.CultureTypes]::AllCultures )
```

[PowerShellで[DateTime]::ParseExact利用時にCultureInfoを指定する 2013/2/9](https://tech.guitarrapc.com/entry/2013/02/09/040209)

```powershell
[System.Globalization.Cultureinfo]::getcultures([System.Globalization.CultureTypes]::SpecificCultures)
```

別解としてこれでも出力できる。


### Cultureinfoのリストは出力のスクリプトで微妙に異なる
127が出るか出ないかが違う。
InvariantCultureまで入れるとAllCultureほうがいいかもしれない。

```powershell
[System.Globalization.CultureInfo]::GetCultures( [System.Globalization.CultureTypes]::AllCultures )
LCID             Name             DisplayName
----             ----             -----------
127                               ロケールに依存しない言語 (ロケールに依存しない国)
4096             aa               アファル語
4096             aa-DJ            アファル語 (ジブチ)
4096             aa-ER            アファル語 (エリトリア)
4096             aa-ET            アファル語 (エチオピア)
54               af               アフリカーンス語
4096             af-NA            アフリカーンス語 (ナミビア)
1078             af-ZA            アフリカーンス語 (南アフリカ)
4096             agq              アゲム語
4096             agq-CM           アゲム語 (カメルーン)
```

```powershell
[System.Globalization.Cultureinfo]::getcultures([System.Globalization.CultureTypes]::SpecificCultures)
```


```powershell
LCID             Name             DisplayName                                                                                                                    
----             ----             -----------                                                                                                                    
4096             aa-DJ            アファル語 (ジブチ)                                                                                                                    
4096             aa-ER            アファル語 (エリトリア)                                                                                                                  
4096             aa-ET            アファル語 (エチオピア)                                                                                                                  
4096             af-NA            アフリカーンス語 (ナミビア)                                                                                                                
1078             af-ZA            アフリカーンス語 (南アフリカ)                                                                                                               
4096             agq-CM           アゲム語 (カメルーン)       
```

### 4096エラー

[powershell - powershell言語を一時的に英語に変更しますか？](https://cloud6.net/so/powershell/2143855)


```
  # Note: In Windows 10, a culture-info object can be created from *any* string.
  #        However, an identifier that does't refer to a *predefined* culture is 
  #        reflected in .LCID containing 4096 (0x1000)
  if ($Culture.LCID -eq 4096) { Throw "Unrecognized culture: $($Culture.DisplayName)" }
```

###InVariantCulture
[system.globalization.cultureinfo.invariantculture](https://docs.microsoft.com/ja-jp/dotnet/api/system.globalization.cultureinfo.invariantculture?view=net-5.0)

### TostringはC#的な書き方もできる
[ToString(String, IFormatProvider) ](https://docs.microsoft.com/ja-jp/dotnet/api/system.datetime.tostring?redirectedfrom=MSDN&view=net-5.0#System_DateTime_ToString_System_String_System_IFormatProvider_)
これも dotonet / apiではあるが、サンプルはC#となっている。


### 0の意味
0はインデックス。本当はコンマでどんどん続けられる。
また、同じ0番の文字列を使い回すこともできる。

```powershell
"{2} \{0:C} {1}" -f 123,"円","もも肉gあたり"
もも肉gあたり \¥123 円
'{2} \{0:C} {1}' -f 123,"円","もも肉gあたり"
もも肉gあたり \¥123 円
'{2} （税込み）{0:C} {1}' -f 123,"円","もも肉gあたり"
もも肉gあたり （税込み）¥123 円
'{2} {0:C}円 {1}' -f 123,"（税込）","もも肉gあたり"
もも肉gあたり ¥123円 （税込）
```

さらにこの場合でも &yen; マークを前置したり、円を後置できる




### 正式なフォーマット演算子の構文

```powershell
"{I,A:FS} {I,A:FS} {I,A:FS}.." -f "string0", "string1", "string2"...
```

Iがインデックス Aがアロケート、FSがフォーマットストリング
ただし、{}の前後に文字をおいてもよい。また順番を入れ替えてもよい。
### 中括弧のエスケープは中括弧
"{0} vs. {{0}}" -f 'foo'



### アロケート

```powershell
'{2,-3} {0:C}円 {1}' -f 123,"（税込）","もも肉gあたり"
もも肉gあたり ¥123円 （税込）
'{2,5} {0:C}円 {1}' -f 123,"（税込）","もも肉gあたり"
もも肉gあたり ¥123円 （税込）
'{2,5} {0:C}円 {1,5}' -f 123,"（税込）","もも肉gあたり"
もも肉gあたり ¥123円  （税込）
'{2,5} {0:C}円 {1,6}' -f 123,"（税込）","もも肉gあたり"
もも肉gあたり ¥123円   （税込）
'{2,5} {0,1:C}円 {1,6}' -f 123,"（税込）","もも肉gあたり"
もも肉gあたり ¥123円   （税込）
'{2,5} {0,7:C}円 {1,6}' -f 123,"（税込）","もも肉gあたり"
もも肉gあたり    ¥123円   （税込）
```

マイナスだと左に、プラスだと右に行く。なお、1文字単位では動かないようだ。
また左側に行けない場合にはマイナスをつけても無効。また、文字が重なりそうな場合にも動かない。

## Powershell1.0の記事:Currencyf型がないのは.NETがサポートしていないからだとのこと
[Converting VBScript's CCur Function](https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-powershell-1.0/ee156802(v=technet.10)?redirectedfrom=MSDN)
>Windows PowerShell uses the same data types as the `.NET` Framework; because the .NET Framework does not support the Currency data type that means Windows PowerShell doesn’t support this data type, either. However, while you cannot convert a variable to the Currency data type you can at least format the value so that it looks like a currency value when displayed onscreen. The following command assigns the value 13 to the variable `$a` and formats the value so it displays as currency:
Windows PowerShellは、`.NETFramework`と同じデータ型を使用します。 `.NET Framework`はCurrencyデータ型をサポートしていないため、WindowsPowerShellもこのデータ型をサポートしていません。 ただし、変数を通貨データ型に変換することはできませんが、少なくとも、画面に表示されたときに通貨値のように見えるように値をフォーマットすることはできます。 次のコマンドは、値13を変数`$a`に割り当て、通貨として表示されるように値をフォーマットします。 

>```powershell
$a = "{0:C}" -f 13
# 補足　または
$a = '{0:C}' -f 13
# なお、CultureInfoは解説が英語なので、当然en-USである。
```

>This command uses the .NET Framework currency formatting string `“{0:C}”` to format the value as currency (note that the value to be formatted is included as part of the `-f` parameter).
When you run the command and then echo back the value of `$a` you should get the following:
このコマンドは、`.NET Framework`の通貨フォーマット文字列`'{0：C}'`を使用して値を通貨としてフォーマットします（フォーマットされる値は-fパラメーターの一部として含まれていることに注意してください）。
コマンドを実行してから`$a`の値をエコーバックすると、次のようになります。 
`$13.00`
ja-JPの場合は
&yen;13

[System.Globarization.CultureInfoクラス](https://docs.microsoft.com/ja-jp/dotnet/api/system.globalization.cultureinfo?view=net-6.0)

###VBSCrからPowershellに変換をまとめると2つ変換できない
ABS
`$a = [math]::abs(-15)`
ASC
`$a = [byte][char] "A"`
Clng [Long]
`$a = "123456789.45"`
`$a = [long] $a`
CInt [int]
この場合[int32]
CDate(DateValueも同)
`$a = "01/31/2001"`
`$a = [Datetime]"01/31/2001"`
暗黙の型変換が行われる
または指定（Casrt)する
DateSerial
`$a = get-date -y 2006 -mo 12 -day 31`
CSng [single]
CDbl [Double]
CBool [bool]
0がFalseそうでないものはTrue
仮に文字列 cat でもTrue

CStr [string]
[HEX](https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-powershell-1.0/ee176867(v=technet.10))
`$a = 4517`
`$a = "{0:X}" -f $a`
How do you convert a decimal number to a hexadecimal value? Why, you use the .NET formatting methods, of course. (How else would you do it?) For example, these two commands assign the value 4517 to the variable $a, then use .NET formatting to return the hexadecimal equivalent of 4517:

`$a = 4517`
`$a = "{0:X}" -f $a`
The formatting command is interpreted like this: you specify the format type in brackets - {} - with the entire method (brackets and all) enclosed in double quote marks. The 0 is the index number of the item to be formatted (in this case 0, because we're dealing with a single string value). The X indicates that we want to format the value as a hexadecimal number.
The format method is then followed by the -f parameter and the value to be formatted ($a).
When you run this command and then echo back the value of `$a you should get the following:
10進数を16進数に変換するにはどうすればよいですか？ もちろん、.NETフォーマット方法を使用するのはなぜですか。 （他にどのようにしますか？）たとえば、次の2つのコマンドは、値4517を変数`$aに割り当て、.NETフォーマットを使用して4517に相当する16進数を返します。
`$a = 4517`
`$a = "{0：X}" -f $ a`

>formatコマンドは次のように解釈されます。フォーマットタイプを角かっこで指定します-{}-メソッド全体（角かっことすべて）を二重引用符で囲みます。 0は、フォーマットされるアイテムのインデックス番号です（この場合、単一の文字列値を処理しているため、0です）。 Xは、値を16進数としてフォーマットすることを示します。
次に、formatメソッドの後に、-fパラメーターとフォーマットする値`$a`が続きます。

というわけで変換系で対応していないものは、`HEX``CCur`がある。HEXはちょっと意外。