I am using TIdHTTP from Indy. I am issuing 2 different requests, one header contains the 'Last-Modified' tag, the other one does not. The header with the tag, throws an exception:
'Invalid Argument to date encode'
I already came across this question where Remy Lebeau said, that TIdHttp is now able to parse ISO8601 dates, but it doesnt seem to work for me. As you can see below, i am not doing anything with the component other than changing the UserAgent. Am I missing something?
url := 'https://api.priceapi.com/v2/jobs/' + JobID+ '?token=' + Token;
http := TIdHTTP.Create(nil);
http.Request.UserAgent := 'XXXXX'; //Some UserAgent
try
ssl := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
try
http.IOHandler := ssl;
try
jo := TJsonObject.ParseJSONValue(http.get(url)) as TJSONObject;
result := jo.GetValue('status').Value;
finally
end;
finally
ssl.Free;
end;
finally
http.Free;
end;
Header with Last-Modified:
Cache-Control: no-cache
Content-Disposition: attachment;
filename="20181025145103_google_shopping_de_5bd1d857bbd7e520c12841d7.json"
Content-Transfer-Encoding: binary
Content-Type: application/json
Last-Modified: 2018-10-25 14:51:23 +0000
Vary: Origin
X-Accel-Buffering: no
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-Request-Id: b05aa8fe-7ea9-4152-8470-a75f9816549f
X-Runtime: 0.099212
X-XSS-Protection: 1; mode=block
transfer-encoding: chunked
Connection: keep-alive
Header without Last-Modified:
Cache-Control: max-age=0, private, must-revalidate', nil
Content-Type: application/json; charset=utf-8', nil
ETag: W/"43c4a8865a5ebe565f3920779a962e93"', nil
Vary: Origin', nil
X-Content-Type-Options: nosniff', nil
X-Frame-Options: SAMEORIGIN', nil
X-Request-Id: 344ac82e-0d14-4838-ae7e-627c79b78edc', nil
X-Runtime: 0.062357', nil
X-XSS-Protection: 1; mode=block', nil
Content-Length: 157', nil
Connection: Close', nil
StackTrace:
:744717d2 KERNELBASE.RaiseException + 0x62
HIWBase.System.SysUtils.ConvertError($3B68860)
HIWBase.System.SysUtils.EncodeDate(???,???,???)
HIWBase.IdGlobalProtocols.RawStrInternetToDateTime('07:53:37 +0000',0)
HIWBase.IdGlobalProtocols.GMTToLocalDateTime('07:53:37 +0000')
HIWBase.IdHTTPHeaderInfo.TIdEntityHeaderInfo.ProcessHeaders
HIWBase.IdHTTPHeaderInfo.TIdResponseHeaderInfo.ProcessHeaders
HIWBase.IdHTTP.TIdHTTPProtocol.RetrieveHeaders(255)
HIWBase.IdHTTP.TIdCustomHTTP.DoRequest('GET','My URL',nil,$ADF09E0,(...))
HIWBase.IdHTTP.TIdCustomHTTP.Get('My URL',$ADF09E0,(...))
HIWBase.IdHTTP.TIdCustomHTTP.Get('My URL',(...))
HIWBase.IdHTTP.TIdCustomHTTP.Get('My URL')
I am on Indy version 10.6.2.5311
The Last-Modified header is defined in RFC 2616 Section 14.29 1 as:
Last-Modified = "Last-Modified" ":" HTTP-date
1: An equivalent definition appears in RFC 7232 Section 2.2.
HTTP-date is defined in RFC 2616 Section 3.3 2 as:
HTTP-date = rfc1123-date | rfc850-date | asctime-date
rfc1123-date = wkday "," SP date1 SP time SP "GMT"
rfc850-date = weekday "," SP date2 SP time SP "GMT"
asctime-date = wkday SP date3 SP time SP 4DIGIT
date1 = 2DIGIT SP month SP 4DIGIT
; day month year (e.g., 02 Jun 1982)
date2 = 2DIGIT "-" month "-" 2DIGIT
; day-month-year (e.g., 02-Jun-82)
date3 = month SP ( 2DIGIT | ( SP 1DIGIT ))
; month day (e.g., Jun 2)
time = 2DIGIT ":" 2DIGIT ":" 2DIGIT
; 00:00:00 - 23:59:59
wkday = "Mon" | "Tue" | "Wed"
| "Thu" | "Fri" | "Sat" | "Sun"
weekday = "Monday" | "Tuesday" | "Wednesday"
| "Thursday" | "Friday" | "Saturday" | "Sunday"
month = "Jan" | "Feb" | "Mar" | "Apr"
| "May" | "Jun" | "Jul" | "Aug"
| "Sep" | "Oct" | "Nov" | "Dec"
2: Equivalent definitions appear in RFC 7231 Section 7.1.1.1.
The Last-Modified value you have shown does not match any of those formats allowed by HTTP.
TIdHTTP uses Indy's GMTToLocalDateTime() function to parse the Last-Modified (and Date and Expires) header. That function is shared by HTTP, IMAP, NNTP, and email components, so it is a little more flexible in the date/time formats that it supports. For instance, it does parse ISO 8601, which you claim the Last-Modified value is. However, the value you have shown does not actually conform to ISO 8601, either. If it had, it would have looked more like this instead:
Last-Modified: 2018-10-26T08:37:01+00:00
To make matters worse, according to the stack trace you have provided, GMTToLocalDateTime() is being called without any date portion at all:
HIWBase.IdGlobalProtocols.GMTToLocalDateTime('07:53:37 +0000')
The only way that can happen in TIdHTTP is if the HTTP server is sending a Last-Modified (or Date or Expires) header with that exact value, which is also not conformant to the HTTP or ISO 8601 standards, and is not handled as-is by GMTToLocalDateTime().
In short, the API you are querying is sending an illegal date/time format that TIdHTTP does not support parsing (which is ironic, because the main https://www.priceapi.com website does send properly formatted HTTP date/time strings). You should contact the website admin and report that their API server is violating HTTP protocol standards in this regard.
That being said, GMTToLocalDateTime() DOES NOT raise an 'Invalid Argument to date encode' exception when it encounters a malformed date/time string. It returns a TDateTime of 0.0 instead. The only way you could be seeing that exception is if you are running your code inside the debugger. When GMTToLocalDateTime() is given a malformed date/time string, it is possible that it may extract numeric components that it thinks are valid but then fails when it attempts to encode the final TDateTime with them. The exception you are seeing comes from the RTL's EncodeDate() function when it is given an invalid month/day/year as input. But GMTToLocalDateTime() catches that exception internally. Your code will never see it at runtime, only the debugger can see it.
I was playing time arithmetic in rails console. I have subtracted two different times and got back a long number, please see below my code.
a = User.find(1).updated_at # returns Mon, 03 Mar 2014 11:07:43 UTC +00:00
b = User.find(1).created_at # returns Mon, 03 Mar 2014 08:36:50 UTC +00:00
a - b # returns 9053.699814796448
My question is. What is this number 9053.699814796448? Is it time? What is it's unit? and how is that calculated? Is it possible to change the default unit of the outcome?
Thanks
a - b gives you the time in seconds. Check out the Time#- .
How do I pass date in RFC2822 format, to an URL in GET method:
e.g I want to pass 18th jan 2013 as min_date_created in the URL https://www.xyz.com/orders
18th jan 2013 in RFC format is ==> Fri, 18 Jan 2013 17:58:49 +0000
how can I pass it to URL?
TIA!
Instead of passing the date as a string, you could convert it to the number of milliseconds since midnight Jan 1, 1970:
var time = new Date('Fri, 18 Jan 2013 17:58:49 +0000').getTime(); // 1358531929000
widow.location.href = 'https://www.xyz.com/orders?min_date_created='+time;
This way you won't have encoding problems.
Or you could also use encodeURIComponent(), see URL encode sees “&” (ampersand) as “&” HTML entity
The user inputs a date range let's say from yesterday with any timezone.
from_datetime = "10/01/2012 00:00 +0545"
I get purchased time for the book like below:
purchased_at = Book.where("created_at > #{from_date}").purchased_at
=> Fri, 08 Jun 2012 09:44:26 UTC +00:00
The problem is this gives me UTC time but I want to show the purchased_at time in the requested time_zone which can vary.
I can't use in_time_zone as the input from_date only has time offset, can I ?
purchased_at.in_time_zone("Kathmandu")
=> Fri, 08 Jun 2012 15:29:26 NPT +05:45
Is there any way around?
Give an offset, you can get a timezone name from ActiveSupport::TimeZone:
> ActiveSupport::TimeZone[5.hours + 45.minutes]
=> (GMT+05:45) Kathmandu
Then you can hand that to in_time_zone:
> Time.now.in_time_zone(ActiveSupport::TimeZone[5.hours + 45.minutes])
=> Thu, 18 Oct 2012 12:33:12 NPT +05:45
You can pull the offset out of the from_datetime with a bit of simple wrangling if you know the incoming format.
There are issues with this approach:
The mapping from offset to name isn't unique.
DST could be a problem if ActiveSupport::TimeZone[] gives you the wrong name.
Depending on your needs, you could just apply the offset manually and ignore the timezone when formatting the timestamp:
> (Time.now.utc + 5.hours + 45.minutes).strftime('%Y-%m-%d %H:%M:%S +0545')
=> "2012-10-18 12:40:12 +0545"
I need to store some time periods in a yaml file, like this:
test:
testing: <%= 1.month %>
I grab the data in an initializer like this:
Class::TIMES = YAML.load(ERB.new(File.new(Rails.root.join('config', 'times.yml')).read).result)
So far so good. But the times I get from this are always off by a day; testing shows this. For example:
<Tue, 06 Mar 2012> expected but was <Wed, 07 Mar 2012>.
The test line:
assert_equal 1.month.from_now.to_date, #object.finished_at.to_date
Elsewhere, #object's finished_at is set like this:
# duration is from the YAML file
self.finished_at = duration.from_now
It always seems to be off by a day, no matter what time period I put in the YAML file. Why does this happen?
EDIT: This seems to be an issue with Fixnums and from_now:
> 1.month.from_now
=> Tue, 06 Mar 2012 21:05:00 UTC +00:00
> 1.month.to_i.from_now
=> Wed, 07 Mar 2012 21:05:05 UTC +00:00
When you convert 1.month to an integer it arbitrary sets the duration getting passed into from_now to 2592000 seconds i.e 30 days regardless of what month it is.
I ran into this issue once before. Check out the documentation for the Data and Time Helper Methods .
While these methods provide precise calculation when used as in the
examples above(".. 1.month.from_now .."), care should be taken to note
that this is not true if the result of months’,years’, etc is
converted before use