<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Hot Koehls &#187; programming</title>
	<atom:link href="http://frankkoehl.com/tag/programming/feed/" rel="self" type="application/rss+xml" />
	<link>http://frankkoehl.com</link>
	<description>The more you know, the more you don&#039;t know</description>
	<lastBuildDate>Thu, 10 May 2012 18:34:34 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
<xhtml:meta xmlns:xhtml="http://www.w3.org/1999/xhtml" name="robots" content="noindex" />
		<item>
		<title>jQuery 1.4 released</title>
		<link>http://frankkoehl.com/2010/01/jquery-14-released/</link>
		<comments>http://frankkoehl.com/2010/01/jquery-14-released/#comments</comments>
		<pubDate>Wed, 20 Jan 2010 16:33:14 +0000</pubDate>
		<dc:creator>Frank</dc:creator>
				<category><![CDATA[For techies]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[handy functions]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[open source]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[software development]]></category>
		<category><![CDATA[standards]]></category>
		<category><![CDATA[usability]]></category>

		<guid isPermaLink="false">http://frankkoehl.com/?p=1118</guid>
		<description><![CDATA[The latest and greatest version of jQuery, version 1.4, was released on January 14, the birthday of jQuery&#8217;s original launch. Bugfixes and improvements abound! The jQuery team has put together a site devoted to the new version, called the 14 days of jQuery, covering the major version changes as well as infrastructure updates coinciding with [...]]]></description>
			<content:encoded><![CDATA[<p>The latest and greatest version of jQuery, version 1.4, was released on January 14, the birthday of jQuery&#8217;s original launch. <a href="http://jquery14.com/day-01/jquery-14">Bugfixes and improvements abound!</a></p>
<p>The jQuery team has put together a site devoted to the new version, called the <a href="http://jquery14.com">14 days of jQuery</a>, covering the major version changes as well as infrastructure updates coinciding with the new release. For example, the documentation site has been completely redesigned, and been moved to it&#8217;s own subdomain home, <a href="http://api.jquery.com">api.jquery.com</a>. Links from the primary jquery.com site should be updated within the next week. With video demos of new features, Q&#038;A&#8217;s with the core team (including founder John Resig), it&#8217;s well-worth checking out for every jQuery developer.</p>
]]></content:encoded>
			<wfw:commentRss>http://frankkoehl.com/2010/01/jquery-14-released/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Get domain out of any URL string (yes, really)</title>
		<link>http://frankkoehl.com/2009/10/get-domain-out-of-any-url-string/</link>
		<comments>http://frankkoehl.com/2009/10/get-domain-out-of-any-url-string/#comments</comments>
		<pubDate>Tue, 27 Oct 2009 19:21:44 +0000</pubDate>
		<dc:creator>Frank</dc:creator>
				<category><![CDATA[For techies]]></category>
		<category><![CDATA[coding theory]]></category>
		<category><![CDATA[crappy coding]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[regular expressions]]></category>
		<category><![CDATA[software development]]></category>

		<guid isPermaLink="false">http://frankkoehl.com/?p=951</guid>
		<description><![CDATA[It&#8217;s a common problem with no single right answer: extract the top domain (e.g. example.com) from a given string, which may or may not be a valid URL. I had need of such functionality recently and found answers around the web lacking. So if you ever &#8220;just wanted the domain name&#8221; out of a string, [...]]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s a common problem with no single right answer: extract the top domain (e.g. example.com) from a given string, which may or may not be a valid URL. I had need of such functionality recently and found answers around the web lacking. So if you ever &#8220;just wanted the domain name&#8221; out of a string, give this a shot&#8230;</p>
<pre lang="php">
<?php
function get_top_domain($url, $remove_subdomains = 'all') {
  $host = strtolower(parse_url($url, PHP_URL_HOST));
  if ($host == '') $host = $url;
  switch ($remove_subdomains) {
    case 'www':
      if (strpos($host, 'www.') === 0) {
        $host = substr($host, 4);
      }
      return $host;
    case 'all':
    default:
      if (substr_count($host, '.') > 1) {
        preg_match("/^.+\.([a-z0-9\.\-]+\.[a-z]{2,4})$/", $host, $host);
        if (isset($host[1])) {
          return $host[1];
        } else {
          // not a valid domain
          return false;
        }
      } else {
        return $host;
      }
    break;
  }
}

// some examples
var_dump(get_top_domain('http://www.validurl.example.com/directory', 'all'));
var_dump(get_top_domain('http://www.validurl.example.com/directory', 'www'));
var_dump(get_top_domain('domain-string.example.com', 'all'));
var_dump(get_top_domain('domain-string.example.com/nowfails', 'all'));
var_dump(get_top_domain('finds the domain url.example.com', 'all'));
var_dump(get_top_domain('12.34.56.78', 'all'));
?>
</pre>
<p>Most of the examples are simply proofs, but I want to draw attention to the string in example #4, <code>'domain-string.example.com/nowfails'</code>. This is not a valid URL, so the call to <code>parse_url()</code> fails, forcing the script to use the entire original string. In turn, the path part of the string causes the regex to break, causing a complete failout (<code>return false;</code>).</p>
<p>Is there a way to account for this? Surely, however I&#8217;m not about to tap that massive keg of exceptions (i.e. just a slash, slash plus path, slash plus another domain in a human-readable string, etc).</p>
<p>No regex for validating URL&#8217;s or email addresses is ever perfect; the &#8220;strict&#8221; RFC requirements are too damn broad. So I did what I always do: chose &#8220;what works&#8221; over &#8220;what&#8217;s technically right.&#8221; This one requires any 2-4 characters for a the top level domain (TLD), so it doesn&#8217;t allow for the .museum TLD, and doesn&#8217;t check to see if the provided TLD is actually valid. If you need to do further verification, that&#8217;s on you. Here&#8217;s the <a href="http://data.iana.org/TLD/tlds-alpha-by-domain.txt">current full list of valid TLD&#8217;s provided by the IANA</a>.</p>
<p>If you need to modify the regex at all, I highly recommend you read this <a href="http://www.regular-expressions.info/email.html">article about email address regex</a> first for two reasons: </p>
<ol>
<li>There&#8217;s a ton of overlap between email and URL regex matching</li>
<li>It will point out all the gotcha&#8217;s in your &#8220;better&#8221; regex theory that you didn&#8217;t think about</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://frankkoehl.com/2009/10/get-domain-out-of-any-url-string/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Build a slick Twitter feed on your site</title>
		<link>http://frankkoehl.com/2009/07/build-a-slick-twitter-feed-on-your-site/</link>
		<comments>http://frankkoehl.com/2009/07/build-a-slick-twitter-feed-on-your-site/#comments</comments>
		<pubDate>Tue, 14 Jul 2009 20:35:38 +0000</pubDate>
		<dc:creator>Frank</dc:creator>
				<category><![CDATA[For techies]]></category>
		<category><![CDATA[lifestream]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[regular expressions]]></category>
		<category><![CDATA[social networking]]></category>
		<category><![CDATA[Twitter]]></category>

		<guid isPermaLink="false">http://frankkoehl.com/?p=660</guid>
		<description><![CDATA[A few months ago I published an article describing how to output a Twitter stream on a page using PHP, and later followed up with two more to polish the display. The article content and code examples have since been tweaked based on feedback and my own debugging. If you haven&#8217;t already had a look, [...]]]></description>
			<content:encoded><![CDATA[<p>A few months ago I published an article describing how to output a Twitter stream on a page using PHP, and later followed up with two more to polish the display. The article content and code examples have since been tweaked based on feedback and my own debugging.</p>
<p>If you haven&#8217;t already had a look, or missed a portion, here&#8217;s the full series:</p>
<ol>
<li><a href="/2008/11/display-twitter-updates-on-your-website">Display Twitter updates on your website</a></li>
<li><a href="/2008/11/easily-calculate-dates-and-times-in-different-timezones">Calculate dates and times in different timezones</a> (translate Twitter timestamps)</li>
<li><a href="/2008/12/parse-urls-in-text-create-links">Parse URL’s in text, create links</a> (automatically link URL&#8217;s in stream)</li>
<li><a href="http://frankkoehl.com/2009/08/archive-entire-twitter-timeline/">Download and store your Twitter posts in a database</a>
</ol>
<p>If you have any comments or questions, be sure to post them under the proper article.</p>
]]></content:encoded>
			<wfw:commentRss>http://frankkoehl.com/2009/07/build-a-slick-twitter-feed-on-your-site/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Find the second (or third, or fourth) occurence in a string</title>
		<link>http://frankkoehl.com/2009/03/second-third-fourth-occurence-string/</link>
		<comments>http://frankkoehl.com/2009/03/second-third-fourth-occurence-string/#comments</comments>
		<pubDate>Mon, 30 Mar 2009 13:53:22 +0000</pubDate>
		<dc:creator>Frank</dc:creator>
				<category><![CDATA[For techies]]></category>
		<category><![CDATA[handy functions]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[programming]]></category>

		<guid isPermaLink="false">http://frankkoehl.com/?p=436</guid>
		<description><![CDATA[PHP includes some handy functions to find the first or last occurrence of a given string token in a string: strpos and strrpos. However these functions are limited to just the first occurrence; what if I want to know the location of the second token&#8217;s position, or the third? These problems usually result in some [...]]]></description>
			<content:encoded><![CDATA[<p>PHP includes some handy functions to find the first or last occurrence of a given string token in a string: <code><a href="http://www.php.net/strpos">strpos</a></code> and <code><a href="http://www.php.net/strrpos">strrpos</a></code>. However these functions are limited to just the first occurrence; what if I want to know the location of the second token&#8217;s position, or the third? These problems usually result in some serious coding acrobatics.</p>
<p>Well no need for code-jitsu anymore. Based almost completely on a post I found at another blog &mdash; which is now down, how&#8217;s that for timing? &mdash; here are two functions which allow you to search for any occurrence of a specific token in a string&#8230; </p>
<pre lang="php">
/**
 * Find position of Nth $occurrence of $needle in $haystack
 * Starts from the beginning of the string
**/
function strpos_offset($needle, $haystack, $occurrence) {
  // explode the haystack
  $arr = explode($needle, $haystack);
  // check the needle is not out of bounds
  switch( $occurrence ) {
    case $occurrence == 0:
      return false;
    case $occurrence > max(array_keys($arr)):
      return false;
    default:
      return strlen(implode($needle, array_slice($arr, 0, $occurrence)));
  }
}

/**
 * Find position of Nth $occurrence of $needle in $haystack
 * Starts from the end of the string
**/
function strrpos_offset($needle, $haystack, $occurrence) {
  // explode the haystack
  $arr = array_reverse(explode($needle, $haystack));
  // check the needle is not out of bounds
  switch( $occurrence ) {
    case $occurrence == 0:
      return false;
    case $occurrence > max(array_keys($arr)):
      return false;
    default:
      $inverted = strlen(implode($needle, array_slice($arr, 0, $occurrence)));
      $actual = (strlen($haystack) - 1) - $inverted;
      return $actual;
  }
}

// look for second occurrence of letter 'a' from the start of string
echo strpos_offset('a', 'abracadabra', 2);
// returns 3

// look for second occurrence of letter 'a' from the end of string
echo strrpos_offset('a', 'abracadabra', 2);
// returns 7
</pre>
<p>In terms of use, we&#8217;ve essentially added an extra argument to <code>strpos</code> and <code>strrpos</code> that specifies which occurrence you&#8217;re looking for. In other words, you can make both of these functions work like the PHP standards by setting the third <code>$occurrence</code> variable to 1.</p>
]]></content:encoded>
			<wfw:commentRss>http://frankkoehl.com/2009/03/second-third-fourth-occurence-string/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Extract email addresses from tags</title>
		<link>http://frankkoehl.com/2009/01/extract-email-addresses-from-tags/</link>
		<comments>http://frankkoehl.com/2009/01/extract-email-addresses-from-tags/#comments</comments>
		<pubDate>Thu, 15 Jan 2009 22:26:15 +0000</pubDate>
		<dc:creator>Frank</dc:creator>
				<category><![CDATA[For techies]]></category>
		<category><![CDATA[email]]></category>
		<category><![CDATA[fwdvault]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[regular expressions]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[usability]]></category>

		<guid isPermaLink="false">http://frankkoehl.com/?p=309</guid>
		<description><![CDATA[Ran into another cool hurdle today for my Fwd:Vault development. When I grab the message content to archive it in the system, first thing I do is scrub it out to ensure that (a) it displays properly, and (b) there are no misbehaving characters. I grab both plain text and HTML email formats (if present), [...]]]></description>
			<content:encoded><![CDATA[<p>Ran into another cool hurdle today for my <a href="http://fwdvault.com">Fwd:Vault</a> development. When I grab the message content to archive it in the system, first thing I do is scrub it out to ensure that (a) it displays properly, and (b) there are no misbehaving characters. I grab both plain text and HTML email formats (if present), so the scrubbing process is a little different in each case. For the plain text, I take some extra steps to ensure there is no HTML whatsoever. Naturally, at one point this involves a call to PHP&#8217;s ultra-useful <code><a href="http://www.php.net/strip_tags">strip_tags()</a></code> function.</p>
<p>However, in the course of testing today, I realized that when a message is forwarded, sometimes the forward header will encode the email address, which gets stripped when I process the message. Allow me to demonstrate. Here&#8217;s the body an example message that someone might send to Fwd:Vault for safe keeping&#8230;</p>
<pre>---------- Forwarded message ----------
From: "Office Flirt" &lt;flirt@example.com&gt;
Date: Wed, Jan 14, 2009 at 10:14 AM
Subject: Delete those images
To: you@example.com

My boss is sniffing around. I want you to delete those pictures I sent you right away.

Signed,
Office Flirt</pre>
<p>Obviously you&#8217;re tucking this one away in Fwd:Vault to provide a little CYA-insurance when the boss calls you into his office. Good call. Now, before today, this message would come out of the scrubbing process looking like this:</p>
<pre>---------- Forwarded message ----------
<span style="color: #ff0000;"><strong>From: Office Flirt</strong></span>
Date: Wed, Jan 14, 2009 at 10:14 AM
Subject:
To: you@example.com
...</pre>
<p>Look at the bolded red line. The email address is gone. You don&#8217;t have any other copies of it, so your boss doesn&#8217;t believe your story, and you get the blame. You&#8217;re forced to attend one of those god-awful sexual harassment classes. Fail.</p>
<p>So, what happened? Remember, you are looking at the <strong>body</strong> of a message in <strong>plain text</strong>. That &#8220;Forwarded message&#8221; block at the beginning is just part of the body text. So when the text was scrubbed by <code>strip_tags()</code>, the function picked it up as just another tag, which it dutifully removed.</p>
<p>To handle this situation, I came up with a piece of code that will look for email addresses in &#8220;tagged format&#8221; — i.e. surrounded by &lt; and &gt; — and remove the surrounding symbols, leaving us with harmless text.</p>
<pre lang="php">$test = 'some surrounding text';
$test = preg_replace( "/(\<)(.+@[^\(\);:,<>]+\.[a-zA-Z]{2,4})(\>)/",
                      ' $2 ',
                      $test);
$test = preg_replace('/[\s]+/', '', $test);
echo $test;</pre>
<p>Let&#8217;s break this down. First, we have a regular expression that identifies email addresses: <code>.+@[^\(\);:,&lt;&gt;]+\.[a-zA-Z]{2,4}</code>. This is the same expression set in the example on the <a href="http://www.quanetic.com/Regex">Quanetic Software Regular Expression Tester</a> (an excellent tool). We surround that in parentheses to isolate it as a <strong>subpattern</strong>. Then on either end of the expression, we tack on more regex voodoo to look for tag syntax: <code>(\&lt;)</code> and <code>(\&gt;)</code>. These also get parentheses to identify them as subpatterns. Once its finished, we have an expression that will only match addresses wrapped in tagging structure.</p>
<p>The second argument in <code>preg_replace()</code> is the replacement, or what we should replace any matches with. In this case, we&#8217;ve isolated the address from the tags using subpatterns. So all we need to do is make a single call to the proper reference, which is <code>$2</code>, because its the second set of parentheses in the expression. Confused? You can learn about subpatterns on the PHP manual page for <a href="http://www.php.net/manual/en/function.preg-replace.php"><code>preg_replace()</code></a>.</p>
<p>Note the spaces around the <code>$2</code> in the second argument. Sometimes the address will not have any spaces between the person&#8217;s name and the actual address. This could lead to the address being combined with the name which, in the case of Fwd:Vault, would screw up our search indexing. So we add spaces during the replace, then make a second call to <code>preg_replace()</code> to eliminate extra spaces: <code>$test = preg_replace('/[\s]+/', '', $test);</code>.</p>
<p><strong>Legal Disclaimer:</strong> In case you do end up using Fwd:Vault when it launches, I&#8217;m fairly certain the service wouldn&#8217;t be liable in this silly hypothetical. Just make sure you read the terms before you sign up if you play the field at your office. Sorry to everyone going &#8220;duh&#8221; right now; it&#8217;s a sue-happy world.</p>
<p><strong>Update:</strong> When I went to implement this change today, I discovered that the code was catching newlines (\n or \r) in the crossfire. It was actually due to the second call to <code>preg_replace()</code>, the &#8220;\s&#8221; character class includes not only spaces but line terminators as well. Oops. The revised version looks like this:</p>
<pre lang="php">
$body_text = preg_replace('/[ ]{2,}/', ' ', $body_text);
</pre>
]]></content:encoded>
			<wfw:commentRss>http://frankkoehl.com/2009/01/extract-email-addresses-from-tags/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Versatile random string generator</title>
		<link>http://frankkoehl.com/2008/12/versatile-random-string-generator/</link>
		<comments>http://frankkoehl.com/2008/12/versatile-random-string-generator/#comments</comments>
		<pubDate>Thu, 18 Dec 2008 22:34:30 +0000</pubDate>
		<dc:creator>Frank</dc:creator>
				<category><![CDATA[For techies]]></category>
		<category><![CDATA[extension]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[security]]></category>

		<guid isPermaLink="false">http://frankkoehl.com/?p=221</guid>
		<description><![CDATA[A cursory glance around the web will reveal a ton of PHP-based random string generators. With enough looking you&#8217;ll find generators that do any of the following: Strings with letters Strings with numbers Strings with letters and numbers Uppercase, lowercase Fixed, variable length strings Option to include symbols Problem is, none of them ever incorporated [...]]]></description>
			<content:encoded><![CDATA[<p>A cursory glance around the web will reveal a ton of PHP-based random string generators. With enough looking you&#8217;ll find generators that do any of the following:</p>
<ul>
<li>Strings with letters</li>
<li>Strings with numbers</li>
<li>Strings with letters <strong>and</strong> numbers</li>
<li>Uppercase, lowercase</li>
<li>Fixed, variable length strings</li>
<li>Option to include symbols</li>
</ul>
<p>Problem is, none of them ever incorporated all this functionality.  Every generator was a hodgepodge, e.g. some forced inclusion of numbers, or allowed either upper or lowercase, not both.  All of these are great options, and it would be great to have all of them at your disposal in one tight function.</p>
<p>No more! I got so sick of finding shortcomings that I finally just put it all together myself. The following function allows you to choose a string length, as well as the character sets to use when building your random string. You can even include a set more than once, giving greater usage weight to certain characters. Finally, complete flexibility!</p>
<pre lang="php">
function koehl_generator($length = 10, $charsets = 'lower') {
  $upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  $lower = 'abcdefghijklmnopqrstuvwxyz';
  $numbers = '1234567890';
  $symbols = '!@#$%^&#038;*()-_=+<>,.?/:;[]{}|~';

  if (!is_array($charsets)) $charsets = array($charsets);
  $charset_pool = array();
  foreach ($charsets as $set) {
    $charset_pool[] = $$set;
  }
  $max = (sizeof($charset_pool) - 1);
  $v = '';
  for ($i = 0; $i < $length; $i++) {
    $this_pool = $charset_pool[rand(0, $max)];
    $v .= $this_pool[(rand() % strlen($this_pool))];
  }
  return $v;
}

// usage examples
echo koehl_generator(10, 'upper') . '';
echo koehl_generator(10, 'lower') . '';
echo koehl_generator(10, 'numbers') . '';
echo koehl_generator(10, 'symbols') . '';
echo koehl_generator(10, array('upper', 'lower')) . '';
// order of the array in the second argument does not matter
echo koehl_generator(15, array('lower', 'upper', 'numbers')) . '';
echo koehl_generator(15, array('lower', 'numbers', 'upper')) . '';
echo koehl_generator(15, array('upper', 'lower', 'numbers', 'symbols')) . '';
echo koehl_generator(15, array('lower', 'symbols')) . '';
// note how often letters appear in the next one
echo koehl_generator(20, array('lower', 'lower', 'numbers')) . '';
</pre>
<p>Adding your own custom set is easy too. Include a new set following the syntax of the existing ones, then call your new set by variable name in the second argument...</p>
<pre lang="php">
// add this to the top of the function...
$the_basics = 'abc123';

// then use it like so...
echo koehl_generator(5, 'the_basics') . '';
echo koehl_generator(10, array('the_basics', 'symbols')) . '';
</pre>
<p>If you find this useful, please be sure to give me some link love, just a reference URL to this page in your code would be fine. Using the share buttons below would be great as well!</p>
]]></content:encoded>
			<wfw:commentRss>http://frankkoehl.com/2008/12/versatile-random-string-generator/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Easily calculate dates and times in different timezones</title>
		<link>http://frankkoehl.com/2008/11/easily-calculate-dates-and-times-in-different-timezones/</link>
		<comments>http://frankkoehl.com/2008/11/easily-calculate-dates-and-times-in-different-timezones/#comments</comments>
		<pubDate>Tue, 25 Nov 2008 22:18:47 +0000</pubDate>
		<dc:creator>Frank</dc:creator>
				<category><![CDATA[For techies]]></category>
		<category><![CDATA[extension]]></category>
		<category><![CDATA[fwdvault]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[standards]]></category>

		<guid isPermaLink="false">http://frankkoehl.com/?p=157</guid>
		<description><![CDATA[Building off my last post, where I showed you how to easily display any public Twitter feed on your site, I ran into another problem: the dates that are delivered by the Twitter API all reflect Greenwich Mean Time (GMT). Now I thought about going the old route and doing some convoluted math using date(), [...]]]></description>
			<content:encoded><![CDATA[<p>Building off my last post, where I showed you how to <a href="http://frankkoehl.com/2008/11/display-twitter-updates-on-your-website/">easily display any public Twitter feed on your site</a>, I ran into another problem: the dates that are delivered by the Twitter API all reflect Greenwich Mean Time (GMT). Now I thought about going the old route and doing some convoluted math using <code><a href="http://us3.php.net/manual/en/function.date.php">date()</a></code>, <code><a href="http://us3.php.net/manual/en/function.strtotime.php">strtotime()</a></code>, and <code><a href="http://us3.php.net/manual/en/function.mktime.php">mktime()</a></code>, but I thought it was time find a serious solution, one that would allow me to display the correct time for whatever timezone I wished.</p>
<p>So while searching the web, I came across an insightful post that discussed PHP&#8217;s built-in <a href="http://us3.php.net/manual/en/function.date-create.php">DateTime object</a>. I try hard to keep up with all the tech in my job, but this one got through the cracks, and I was instantly intrigued. So after some research and fiddling, I came up with the following block of code, which will allow you to <strong>easily translate dates/times to and from any timezone</strong>.</p>
<pre lang="php">date_default_timezone_set('GMT');
$datetime = new DateTime('2008-11-25 16:48:13');
$tz = new DateTimeZone('America/New_York');
$datetime->setTimezone($tz);
$time_display = $datetime->format('D, M jS g:ia T');</pre>
<p>The process is a little convoluted, so here&#8217;s what&#8217;s going on. First we must set the system-wide timezone to match that of the date/time we would like to translate. In this example, it&#8217;s about 4:50pm on Nov 25, 2008 in the GMT timezone. Next we create a DateTime object with that time (FYI you can use any format accepted by <a href="http://us3.php.net/manual/en/function.strtotime.php">strtotime()</a><code>).</code></p>
<p>Next we create a <code><a href="http://us3.php.net/manual/en/function.timezone-open.php">DateTimeZone</a></code> object. Keep in mind that this step stands completely on its own, and can be performed anytime up to now.</p>
<p>Finally, we pass the <code>DateTimeZone</code> object as the lone argument in a call to the <code><a href="http://us3.php.net/manual/en/function.date-timezone-set.php">setTimezone()</a></code> method. This will change the timezone stored in $datetime from GMT to EST, and automatically update the date/time accordingly, which we output with a call to the <code><a href="http://us3.php.net/manual/en/function.date-format.php">format</a></code> method.</p>
<p>Note that I used object-oriented syntax for this process, but there is a procedural style (i.e. functions) as well. I prefer OOP here because it&#8217;s just easier to read and follow.</p>
<p>Valid arguments for <code>date_default_timezone_set()</code> and <code>DateTimeZone()</code> come from PHP&#8217;s <a href="http://us3.php.net/manual/en/timezones.php">list of supported timezones</a>.</p>
<p>So now that we can translate timezones, let&#8217;s apply it to <a href="http://frankkoehl.com/2008/11/display-twitter-updates-on-your-website/">my Twitter example&#8230;</a></p>
<pre lang="php">
<ul>
<?php
  if ( $twitter_xml = twitter_status('12345678') ) {
    date_default_timezone_set('GMT');
    $tz = new DateTimeZone('America/New_York');
    $i = 0;
    foreach ($twitter_xml->status as $key => $status) {
      $datetime = new DateTime($status->created_at);
      $datetime->setTimezone($tz);
      $time_display = $datetime->format('D, M jS g:ia T');
?>
<li><?php echo $status->text . '' . $time_display; ?></li>

<?php
      ++$i;
      if ($i == 5) break;
    }
?>
<li><a href="http://twitter.com/YOUR_PROFILE_HERE">more...</a></li>

<?php
  } else {
    echo 'Sorry, Twitter seems to be unavailable at the moment...again...';
  }
?>
</ul>
</pre>
<p>Sidenote: While I was working on this I found GMT and UTC used almost interchangeably. For all intents and purposes, they are identical. But there is a technical difference: GMT is based on the rotation of the earth (less precise), while UTC is based on an atomic clock (more precise). Don&#8217;t worry, you and I are not the only ones who got confused.</p>
<p><strong>Build a slick Twitter feed on your site</strong></p>
<ol>
<li><a href="/2008/11/display-twitter-updates-on-your-website/">Display Twitter updates on your website</a></li>
<li class="green bold">Calculate dates and times in different timezones (translate Twitter timestamps)</li>
<li><a href="/2008/12/parse-urls-in-text-create-links">Parse URL’s in text, create links</a></li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://frankkoehl.com/2008/11/easily-calculate-dates-and-times-in-different-timezones/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Format a RFC2822 date for mysql datetime fields</title>
		<link>http://frankkoehl.com/2008/09/format-a-rfc2822-date-for-mysql-datetime-fields/</link>
		<comments>http://frankkoehl.com/2008/09/format-a-rfc2822-date-for-mysql-datetime-fields/#comments</comments>
		<pubDate>Tue, 30 Sep 2008 14:50:42 +0000</pubDate>
		<dc:creator>Frank</dc:creator>
				<category><![CDATA[For techies]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[standards]]></category>

		<guid isPermaLink="false">http://frankkoehl.com/?p=96</guid>
		<description><![CDATA[I was crash-coursing myself on PHP&#8217;s IMAP functionality recently, one of the first questions I came across was how I might store the date from an e-mail header in a MySQL DATETIME field. I was afraid I was going to have to parse out the string using a bunch of calls to substr(), but then [...]]]></description>
			<content:encoded><![CDATA[<p>I was crash-coursing myself on <a href="http://www.php.net/imap">PHP&#8217;s IMAP functionality</a> recently, one of the first questions I came across was how I might store the date from an e-mail header in a MySQL DATETIME field. I was afraid I was going to have to parse out the string using a bunch of calls to substr(), but then I remembered that the strtotime() function recognizes various RFC formats.</p>
<p>So, if you need to get the timestamp of an RFC2822 e-mail header&mdash;or most any RFC-based timestamp&mdash;into MySQL DATETIME format, it&#8217;s easy:</p>
<pre class="brush: php; title: ; notranslate">$timestamp = strtotime('Tue, 30 Sep 2008 10:30:00 EDT');
echo date('Y-m-d g:i:s a', $timestamp);</pre>
<p>Keep in mind that if there are differences between the timezones of your server and the timestamp, the date() function could screw with your desired output. You should probably take a look at <a href="http://www.php.net/manual/en/function.date-default-timezone-set.php">date_default_timezone_set()</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://frankkoehl.com/2008/09/format-a-rfc2822-date-for-mysql-datetime-fields/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Automating SSH or SFTP in scripts</title>
		<link>http://frankkoehl.com/2008/09/automating-ssh-or-sftp-in-scripts/</link>
		<comments>http://frankkoehl.com/2008/09/automating-ssh-or-sftp-in-scripts/#comments</comments>
		<pubDate>Mon, 29 Sep 2008 16:00:58 +0000</pubDate>
		<dc:creator>Frank</dc:creator>
				<category><![CDATA[For techies]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[security]]></category>

		<guid isPermaLink="false">http://frankkoehl.com/?p=98</guid>
		<description><![CDATA[Recently I needed to automate copying a MySQL database to a backup server. We keep a copy of our site and DB on this box in the event that our main systems go down, or there&#8217;s a problem with our internet connection. It&#8217;s kind of like a poor man&#8217;s colocation setup. I actually prefer the [...]]]></description>
			<content:encoded><![CDATA[<p>Recently I needed to automate copying a MySQL database to a backup server. We keep a copy of our site and DB on this box in the event that our main systems go down, or there&#8217;s a problem with our internet connection. It&#8217;s kind of like a poor man&#8217;s colocation setup. I actually <strong>prefer</strong> the setup over true colocation for the vast majority of small and medium-sized business, because it&#8217;s far simpler and requires far less overhead and continuous support.</p>
<p>When I started searching around for resources on how to automate the SFTP connection, I was hit in the face with tons of dead ends.  <a href="http://www.google.com/search?q=batch+script+sftp+backup">Several</a> <a href="http://www.google.com/search?q=sftp+unix+command+line">Google</a> <a href="http://www.google.com/search?q=sftp+non-interactive+authentication">searches</a> were spitting back mailing list and forum archives of plenty of questions regarding how to create backup scripts that connect to a remote server via SFTP. If you are in this boat, read on.</p>
<p>Here&#8217;s the problem. At some point in the development of SFTP, the writers decided that storing access credentials in files as part of an automated process was a very bad idea. So they coded SFTP to bypass the password challenge when invoked from a script (aka the -b flag, which runs commands from a file).</p>
<p>Instead, they recommend that you create a private key pair between the two systems. This preemptive measure handshake eliminates the need for passwords entirely, making your code a bit simpler. It&#8217;s fairly easy to do but, of course, most developers groan at the thought of having to learn yet another technique, and looks for ways around the restriction. I did both, and recommend the key pair approach. I&#8217;ll describe both here, and let you decide for yourself.</p>
<p><strong>SSH/SFTP connection without passwords</strong></p>
<p>The following example is borrowed from <a href="http://linuxproblem.org/art_9.html">an article</a> on <a href="http://linuxproblem.org">The Linux Problem Base</a>, but there are several out there explaining the same approach.</p>
<p>First log in on A as user a and generate a pair of authentication keys. Do not enter a passphrase:</p>
<pre>a@A:~&gt; ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/a/.ssh/id_rsa):
Created directory '/home/a/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/a/.ssh/id_rsa.
Your public key has been saved in /home/a/.ssh/id_rsa.pub.
The key fingerprint is:
3e:4f:05:79:3a:9f:96:7c:3b:ad:e9:58:37:bc:37:e4 a@A</pre>
<p>Now use ssh to create a directory ~/.ssh as user b on system B. The directory may already exist, which is fine:</p>
<pre>a@A:~&gt; ssh b@B mkdir -p .ssh
b@B's password:</pre>
<p>Finally append a&#8217;s new public key to b@B:.ssh/authorized_keys and enter b&#8217;s password one last time. Note that you must be looking at the local directory in which you saved the key in step one.</p>
<pre>cd /home/a/
a@A:~&gt; cat .ssh/id_rsa.pub | ssh b@B 'cat &gt;&gt; .ssh/authorized_keys'
b@B's password:</pre>
<p>From now on you can log into B as b from A as a without password. Try this to confirm:</p>
<pre>a@A:~&gt; ssh b@B hostname</pre>
<p>It should return the hostname of system B without prompting for a password.</p>
<p>It&#8217;s painless and easy, and every SSH connection you make going forward requires less typing. However, if you still really want to use a password, you have two options.</p>
<p><strong>Utilize the -o Flag</strong></p>
<p>So the SFTP team made their stance clear, and backed it up with action. However, it&#8217;s not impossible to bypass the restriction.  The -o flag allows you to access all the options available in the sshd_config file, so you can change any of them on the fly. Here we need to disable the batchmode directive, so your SFTP call would look something like this:</p>
<pre>sftp -o "batchmode no" -b /tmp/bat user@host</pre>
<p>I found this one on a random forum post, and it comes with an important warning:</p>
<blockquote><p>Note that it must come *before* -b, which may be surprising &#8211; this is<br />
due to ssh processing -o options as if they were read from the config<br />
file &#8211; ssh_config(5) again:</p></blockquote>
<p>The only problem here is that the password challenge gets sent back out to the command line, requiring normal keyboard interaction.</p>
<p><strong>Use SSHPass</strong></p>
<p>So if that&#8217;s still not good enough for you, check out a SourceForge project called <a href="http://sourceforge.net/projects/sshpass/">SSHPass</a>. From the link:</p>
<blockquote><p>Sshpass is a tool for non-interactivly performing password authentication with SSH&#8217;s so called &#8220;interactive keyboard password authentication&#8221;. Most user should use SSH&#8217;s more secure public key authentiaction instead.</p></blockquote>
<p>SSHPass is available from default Debian apt servers; I couldn&#8217;t find anything reliable on its availability through yum.</p>
<p>Proceed at your own risk. If you server allows any sort of public access, even to a large handful of outside users, I strongly recommend going the key route.</p>
<p><strong>Update Feb 27, 2009:</strong> Reader pointed out that OpenSSH has a shortcut function, <a href="http://linux.die.net/man/1/ssh-copy-id"><code>ssh-copy-id</code></a>, to install your public key on a remote machine. Nice.</p>
]]></content:encoded>
			<wfw:commentRss>http://frankkoehl.com/2008/09/automating-ssh-or-sftp-in-scripts/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Using SQL computations in WHERE clause</title>
		<link>http://frankkoehl.com/2008/08/using-sql-computations-in-where-clause/</link>
		<comments>http://frankkoehl.com/2008/08/using-sql-computations-in-where-clause/#comments</comments>
		<pubDate>Fri, 08 Aug 2008 16:03:58 +0000</pubDate>
		<dc:creator>Frank</dc:creator>
				<category><![CDATA[For techies]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[programming]]></category>

		<guid isPermaLink="false">http://frankkoehl.com/?p=25</guid>
		<description><![CDATA[When writing SQL statements, sometimes I want to filter the result set based on a value that must be computed out of the stored data. Here&#8217;s an example: I want to see which widgets have been sold more than once, and how many times each of those remaining widgets have sold. Obviously we could pass [...]]]></description>
			<content:encoded><![CDATA[<p>When writing SQL statements, sometimes I want to filter the result set based on a value that must be computed out of the stored data.  Here&#8217;s an example:</p>
<pre class="brush: sql; title: ; notranslate">SELECT widgetID, count( widgetID ) AS totalWidgets
FROM table_widget_sales
WHERE totalWidgets &gt; 1
GROUP BY widgetID
ORDER BY totalWidgets DESC</pre>
<p>I want to see which widgets have been sold more than once, and how many times each of those remaining widgets have sold.  Obviously we could pass a simpler query into a scripting language to calculate it for us, but that&#8217;s more cycles and memory.  I love to squeeze as much out of my SQL statements as possible.</p>
<p>Problem is, when I run that query above, I get this error:</p>
<p><code>#1054 - Unknown column 'totalWidgets' in 'where clause' </code></p>
<p>My first reaction is, &#8220;I can use that column in the order by clause, why not where clauses!?&#8221; Simple, the value doesn&#8217;t exist when the WHERE clause is evaluated. The number is calculated as each matching record is passed.  By the time we reach the ORDER BY clause, we have the data we need.  But the WHERE clause is used to actually figure out what records we need to perform that calculation, so we&#8217;ve got a chicken-egg issue.</p>
<p>The solution is to use a <a href="http://dev.mysql.com/doc/refman/5.0/en/subqueries.html">subquery</a>, sometimes called a &#8220;sub-select statement.&#8221;  Using two select statements, we can split the duties for filtering (<em>more than 1 widget sale</em>) and counting (<em>total number of sales per widget</em>).</p>
<p>Here&#8217;s the working query:</p>
<pre class="brush: sql; title: ; notranslate">SELECT w.widgetID, COUNT( ws.widgetID ) AS totalWidgets
FROM table_widgets AS w
INNER JOIN table_widget_sales AS ws ON ( w.widgetID = ws.widgetID )
WHERE (
SELECT COUNT( ws.widgetID ) AS number
FROM table_widget_sales AS ws
WHERE ws.widgetID = w.widgetID
) &gt; 1
GROUP BY widgetID
ORDER BY totalWidgets DESC</pre>
<p>See what&#8217;s going on?  We&#8217;re doing the same count twice, but the subquery returns everything (number of sales per widget for all widgets), while the first select only grabs the results we want (sales greater than 1).</p>
]]></content:encoded>
			<wfw:commentRss>http://frankkoehl.com/2008/08/using-sql-computations-in-where-clause/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

