Convert String to DateTime Object in specific timezone with Powershell - powershell

With my limited knowledge of Powershell, I'm trying to convert a string in the current from:
2020-01-23 10:06:07
to a datetime object in the timezone Eastern Standard Time. Ultimately I want to be able to format to ISO8601 standard with the correct offset from UTC.
20-01-23T10:06:07-05:00
Is this achievable within powershell? I have looked at ConvertTimeFromUtc however I am struggling to initially specify that the timezone is Eastern Standard Time instead of GMT.

To treat a given nominal date (one that is unspecified with respect to what time zone it relates to) as one in the EST (US Eastern Standard Time) time zone:
That is, convert a date string such as '2020-01-24 03:00:57' into
a [datetimeoffset] instance that represents this unspecified-in-terms-of-time-zone string as a date/time local to the EST (Eastern Standard Time) time zone (possibly with DST (daylight-saving time) offset applied), which can then be formatted in a ISO 8601 format that includes the resulting date's specific UTC offset.
# Construct a nominal [datetime] instance whose .Kind property value is
# Unspecified (which means unspecified with respect to any particular
# time zone), which a cast from a string achieves:
$nominalDate = [datetime] '2020-01-24 03:00:57'
# Determine the target time zone.
# Note: On macOS and Linux, use 'America/New_York' (ICU library IDs).
$tz = [TimeZoneInfo]::FindSystemTimeZoneById('Eastern Standard Time')
# Get the UTC offset for the nominal date (.Kind == Unspecified),
# which is interpreted as local to that time zone.
# The offset is returned as a [timespan] instance that properly reflects
# DST, if the date falls into the DST window of the target time zone.
# If the input date is ambiguous or invalid, standard time is assumed.
$utcOffset = $tz.GetUtcOffset($nominalDate)
# Construct a [datetimeoffset] instance with the UTC offset determined above.
# This in effect creates a date that represents the nominal date in the
# target time zone, using that time zone's DST-appropriate UTC offset.
$dto = [DateTimeOffset]::new($nominalDate.Ticks, $utcOffset)
# Format according to ISO 8601 with UTC offset, but remove the
# fractional-seconds part:
# Note: With the standar "o" format specifier, only [datetimeoffset]
# instances include their UTC offset in the resulting string,
# not [datetime] instances.
$dto.ToString('o') -replace '\.\d+(?=-)'
The above yields '2020-01-24T03:00:57-05:00', as desired.
With a DST-window input date such as '2020-07-24 03:00:57', it would yield
'2020-07-24T03:00:57-04:00' - note how the UTC offset is now one hour less.
See also: The System.DateTime ([datetime], as a PowerShell type literal), System.DateTimeOffset ([datetimeoffset]), and System.TimeZoneInfo ([TimeZoneInfo]) types, and Standard date and time format strings.
The following is a related use case with a different premise:
To translate a given local date into its EST equivalent:
That is, translate a local point in time, such as obtained by Get-Date, into the equivalent time in the EST time zone.
# Start with a local date, in any time zone.
# (A [datetime] instance whose .Kind property value is Local, though
# Unspecified would work the same).
# Alternatively, start with a UTC date (where .Kind is UTC)
$localDate = Get-Date
# Translate it to Eastern Standard time, as a [datetimeoffset] instance.
# Note: Casting $localDate to [datetimeoffset] is crucial to ensure
# that a [datetimeoffset] with the proper UTC offset is returned.
# Without it, you'd get a [datetime] instance that is nominally
# the correct time, but has an Unspecified .Kind value.
# Also, only a [datetimeoffset] instance includes a UTC offset
# when stringified with format string 'o'
$dtoEST = [TimeZoneInfo]::ConvertTimeBySystemTimeZoneId(
[datetimeoffset] $localDate,
'Eastern Standard Time'
)
# Format according to ISO 8601 with UTC offset, but remove the
# fractional-seconds part:
$dtoEST.ToString('o') -replace '\.\d+(?=-)'
The above yields a string such as '2020-01-23T16:44:41-05:00'.

DataTime itself doesn't have info about time zone. If you want to convert UTC date to other zone date you may use ConvertTimeFromUtc as you mentioned. Example:
$DateTime = Get-Date "2020-01-23 10:06:07"
$TimeZone = [TimeZoneInfo]::FindSystemTimeZoneById("Eastern Standard Time")
[TimeZoneInfo]::ConvertTimeFromUtc($DateTime, $TimeZone)
Or you can convert from any time zone. Get time zones:
[TimeZoneInfo]::GetSystemTimeZones() | select Id | sort Id
Convert from one time zone to another:
$DateTime = Get-Date "2020-01-23 10:06:07"
$SourceTimeZone = [TimeZoneInfo]::FindSystemTimeZoneById("Eastern Standard Time")
$DestinationTimeZone = [TimeZoneInfo]::FindSystemTimeZoneById("Azores Standard Time")
[TimeZoneInfo]::ConvertTime($DateTime, $SourceTimeZone, $DestinationTimeZone)

Related

How to convert a string date format `October 18th 2019` into a valid date `2019-10-17T23:00:00.000Z`

I need to convert a string date format like this October 18th 2019 ('MMMM Do YYYY') into a valid date 2019-10-17T23:00:00.000Z or similar 17/10/2019
i have tried using parsing the string into moment but i keep getting errors
update: I used moment('October 18th 2019').format(). received invalid date as the error, sorry I should clarify i'm trying to convert the string October 18th 2019 into a valid date format,
You simply need to supply the format string for the input (MMMM Do YYYY) when constructing the Moment object, with one of the following approaches:
// this way interprets the input at the start of the day in the local time zone
moment('October 18th 2019', 'MMMM Do YYYY')
// this way interprets the input at the start of the day in UTC
moment.utc('October 18th 2019', 'MMMM Do YYYY')
// this way interprets the input at the start of the day in a specific named time zone
// (requires the moment-timezone add-on)
moment.tz('October 18th 2019', 'MMMM Do YYYY', 'Europe/London')
Then you can format and/or convert it however desired. For example:
// this way keeps the local time, includes the local time offset when formatting
moment('October 18th 2019', 'MMMM Do YYYY').format()
// this way converts from local to utc before formatting
moment('October 18th 2019', 'MMMM Do YYYY').utc().format()
// this way converts from local to utc before formatting and includes milliseconds
moment('October 18th 2019', 'MMMM Do YYYY').toISOString()

Error with String to DateTime conversion in PowerShell (it used to work before)

A script is parsing values from a *.csv file.
One of the values is a timestamp in the format 7/21/2018 7:07 AM UTC
This line used to work before:
$DR.Item($property.Name) = (
[System.DateTime](Get-Date($property.value -replace " UTC", ""))
).TolocalTime() -f "MM/d/yyyy HH:mm:ss"
It is not working anymore. I get the following error:
Get-Date : Cannot bind parameter 'Date'. Cannot convert value "7/21/2018 7:07 AM" to type "System.DateTime". Error: "String was not recognized as a valid
DateTime."
Your command failing suggests that the current culture ([cultureinfo]::CurrentCulture) at the time of execution is something other than en-US (US-English), in which case Get-Date won't recognize a date/time string such as "7/21/2018 7:07 AM[1].
The solution is to use a [datetime] cast directly, without involving Get-Date, because a [datetime]cast always uses the invariant culture, which is like the en-US culture with respect to date/time formats:
$DR.Item($property.Name) = ([datetime] ($property.value -replace ' UTC$')) -f "MM/d/yyyy HH:mm:ss"
Caveat: Your call to .ToLocalTime() is not needed for output formatting, as the [datetime] instance you receive will behave as if it were in local time by default, even though it technically is time-zone-agnostic, as reflected in its .Kind property containing Unspecified. That is, your output will reflect the UTC date and time, without indicating so.
If you truly want to translate the input UTC timestamp into the equivalent local time, more work is needed:
$dtUtc = [datetime]::SpecifyKind(($property.value -replace ' UTC$'), 'Utc')
$DR.Item($property.Name) = $dtUtc.ToLocalTime() -f "MM/d/yyyy HH:mm:ss"
[1] PowerShell generally uses the invariant culture when it comes to from-string and to-string type conversions, but, curiously, doesn't do so when passing strings as arguments to compiled cmdlets, as opposed to [advanced] functions (written in PowerShell).
This is a known problem, but it may not get fixed for reasons of backward compatibility.

Add nanosecond resolution unix time

I am currently converting a date time value to unix time so that it can be inserted into a time series database (influxdb) using the following code:
(Get-Date -Date $_.timecreated -UFormat %s)
Unfortunately influx requires nano second resolution while the above gives me ms resolution. What is the best way to add nano second resolution that influx can accept as a valid unix timestamp? I have tries adding some 0000 characters at the end but that does not always work.
You will have to compute the unix timestamp yourself like this, by computing the offset from the unix epoch:
$utime = ((Get-Date -Date $_.timecreated) - (Get-Date "1/1/1970")).TotalSeconds
that gives you a double that you can format as you like. This will give nanosecond precision:
"{0:F06}" -f $utime
Update: It's important to note that I don't think Get-Date will give you a nanosecond precision of the time. On my machine the nanosecond place value is always 0.

Error parsing time-zone with Time::Piece

Executing a simple Perl script
use Time::Piece;
my $t = Time::Piece->strptime('08:00 PM AST', "%I:%M %p %Z");
I got the following error: Error parsing time at /usr/local/lib/perl5/Time/Piece.pm line 469.
Is this a bug in the library or there is something wrong with the above code? When I remove AST time-zone from the input string, it works, but when time-zone is left, it fails.
I don't know what exact time-zone will be in input string, so I cannot adjust that part on my end. AST (see Wikipedia) is a proper abbreviation for Atlantic Time Zone, so it should work. But it does not!
The time zone field is ambiguous and cannot be parsed. For instance, CST is the abbreviation for China Standard Time, Central Standard Time, and Cuba Standard Time.
The module documentation says that the strptime method is from FreeBSD, where the %Z format accepts either the local time zone or GMT and nothing else. This may be true of strptime, but I can confirm only that, where I am located, GMT is acceptable while UTC and AST are not.
The solution I would recommend is to preprocess your time strings, replacing the time zone abbreviation with an unambiguous time zone offset. For instance AST (assuming you meant Atlantic Standard Time and not Arabia Standard Time) would be replaced with -0400, since it is four hours behind UTC. Then you can parse it with a %z format specifier and get the correct result.
use Date::Parse;
my $t = str2time('08:00 PM AST');

How to get seconds of given timezone - UTC time in Perl

I have a file with lines of the form : 1311597859.567497 y_value_to_plot. The first token is time since epoch, i.e. unix time. The user wants to call plot_file.pl with this filename and a timezone specification such as "America/New_York" or "Europe/London". Calling gnuplot on this file with set xdata time; set timefmt "%s" works but it shows the hours in UTC time. But the user would like to see the local time. So for 1311597859.567497, without any timezone changes, gnuplot would show 12:44:19, but if the user specifies America/New_York, he would like to see 08:44:19 in the gnuplot window.
I though a simple fix would be to calculate the offset between utc and the given timezone and subtract that from the token, and then run new plot on this new file.
Hence I was looking for a way to get offset_seconds of UTC from a given timezone in Perl.
By unix time I assume you mean seconds since the epoch in local time and you are trying to convert to seconds since the epoch in UTC.
Consider using a module such as Time::Zone or DateTime::TimeZone (part of DateTime) to help with such a calculation.
For example with Time::Zone:
use Time::Zone;
my $offset_sec = tz_local_offset(); # or tz_offset($tz) if you have the TZ
# in a variable and it is not local
my $time = time(); # realistically it will be the time value you provide in localtime
my $utc_time = $time + $offset_sec;
With DateTime and DateTime::TimeZone:
use DateTime;
use DateTime::TimeZone;
# cache local timezone because determining it can be slow
# if your timezone is user specified get it another way
our $App::LocalTZ = DateTime::TimeZone->new( name => 'local' );
my $tz = DateTime::TimeZone->new( name => $App::LocalTZ );
my $dt = DateTime->now(); # again, time will be whatever you are passing in
# formulated as a DateTime
my $offset = $tz->offset_for_datetime($dt);
Note that using DateTime you can simply convert a DateTime object from local time to UTC time via set_time_zone('UTC') and then format it for gnuplot also.
To do it all by hand, you can format the output of gmtime if you can get to epoch seconds from your local time (perhaps using mktime out of a date/time string).