Badge 2

Localizing your web app for per-client date/time display

July 27th, 2010

Many frameworks do this for you; but, if you’re writing a small application or updating an older one, you might need to take a more manual approach to dealing with localization and date/time values. I’ll go over the common settings, how to store them, and lists of possible values.

Date/Month/Year

Everyone in the world can read a date as either 12/31/2010 or 31/12/2010 (alternate slashes and two digit years can be substituted). You don’t need to allow customizing the display beyond these two formats — it’s just cosmetic after that. If you hard coded column sizes on grids and forms, you can store this setting as boolean called “date_dmy” or similar. Or, you can take the WordPress/phpBB approach and store it as a string with php’s date() formatter strings.

Daylight Saving Time (DST)

Most of North America, Europe, southern South America, New Zealand and southern Australia observe daylight saving time (notice daylight is singular, not plural) which changes their time zone offset. First, you’ll need an “is_dst” boolean configuration parameter to store whether or not the client observes DST.

The dates each country observes DST varies. The first piece of information you’ll need when generating times is a true/false value reflecting whether or not the client is in DST as of a date (warning, this can change a date if it’s midnight and we’re adding hours!).

There are three ways to check if the client is in DST:

  1. Lazy Method: Most of the time in the northern hemisphere, it starts in March and ends in November. Rely on the server’s date(‘I’) invocation to determine whether the server local time is DST or not (this only works if your server time is DST).
  2. Accurate Method: Install the tz database (other formats). Determine their DST start and end dates by querying the tz database with their 2-letter country code and the date (so you need to capture the country code for each client).
  3. Proper/Framework Method: Collect and store the client’s locale as a PHP timezone string. Use date_default_timezone_set() to change the script to their local zone. Check date(‘I’) to check if they’re in DST time (or just display their time/date at this point after converting server time to GMT and let PHP handle conversion).

Time Zone

When storing a time zone, make sure to use a signed float. There are half-hour time zones! Generally, time zones are stored in relation to GMT. Possible time zones are: -12, -11, -10, -9.5, -9, -8, -7, -6, -5, -4, -3.5, -3, -2, -1, 0, 1, 2, 3, 3.5, 4, 4.5, 5, 5.5, 5.75, 6, 6.5, 7, 8, 8.75, 9, 9.5, 10, 10.5, 11, 11.5, 12, 12.75, 13, 14.

For reasons like database timestamp auto-creation and administrative reporting, all times in databases are usually stored in server local time. You should write a script that converts server time into GMT or use gmdate(). Filtering into the client time zone can then be done from GMT which is generally easier to read in source code.

24-Hour Time

Everyone in the world can read a time either as 23:45 or 11:45pm. You can easily store this configuration as a “is_24hr” boolean value. If you want to allow greater customization, you can allow clients to use PHP date() format strings (like WordPress/phpBB does).

Separate and Consolidate Date and Time Display Functions

Run all your date/time logic through three functions. This allows you to easily test for failure and reduces work.

I usually recommend writing three date/time display functions. One to display date, one to display time, and one that concatenates the two. Several applications have a single display which returns both, but there are many cases where it’s helpful to display one or the other and it really doesn’t hurt to separate the two.

Lazy Date Problem

Many people get lazy when displaying dates and simply return them in server time. You won’t notice problems if you’re primarily in the US with a maximum of a 3 hour difference between time zones; but, If you’re server is in EST and your client is in London, 8pm server time on the 11th of December is the 12th of December for the client. Remember to do the same time zone and DST logic when calculating dates as you do when calculating times.

Lazy Range Problem

A client enters a date range search in your application from January 4th, 2010 to January 7th, 2010 and you store the date as a timestamp or datetime value. First, make sure you follow the last paragraph (lazy date problem) and convert the dates the client enters from his time to server time (backwards conversion). Second, add times to the dates:

WHERE created_date BETWEEN "2010-01-04 00:00:00"
AND "2010-01-07 23:59:59"

It’s important to include the times or you’ll lose up to a day’s worth of results. Clients will expect this behavior.

PHP versus Database

As tempting as it is to store database records in client local time, it should be avoided. If a client updates their time zone, you have to update every record’s time and date accordingly. It’s also more confusing when querying the data directly from an administrator’s point of view, or when importing into other systems.

  • Facebook
  • Twitter
  • LinkedIn
  • Digg
  • Reddit

Posted in PHP, SQL, Tips | Comments Off

WordPress 3.0 “Thelonious”. Say goodnight, Drupal.

June 25th, 2010

I recently got a chance to do some professional development with the latest WordPress 3.0 “Thelonious” release and it blew me out of the water. For a while now, I’ve been doing some work on the side in web design/development (I love to code, but I’m a designer at heart) and I use WordPress almost exclusively. I have to say, I’m very excited about the new features, menus in general, and their apparent redirection towards content management and less of a sole focus on blogging.

The menus are absolutely awesome. I’ve written and used extensions in the past to accomplish the same goal, but nothing beats out-of-the-box support with a Apple-esk UI only WordPress can provide. Multi-level support, drag/drop from content areas and built-in javascript for drop-downs all equal a vast improvement to an already great system. Best yet, the API function and the modifications to the theme to enable menus took me about 5 minutes to figure out.

For me, working with Drupal for an extended amount of time was painful. If a client wanted a complete CMS, it was hard to argue that a blog was their best solution. Often times, I’d green light Drupal just to avoid my least favorite word: Joomla (aims gun at head). Don’t get me wrong, these are fine products; it’s just that sometimes if you need to write in space, a pencil works better than a quill connected with a straw to an ink tank connected to a vacuum connected to a car battery. Is it really necessary to have a settings page with more options than there are hairs on my body?

Oh, and as someone who’s written extensions for all three of the PHP-base CMS players, you can not beat WordPress. I had my CATS JobSite page up and running in an hour — svn and release management included. Did I mention that Joomla has like 5 names for ‘plugin’? As if figuring out whether to call something a plugin, extension, addon, toolbar, module, etc. wasn’t hard enough, Joomla just grabbed the top 5… and I’m done with the complaining.

Back to point: if you need a blog, use WordPress. If you need a website your mom could add content to, use WordPress. Need more proof? I dare you to find a sexier video from an open source project.

  • Facebook
  • Twitter
  • LinkedIn
  • Digg
  • Reddit

Posted in HTML, News | 1 Comment »

Fixed MySQL slow queries using indexes by removing string to date conversion warnings

May 23rd, 2010

This is just a quick one for an issue I ran into today when I was tuning some MySQL indexes for better performance. Some of our older PHP code was performing a SELECT using a BETWEEN to specify a date range. After adding an index to speed up this range query, I noticed that it wasn’t taking and was still performing a table scan/file sort.

The code was using PHP’s date function with the ‘c’ format string, which inserts a date/time string like so: “2010-05-23T16:45:08-05:00″. When inserting or comparing dates, MySQL will automatically convert this to it’s internal date/time format; but, it will throw a warning.

It turns out that the warning was causing MySQL to ignore the new index for the range query even through it was listed as the used index in the EXPLAIN. My guess is that this has something to do with MySQL having to convert each string to a date on a per-row basis, causing its optimizer to use the index inefficiently.

Simply changing the PHP date function’s format string from ‘c’ to ‘Y-m-d H:i:a’ (which is a native MySQL date/time format) did the trick. The explain remained the same, but the number of rows was drastically reduced from hundreds of thousands to a few hundred (the result of the range) which took a 20 second query down to about a tenth of a second.

I haven’t tested it, but my guess is this will occur with any type of conversion that results in a warning, such as a string with text in it being converted to a number, truncated values, etc.. MySQL probably doesn’t cache those conversions up front, which leads to expensive conversions on a per-row basis which ultimately tanks any benefit an index will give you — despite what the EXPLAIN tells you!

Something to keep in mind — watch those warnings!

  • Facebook
  • Twitter
  • LinkedIn
  • Digg
  • Reddit

Posted in Linux, News, Uncategorized | Comments Off