How to format a DateTime in PowerShell - datetime

I can format the Get-Date cmdlet no problem like this:
$date = Get-Date -format "yyyyMMdd"
But once I've got a date in a variable, how do I format it? The statement below
$dateStr = $date -format "yyyMMdd"
returns this error:
"You must provide a value expression
on the right-hand side of the '-f'
operator"

The same as you would in .NET:
$DateStr = $Date.ToString("yyyyMMdd")
Or:
$DateStr = '{0:yyyyMMdd}' -f $Date

A simple and nice way is:
$time = (Get-Date).ToString("yyyy:MM:dd")

The question is answered, but there is some more information missing:
Variable vs. Cmdlet
You have a value in the $Date variable and the -f operator does work in this form: 'format string' -f values. If you call Get-Date -format "yyyyMMdd" you call a cmdlet with some parameters. The value "yyyyMMdd" is the value for parameter Format (try help Get-Date -param Format).
-f operator
There are plenty of format strings. Look at least at part1 and part2. She uses string.Format('format string', values'). Think of it as 'format-string' -f values, because the -f operator works very similarly as string.Format method (although there are some differences (for more information look at question at Stack Overflow: How exactly does the RHS of PowerShell's -f operator work?).

A very convenient -- but probably not all too efficient -- solution is to use the member function GetDateTimeFormats(),
$d = Get-Date
$d.GetDateTimeFormats()
This outputs a large string-array of formatting styles for the date-value. You can then pick one of the elements of the array via the []-operator, e.g.,
PS C:\> $d.GetDateTimeFormats()[12]
Dienstag, 29. November 2016 19.14

One thing you could do is:
$date.ToString("yyyyMMdd")

Do this if you absolutely need to use the -Format option:
$dateStr = Get-Date $date -Format "yyyMMdd"
However
$dateStr = $date.toString('yyyMMdd')
is probably more efficient.. :)

Very informative answer from #stej, but here is a short answer:
Among other options, you have 3 simple options to format [System.DateTime] stored in a variable:
Pass the variable to the Get-Date cmdlet:
Get-Date -Format "HH:mm" $date
Use toString() method:
$date.ToString("HH:mm")
Use Composite formatting:
"{0:HH:mm}" -f $date

For anyone trying to format the current date for use in an HTTP header use the "r" format (short for RFC1123) but beware the caveat...
PS C:\Users\Me> (get-date).toString("r")
Thu, 16 May 2019 09:20:13 GMT
PS C:\Users\Me> get-date -format r
Thu, 16 May 2019 09:21:01 GMT
PS C:\Users\Me> (get-date).ToUniversalTime().toString("r")
Thu, 16 May 2019 16:21:37 GMT
I.e. Don't forget to use "ToUniversalTime()"

If you got here to use this in cmd.exe (in a batch file):
powershell -Command (Get-Date).ToString('yyyy-MM-dd')

I needed the time and a slight variation on format. This works great for my purposes:
$((get-date).ToLocalTime()).ToString("yyyy-MM-dd HHmmss")
2019-08-16 215757
According to #mklement0 in comments, this should yield the same result:
(get-date).ToString("yyyy-MM-dd HHmmss")

Format Date Time to your Output Needs
If you want to format the date and assign the string to a variable.
I have combined both PowerShell and .NET to provide the flexibility.
$oDate = '{0}' -f ([system.string]::format('{0:yyyyMMddHHmmss}',(Get-Date)))
How this Works
PowerShell Operator - '{0}' -f (.....)
.NET Notation - [system.string]::format('customformat',InputObject)
Customised Format by combining PowerShell with .NET - '{0:yyyyMMddHHmmss}'
Input Object provided by PowerShell cmdlet - (Get-Date)
Stored in the PowerShell variable - $oDate
Example
If the date and time when run was Monday, 5 July 2021 5:45:22 PM (Format '{0:F}').
$oDate = 20210705174522
Using the Code
You can customise the the string to meet your requirements by modifying 'yyyMMddHHmmss' using the Microsoft .NET Custom Date Time Notation.

You could just use this to select the format you want and then past it wherever it is needed.
$DTFormats = (Get-Date).GetDateTimeFormats()
$Formats = #()
$i=0
While ($i -lt $DTFormats.Count){
$row = [PSCustomObject]#{
'IndexNumber' = $i
'DateTime Format' = $DTFormats[$i]
}
$Formats += $row
$i++
}
$DTSelection = ($Formats | Out-GridView -OutputMode Single -Title 'Select DateTime Format').IndexNumber
$MyDTFormat = "(Get-Date).GetDateTimeFormats()[$DTSelection]"
Write-Host " "
Write-Host " Use the following code snippet to get the DateTime format you selected:"
Write-Host " $MyDTFormat" -ForegroundColor Green
Write-Host " "
$MyDTFormat | Clip
Write-Host " The code snippet has been copied to your clipboard. Paste snippet where needed."

I converted my dueDate string to a datetime then formatted it.
[Datetime]::ParseExact($dueDate,'MM/dd/yyyy H:mm:ss',$null).ToString('MM/dd/yyyy')

Related

How do I pass DateTime as a parameter in PowerShell?

I have a script that's calling another script and passing in parameters along with it. Whenever I try to pass in a datetime, parts of the datetime are used as arguments for the other parameters.
script1.ps1
$dateEnd = Get-Date "12/31/14"
$siteUrl = "http://foo.com"
$outputPath = "c:\myFolder"
$argumentList = "-file .\script2.ps1", "test1", $dateEnd, $siteUrl, $outputPath
Start-Process powershell.exe -ArgumentList $argumentList
script2.ps1
param
(
[string]$test1,
[DateTime]$dateEnd,
[string]$siteUrl,
[string]$outputFile
)
$test1
$dateEnd
$siteUrl
$outputFile
Read-Host -Prompt "Press Enter to exit"
This would result in:
test1
Wednesday, December 31, 2014 12:00:00 AM
00:00:00
http://foo.com
EDIT - string as date, typo on my part:
If I pass the string 12/31/14 it works fine, but I'd like to be able to pass a date.
In the line which assigns $argumentList, change the $dateEnd parameter to be $dateEnd.toString('s').
Arguments to Windows processes are strings, and not objects, so Start-Process must convert the ArgumentList into a string. Powershell.exe then parses that string by splitting on spaces (like any Windows process), and it turns it back into your parameters.
Normally, this should work perfectly well, but in this case notice what happens when you run (get-date).tostring(). The default output for a DateTime object contains a space, which is interfering with the parsing.
The solution, then, is to format the date parameter in your argument list to have no spaces and yet still be in a format that DateTime::Parse() can understand (so PowerShell can reload the variable on the other end). Passing 's' to DateTime::toString() gives you such a format.
This is due to a combination of positional parameter usage and quoting. Here's a single change that should make it work (quoted the date input):
$argumentList = "-file D:\script2.ps1", "test1", "`"$dateEnd`"", $siteUrl, $outputPath
Is there any reason you call a separate PowerShell process? You could call this like so:
#This will run in separate scope
& ".\script2.ps1" test1 $dateEnd $siteUrl $outputPath
#This will run in the local (current) scope:
. ".\script2.ps1" test1 $dateEnd $siteUrl $outputPath
I think this is an issue of you not using DateTime objects correctly. If you want to specify a date, then do so. What you suggest as your string is a Time, not a date. If you want to specify both then provide both to Get-Date.
$endDate = "12/31/14 08:00:00"
That's 8:00 AM on December 31st.
Then in your script call whatever it is that you want specifically.
param
(
[string]$test1,
[DateTime]$dateEnd,
[string]$siteUrl,
[string]$outputFile
)
$test1
$dateEnd.ToLongDateString()
$dateEnd.ToLongTimeString()
$siteUrl
$outputFile
When a DateTime object is passed to that (shown here with your other test data) you should get:
test1
Wednesday, December 31, 2014
8:00:00 AM
http://foo.com
C:\myfolder

Unix shell scripting with counter

i am new to unix shell scripting
I need to write a shell script that should increment a variable value (numeric) and when i run the script next time the variable should take the incremented value and it should increment once agian
any help in this would be really thankful
thanks
Try the following code:
vale=`expr 0000000000 + 1`
ed -s $0 <<EOT
1s/ ........../`printf ' %010d' $vale`/
w
q
EOT
echo $vale
You need for the value of the variable to persist somehow. One way is to save it to a file.
outputfile="~/variable.txt"
value=`cat $outputFile`
newValue=`expr $value + 1`
echo $newValue > $outputFile

In PowerShell, how do I convert DateTime to UNIX time?

In PowerShell, how can I convert string of DateTime to sum of seconds?
PS H:\> (New-TimeSpan -Start $date1 -End $date2).TotalSeconds
1289923177.87462
New-TimeSpan can be used to do that. For example,
$date1 = Get-Date -Date "01/01/1970"
$date2 = Get-Date
(New-TimeSpan -Start $date1 -End $date2).TotalSeconds
Or just use this one line command
(New-TimeSpan -Start (Get-Date "01/01/1970") -End (Get-Date)).TotalSeconds
With .NET Framework 4.6 you can use ToUnixTimeSeconds method of DateTimeOffset class:
[DateTimeOffset]::Now.ToUnixTimeSeconds()
$DateTime = Get-Date #or any other command to get DateTime object
([DateTimeOffset]$DateTime).ToUnixTimeSeconds()
As mentioned, the UNIX Epoch is January 1st, 1970 at 12:00 AM (midnight) UTC.
To get the current seconds-since-the-epoch in UTC in a whole-number I use this 80-character one-liner
$ED=[Math]::Floor([decimal](Get-Date(Get-Date).ToUniversalTime()-uformat "%s"))
The code above is PowerShell 2.0 compliant & rounds-down (to ensure consistent behavior w/ UNIX)
Not sure when -UFormat was added to Get-Date but it allows you to get the date and time in UNIX epoch timestamp format:
[int64](Get-Date -UFormat %s)
It's supported by both the PowerShell and PowerShell Core.
This one-liner works for me (compared it to http://www.unixtimestamp.com/)
[int64](([datetime]::UtcNow)-(get-date "1/1/1970")).TotalSeconds
For milliseconds
[int64](([datetime]::UtcNow)-(get-date "1/1/1970")).TotalMilliseconds
To get seconds since 1970 independent of time zone, I would go with:
$unixEpochStart = new-object DateTime 1970,1,1,0,0,0,([DateTimeKind]::Utc)
[int]([DateTime]::UtcNow - $unixEpochStart).TotalSeconds
I just wanted to present yet another, and hopefully simpler, way to address this. Here is a one liner I used to obtain the current Unix(epoch) time in UTC:
$unixTime = [long] (Get-Date -Date ((Get-Date).ToUniversalTime()) -UFormat %s)
Breaking this down from the inside out:
(Get-Date).ToUniversalTime()
This gets the current date/time in UTC time zone. If you want the local time, just call Get-Date.
This is then used as input to...
[long] (Get-Date -Date (UTC date/time from above) -UFormat %s)
Convert the UTC date/time (from the first step) to Unix format.
The -UFormat %s tells Get-Date to return the result as Unix epoch time (seconds elapsed since January 01, 1970 00:00:00). Note that this returns a double data type (basically a decimal). By casting it to a long data type, it is automatically converted (rounded) to a 64-bit integer (no decimal). If you want the extra precision of the decimal, don't cast it to a long type.
Extra credit
Another way to convert/round a decimal number to a whole number is to use System.Math:
[System.Math]::Round(1485447337.45246)
Powershell
$epoch = (Get-Date -Date ((Get-Date).DateTime) -UFormat %s)
I suggest the following, which is based on ticks (Int64), rather than seconds (Int32), to avoid the Year 2038 problem. [Math]::Floor is used, as Unix time is based on the number of whole seconds since the epoch.
[long][Math]::Floor((($DateTime.ToUniversalTime() - (New-Object DateTime 1970, 1, 1, 0, 0, 0, ([DateTimeKind]::Utc))).Ticks / [timespan]::TicksPerSecond))
Here's a script which converts both TO and FROM CTIME that I've been using for a while (longer, because it was written for a "new to scripting" type crowd, with various comments.
# Here's a very quick variant to 'get the job done'
[Int64]$ctime=1472641743
[datetime]$epoch = '1970-01-01 00:00:00'
[datetime]$result = $epoch.AddSeconds($Ctime)
write-host $result
# A few example values for you to play with:
# 1290100140 should become ... 2010-11-18 17:09:00.000
# 1457364722 should become ... 2016-03-07 15:32:02.000
# 1472641743 should become ... 31/08/2016 11:09:03
# For repeated use / calculations, functions may be preferable. Here they are.
# FROM C-time converter function
# Simple function to convert FROM Unix/Ctime into EPOCH / "friendly" time
function ConvertFromCtime ([Int64]$ctime) {
[datetime]$epoch = '1970-01-01 00:00:00'
[datetime]$result = $epoch.AddSeconds($Ctime)
return $result
}
# INTO C-time converter function
# Simple function to convert into FROM EPOCH / "friendly" into Unix/Ctime, which the Inventory Service uses.
function ConvertToCTime ([datetime]$InputEpoch) {
[datetime]$Epoch = '1970-01-01 00:00:00'
[int64]$Ctime = 0
$Ctime = (New-TimeSpan -Start $Epoch -End $InputEpoch).TotalSeconds
return $Ctime
}
Hope that helps, especially if you just want something that's a little friendlier for beginners or so :).
For sending data to Grafana I needed the Unix Epoch time as 32 bit Integer from UTC. The best solution in the end was this:
$unixtime = (get-date -Date (get-date).ToUniversalTime() -UFormat %s).Substring(0,10)
This results in a string, but can easy converted to an integer:
[int]$unixtime = (get-date -Date (get-date).ToUniversalTime() -UFormat %s).Substring(0,10)
I tested this against an Ubuntu machine. The results from the commands above and the Linux command
date +%s
are identically.
Below cmdlet will convert the windows uptime into Unix understandable epoch time format:
$s=Get-WmiObject win32_operatingsystem | select csname,#{LABEL='LastBootUpTime';EXPRESSION{$_.ConverttoDateTime($_.lastbootuptime)}};
[Math]::Floor([decimal](Get-Date($s.LastBootUpTime.ToUniversalTime()).ToUniversalTime()-uformat "%s"))
Again comparing to http://www.unixtimestamp.com and building on others above
$date1 = (Get-Date -Date "01/01/1970").ToUniversalTime()
$date2 = (Get-Date).ToUniversalTime()
$epochTime = [Math]::Floor((New-TimeSpan -Start $date1 -End $date2).TotalSeconds)
This one should also work since javascript uses milliseconds since epoch :
ConvertTo-Json (Get-Date) | ? { $_ -Match '\(([0-9]+)\)' } | % { $Matches[1]/1000 }
Step by Step :
PS P:\> Get-Date
lundi 15 janvier 2018 15:12:22
PS P:\> ConvertTo-Json (Get-Date)
{
"value": "\/Date(1516025550690)\/",
"DisplayHint": 2,
"DateTime": "lundi 15 janvier 2018 15:12:30"
}
PS P:\> (ConvertTo-Json (Get-Date)) -Match '\(([0-9]+)\)'
True
PS P:\> $Matches
Name Value
---- -----
1 1516025613718
0 (1516025613718)
You can use the Uformat parameter of get-date. But first I like to be sure the date of a given workstation is correct (I consider a workstation connected to a company network where there is a server with a correct time set).
#Synchronize workstation time with server
cmd /c "sc config w32time start= auto"
cmd /c "w32tm /unregister"
cmd /c "w32tm /register"
cmd /c "net start w32time"
cmd /c 'tzutil.exe /s "W. Europe Standard Time"'
cmd /c 'reg add "HKLM\SYSTEM\CurrentControlSet\Control\TimeZoneInformation" /v DisableAutoDaylightTimeSet /t REG_DWORD /d 0 /f'
cmd /c "net time \\full-servername.ru /set /yes"
Then I get the actual unix timestamp to compare objects (accounts) between actual date and creation date (account deletion tasks when unix timestamp exceeds limit date)
#Get actual unix timestamp and compare it to something
$actual_date = (get-date -UFormat "%s")
$final_date = "some unix date of the database"
if(($final_date - $actual_date) -lt 0 ){
#make deletion task
}
Signal15's answer is a bit verbose for me. I did it this way:
[int] (Get-Date (Get-Date).ToUniversalTime() -uformat '%s')
Late answer...
Hare are both convert functions ConvertTo-UnixTime & ConvertFrom-UnixTime for convenience (both pipeline capable)
function ConvertFrom-UnixTime () {
[CmdletBinding()]
param(
[Parameter(Mandatory, ValueFromPipeline, Position = 0)]
[Int64]$UnixTime
)
begin {
$epoch = [DateTime]::SpecifyKind('1970-01-01', 'Utc')
}
process {
$epoch.AddSeconds($UnixTime)
}
}
function ConvertTo-UnixTime {
[CmdletBinding()]
param(
[Parameter(Mandatory, ValueFromPipeline, Position = 0)]
[DateTime]$DateTime
)
begin {
$epoch = [DateTime]::SpecifyKind('1970-01-01', 'Utc')
}
process {
[Int64]($DateTime.ToUniversalTime() - $epoch).TotalSeconds
}
}
A culture-independent, and actually pretty fast answer:
[int64]([double]::Parse((get-date -uformat "%s"),[cultureinfo][system.threading.thread]::currentthread.currentculture))
This invokes some .NET "magic" when it comes to actually produce a formatted string, it gets converted to double using current thread's culture settings, then it converts to int64 which by default does exactly floor the double provided. Should you desire a UTC timestamp, use -date ([DateTime]::UtcNow) in get-date to use current UTC time as the time to convert.
[int64]([double]::Parse((get-date -date ([DateTime]::UtcNow) -uformat "%s"),[cultureinfo][system.threading.thread]::currentthread.currentculture))
PS: Unless you really need a string as your output, having an integer is overall better to your programming culture.

Creating string of repeated characters in shell script [duplicate]

This question already has answers here:
How can I repeat a character in Bash?
(36 answers)
Closed 8 years ago.
I need to generate a string of dots (.characters) as a variable.
I.e., in my Bash script, for input 15 I need to generate this string of length 15: ...............
I need to do so variably. I tried using this as a base (from Unix.com):
for i in {1..100};do printf "%s" "#";done;printf "\n"
But how do I get the 100 to be a variable?
You can get as many NULL bytes as you want from /dev/zero. You can then turn these into other characters. The following prints 16 lowercase a's
head -c 16 < /dev/zero | tr '\0' '\141'
len=100 ch='#'
printf '%*s' "$len" | tr ' ' "$ch"
Easiest and shortest way without a loop
VAR=15
Prints as many dots as VAR says (change the first dot to any other character if you like):
printf '.%.0s' {1..$VAR}
Saves the dotted line in a variable to be used later:
line=`printf '.%.0s' {1..$VAR}`
echo "Sign here $line"
-Blatantly stolen from dogbane's answer https://stackoverflow.com/a/5349842/3319298
Edit: Since I have now switched to fish shell, here is a function defined in config.fish that does this with convenience in that shell:
function line -a char -a length
printf '%*s\n' $length "" | tr ' ' $char
end
Usage: line = 8 produces ========, line \" 8 produces """""""".
On most systems, you could get away with a simple
N=100
myvar=`perl -e "print '.' x $N;"`
I demonstrated a way to accomplish this task with a single command in another question, assuming it's a fixed number of characters to be produced.
I added an addendum to the end about producing a variable number of repeated characters, which is what you asked for, so my previous answer is relevant here:
https://stackoverflow.com/a/17030976/2284005
I provided a full explanation of how it works there. Here I'll just add the code to accomplish what you're asking for:
n=20 # This the number of characters you want to produce
variable=$(printf "%0.s." $(seq 1 $n)) # Fill $variable with $n periods
echo $variable # Output content of $variable to terminal
Outputs:
....................
You can use C-style for loops in Bash:
num=100
string=$(for ((i=1; i<=$num; i++));do printf "%s" "#";done;printf "\n")
Or without a loop, using printf without using any externals such as sed or tr:
num=100
printf -v string "%*s" $num ' ' '' $'\n'
string=${string// /#}
The solution without loops:
N=100
myvar=`seq 1 $N | sed 's/.*/./' | tr -d '\n'`
num=100
myvar=$(jot -b . -s '' $num)
echo $myvar
When I have to create a string that contains $x repetitions of a known character with $x below a constant value, I use this idiom:
base='....................'
# 0 <= $x <= ${#base}
x=5
expr "x$base" : "x\(.\{$x\}\)" # Will output '\n' too
Output:
.....

Delete a line with a pattern

Hi I want to delete a line from a file which matches particular pattern
the code I am using is
BEGIN {
FS = "!";
stopDate = "date +%Y%m%d%H%M%S";
deletedLineCtr = 0; #diagnostics counter, unused at this time
}
{
if( $7 < stopDate )
{
deletedLineCtr++;
}
else
print $0
}
The code says that the file has lines "!" separated and 7th field is a date yyyymmddhhmmss format. The script deletes a line whose date is less than the system date. But this doesn't work. Can any one tell me the reason?
Is the awk(1) assignment due Tuesday? Really, awk?? :-)
Ok, I wasn't sure exactly what you were after so I made some guesses. This awk program gets the current time of day and then removes every line in the file less than that. I left one debug print in.
BEGIN {
FS = "!"
stopDate = strftime("%Y%m%d%H%M%S")
print "now: ", stopDate
}
{ if ($7 >= stopDate) print $0 }
$ cat t2.data
!!!!!!20080914233848
!!!!!!20090914233848
!!!!!!20100914233848
$ awk -f t2.awk < t2.data
now: 20090914234342
!!!!!!20100914233848
$
call date first to pass the formatted date as a parameter:
awk -F'!' -v stopdate=$( date +%Y%m%d%H%M%S ) '
$7 < stopdate { deletedLineCtr++; next }
{print}
END {do something with deletedLineCrt...}
'
You would probably need to run the date command - maybe with backticks - to get the date into stopDate. If you printed stopDate with the code as written, it would contain "date +...", not a string of digits. That is the root cause of your problem.
Unfortunately...
I cannot find any evidence that backticks work in any version of awk (old awk, new awk, GNU awk). So, you either need to migrate the code to Perl (Perl was originally designed as an 'awk-killer' - and still includes a2p to convert awk scripts to Perl), or you need to reconsider how the date is set.
Seeing #DigitalRoss's answer, the strftime() function in gawk provides you with the formatting you want (check 'info gawk' as I did).
With that fixed, you should be getting the right lines deleted.

Resources