Skip to content

Instantly share code, notes, and snippets.

@jhoneill
Last active March 7, 2022 12:52
Show Gist options
  • Save jhoneill/fe72f1bf6efc4f28d195c288250c5667 to your computer and use it in GitHub Desktop.
Save jhoneill/fe72f1bf6efc4f28d195c288250c5667 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{"cells":[{"cell_type":"markdown","metadata":{},"source":["# .NET (PowerShell) Formatting and Date/Time behavior."]},{"cell_type":"markdown","metadata":{},"source":["## Environment info"]},{"cell_type":"code","execution_count":null,"metadata":{"dotnet_interactive":{"language":"pwsh"}},"outputs":[{"data":{"text/html":["<table><tbody><tr><td><img src=\"data:image/png;base64, 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\" width=\"125em\"></img></td><td style=\"line-height:.8em\"><p style=\"font-size:1.5em\"><b>.NET Interactive</b></p><p>&#169; 2020 Microsoft Corporation</p><p><b>Version: </b>1.0.260601+9d1ecd3c06ba93e59bfef3842d2660c08d9e2ce5</p><p><b>Build date: </b>2021-12-18T10:36:48.0565225Z</p><p><a href=\"https://github.com/dotnet/interactive\">https://github.com/dotnet/interactive</a></p></td></tr></tbody></table>"]},"metadata":{},"output_type":"display_data"},{"name":"stdout","output_type":"stream","text":["\r\n","LCID Name DisplayName\u001b[0m\r\n","---- ---- -----------\u001b[0m\r\n","1036 fr-FR French (France)\r\n","\r\n","PowerShell Version 7.2.0\r\n","\r\n"]}],"source":["#!about\n","\n","Get-Culture\n","\"`r`nPowerShell Version $($psversionTable.PSVersion)\""]},{"cell_type":"markdown","metadata":{},"source":["## Culture and number format specifiers\n","\n","The signs used for negative, currency, infinity, percent, and 'Not a Number' \n","the decimal and group separators, sizes of groups, \n","and position of currency and negative signs\n","are all set as part of the **culture**. \n","\n","You can see the number settings for the current culture with \n","`(Get-culture).NumberFormat` \n","or for a specific culture like, French-France, using \n","`(Get-Culture 'fr-FR').NumberFormat` \n","And discover the culture IDs with: \n","`Get-Culture -ListAvailable`\n","\n","The string formatter (`-f`) can use a number to position inserted text; Negative numbers mean \"left aligned\", positive numbers \"right aligned\", as shown in the next cell. The cell after that shows how the formatter or `.ToString()` can also use _format-specifiers_ for numbers. \n","Note that specifiers some only apply to integers and one of them only applies to floating point numbers."]},{"cell_type":"code","execution_count":null,"metadata":{"dotnet_interactive":{"language":"pwsh"}},"outputs":[{"name":"stdout","output_type":"stream","text":["*banana *\r\n","* banana*\r\n"]}],"source":["\"*{0,-10}*\" -f \"banana\"\n","\"*{0,10}*\" -f \"banana\""]},{"cell_type":"code","execution_count":null,"metadata":{"dotnet_interactive":{"language":"pwsh"}},"outputs":[{"name":"stdout","output_type":"stream","text":["\r\n","FormatString Positive Negative BigPositive BigNegative Zero pi Mixed\u001b[0m\r\n","------------ -------- -------- ----------- ----------- ---- -- -----\u001b[0m\r\n","00000 00031 -00031 314159 -314159 00000 00003 314abc\r\n","0,000 0,031 -0,031 314,159 -314,159 0,000 0,003 314abc\r\n","0.00 31.00 -31.00 314159.00 -314159.00 0.00 3.14 314abc\r\n","##### 31 -31 314159 -314159 3 314abc\r\n","#,### 31 -31 314,159 -314,159 3 314abc\r\n","#.## 31 -31 314159 -314159 3.14 314abc\r\n","X 1F FFFFFFE1 4CB2F FFFB34D1 0 314abc\r\n","X4 001F FFFFFFE1 4CB2F FFFB34D1 0000 314abc\r\n","C £31.00 -£31.00 £314,159.00 -£314,159.00 £0.00 £3.14 314abc\r\n","C4 £31.0000 -£31.0000 £314,159.0000 -£314,159.0000 £0.0000 £3.1416 314abc\r\n","D 31 -31 314159 -314159 0 314abc\r\n","D4 0031 -0031 314159 -314159 0000 314abc\r\n","E 3.100000E+001 -3.100000E+001 3.141590E+005 -3.141590E+005 0.000000E+000 3.141593E+000 314abc\r\n","E4 3.1000E+001 -3.1000E+001 3.1416E+005 -3.1416E+005 0.0000E+000 3.1416E+000 314abc\r\n","F 31.00 -31.00 314159.00 -314159.00 0.00 3.14 314abc\r\n","F4 31.0000 -31.0000 314159.0000 -314159.0000 0.0000 3.1416 314abc\r\n","G 31 -31 314159 -314159 0 3.141592653589793 314abc\r\n","G4 31 -31 3.142E+05 -3.142E+05 0 3.142 314abc\r\n","N 31.00 -31.00 314,159.00 -314,159.00 0.00 3.14 314abc\r\n","N4 31.0000 -31.0000 314,159.0000 -314,159.0000 0.0000 3.1416 314abc\r\n","P 3,100.00% -3,100.00% 31,415,900.00% -31,415,900.00% 0.00% 314.16% 314abc\r\n","P4 3,100.0000% -3,100.0000% 31,415,900.0000% -31,415,900.0000% 0.0000% 314.1593% 314abc\r\n","R 3.141592653589793 314abc\r\n","R4 3.141592653589793 314abc\r\n","\r\n"]}],"source":["[int]$pos = 31\n","[int]$neg = -31\n","[int]$bigpos = 314159\n","[int]$bigneg = -314159\n","[int]$zero = 0\n","[double]$float = [math]::pi\n","[string]$strInt = \"314abc\";\n"," \n","\n","\"{0:00000}\", \"{0:0,000}\",\"{0:0.00}\", \"{0:#####}\", \"{0:#,###}\",\"{0:#.##}\", \n","\"{0:X}\", \"{0:X4}\", \"{0:C}\", \"{0:C4}\", \"{0:D}\", \"{0:D4}\", \n","\"{0:E}\" , \"{0:E4}\", \"{0:F}\", \"{0:F4}\", \"{0:G}\", \"{0:G4}\",\n","\"{0:N}\", \"{0:N4}\", \"{0:P}\", \"{0:P4}\", \"{0:R}\", \"{0:R4}\" | \n"," ForEach-Object {\n"," $p = $n = $bp =$bn =$z = $pi =$si = $null;\n"," try {$p = $_ -f $pos } catch{}\n"," try {$n = $_ -f $neg } catch{}\n"," try {$bp = $_ -f $bigPos } catch{}\n"," try {$bn = $_ -f $bigneg } catch{}\n"," try {$z = $_ -f $zero } catch{}\n"," try {$pi = $_ -f $float } catch{}\n"," try {$si = $_ -f $strInt } catch{}\n"," New-Object psobject -Property @{FormatString = ($_ -replace \"{0:(.*)}\",'$1') ; \n"," Zero = $z; \n"," Positive = $p; Negative = $n; \n"," BigPositive = $bp; BigNegative = $bn; \n"," pi = $pi; Mixed = $si }\n"," } | Format-Table -autosize -Property FormatString,Positive,Negative,Bigpositive,BigNegative,Zero,pi,Mixed\n"," "]},{"cell_type":"markdown","metadata":{},"source":["## Format Specifiers for dates and times\n","Like the number specifiers the Date ones have a lot of a overlap with Excel. \n","Because there are some single character short cuts, to use the \"d\" for day, and not \"short date format\", it needs to be prefixed with \"%\"\n"," "]},{"cell_type":"code","execution_count":null,"metadata":{"dotnet_interactive":{"language":"pwsh"}},"outputs":[{"name":"stdout","output_type":"stream","text":["(UTC+00:00) Dublin, Edinburgh, Lisbon, London\r\n","\r\n","08 September 2021 14:06:05\r\n","\r\n","\r\n","FormatString Result\u001b[0m\r\n","------------ ------\u001b[0m\r\n","FullDateTimePattern 08 September 2021 14:06:05\r\n","LongDatePattern 08 September 2021\r\n","LongTimePattern 14:06:05\r\n","MonthDayPattern 8 September\r\n","RFC1123Pattern Wed, 08 Sep 2021 14:06:05 GMT\r\n","ShortDatePattern 08/09/2021\r\n","ShortTimePattern 14:06\r\n","SortableDateTimePattern 2021-09-08T14:06:05\r\n","UniversalSortableDateTimePattern 2021-09-08 14:06:05Z\r\n","YearMonthPattern September 2021\r\n","yy 21\r\n","yyy 2021\r\n","yyyy 2021\r\n","yyyyy 02021\r\n","%M 9\r\n","MM 09\r\n","MMM Sep\r\n","MMMM September\r\n","%d 8\r\n","dd 08\r\n","ddd Wed\r\n","dddd Wednesday\r\n","HH 14\r\n","h tt 2 PM\r\n","%h 2\r\n","hh 02\r\n","%t P\r\n","tt PM\r\n","%m 6\r\n","mm 06\r\n","mmmm 06\r\n","%s 5\r\n","ss 05\r\n","ff 33\r\n","%g AD\r\n","gg AD\r\n","%z +1\r\n","zz +01\r\n","zzz +01:00\r\n","%K +01:00\r\n","D 08 September 2021\r\n","d 08/09/2021\r\n","G 08/09/2021 14:06:05\r\n","g 08/09/2021 14:06\r\n","O 2021-09-08T14:06:05.3371727+01:00\r\n","O 2021-09-08T14:06:05.3371727+01:00\r\n","T 14:06:05\r\n","t 14:06\r\n","F 08 September 2021 14:06:05\r\n","f 08 September 2021 14:06\r\n","M 8 September\r\n","m 8 September\r\n","R Wed, 08 Sep 2021 14:06:05 GMT\r\n","r Wed, 08 Sep 2021 14:06:05 GMT\r\n","s 2021-09-08T14:06:05\r\n","U 08 September 2021 13:06:05\r\n","u 2021-09-08 14:06:05Z\r\n","Y September 2021\r\n","y September 2021\r\n","\r\n"]}],"source":["(Get-TimeZone).displayname\n","$d = Get-Date -Month 9 -Day 8 -Hour 14 -Minute 6 -Second 5\n","$d | Out-Default\n","$dtf = (Get-Culture).datetimeformat ; \n","$results = Get-Member -InputObject $dtf -Name *pattern | ForEach-Object { \n"," New-Object psobject -Property @{FormatString = $_.name; Result=\"{0:$($dtf.($_.name))}\" -f $d;} }\n","$results += \"{0:yy}\", \"{0:yyy}\", \"{0:yyyy}\", \"{0:yyyyy}\", \n"," \"{0:%M}\", \"{0:MM}\", \"{0:MMM}\", \"{0:MMMM}\", \n"," \"{0:%d}\", \"{0:dd}\", \"{0:ddd}\", \"{0:dddd}\", \n"," \"{0:HH}\", \n"," \"{0:h tt}\", \"{0:%h}\", \"{0:hh}\", \n"," \"{0:%t}\", \"{0:tt}\", \n"," \"{0:%m}\", \"{0:mm}\", \"{0:mmmm}\", \n"," \"{0:%s}\", \"{0:ss}\", \"{0:ff}\", \n"," \"{0:%g}\", \"{0:gg}\", \n"," \"{0:%z}\", \"{0:zz}\", \"{0:zzz}\", \"{0:%K}\", \n"," \"{0:D}\", \"{0:d}\", \"{0:G}\", \"{0:g}\",\"{0:O}\", \"{0:O}\", \"{0:T}\", \"{0:t}\", \n"," \"{0:F}\", \"{0:f}\", \"{0:M}\", \"{0:m}\",\"{0:R}\", \"{0:r}\", \"{0:s}\", \"{0:U}\", \"{0:u}\", \"{0:Y}\", \"{0:y}\" | \n"," foreach -Begin {} -process {\n"," try {$f = $null ; $f = $_ -f $d} catch{} \n"," New-Object psobject -Property @{FormatString = ($_ -replace '\\{0:(.*)\\}','$1') ; Result=$f;}\n"," } \n","$results | Format-Table -autosize FormatString,Result"]},{"cell_type":"markdown","metadata":{},"source":["Notice that above, *September is on Daylight savings time.*\n","\n","**To avoid nonsense output, you must convert to UTC before using the R/r (RFC1123Pattern) or u (UniversalSortableDateTimePattern) specifiers** \n","both of which add a suffix indicating UTC without ensuring the time is a UTC time. "]},{"cell_type":"markdown","metadata":{},"source":["## Time Zones & Daylight Savings Time\n","The following example shows that using the _Base UTC Offset_ for two TimeZones will not correctly translate local-time in one TimeZone, to local-time in another when one is on Daylight Savings Time. The example takes 3 Time zones, and calculates what local times will be in each on different days of the year around the change of Daylight Savings"]},{"cell_type":"code","execution_count":null,"metadata":{"dotnet_interactive":{"language":"pwsh"}},"outputs":[{"name":"stdout","output_type":"stream","text":["\r\n","DisplayName BaseUtcOffset SupportsDaylightSavingTime\u001b[0m\r\n","----------- ------------- --------------------------\u001b[0m\r\n","(UTC+00:00) Dublin, Edinburgh, Lisbon, London 00:00:00 True\r\n","(UTC-05:00) Eastern Time (US & Canada) -05:00:00 True\r\n","(UTC+10:00) Canberra, Melbourne, Sydney 10:00:00 True\r\n","\r\n"]}],"source":["\"GMT Standard Time\", \"Eastern Standard Time\" ,\"AUS Eastern Standard Time\" | Get-TimeZone | \n"," Format-Table Displayname, BaseUtcOffset, SupportsDaylightSavingTime "]},{"cell_type":"code","execution_count":null,"metadata":{"dotnet_interactive":{"language":"pwsh"}},"outputs":[{"name":"stdout","output_type":"stream","text":["Start at Friday 01 October 2021 11:00:00 Local in (UTC+00:00) Dublin, Edinburgh, Lisbon, London\r\n","\r\n","Date LondonIsDST LondonTime NewYorkTime SydneyTime\u001b[0m\r\n","---- ----------- ---------- ----------- ----------\u001b[0m\r\n","01 October 2021 True 11:00 06:00 20:00\r\n","08 October 2021 True 11:00 06:00 21:00\r\n","15 October 2021 True 11:00 06:00 21:00\r\n","22 October 2021 True 11:00 06:00 21:00\r\n","29 October 2021 True 11:00 06:00 21:00\r\n","05 November 2021 False 11:00 07:00 22:00\r\n","12 November 2021 False 11:00 06:00 22:00\r\n","\r\n"]}],"source":["$d = get-date -month 10 -day 1 -hour 11 -Minute 0 -Second 0 \n","\"Start at \" + $d.tostring(\"dddd dd MMMM yyyy HH:mm:ss\") + \" \" + $d.Kind + \" in \" + (get-timezone).displayname\n","1..7 | foreach {\n"," New-Object pscustomobject -Property ([ordered]@{\n"," Date = $d.ToString(\"D\"); \n"," LondonIsDST = $d.IsDaylightSavingTime(); \n"," LondonTime = $d.tostring('t') ;\n"," NewYorkTime = ([System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId($d, \"Eastern Standard Time\")).tostring('t');\n"," SydneyTime = ([System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId($d, \"AUS Eastern Standard Time\")).tostring('t')\n"," }) \n"," $d = $d.AddDays(7)\n","} | Format-Table"]},{"cell_type":"markdown","metadata":{"dotnet_interactive":{"language":"pwsh"}},"source":["The base offsets without Day Savings Time (DST) are:\n","\n","| Zone | Offset |\n","|----------|--------|\n","| Sydney | +10 |\n","| London | +0 |\n","| New York | -5 |\n","\n","In the example above: \n","\n","* On the 1st day of October (which fell on a Friday in 2021), London and New York were on DST, but being in the Southern Hemisphere, Sydney was not. So in Sydney the clocks read their _base_ time of UTC +10 but in London and New York the clocks are one hour ahead of _their_ base, reading UTC +1; UTC -4 respectively.\n","* `(Get-TimeZone -Id \"AUS Eastern Standard Time\"). GetAdjustmentRules()[-1].DaylightTransitionStart` \n"," shows that **daylight saving starts in Sydney on the First Sunday of October** (which fell on the 3rd of the month in 2021), putting the clocks there onto UTC +11. \n"," All three places are one hour ahead of their _base_ so differences are as the base offsets say they should be. There is a similar point in the spring where all are once again all on DST simultaneously. In both cases it only lasts a short time, because...\n","* **DST ends in Europe on Last Sunday of October** (the 31st in 2021), putting London's clocks onto UTC. Now London's clocks are 4 hours ahead of New York and 11 behind Sydney.\n","* **On First Sunday of November, DST ends in New York** so clocks there go back to their _base_ of UTC -5 \n","\n","The question \"What is the time difference from London to Sydney\" is usually answered as \"Ten hours\" but this is only true for a few weeks each year when both are on \"Summer\" time. \n","When London is on \"Winter\" time, the clocks differ by 11 hours and when Sydney is on \"Winter\" time they're only 9 hours apart. \n"," "]},{"cell_type":"markdown","metadata":{},"source":["The `TimeZoneInfo` object is **smart**: the first example below is using my time zone, and in 2021 clocks here went back from British Summer Time to Greenwich Mean Time on Sunday 31st October: at 2AM they moved back to 1AM. Therefore we had the hour from 1AM-2AM twice. A local time of 1:10 is ambiguous (When the clocks go forward we jump from 2AM to 3AM and we don't have the local hour between 2 and 3 at all.) \n","\n","The example below shows that `TimeZoneInfo` knows that time is ambiguous. And the following one shows how it can take a `Datetime` object and a TimeZone and decide what the offset between local and UTC would be in that timezone at that time. TimeZoneInfo holds multiple rule sets so it knows that basis for changing times in some places changed so it doesn't apply todays rules on past dates when something else was in use."]},{"cell_type":"code","execution_count":null,"metadata":{"dotnet_interactive":{"language":"pwsh"}},"outputs":[{"name":"stdout","output_type":"stream","text":["True\r\n"]}],"source":["[System.TimeZoneInfo]::Local.IsAmbiguousTime([datetime]'2021/10/31 01:10')"]},{"cell_type":"code","execution_count":null,"metadata":{"dotnet_interactive":{"language":"pwsh"}},"outputs":[{"name":"stdout","output_type":"stream","text":["Start at Friday 01 October 2021 11:00:00 Local in (UTC+00:00) Dublin, Edinburgh, Lisbon, London\r\n","\r\n","Date LondonOffset NewYorkOffSet SydneyOffset\u001b[0m\r\n","---- ------------ ------------- ------------\u001b[0m\r\n","01 October 2021 1 -4 10\r\n","08 October 2021 1 -4 11\r\n","15 October 2021 1 -4 11\r\n","22 October 2021 1 -4 11\r\n","29 October 2021 1 -4 11\r\n","05 November 2021 0 -4 11\r\n","12 November 2021 0 -5 11\r\n","\r\n"]}],"source":["$d = get-date -month 10 -day 1 -hour 11 -Minute 0 -Second 0 \n","\"Start at \" + $d.tostring(\"dddd dd MMMM yyyy HH:mm:ss\") + \" \" + $d.Kind + \" in \" + (get-timezone).displayname\n","$London = Get-TimeZone \"GMT Standard Time\"\n","$newyork = Get-TimeZone \"Eastern Standard Time\" \n","$sydney = Get-TimeZone \"AUS Eastern Standard Time\"\n","\n","1..7 | foreach {\n"," New-Object pscustomobject -Property ([ordered]@{Date=$d.ToString(\"D\"); \n"," LondonOffset = $london.GetUtcOffset($d).hours;\n"," NewYorkOffSet = $NewYork.GetUtcOffset($d).hours;\n"," SydneyOffset = $sydney.GetUtcOffset($d).hours} ) \n"," $d = $d.AddDays(7)\n","} | Format-Table"]},{"cell_type":"markdown","metadata":{},"source":["## Time differences and clock changes\n","Just one point on calculating the elapsed time when the start and end have a change of DST between them.\n","\n","The next example looks wrong; when the clocks changed there was an extra hour between two times of 10:00. Converting to Universal time shows how much time a stop watch might show. Using Local time shows how far the hands of the clock moved.\n","\n","You might wonder if subtracting times where one was a UTC time and and one wasn't would convert one to match the other. It doesn't (whether you use the `Subtract` method or PowerShell's `-` operator.)"]},{"cell_type":"code","execution_count":null,"metadata":{"dotnet_interactive":{"language":"pwsh"}},"outputs":[{"name":"stdout","output_type":"stream","text":["True\r\n","False\r\n","24\r\n","25\r\n","24\r\n"]}],"source":["$before = [datetime]'2021/10/30 10:00'\n","$after = [datetime]'2021/10/31 10:00'\n","\n","$before.IsDaylightSavingTime()\n","$after.IsDaylightSavingTime()\n","$after.Subtract($before).totalhours\n","$after.ToUniversalTime().Subtract($before.ToUniversalTime()).Totalhours\n","$after.ToUniversalTime().Subtract($before).Totalhours"]},{"cell_type":"markdown","metadata":{},"source":["## Are you local?\n","\n","The `DateTime` object is less smart. It understands two kinds of time. UTC and Local. \n","\n","Local _to where_ is a good question, and the `DateTime` just reads it as _\"what's set for this machine\"_ There's a third kind \"Undefined\", which is treated as local. \n"," \n","Let's go back to London and New York times. If we convert a London time to UTC; and if we convert it to New York, and convert _that_ to UTC we'd hope to get the same time. Alas, we don't "]},{"cell_type":"code","execution_count":null,"metadata":{"dotnet_interactive":{"language":"pwsh"}},"outputs":[{"name":"stdout","output_type":"stream","text":["Start at Friday 01 October 2021 11:00:00 Local in (UTC+00:00) Dublin, Edinburgh, Lisbon, London\r\n","\r\n","01 October 2021 11:00:00\r\n","01 October 2021 10:00:00\r\n","\r\n","01 October 2021 06:00:00\r\n","01 October 2021 05:00:00\r\n","\r\n"]}],"source":["$d = get-date -month 10 -day 1 -hour 11 -Minute 0 -Second 0 \n","\"Start at \" + $d.tostring(\"dddd dd MMMM yyyy HH:mm:ss\") + \" \" + $d.Kind + \" in \" + (get-timezone).displayname\n","\n","$d\n","$d.ToUniversalTime()\n","\"\"\n","$NYTime = ([System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId($d, \"Eastern Standard Time\")) \n","$NyTime\n","$NyTime.ToUniversalTime()"]},{"cell_type":"markdown","metadata":{"dotnet_interactive":{"language":"pwsh"}},"source":["`Datetime` stores a _\"local\"_ time of 6AM but it isn't \"6AM New York\" or \"6AM London\"; instead it is \"6AM for the time zone in force wherever and whenever the code runs\" \n","\n","We *can* specify the offset when converting a string to a `DateTime` object, but that offset is forgotten once the change has been done"]},{"cell_type":"code","execution_count":null,"metadata":{"dotnet_interactive":{"language":"pwsh"}},"outputs":[{"name":"stdout","output_type":"stream","text":["\r\n","01 October 2021 11:00:00\r\n","01 October 2021 10:00:00\r\n","01 October 2021 11:00:00\r\n","\r\n"]}],"source":[" [datetime]\"2021-10-01T06:00:00-04:00\"\n","([datetime]\"2021-10-01T06:00:00-04:00\").ToUniversalTime()\n","([datetime]\"2021-10-01T06:00:00-04:00\").ToLocalTime()"]},{"cell_type":"markdown","metadata":{},"source":["For preference I would always write dates and times to a file either converted to UTC with a Z indicating 'Zero offset' (sometimes called 'Zulu time' because Z is 'Zulu' in the NATO phonetic alphabet.) or write local times _with the offset included_. Without either the time loses a lot of its meaning. And if you absolutely must save local times (or won't put a Z on utc times) doing things like naming columns of data \"LocalTime\" and/or \"UTCTime\" helps\n","Calculations of time are best done with everything on UTC. Displaying something to a person is usually better in their own local time, and the next best is everything in UTC. Displaying local time at the source, is usually the worst choice: if something is logged in 3 different data centers do you want to be figuring out the time differences and doing the sums to decide if two events were roughly simultaneous, or hours apart?"]},{"cell_type":"markdown","metadata":{},"source":["## Invariant culture\n","\n","`-f` and `.ToString` use local culture - this generally what we want when rendering something for a human to read.\n","\n","This means that if the culture is set to France `[math]::pi` prints 3 **,** 14159265358979 because the decimal separator is \",\" , and in Great Britain dates are written day/month/year.\n","\n","_Casting_ a string to a date or a floating point number doesn't follow local culture - which would mean one script could read the same data file on two machines and produce two different results. These operations use **Invariant culture**. \n","You can get the invariant settings with `Get-Culture -Name \"\"`, they're mostly US but the US culture has 12 clock with AM/PM and Invariant uses a 24 hour clock. In addition _casting_ can make sense of additional formats which don't match US, but are still unambiguous for example `[datetime]\"31 2021 December\"` works\n","\n","_Parsing_ a string (either with the `[convert]` class or `.Parse()` / `TryParse()` methods provided by a class) defaults to local culture. "]},{"cell_type":"code","execution_count":null,"metadata":{"dotnet_interactive":{"language":"pwsh"}},"outputs":[{"name":"stdout","output_type":"stream","text":["\r\n","LCID Name DisplayName\u001b[0m\r\n","---- ---- -----------\u001b[0m\r\n","2057 en-GB English (United Kingdom)\r\n","01/10/2021 11:00:00\r\n","01/10/2021 11:00:00\r\n","\r\n","10/01/2021 11:00:00\r\n","10/01/2021 11:00:00\r\n","\r\n","01 October 2021 11:00:00\r\n","\r\n","\r\n","01 October 2021 11:00:00\r\n","\r\n","\r\n","10 January 2021 11:00:00\r\n","\r\n","\r\n"]}],"source":["Get-Culture\n","$d = get-date -month 10 -day 1 -hour 11 -Minute 0 -Second 0 \n","$d.ToString()\n","\"{0}\" -f $d\n","\"\"\n","\"$d\"\n","[string]$d\n","[convert]::ToDateTime('01/10/2021 11:00:00') | Out-Default\n","[datetime]::Parse('01/10/2021 11:00:00') | Out-Default\n","[datetime]'01/10/2021 11:00:00' | Out-Default\n"]}],"metadata":{"kernelspec":{"display_name":".NET (PowerShell)","language":"PowerShell","name":".net-pwsh"},"language_info":{"name":"PowerShell"}},"nbformat":4,"nbformat_minor":2}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment