It seems that Indy GMTToLocalDateTime does not ignore comments when decoding date:
TDateTime dtDate1 = GMTToLocalDateTime("12 Mar 2015 14:03:21 -0000");
TDateTime dtDate2 = GMTToLocalDateTime("Thu, 12 Mar 2015 14:03:20 +0000 (GMT)");
TDateTime dtDate3 = GMTToLocalDateTime("Thu, 12 Mar 2015 14:03:20 +0000 (envelope-from <aaa#bbb.ccc>)");
TDateTime dtDate4 = GMTToLocalDateTime("Thu, 12 Mar 2015 14:03:20 +0000 (aaa#bbb.ccc)");
UnicodeString Dt1 = DateTimeToStr(dtDate1);
UnicodeString Dt2 = DateTimeToStr(dtDate2);
UnicodeString Dt3 = DateTimeToStr(dtDate3);
UnicodeString Dt4 = DateTimeToStr(dtDate4);
First 2 are decoded correctly. The last 2 are not.
The part in the parenthesis is supposed to be ignored because it is just a comment but it seems that it is not.
Is this a bug in Indy?
Also - is there a bug-tracker for Indy (as it appears forums are down)?
GMTToLocalDateTime() (more specifically, RawStrInternetToDateTime()) is not meant to accept or look for embedded comments. Comments do not belong in the input and must be stripped off beforehand. Embedded comments are a feature of email, but are to be ignored when processing data (see RFC 822 Section 3.4.3).
In this situation, the comments were not stripped by the caller, and the presence of the '.' character in the comments of the last 2 examples was throwing off RawStrInternetToDateTime() when it checks for the presence of a timestamp and whether it uses ':' or '.' as a delimiter between the hour/minutes/seconds.
Indy as a whole is not designed to even recognize, let alone handle, embedded comments in headers. However, in this situation, I have made a small tweak to RawStrInternetToDateTime() so comments will not confuse the timestamp parsing anymore (though it is really the caller's responsibility to strip comments before parsing).
And yes, there are bug trackers for Indy:
http://code.google.com/p/indyproject
(though Google Code is shutting down, so this one will go away eventually).
http://indy.codeplex.com
Related
Cisco IOS routers, doing a "dir", and I want to grab all file names with ".bin" in the name.
Example string:
Directory of flash0:/
1 -rw- 95890300 May 24 2015 11:27:22 +00:00 c2900-universalk9-mz.SPA.153-3.M5.bin
2 -rw- 68569216 Feb 8 2019 20:15:26 +00:00 c3900e-universalk9-mz.SPA.151-4.M10.bin
3 -rw- 46880 Oct 25 2017 19:08:56 +00:00 pdcamadeusrtra-cfg
4 -rw- 600 Feb 1 2019 19:36:44 +00:00 vlan.dat
260153344 bytes total (95637504 bytes free)
I've figured out how to pull "bin", but I can't figure out how to pull the whole filename (starting with " c", ending in "bin"), because I want to then use the values and delete unwanted files.
I'm new to programming, so the regex examples are a little confusing.
You can use this regex
^[\w\W]+?(?=(c.*\.bin))\1$
^ - Start of string.
[\w\W]+? - Match anything one or more time ( Lazy mode ).
(?=(c.*\.bin)) - Positive lookahead match c followed by anything followed by \.bin ( Group 1)
\1 - Match group 1.
$ - End of string.
Demo
To match the filename that start with a c (or at the start of the string) you might use a negative lookbehind (?<!\S) to check what is on the left is not a non-whitespace character.
Then match either 1+ times not a whitespace character \S+ or list in a character class [\w.-]+ what the allowed characters are to match. After that match a dot \. followed by bin.
At the end you might use a word boundary \b to prevent bin being part of a larger word:
(?<!\S)[\w.-]+\.bin\b
regex101 demo
Thank you Code Maniac!
Your code finds one instance, and I needed to find all. Using what you gave me plus messing around with some other examples, I found this to work:
binfiles="{{ dir_response.stdout[0] | regex_findall('\b(?=(c.*.bin))\b') }}"
Now I get this:
TASK [set_fact] ********************************************************************************************************
task path: /export/home/e130885/playbooks/ios-switch-upgrade/ios_clean_flash.yml:16
Tuesday 12 February 2019 08:29:58 -0600 (0:00:00.350) 0:00:03.028 ******
ok: [10.35.91.200] => changed=false
ansible_facts:
binfiles:
- c2900-universalk9-mz.SPA.153-3.M5.bin
- c3900e-universalk9-mz.SPA.151-4.M10.bin
- c2800nm-adventerprisek9-mz.151-4.M12a.bin
Onto the next task of figuring out how to use each element. Thank you!
I found in some code I maintain they used this format for an update query
UPDATE X=to_date('$var','%iY-%m-%d %H:%M:%S.%F3') ...
But I can't find anywhere in Informix documentation what the i is for. Running this next query will result the same values.
SELECT TO_CHAR(CURRENT, '%Y-%m-%d %H:%M:%S%F3') as wo_I,
TO_CHAR(CURRENT, '%iY-%m-%d %H:%M:%S%F3') as with_I FROM X;
wo_i | with_i
------------------------|------------------------
2017-06-20 16:49:44.712 | 2017-06-20 16:49:44.712
So what am I missing?
Resources I looked into:
https://www.ibm.com/support/knowledgecenter/SSGU8G_11.70.0/com.ibm.sqlt.doc/ids_sqt_130.htm
https://www.ibm.com/support/knowledgecenter/SSGU8G_11.70.0/com.ibm.sqlt.doc/ids_sqt_129.htm
http://www.sqlines.com/informix-to-oracle/to_char_datetime
It's a trifle hard to find, but one location for the information you need (assuming you use Informix 11.70 rather than 12.10, though it probably hasn't changed much) is:
Client APIs and Tools — GLS User's Guide — GLS Environment Variables
In particular, it says:
%iy — Is replaced by the year as a two-digit number (00 - 99) for both reading and printing. It is the formatting directive specific to IBM Informix for %y.
%iY — Is replaced by the year as a four-digit number (0000 - 9999) for both reading and printing. It is the formatting directive specific to IBM Informix for %Y.
…
%y — Requires that the year is a two-digit number (00 through 99) for both reading and printing.
%Y — Requires that the year is a four-digit number (0000 through 9999) for both reading and printing.
There clearly isn't much difference between the two — I'm not even sure I understand what the difference is supposed to be. I think it may be the difference between accepting but not requiring leading zeros on 1, 2 or 3 digit year numbers. But for the most part, it seems you can treat them as equivalent.
I never wrote any complex regular expression before, and what I need seems to be (at least) a bit complicated.
I need a Regex to find matches for the following:
"On Fri, Jan 16, 2015 at 4:39 PM"
Where On will always be there;
then 3 characters for week day;
, is always there;
space is always there;
then 3 characters for month name;
space is always there;
day of month (one or two numbers);
, is always there;
space is always there;
4 numbers for year;
space at space always there;
time (have to match 4:39 as well as 10:39);
space and 2 caps letters for AM or PM.
Here's a very simple and readable one:
/On \w{3}, \w{3} \d{1,2}, \d{4} at \d{1,2}:\d{2} [AP]M/
See it on rubular
Try this:
On\s+(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), (?:Jan|Feb|Mar|Apr|May|June|July|Aug|Sept|Oct|Nov|Dec) \d{1,2}, \d{4} at \d{1,2}:\d{2} (?:AM|PM)
/On \w{3}, \w{3} \d{1,2}, \d{4} at \d{1,2}:\d{1,2} [A-Z]{2}/
# \w{3} for 3 charecters
# \d{1,2} for a or 2 digits
# \d{4} for 4 digits
# [A-Z]{2} for 2 capital leters
You could try the below regex and it won't check for the month name or day name or date.
^On\s[A-Z][a-z]{2},\s[A-Z][a-z]{2}\s\d{1,2},\s\d{4}\sat\s(?:10|4):39\s[AP]M$
DEMO
You can use Rubular to construct and test Ruby Regular Expressions.
I have put together an Example: http://rubular.com/r/45RIiwheqs
Since it looks you try to parse dates, you should use Date.strptime.
/On [A-Za-z]{3}, [A-Za-z]{3} \d{1,2}, \d{4} at \d{1,2}:\d{1,2}/g
The way you are describing the problem makes me thing that the format will always be preserved.
I would then in your case use the Time.parse function, passing the format string
format = "On %a, %b"On Fri, Jan 16, 2015 at 4:39 PM", format)
which is more readable than a regexp (in my opinion) and has the added value that it returns a Time object, which is easier to use than a regexp match, in case you need to perform other time-based calculations.
Another good thing is that if the string contains an invalid date (like "On Mon, Jan 59, 2015 at 37:99 GX" ) the parse function will raise an exception, so that validation is done for free for you.
I have a date of this type: 2004-12-31 23:00:00-08 but no one of the patterns i know and i have used from the documentation is working. I thought it should something like "yyyy-MM-dd HH:mm:ssX" but it isn't working.
Sorry for you, but this is a known bug and was already reported in January 2014. According to the bug log a possible solution is deferred.
A simple workaround avoiding alternative external libraries is text preprocessing. That means: Before you parse the text you just append the prefix ":00". Example:
String input = "2004-12-31 23:00:00-08";
String zero = ":00";
if (input.charAt(input.length() - 3) == ':') {
zero = "";
}
ZonedDateTime zdt =
ZonedDateTime.parse(
input + zero,
DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ssXXX"));
System.out.println(zdt);
// output: 2004-12-31T23:00-08:00
UPDATE due to debate with #Seelenvirtuose:
As long as you ONLY have offsets with just hours but without minute part then the pattern "uuuu-MM-dd HH:mm:ssX" will solve your problem, too (as #Seelenvirtuose has correctly stated in his comment).
But if you have to process a list of various strings with mixed offsets like "-08", "Z" or "+05:30" (latter is India standard time) then you should usually apply the pattern containing three XXX. But this currently fails (have verified it by testing in last version of Java-8). So in this case you still have to do text preprocessing and/or text analysis.
A user will enter a string value for a date. StrToDate will be used to convert the string value to a DateTime. If the user enter's a date with a 2 digit year the date may be parsed as the current century (20xx) or the previous century (19xx).
To clear up any ambiguity, how do require the user enter a 4 digit year?
if isFourDigitYear(txbDate.Text) then
date := StrToDate(txbDate.Text)
else
ShowMessage('enter date with 4 digit year');
I think that the best choice would be to use TDateTimePicker
http://docwiki.embarcadero.com/Libraries/XE6/en/Vcl.ComCtrls.TDateTimePicker
If you are using older Delphi than Delphi 2009 with update pack 3 then you would wanna read next article to fix a bug found in TDateTimePicker.
http://www.tmssoftware.com/site/blog.asp?post=117
This bug has been fixed in newer versions.
Now if you are using FireMonkey take care about using TDateTimePicker as in Delphi XE3 it has a bug which srews up the date when entered through keyboard (can still be picked fine by mouse). I'm not sure if this was already fixed in later versions of Delphi or not.
If using of TDateTimePicker is out of the question then definitly use TMaskedEdit instead of regular TEdit since the chosen mask forces user to enter in text in proper format.
http://docwiki.embarcadero.com/CodeExamples/XE6/en/EditMask_(Delphi)
EDIT: The best advantage of using TDateTimePicker is that it automatically uses Date Time format that has been set on that specific system.
This means that date time format used will be the one user is used to. So there will be no mistakes in case if user local settings use dd/mm/yy format instead of mm/dd/yy.
One possibility is this:
FUNCTION IsFourDigitYear(DateStr : STRING ; DateSep : CHAR = '/') : BOOLEAN;
VAR
P : Cardinal;
BEGIN
DateStr:=DateStr+DateSep; Result:=TRUE;
REPEAT
P:=POS(DateSep,DateStr);
IF P=5 THEN EXIT;
DELETE(DateStr,1,P)
UNTIL DateStr='';
Result:=FALSE
END;
It will check that there is a part of the given string that has 4 characters.
It currently won't check if that part is numerical (ie. only contains digits). And it will require you to pass in the seperator character used if you want it to be truly international - there are some countries that use '-' as a date seperator, and most other countries in the world doesn't use the strange M/D/Y format, but either D/M/Y or Y/M/D format (where "/" may be "-" in some countries).
If you want a truly international function that also checks if the four-digit part is actually in the year part of the currently valid date format, then it'll need a much more complex parser. The above may get you started, however...