DanBot
DanBot

Reputation: 121

Powershell cast string to time only

I have created a Powershell/XAML app, that on button press makes a RESTAPI call, parses the JSON response into fields in the app front end. All fine so far.

These fields will be populated with a string representing a time, so "1800" or "2000" etc.

The user can then change this from 1800 to 1900 for example.

This is all fine, and in the background the app will use 1900 to update a setting to be used in a POST back.

However there are other settings that are offset by 90 mins of the time above. I don't want the user to have update each one, which is why I am trying to programmatically.

But try as I might, I cannot take a string of 1800, add 90 mins to it and make the value 1930 (not 1890).

Upvotes: 2

Views: 558

Answers (3)

jimhark
jimhark

Reputation: 5046

When I read this question I wanted to solve it with minimal string manipulation, leaning on time related objects and methods instead. datetime was the first object I thought of, but it expects a date (year, month, day). Things actually simplify if we use timespan. Its static method, ParseExact, can parse the string directly.

$offsetTimeSpan = [timespan]::FromMinutes(90)

$timeField = '830'
$timeStr = $timeField.PadLeft(4, '0')
$timeSpan = [timespan]::ParseExact($timeStr, 'hhmm', [CultureInfo]::InvariantCulture)
$offsetTime = $timeSpan.Add($offsetTimeSpan)

$offsetTime.ToString('hhmm')

$timeField is used to represent the time you get from the RESTAPI. PadLeft is only needed if it's possible for a leading 0 to be missing. ParseExact does the heavy lifting of converting the string to a time type. Because timespan doesn't have an AddMinutes member, we use the Add method passing in a timespan of 90 minutes, $offsetTimeSpan.

You don't mention anything about overflowing past midnight. You can test for overflow using $offsetTime.Days, if any special processing is required.

Upvotes: 0

mklement0
mklement0

Reputation: 439477

Using [timespan] instances is another option:

$time = '1800' 
([timespan] ($time -replace '(?<=^..)', ':') + '01:30').ToString('hhmm') #->'1930'
  • $time -replace '(?<=^..)', ':' uses the regex-based -replace operator to insert : after the first two characters - see this regex101.com page for an explanation of the regex and the ability to experiment with it.

  • Due to expressing the results only in terms of hours and minutes, the calculation wraps around at midnight, so that adding '05:30', for instance, would yield '0030'

  • The RHS operand needn't be cast to [timespan] directly, because the data type of the LHS - with its explicit [timespan] cast - implicitly converts the RHS to [timespan] too, with '01:30' representing 1 hour and 30 minutes, i.e. 90 minutes.

    • If you want to define the duration to add in terms of 90 minutes, use the following instead (there are analogous static methods for other units, such as ::FromSeconds():

      [timespan]::FromMinutes(90)
      
    • Alternatively, you can cast a number to [timespan], which is interpreted as ticks, which are 100-nanosecond units; there are 1e9 (10 to the power of 9) nanoseconds in a second, and therefore 1e7 100-nanosecond units in a second. Thus, multiplying with 1e7 gives you seconds, and multiplying that with 60 minutes.

      # 90 minutes expressed as ticks
      [timespan] 90 * (60 * 1e7)
      

Upvotes: 0

Christian Hagelid
Christian Hagelid

Reputation: 8355

You could parse the input as a DateTime object (ignoring the date part) and then use the AddMinutes method.

$input = '1800'
$hour = $input.Substring(0,2)
$minute = $input.Substring(2,2)

$dateInputStr = "0001-01-01,${hour}:${minute}:00"
[datetime]$dateInput = ([datetime]$dateInputStr)
$dateInput = $dateInput.AddMinutes(90)
$dateInput.ToString("HHmm")

Upvotes: 1

Related Questions