One of the fields I get back from a Stripe request is the created field, which contains the value "1351894331". I have tried using DateFormat() in Coldfusion to format this, but that did not work out. I imagine this is some sort of date type / timestamp that needs to be converted before DateFormatting it, but what do I need to use? What type of date format is that?
Thanks
That could be a number of seconds from 1970-01-01? This code:
<cfoutput>#dateAdd("s", 1351894331, createDateTime(1970, 1, 1, 0, 0, 0))#</cfoutput>
Outputs:
{ts '2012-11-02 21:12:11'}
Would that be around about the timestamp you'd expect?
Have you read the "Stripe" docs? If I wanted to know how something worked, my first port of call would be to RTFM. To put it appropriately, if slightly indelicately.
On a whim, I googled "what format is stripe created timestamps in" for you. The first link goes to here. The second link goes to the relevant question on strip.com's docs, which say:
Yes, all Stripe times are in UTC represented as the Unix epoch.
And just to verify the Unix Epoch date, I googled that too. And the answer is:
Unix time, [...] is [...] defined as the number of seconds that have elapsed since midnight Coordinated Universal Time (UTC), 1 January 1970
Of course if you're meaning some other sort of "stripe", you're gonna have to elaborate.
That's a unix timestamp - number of seconds since 1 Jan 1970 - which is used often as a method for storing dates.
But not in CFML, where dates are handled as a floating number of days since 30-Dec-1899.
To convert from a unix timestamp to a CF date number, you need to divide by number of seconds in a day (86400), then add the number of days between the two dates.
<cfset UnixTime = 1351894331 />
<!--- days between 1-Jan-1970 and 30-Dec-1899) --->
<cfset UnixTimeOffset = 25569 />
<cfset CfTime = UnixTimeOffset + UnixTime/86400 />
You can then use the CfTime variable in any date functions and they will work as usual.
Stripe is a supported gateway in CFPAYMENT (a well-tested library that helps you avoid writing e-commerce plumbing code) and has helper functions for the date conversions. You can see the methods here:
https://github.com/ghidinelli/cfpayment/blob/master/api/gateway/stripe/stripe.cfc
<cffunction name="dateToUTC" output="false" access="public" returntype="any" hint="Take a date and return the number of seconds since the Unix Epoch">
<cfargument name="date" type="any" required="true" />
<cfreturn dateDiff("s", dateConvert("utc2Local", "January 1 1970 00:00"), arguments.date) />
</cffunction>
<cffunction name="UTCToDate" output="false" access="public" returntype="date" hint="Take a UTC timestamp and convert it to a ColdFusion date object">
<cfargument name="utcdate" required="true" />
<cfreturn dateAdd("s", arguments.utcDate, dateConvert("utc2Local", "January 1 1970 00:00")) />
</cffunction>
Related
I have a variable called date1 which contains a ColdFusion date/time object generated using parseDateTime. When I dump the variable I get {ts '2014-12-20 15:46:57'}.
I have another variable called date2 which contains another ColdFusion date/time object generated by dateConvert("local2utc",now()). When I dump the variable I get {ts '2014-12-20 15:49:40'}.
But when I do dateDiff("s",date1,date2) I get -21436 which is far too many seconds. Can anybody explain why this happening? I think it might be a time zone issue but I can't get my head around it.
Repro code
<cfset dtString = "Saturday, December 20, 2014 05:07:30 PM">
<cfset dtObject = parseDateTime(dtString)>
<cfdump var="#dtObject#">
<cfset utcNow = dateConvert("local2utc",now())>
<br><br><cfdump var="#utcNow#">
<br><br><cfdump var="#dateDiff("s",dtObject,utcNow)#">
The cause of this issue seems to be due to the bug described at https://bugbase.adobe.com/index.cfm?event=bug&id=3338974.
As described at the above URL 'the variable returned from DateConvert("local2Utc",now()) seems to carry around the offset from local to UTC. When, you use that variable for just about anything (including cfqueryparam), the value you get back is off by the amount of the offset (i.e. back to the value you passed in)'.
A workaround seems to be to convert the date to a string after conversion. I have created a simple wrapper function for this as follows:
<cffunction name="local2utc" output="no">
<cfargument name="date">
<cfreturn dateConvert("local2utc",arguments.date).toString()>
</cffunction>
Another fix for this is to use parseDateTime.
<cfset localDate = now()>
<cfset utcDate = DateConvert("local2utc", localDate)>
<cfset utcfix = parseDateTime(utcDate)>
utc epoch: #DateDiff("s", "January 1 1970 00:00", utcfix)#<br>
loc epoch: #DateDiff("s", "January 1 1970 00:00", localDate)#
I am trying to set the expression #Now() + CreateTimeSpan('30','0','0','0')# in a cookie and manipulating the values in a JS function.
The value of the expression is being displayed as 41720.406? What does this value mean?
Any pointers would be really helpful.
41720.406? What does this value mean?
To answer your question, apparently it represents a number of days from the CF epoch ie 1899-12-30, plus or minus time zone offsets.
<cfscript>
cfEpoch = createDate(1899, 12, 30);
// add number of whole days ie 41720
finalDate = dateAdd("d", 41720, cfEpoch );
// add partial days ie partialDay * millisecondsPerDay
finalDate = dateAdd("l", 0.406 * 86400000, finalDate);
// Result: Today's date and time plus 30 days
writeOutput( "finalDate="& dateConvert("local2UTC", finalDate) );
</cfscript>
Having said that, it is much simpler to use date functions as others suggested, rather than mucking with time span objects.
You may want to use DateAdd() instead. So use DateAdd('d', 30, Now()).
<cfset x = DateAdd('d', 30, now())>
<cfoutput>#toScript(x, "time")#</cfoutput>
output:
time = new Date(2014, 2, 22, 2, 36, 26);
Since you mentioned cookies, it's worth pointing out that dates for cookies use a specific format, i.e. Thu, 01-Jan-1970 00:00:01 GMT
This is not how CF formats dates by default - when you output #SomeDate# the format used is {ts '1970-01-01 00:00:01'} instead.
Here's a function that returns a date as a string in the format cookies use:
<cffunction name="formatCookieDate" returntype="String" output=false access="public">
<cfargument name="DateTime" type="Date" default=#Now()# />
<cfargument name="isUtc" type="String" default=false />
<cfif NOT Arguments.isUtc >
<cfset Arguments.DateTime = DateConvert('local2utc',Arguments.DateTime) />
</cfif>
<cfreturn DateTimeFormat( Arguments.DateTime ,'E, dd Mmm yyyy HH:nn:ss' ) & ' GMT' />
</cffunction>
You can use it like this:
formatCookieDate( Now() + 30 )
Whilst some prefer using DateAdd, it is perfectly valid to add days directly - though you might want to consider if you're actually wanting to add 30 days versus adding 1 month.
NOTE: On versions prior to CF10 you'll need to split the DateTimeFormat into DateFormat and TimeFormat and update the masks accordingly.
I've just encountered CF's unwanted "feature" which involves stripping leading zeroes from the values returned to an autosuggest input. I was thinking I could prepend some character to the values and strip them out after the return, but have hit a snag. I'm modifying an existing function, which looks like this:
<cffunction name="lookupTailNumber" access="remote" returntype="Array" >
<cfargument name="search" type="any" required="false" default="">
<!--- Define variables --->
<cfset var data="">
<cfset var result=ArrayNew(1)>
<!--- Do search --->
<cfquery name="data">
SELECT DISTINCT SERIAL_NUMBER AS list
FROM aircraft_status
WHERE SERIAL_NUMBER LIKE '%#trim(ARGUMENTS.search)#%'
ORDER BY list
</cfquery>
<!--- Build result array --->
<cfloop query="data">
<cfset ArrayAppend(result, list)>
</cfloop>
<!--- And return it --->
<cfreturn result>
</cffunction>
which returns a response which looks like this:
[3001.0,1.00002E8,1.00002001E8,1.00002002E8,1.00002003E8,1.00002004E8]
or in JSON format:
0
3001
1
100002000
2
100002001
3
100002002
4
100002003
where all the results have had leading zeroes stripped away. I've tried modifying the query to prepend a character to each value:
<cfquery name="data">
SELECT DISTINCT (concat(' ', SERIAL_NUMBER)) AS list
FROM aircraft_status
WHERE SERIAL_NUMBER LIKE '%#trim(ARGUMENTS.search)#%'
ORDER BY list
</cfquery>
which returns this:
[" 0000003001"," 0100002000"," 0100002001"," 0100002002"," 0100002003"," 0100002004"]
so you'd think all was well, right? Problem: when returned, none of the values show up in the autosuggest field!!! I've also tried prepending different characters, including numbers, with no luck. Looking at the elements in yui-ac-bd div > ul, none are populated or displayed.
The input is declared like so:
<cfinput style = "width:300px;"
class = ""
type="text"
name="txtvalueFilter"
maxlength="15"
id="txtvalueFilter"
autosuggest="cfc:mycfcpath({cfautosuggestvalue})"
/>
Thoughts?
Try appending a space, so the built-in JSON serializer will treat it as a string instead of an int in JSON.
Also, make sure you have installed the latest hotfixes for your version of CF.
I wonder if u need to "Build result array". What happen if you return data.list? or, maybe use ListToArray(valueList(data.list)) instead?
I'm looking to learn how to create a REGEX in Coldfusion that will scan through a large item of html text and create a list of items.
The items I want are contained between the following
<span class="findme">The Goods</span>
Thanks for any tips to get this going.
You don't say what version of CF. Since v8 you can use REMatch to get an array
results = REMatch('(?i)<span[^>]+class="findme"[^>]*>(.+?)</span>', text)
Use ArrayToList to turn that into a list.
For older version use REFindNoCase and use Mid() to extract substrings.
EDIT: To answer your follow-up comment the process of using REFind to return all matches is quite involved because the function only returns the FIRST match. This means you actually have to call REFind many times passing a new startpos each time. Ben Forta has written a UDF which does exactly this and will save you some time.
<!---
Returns all the matches of a regular expression within a string.
NOTE: Updated to allow subexpression selection (rather than whole match)
#param regex Regular expression. (Required)
#param text String to search. (Required)
#param subexnum Sub-expression to extract (Optional)
#return Returns a structure.
#author Ben Forta (ben#forta.com)
#version 1, July 15, 2005
--->
<cffunction name="reFindAll" output="true" returnType="struct">
<cfargument name="regex" type="string" required="yes">
<cfargument name="text" type="string" required="yes">
<cfargument name="subexnum" type="numeric" default="1">
<!--- Define local variables --->
<cfset var results=structNew()>
<cfset var pos=1>
<cfset var subex="">
<cfset var done=false>
<!--- Initialize results structure --->
<cfset results.len=arraynew(1)>
<cfset results.pos=arraynew(1)>
<!--- Loop through text --->
<cfloop condition="not done">
<!--- Perform search --->
<cfset subex=reFind(arguments.regex, arguments.text, pos, true)>
<!--- Anything matched? --->
<cfif subex.len[1] is 0>
<!--- Nothing found, outta here --->
<cfset done=true>
<cfelse>
<!--- Got one, add to arrays --->
<cfset arrayappend(results.len, subex.len[arguments.subexnum])>
<cfset arrayappend(results.pos, subex.pos[arguments.subexnum])>
<!--- Reposition start point --->
<cfset pos=subex.pos[1]+subex.len[1]>
</cfif>
</cfloop>
<!--- If no matches, add 0 to both arrays --->
<cfif arraylen(results.len) is 0>
<cfset arrayappend(results.len, 0)>
<cfset arrayappend(results.pos, 0)>
</cfif>
<!--- and return results --->
<cfreturn results>
</cffunction>
This gives you the start (pos) and length of each match so to get each substring use another loop
<cfset text = '<span class="findme">The Goods</span><span class="findme">More Goods</span>' />
<cfset pattern = '(?i)<span[^>]+class="findme"[^>]*>(.+?)</span>' />
<cfset results = reFindAll(pattern, text, 2) />
<cfloop index="i" from="1" to="#ArrayLen(results.pos)#">
<cfoutput>match #i#: #Mid(text, results.pos[i], results.len[i])#<br></cfoutput>
</cfloop>
EDIT: Updated reFindAll with subexnum argument. Setting this to 2 will capture the first subexpression. The default value 1 captures the entire match.
Try looking into the possibility of making your HTML work with a regular DOM Parser and querying it via XPath instead of hammering this trough an regex-based abomination.
to make HTML input usable, pass it through jTidy (see http://jtidy.riaforge.org/)
Once you have well-formed XML/XHTML, build an XML document from it
<cfset dom = XmlParse(scrubbedHtml, true)>
query the XML document using XPath
<cfset result = XmlSearch(dom, "//span[#class='findme']")>
Done.
EDIT: Coldfusion's XmlSearch() doesn't have great XML namespace support. If you end up producing XHTML instead of the more recommendable XML, use the following XPath (note the colon) "//:span[#class='findme']" or "//*:span[#class='findme']". See here and here for more info.
See the jTidy API documentation for a complete overview what jTidy can do.
Using CFML (ColdFusion Markup Langauge, aka ColdFusion), how can you compare if two single dimension arrays are the same?
There's a very simple way of comparing two arrays using CFML's underlying java. According to a recent blog by Rupesh Kumar of Adobe (http://coldfused.blogspot.com/), ColdFusion arrays are an implementation of java lists (java.util.List). So all the Java list methods are available for CFML arrays.
So to compare 2 arrays all you need to do is use the equals method. It returns a YES if the arrays are equal and NO if they are not.
<cfset array1 = listToArray("tom,dick,harry,phred")/>
<cfset array2 = listToArray("dick,harry,phred") />
<cfset array3 = listToArray("tom,dick,harry,phred")/>
<cfoutput>
Array2 equals Array1 #array2.equals(array1)# (returns a NO) <br/>
Array3 equals Array1 #array3.equals(array1)# (returns a YES) <br/>
</cfoutput>
To build on James' answer, I thought that JSON might be preferrable over WDDX. In fact, it proves to be considerably more efficient. Comparing hashes is not that expensive, but serializing the data and then generating the hash could be (for larger and/or more complex data structures).
<cfsilent>
<!--- create some semi-complex test data --->
<cfset data = StructNew() />
<cfloop from="1" to="50" index="i">
<cfif variables.i mod 2 eq 0>
<cfset variables.data[variables.i] = StructNew()/>
<cfset tmp = variables.data[variables.i] />
<cfloop from="1" to="#variables.i#" index="j">
<cfset variables.tmp[variables.j] = 1 - variables.j />
</cfloop>
<cfelseif variables.i mod 3 eq 0>
<cfset variables.data[variables.i] = ArrayNew(1)/>
<cfset tmp = variables.data[variables.i] />
<cfloop from="1" to="#variables.i#" index="j">
<cfset variables.tmp[variables.j] = variables.j mod 6 />
</cfloop>
<cfset variables.data[variables.i] = variables.tmp />
<cfelse>
<cfset variables.data[variables.i] = variables.i />
</cfif>
</cfloop>
</cfsilent>
<cftimer label="JSON" type="inline">
<cfset jsonData = serializeJson(variables.data) />
<cfset jsonHash = hash(variables.jsonData) />
<cfoutput>
JSON: done.<br />
len=#len(variables.jsonData)#<br/>
hash=#variables.jsonHash#<br />
</cfoutput>
</cftimer>
<br/><br/>
<cftimer label="WDDX" type="inline">
<cfwddx action="cfml2wddx" input="#variables.data#" output="wddx" />
<cfset wddxHash = hash(variables.wddx) />
<cfoutput>
WDDX: done.<br />
len=#len(variables.wddx)#<br/>
hash=#variables.wddxHash#<br />
</cfoutput>
</cftimer>
Here's the output that the above code generates on my machine:
JSON: done.
len=7460
hash=5D0DC87FDF68ACA4F74F742528545B12
JSON: 0ms
WDDX: done.
len=33438
hash=94D9B792546A4B1F2FAF9C04FE6A00E1
WDDX: 47ms
While the data structure I'm serializing is fairly complex, it could easily be considered small. This should make the efficiency of JSON serialization over WDDX even more preferrable.
At any rate, if I were to try to write a "compareAnything" method using hash comparison, I would use JSON serialization over WDDX.
Assuming all of the values in the array are simple values, the easiest thing might be to convert the arrays to lists and just do string compares.
<cfif arrayToList(arrayA) IS arrayToList(arrayB)>
Arrays are equal!
</cfif>
Not as elegant as other solutions offered, but dead simple.
The arrayCompare() user-defined function at cflib should do it
http://cflib.org/index.cfm?event=page.udfbyid&udfid=1210
Jasons answer is certainly the best, but something I've done before is performed a hash comparison on objects that have been serialised to WDDX.
This method is only useful for relatively small arrays, but it's another option if you want to keep it purely CFML. It also has the benefit that you can apply the method to other data types as well...
Edit: Adams' entirely right (as you can see from his numbers) - JSON is much more economical, not only in this situation, but for serialization in general. In my defense I'm stuck using CFMX 6.1 that has no inbuilt JSON functions, and was trying to avoid external libs.
I was looking into using CF's native Java objects awhile back and this question reminded me of a few blog posts that I had bookmarked as a result of my search.
ColdFusion array is actually an implementation of java list (java.util.List). So all the list methods are actually available for Array.
CF provides most of the list functionality using Array functions but there are few things possible with java list which you can not do directly with CF functions.
Merge two arrays
Append an array in the middle of another array
Search for an element in an array
Search array 1 to see if array 2's elements are all found
Equality check
Remove elements in array 1 from array 2
From: http://coldfused.blogspot.com/2007/01/extend-cf-native-objects-harnessing.html
Another resource I found shows how you can use the native Java Array class to get unique values and to create custom sorts functions in case you need to sort an array of dates, for instance.
http://www.barneyb.com/barneyblog/2008/05/08/use-coldfusion-use-java/
This second link contains links to other posts where the author shows how to use other Java classes natively to gain either functionality or speed over CF functions.
All of these solutions check that two arrays are equal. They don't check that they are the same object. The only way I know to do that in native CF is to change the object in some way and see if both references have the change in.
I also think that you should be wary of relying on CF implementing certain java classes or exposing methods. These are liable to change.
As for comparing two arrays to see if the contents is the same, why not just
Check the length (if different return false)
If the lengths are the same from 1 to array len and check the elements are the same break and return false if they are not.
This will work for simple values.