Is it possible to take a string of date, such as 2013-02-26 10:45:54,082 and get the time in milliseconds from the "epoch" ? or something similar?
I know clock scan returns the time in seconds.
Also clock scan returns an error for this specific format.
I couldn't find something as I want, and I want to make sure it doesn't exists before I start creating it...
Thanks
The millisecond-scale offset isn't supported by Tcl (as it is a fairly rare format in practice), but you can work around that:
set str "2013-02-26 10:45:54,082"
regexp {^([^\.,]*)[\.,](\d+)$} $str -> datepart millipart; # split on the last occurrence of , or .
set timestamp [clock scan $datepart -format "%Y-%m-%d %H:%M:%S" -gmt 1]
scan $millipart "%d" millis
set millistamp [expr {$timestamp*1000 + $millis}]
(You don't supply timezone info, so I've assumed it is UTC, i.e., “GMT”. I've also allowed for using . instead of , as a millisecond separator, as which you get depends on the locale that created the time string in the first place.)
Related
I need to create a script whithin I can calcute a durations here an example :
assign duration1 5.hours
assign duration2 4.minutes
assign duration3 10.seconds
assign seconds [calc_duration [getVar duration1] + [getVar duration2] + [getVar duration3]]*
I want to get as a respons (seconds) for the example I would have "18250" seconds.
can you please help me with this.
Thanks.
The clock add command can do calculations with durations, but it needs a base time to work with. That's important because, for example, not all months are the same number of days long, and not all days are the same number of hours because of DST rules (which are themselves not the same every year). If you're only working with the small units and not too many of them, you can ignore this and use 0 (the start of the Unix epoch) as the base time.
set seconds [clock add 0 5 hours 4 minutes 10 seconds]
But if you're working with longer amounts of time, you'll need to be more careful. clock add still has the tools, but you'll have to choose the base timestamp correctly and specify your locale of interest.
# You're really supposed to scan with a format, but I'm lazy
set baseTimestamp [clock scan "4/4/2022 10:30 EST"]
set timezone :America/New_York; # EST/EDT
set timeBits {5 months 4 days 3 hours 2 minutes 1 second}
# Do the time arithmetic
set targetTimestamp [clock add $baseTimestamp {*}$timeBits -timezone $timezone]
# Convert into an elapsed number of seconds
set seconds [expr {$targetTimestamp - $baseTimestamp}]
# 13575721 seconds is a fair while...
I have a getdate() field and I want to convert it into 20210211T172650Z this format how do I do it in SSIS expression?
In SSIS, we have data types for strings, numbers and dates. Dates have no format and when it is converted to a string value, you're getting whatever format the localization rules dictate.
If you have a particular format you want, then you need to control that and the only way you can control it, is by using a string data type.
The pattern we're going to use here, for each element,
extract the digit(s)
convert the digits to string
left pad/prepend a leading zero
extract the last 2 characters from our string
When we extract digits, they're numbers and numbers don't have leading zeroes. We convert to string which will allow us to then add the character zero in front of it because we're just concatenating strings. If the number was less than 10, then this prepending of a zero will result in exactly what we want. 9 -> 09 If it was greater than 9, then we have an extraneous value in there. 11 -> 011. We don't care that we went too big because we're then going to take the right 2 most characters making 09 -> 09 and 011 -> 11. This is the shortest logic to making a leading zero string in SSIS.
Using that logic, we're going to create a variable for each element of our formatted string: year, month, day, hour, minute, second.
What's the starting date?
I created a variable called StartDate of type DateTime and hard coded it to a starting point. This is going to allow me to test various conditions. If I used getdate, then I'd either have to adjust my computer's clock to ensure my code works on 2001-01-01 at 01:01:01 as well as 2021-12-31 at 23:59:59. When you're satisfied your code passes all the tests, you can then specify that StartDate property EvaluateAsExpression is True and then use GetDate(). But I wouldn't use GetDate().
GetDate is going to evaluate every time you inspect it. When your package starts, it will show 2021-02-12 # 11:16 AM But your package takes 5 minutes to run, so when you go to re-use the value that is built on GetDate, you will now get 2021-02-12 # 11:21 AM.
In your case, those keys won't match if you send it more than once to your Amazon thing. Instead, use a System scoped variable like #[System::StartTime] That is updated to the time the package starts executing and remains constant for the duration of the SSIS package execution. So when you're satisfied the expression you've build matches the business rules, then change #[User::StartDate] over to use #[System::StartTime]. It provides the updated time but without the challenges of drifting time.
Extract the digit(s)
The SSIS expression language has YEAR, MONTH and DAY defined but no shorthand methods for time components. But, it does have the DATEPART function in which you can ask for any named date part. I'm going to use that for all of my access methods as it makes it nice and consistent.
As an example, this is how I get the Hour. String literal HOUR and we use our variable
DATEPART("HOUR",#[User::StartDate])
Convert the digits to string
The previous step gave us a number but we've got that leading zero problem to solve so convert that to a string
(DT_WSTR, 2)DATEPART("HOUR",#[User::StartDate])
Cast to string, two characters wide max, the number we generated
left pad/prepend a leading zero
String concatenation is the + operator and since we can't concatenate a string to a number, we make sure we have the correct operand types on both sides
"0" + (DT_WSTR, 2)DATEPART("HOUR",#[User::StartDate])
extract the last 2 characters from our string
Since we might have a 2 or 3 character string at this point, we're going to use the RIGHT function to only get the last N characters.
RIGHT("0" + (DT_WSTR, 2)DATEPART("HOUR",#[User::StartDate]), 2)
Final concatenation
Now that we have our happy little variables and we've checked our boundary conditions, the only thing left is to make one last variable, DateAsISO8601 type of string, EvaulateAsExpression = True
#[User::Year] + #[User::Month] +#[User::Day] + "T" +#[User::Hour] +#[User::Minute] +#[User::Second] + "Z"
Consider the following code run on a Windows 7 system:
% info patchlevel
8.6.4
% clock scan "1995-01-35" -format "%Y-%m-%d"
791856000
% clock format [clock scan "1995-01-35" -format "%Y-%m-%d"] -format "%Y-%m-%d"
1995-02-04
%
I ran into this situation when trying to determine if a string was a valid date, I was expecting the first clock scan to fail as 35 isn't a valid day, but what happens is that I get a date 35 days after the 1st of January.
In my code I'm now comparing the output of the 2nd clock command to the original input and deciding that the string isn't really a valid date if the result is different.
Is there a better way to validate a date and is this the expected behaviour of the clock command, I can't find it described in the manual page for clock?
It is not in-built functionality in Tcl. If the dates/months are exceeding, then it is added to the successive days/months.
In your case, you are giving 35th of month January. The additional 4 days (i.e. 31 + 4 = 35) are added and calculated as month February's 4th day.
In the bizarre case that adding the given number of days yields a date
that does not exist because it falls within the dropped days of the
Julian-to-Gregorian conversion, the date is converted as if it was on
the Julian calendar.
Adding a number of months, or a number of years, is similar; it
converts the given time to a calendar date and time of day. It then
adds the requisite number of months or years, and reconverts the
resulting date and time of day to an absolute time.
If the resulting date is impossible because the month has too few days
(for example, when adding 1 month to 31 January), the last day of the
month is substituted. Thus, adding 1 month to 31 January will result
in 28 February in a common year or 29 February in a leap year.
proc is_valid_date {date {date_format "%Y-%m-%d"}} {
return [string equal [clock format [clock scan $date -format $date_format] -format $date_format] $date]
}
The date format is an optional and defaulted to %Y-%m-%d. If the format is not matching then it will fail. You can handle the exceptions.
Output :
% is_valid_date "1995-02-28"
1
% is_valid_date "1995-01-35"
0
%
We are converting the date to a long and reverting to the date again. If both are not same, then the given input is incorrect.
Reference : clock
I'm trying to correctly calculate working Year/Week number from a date field.
I tried to use the clock format command as follows
% clock format [clock scan "2016.01.01" -format "%Y.%m.%d"] -format "%Y-WW%V"
2016-WW53
and while the Week number is correct, the year is not because in this case Jan1 lands in week 53, as it did in 2016. Since the Tcl clock format command specifies the week number and the year separately, I end up with Jan 1, 2016 being shown as 2016-WW53 but this should be shown as 2015-WW53.
Is there a built in function for this or do I need to use an if/then condition to choose the year correctly?
Just use the modifier %G:
clock format [clock scan "2016.01.01" -format "%Y.%m.%d"] -format "%G-WW%V"
Output:
2015-WW53
From the manual:
%G
On output, produces a four-digit year number suitable for use with the week-based ISO8601 calendar; that is, the year number corresponds to the week number produced by %V. On input, accepts such a four-digit year number, possibly with leading whitespace.
The below shown clock manipulation works well in Linux machine which has TCL 8.5. But when I use the same on the SunOS which has the TCL8.4 , I am getting the error "bad switch "-format": must be -base or -gmt".
For some reason, I am not able to upgrade my TCL 8.4 to 8.5 in SunOS.
How can I make it work in TCL 8.4 as well?
The commands are given below and what I am trying to achieve through these commands is to advance the system date by one more day.
$today contains the value "2012 06 15 14 39 20"
set today [clock scan $today -format "%Y %m %d %H %M %S"]
set tomorrow [clock add $today 86600 seconds]
set victim [clock format $tomorrow -format "%a"]
set tomorrow [clock format $tomorrow -format "%m%d%H%M"]
send "sudo date $tomorrow\r"
Jackson's answer is the core of what the issue is: 8.5 added a lot of features to clock and your code is dependent on them. However, he doesn't quite identify the best method for getting the target time.
# Multiple assignment from list
foreach {YY MM DD HH mm ss} $today break
# Get the date tomorrow in one funky step
set tomorrow [clock scan "$YY$MM$DD $HH$mm$ss + 86600 seconds"]
# Rest is the same as before
set victim [clock format $tomorrow -format "%a"]
set tomorrow [clock format $tomorrow -format "%m%d%H%M"]
send "sudo date $tomorrow\r"
(You are aware that a day is never 86600 seconds long? That's 200 seconds longer than the average length…)
As you have found out the clock command changed between Tcl 8.4 and 8.5. In 8.4 the clock scan command only recognized a number of standard formats. So you need to convert your $today value into one of these formats, see here for details.
One possible way is
regexp {(\d\d\d\d) (\d\d) (\d\d) (\d\d) (\d\d) (\d\d)} $today all YY MM DD HH mm ss
set reformatToday "$YY$MM$DD $HH$mm$ss"
set today [clock scan $reformatToday]
Tcl 8.5 will also work with this free form scan code; however this feature is deprecated in Tcl versions later than 8.4.