Search Logger
Posts from: Philip Tellis

Author Archive

Date Formatting with YUI – Part I

7:48 pm - February 11, 2009 in Yahoo! User Interface Blog

With the release of YUI 2.6.0, we’ve added a date formatting component as part of the DataSource utility. This date formatter brings the full power of strftime to Javascript. In a series of blog posts, we’ll go through examples of using the date formatter to best effect.

To start off, we first need to include the DataSource utility:


<script type=”text/javascript” src=”http://yui.yahooapis.com/2.6.0/build/yahoo/yahoo-min.js”></script>
<script type=”text/javascript” src=”http://yui.yahooapis.com/2.6.0/build/event/event-min.js”></script>
<script type=”text/javascript” src=”http://yui.yahooapis.com/2.6.0/build/datasource/datasource-min.js”></script>

Now let’s see it in action straight away:

<script type="text/javascript">
alert(YAHOO.util.Date.format(new Date(), {format: "%Y-%m-%d %T \n %B, %A"}));
</script>

Let’s try something more interactive. We’ll throw in some markup to read in a format and display the output:

<form>
<label>Enter a format: <input type="text" name="date-format" id="date-format" value=""></label><br>
<input type="submit" value="Show Me!" id="btnShow">
</form>
<div id="messages">

</div>

Finally, we write some JavaScript to read in a format from the textbox and write out the formatted date to the target div:

<script type="text/javascript">
YAHOO.namespace("YAHOO.example.DateFormatter");
YAHOO.example.DateFormatter.formatDate = function(e)
{
	YAHOO.util.Event.stopEvent(e);

	var el = document.getElementById("date-format");
	if(el && el.value)
	{
		var messages = document.getElementById("messages");
		var date_str = YAHOO.util.Date.format(new Date(), { format: el.value });
		messages.innerHTML = "<em>" + date_str + "</em>";
	}
};
YAHOO.util.Event.addListener("btnShow", "click", YAHOO.example.DateFormatter.formatDate);

</script>

Put it all together, and we have a working example. Try some formats yourself, as defined by the strftime library. You can combine multiple formats and add your own text as well.


Yes, you can also put some HTML in there.

The full list of supported formats is:

%a
Abbreviated weekday name according to the current locale
%A
Full weekday name according to the current locale
%b
Abbreviated month name according to the current locale
%B
Full month name according to the current locale
%c
Preferred date and time representation for the current locale
%C
Century number (the year divided by 100 and truncated to an integer, range 00 to 99)
%d
Day of the month as a decimal number (range 01 to 31)
%D
Same as %m/%d/%y
%e
Day of the month as a decimal number, a single digit is preceded by a space (range ‘1′ to ‘31′)
%F
Same as %Y-%m-%d (ISO 8601 date format)
%g
Like %G, but without the century
%G
the 4-digit year corresponding to the ISO week number
%h
Same as %b
%H
Hour as a decimal number using a 24-hour clock (range 00 to 23)
%I
Hour as a decimal number using a 12-hour clock (range 01 to 12)
%j
Day of the year as a decimal number (range 001 to 366)
%k
Hour as a decimal number using a 24-hour clock (range 0 to 23); single digits are preceded by a blank. (See also %H.)
%l
Hour as a decimal number using a 12-hour clock (range 1 to 12); single digits are preceded by a blank. (See also %I.)
%m
Month as a decimal number (range 01 to 12)
%M
Minute as a decimal number
%n
Newline character (as good as whitespace in HTML)
%p
Either ‘AM’ or ‘PM’ according to the given time value, or the corresponding strings for the current locale
%P
Like %p, but lower case
%r
Time in a.m. and p.m. notation equivalent to %I:%M:%S %p
%R
Time in 24 hour notation equivalent to %H:%M
%s
Number of seconds since the Epoch, ie, since 1970-01-01 00:00:00 UTC
%S
Second as a decimal number
%t
Tab character
%T
Current time, equivalent to %H:%M:%S
%u
Weekday as a decimal number [1,7], with 1 representing Monday
%U
Week number of the current year as a decimal number, starting with
the first Sunday as the first day of the first week
%V
The ISO 8601:1988 week number of the current year as a decimal number,
range 01 to 53, where week 1 is the first week that has at least 4 days
in the current year, and with Monday as the first day of the week.
%w
Day of the week as a decimal, Sunday being 0
%W
Week number of the current year as a decimal number, starting with the
first Monday as the first day of the first week
%x
Preferred date representation for the current locale without the time
%X
Preferred time representation for the current locale without the date
%y
Year as a decimal number without a century (range 00 to 99)
%Y
Year as a decimal number including the century
%z
Numerical time zone representation
%Z
Time zone name or abbreviation
%%
A literal ‘%’ character

We’ve seen how to use the date formatter to print out the current date in different formats. The current date, however, is interesting only some of the time. Most of the time we have dates from other sources that we need formatted.

The YAHOO.util.Date.format method accepts three parameters. Only the first of these — the date to be formatted — is required. We can pass in a JavaScript Date object that refers to any date, within JavaScript’s acceptable range, and have it formatted to our needs.

Let’s give it a try:

<script type="text/javascript">
	var d = new Date("2009/01/15");
	alert(YAHOO.util.Date.format(d, {format: "%Y-%m-%d %T %Z"}));
</script>

We can pass in the current date, time, and timezone and do it all in the call to format itself:

<script type="text/javascript">
	alert(YAHOO.util.Date.format(new Date("2009/01/15 10:12:45 UTC"), {format: "%Y-%m-%d %T %Z"}));
</script>

Notice how the date is always displayed in the local (browser) timezone, even if we initialise it to UTC. Unfortunately, at this time the date formatter cannot display dates in other timezones. However, future versions may add the ability to display dates in UTC in addition to local time.

We can also enter a Unix timestamp in milliseconds, and have that show up as a formatted date:

<script type="text/javascript">

	alert(YAHOO.util.Date.format(new Date(1234567891011), {format: "%Y-%m-%d %T %Z"}));
</script>

You can pass in a date in any of the formats supported by the JavaScript Date constructor. If you want to go further than that though, help is at hand. Simon Willison built a strtotime clone and Stoyan Stefanov did one as well. Integrating one of those libraries into your date handling routines is left as an exercise to the reader. :)

Let’s now put it all together into an interactive script:


 

Date Formatting with YUI – Part II

4:44 pm - February 25, 2009 in Yahoo! User Interface Blog

In Part I, we saw how to format a date using YUI’s date formatter. In Part II, we’ll look at formatting dates for a specific use case — inside the DataTable control.

DataTables are a great tool for presenting all types of data to the users of your website, including dates. As we’ve seen in Part I, the date formatter makes it easy to transform a Date object into a formatted string. For this example, we’ll take DataTable’s Basic Example and add a custom date formatter to it. We’ll start with the includes we need:

<!-- Individual YUI CSS files -->
<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.7.0/build/datatable/assets/skins/sam/datatable.css"> 

<!-- Individual YUI JS files -->
<script type="text/javascript" src="http://yui.yahooapis.com/2.7.0/build/yahoo-dom-event/yahoo-dom-event.js"></script>
<script type="text/javascript" src="http://yui.yahooapis.com/2.7.0/build/datasource/datasource-min.js"></script>
<script type="text/javascript" src="http://yui.yahooapis.com/2.7.0/build/element/element-min.js"></script>
<script type="text/javascript" src="http://yui.yahooapis.com/2.7.0/build/datatable/datatable-min.js"></script>

Next, we’ll add markup to input a user-defined format and to trigger a DataTable redraw:

<label>Format: <input type="text" id="date-format" value="%b %d %Y" size="30"></label> <input type="button" id="render-table" value="Redraw">

<div id="basic"></div>

The key here is to define a custom date formatter:

YAHOO.namespace("YAHOO.example.DateFormatter");
YAHOO.example.DateFormatter.formatDate = function(elCell, oRecord, oColumn, oData) {
	var el = document.getElementById(”date-format”);
	var sFormat = el.value;

	var str = YAHOO.util.Date.format(oData, {format: sFormat});
	elCell.innerHTML = str;
}

And finally, here is the JavaScript to create the DataTable. Note that we point the formatter for the “date” column to our own:

YAHOO.example.Data = {
    bookorders: [
        {id:"po-0167", date:new Date(1980, 2, 24), quantity:1, amount:4},
        {id:"po-0783", date:new Date("January 3, 1983"), quantity:null, amount:12.12345},
        {id:"po-0297", date:new Date(1978, 11, 12), quantity:12, amount:1.25},
        {id:"po-1482", date:new Date("March 11, 1985"), quantity:6, amount:3.5}
    ]
};

var myColumnDefs = [
	{key:"id", sortable:true},
	{key:"date", formatter:YAHOO.example.DateFormatter.formatDate,
			sortable:true, sortOptions:{defaultDir:YAHOO.widget.DataTable.CLASS_DESC}},
	{key:”quantity”, formatter:YAHOO.widget.DataTable.formatNumber, sortable:true},
	{key:”amount”, formatter:YAHOO.widget.DataTable.formatCurrency, sortable:true}
];

var myDataSource = new YAHOO.util.DataSource(YAHOO.example.Data.bookorders);
myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSARRAY;
myDataSource.responseSchema = {
	fields: [”id”,”date”,”quantity”,”amount”]
};

var myDataTable = new YAHOO.widget.DataTable( “basic”, myColumnDefs, myDataSource );

For our example, we’ll also include an event handler to redraw the table when the “Redraw” button is clicked:

YAHOO.util.Event.addListener("render-table", "click", myDataTable.render, myDataTable, true);

Putting it all together, we get a DataTable with customizeable date formating.

In this example, our DataSource holds actual Date objects. This isn’t strictly necessary. For an application to support internationalization, date/time information should be stored and transmitted in UTC. For instance, if your data resides on your server as a DATETIME field in a MySQL database, then the best way to convert it to a Unix timestamp is to use the UNIX_TIMESTAMP() function:

SELECT id, UNIX_TIMESTAMP(pubdate) AS date, quantity, amount FROM bookorders;

Other database engines have their own method of extracting a Unix timestamp.

The result set can then be JSON encoded using a server-side JSON library in your language of choice, before it is passed back to the browser. In PHP, we’d do something like this:

$bookorders = array();
while($row = mysql_fetch_assoc($results))
{
	$bookorders[] = $row;
}
header("Content-type: application/json");
echo json_encode($bookorders);

On the client side, we’d receive this data using an XHRDataSource:

var myDataSource = new YAHOO.util.XHRDataSource(”http://hostname/your/script.php”);
myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
// See DataSource documentation for full details

Since your data comes in as JSON from the server, you’re probably better off passing dates in as Unix timestamps and using the Date constructor inside your formatter:

YAHOO.example.Data = {
    "bookorders': [
        {id:"po-0167", date:320227200, quantity:1, amount:4},
        {id:"po-0783", date:410428800, quantity:null, amount:12.12345},
        {id:"po-0297", date:279705600, quantity:12, amount:1.25},
        {id:"po-1482", date:479376000, quantity:6, amount:3.5}
    ]
};

YAHOO.example.DateFormatter.formatDate = function(elCell, oRecord, oColumn, oData) {
	var el = document.getElementById("date-format");
	var sFormat = el.value;

	var oDate = new Date(oData*1000);

	var str = YAHOO.util.Date.format(oData, {format: sFormat});
	elCell.innerHTML = str;
}

Note that we multiply the Unix timestamp by 1000 because the Unix timestamps we received were in seconds, while the Date constructor requires milliseconds.

That’s all for now. In Part III, we’ll look at formatting dates in the Charts control.

 

Date Formatting with YUI – Part III

5:59 pm - March 18, 2009 in Yahoo! User Interface Blog

In Part I, we saw how to easily format a date using YUI’s date formatter and in Part II we looked at formatting dates for the DataTable control. We will now take a look at how to format dates for the YUI Charts control.

Interestingly, it was the Charts control that first led to the development of the date formatter. We were building a time-series chart and needed to format dates differently at various locations. Dates displayed along our X-axis needed to be appropriate to the range represented by the chart. For example, for data spanning months, we wanted the labels to be at a month-day level, and for data data years, we wanted the labels to show month and year. We also wanted the chart’s caption to show the entire date range in a human friendly format like “January - March 2008″. Finally, the chart needed a tooltip to show the exact date for the mouse hover point.

We could either render all of this date data into pre-formatted strings using PHP on the back end, or we could send the raw date data to the front end and have JavaScript render it appropriately. We went with the second option to reduce the payload size, reduce data redundancy, and to stay more in line with the MVC pattern. The only problem, of course, was that date formatting in JavaScript isn’t as easy as PHP’s strftime or date functions. That’s when we decided to port strftime to JavaScript.

Let’s now proceed with an example and attempt to create a chart like the one I’ve described above. We’ll start with the YUI Charts Quickstart Example, but instead of working with just month strings, we’ll use data based on Unix timestamps and work with data for 15 months.

The DataSource should look like this:

YAHOO.example.expenses =
[
    // Note: Unix timestamps output seconds since the Epoch,
    // so multiply by 1000 to get a JS timestamp
	{ date: 1199174400000, rent: 880.00, utilities: 894.68 },
	{ date: 1201852800000, rent: 880.00, utilities: 901.35 },
	{ date: 1204358400000, rent: 880.00, utilities: 889.32 },
	{ date: 1207033200000, rent: 880.00, utilities: 884.71 },
	{ date: 1209625200000, rent: 910.00, utilities: 879.811 },
	{ date: 1212303600000, rent: 910.00, utilities: 897.95 },
	{ date: 1214895600000, rent: 910.00, utilities: 894.68 },
	{ date: 1217574000000, rent: 910.00, utilities: 901.35 },
	{ date: 1220252400000, rent: 910.00, utilities: 889.32 },
	{ date: 1222844400000, rent: 910.00, utilities: 884.71 },
	{ date: 1225522800000, rent: 910.00, utilities: 889.81 },
	{ date: 1228118400000, rent: 910.00, utilities: 897.95 },
	{ date: 1230796800000, rent: 910.00, utilities: 894.68 },
	{ date: 1233475200000, rent: 910.00, utilities: 921.35 },
	{ date: 1235894400000, rent: 910.00, utilities: 889.32 }
];

var st=0; en=YAHOO.example.expenses.length-1;

YAHOO.example.getData = function() {
	var data = [];
	for(var i=st; i<=en; i++) {
		data.push(YAHOO.example.expenses[i]);
	}
	return data;
}

var myDataSource = new YAHOO.util.DataSource( YAHOO.example.getData );
myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSARRAY;
myDataSource.responseSchema =
{
	fields: [ "date", "rent", "utilities" ]
};

We use a function to return the data so that we can return subsets of it based on various triggers.

We’ll set up most of our chart like the example:

// Define multiple series
var seriesDef =
[
	{ displayName: "Rent", yField: "rent" },
	{ displayName: "Utilities", yField: "utilities" }
];

// Define a custom function to format numbers as currency along the Y-axis
YAHOO.example.formatCurrencyAxisLabel = function( value )
{
	return YAHOO.util.Number.format( value,
	{
		prefix: "$",
		thousandsSeparator: ",",
		decimalPlaces: 2
	});
}

var currencyAxis = new YAHOO.widget.NumericAxis();
currencyAxis.minimum = 800;
currencyAxis.labelFunction = YAHOO.example.formatCurrencyAxisLabel;

// Create a tool tip that shows up when you mouseover a data point
YAHOO.example.getDataTipText = function( item, index, series )
{
	var toolTipText = series.displayName + " for " + item.date;
	toolTipText += "\n" + YAHOO.example.formatCurrencyAxisLabel( item[series.yField] );
	return toolTipText;
}

// Create the chart
var mychart = new YAHOO.widget.LineChart( "chart", myDataSource, {
		series: seriesDef,
		xField: "date",
		yAxis: currencyAxis,
		dataTipFunction: YAHOO.example.getDataTipText
	}
);

This chart isn’t quite what we wanted though. It displays raw timestamps in the tool tip and on the X-axis. To remedy this, we need to customise both of these using the date formatter. Start with the getDataTipText function. Note the changed lines marked in bold:

YAHOO.example.getDataTipText = function( item, index, series )
{
	var d = new Date(item.date);
	var sDate = YAHOO.util.Date.format(d, {format: “%B %Y”});
	var toolTipText = series.displayName + ” for ” + sDate;
	toolTipText += “\n” + YAHOO.example.formatCurrencyAxisLabel( item[series.yField] );
	return toolTipText;
}

We also need to define a flexible label formatter for the X-axis:

YAHOO.example.xFormat="%b %d";
YAHOO.example.formatDate = function( value )
{
	var d = new Date(value);
	return YAHOO.util.Date.format( d, { YAHOO.example.xFormat });
}

var timeAxis = new YAHOO.widget.TimeAxis();
timeAxis.majorTimeUnit="month";
timeAxis.labelFunction = YAHOO.example.formatDate;

And pass this in to the chart constructor. Note the changes to the earlier constructor marked in bold.

var mychart = new YAHOO.widget.LineChart( "chart", myDataSource, {
		series: seriesDef,
		xField: "date",
		yAxis: currencyAxis,
		xAxis: timeAxis,
		dataTipFunction: YAHOO.example.getDataTipText
	}
);

Finally, we write a little function to draw the caption and change the x-axis label format.

function refreshChart() {
	if(this && this.nodeName && this.nodeName.toUpperCase() == 'BUTTON') {
		var range = this.id.split("_");
		st = parseInt(range[1], 10);
		en = parseInt(range[2], 10);
	}
	var caption = '';
	var d1=new Date(YAHOO.example.expenses[st].date);
	var d2=new Date(YAHOO.example.expenses[en].date);

	if(d1.getFullYear() != d2.getFullYear()) {
		caption = YAHOO.util.Date.format(d1, {format: "%b %Y - "})
					+ YAHOO.util.Date.format(d2, {format: "%b %Y"});
		YAHOO.example.xFormat="%b '%y";
	} else if(d1.getMonth() != d2.getMonth()) {
		caption = YAHOO.util.Date.format(d1, {format: "%b - "})
					+ YAHOO.util.Date.format(d2, {format: "%b %Y"});
		YAHOO.example.xFormat="%b";
	} else {
		caption = YAHOO.util.Date.format(d1, {format: "%d %b - "})
					+ YAHOO.util.Date.format(d2, {format: "%d %b, %Y"});
	    YAHOO.example.xFormat="%m-%d";
	}

	YAHOO.util.Dom.get("caption").innerHTML = caption;

	// redraw the chart with the new range of data
	mychart.refreshData();
}

refreshChart();

YAHOO.util.Event.on(document.getElementsByTagName('button'), 'click', refreshChart);

This function is called whenever we change the range of data displayed. To do this, we attach it to the onclick event of our range selection buttons. We also have to call it right at the start so that the caption is drawn correctly. The markup for the range selection buttons should look like this:

<button id="b_0_2">Q1 '08</button>
<button id="b_3_5">Q2 '08</button>
<button id="b_6_8">Q3 '08</button>
<button id="b_9_11">Q4 '08</button>

<button id="b_12_14">Q1 '09</button>
<button id="b_0_14">Entire range</button>

Putting it all together, we get a custom date-formatted chart. Notice how the labels on the X-Axis show abbreviated months and years while the tool tip shows the full month and year.

In the final part of this series, we’ll look at date localisation for internationalised web apps.

 

Date Formatting with YUI – Part IV

5:28 pm - July 6, 2009 in Yahoo! User Interface Blog

In Part I of this series, we introduced date formatting with the YUI Date utility and integrated it with the DataTable control in Part II and the Charts control in Part III. In this final part, we’ll look at date localisation with YUI.

To recap, we can format dates using YUI using the YAHOO.util.Date class, which is currently distributed as part of the DataSource utility. Any valid strftime format specifier is supported, so for example, YAHOO.util.Date.format(new Date(), { format: "%Y-%b-%d"}); would return the date as <four digit year>-<short month name>-<two digit day of month>.

The Date utility accepts an optional third parameter, which specifies the locale to use when formatting a date. If not specified, this defaults to "en". The locale is a string that may be the two-letter ISO-639-1 language code, optionally followed by a hyphen and a two-letter ISO-3166-1 country code. For example, fr is used for French, while fr-CA is used for the dialect of French spoken in Canada, and fr-CH is used for the dialect of French spoken in Switzerland. de-CH, on the other hand, is the dialect of German spoken in Switzerland.

Examples of valid locale codes
Locale Code Language
en English (default)
fr French
fr-CA French dialect spoken in Canada
fr-CH French dialect spoken in Switzerland
de German
de-DE German dialect spoken in Germany

The locale code impacts only the following format specifiers:

%a
abbreviated weekday name according to the current locale
%A
full weekday name according to the current locale
%b
abbreviated month name according to the current locale
%B
full month name according to the current locale
%c
preferred date and time representation for the current locale
%h
same as %b
%p
either “AM” or “PM” according to the given time value, or the corresponding strings for the current locale
%P
like %p, but lower case
%r
time in AM and PM notation equal to %I:%M:%S %p
%x
preferred date representation for the current locale without the time
%X
preferred time representation for the current locale without the date

Built-in locales

Let’s start off by looking at the built-in locales. The Date utility includes the following locales by default:

  • en – English (the default)
  • en-US – US English
  • en-GB – British English
  • en-AU – Australian English (identical to British English)

In the following example, we’ll print out the locale-specific date format using the built-in locales:

var d = new Date();
var f = { format: "%x%n" };
var s = "%x:\n";
s += "  Default:\t" + YAHOO.util.Date.format(d, f);
s += "  en-US:\t" + YAHOO.util.Date.format(d, f, "en-US");
s += "  en-GB:\t" + YAHOO.util.Date.format(d, f, "en-GB");

alert(s);

See a working example. Note the different output for en-US and en-GB. Similar differences between these two locales can be seen for %r, %c, and %X.

Supporting other languages

Now, there are many languages other than English, and many web applications that cater to speakers of these languages which the date formatter should support. While these aren’t provided by YUI itself, it is fairly easy to add your own locale patch. Let’s create one now for French. We do this by mixing in the YAHOO.util.DateLocale class with our locale definitions using the YAHOO.lang.merge method:

YAHOO.util.DateLocale["fr"] = YAHOO.lang.merge(YAHOO.util.DateLocale, {
	a: ["dim", "lun", "mar", "mer", "jeu", "ven", "sam"],
	A: ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"],
	b: ["jan", "fév", "mar", "avr", "mai", "jun", "jui", "aoû", "sep", "oct", "nov", "déc"],
	B: ["janvier", "février", "mars", "avril", "mai", "juin", "juillet", "août", "septembre", "octobre", "novembre", "décembre"],
	c: "%a %d %b %Y %T %Z",
	p: ["", ""],
	P: ["", ""],
	x: "%d.%m.%Y",
	X: "%T"
});

var d = new Date();
var f = { format: "%c%n" };
var s = "%c:\n";
s += "  Default:\t" + YAHOO.util.Date.format(d, f);
s += "  fr:   \t" + YAHOO.util.Date.format(d, f, "fr");

alert(s);

Try it out.

Similarly, we can create one for Canadian French by using the French locale as a base. The only difference is in the locale-specific date format %x:

YAHOO.util.DateLocale["fr-CA"] = YAHOO.lang.merge(YAHOO.util.DateLocale["fr"], {
	x: "%Y-%m-%d"
});

var d = new Date();
var f = { format: "%A, %x%n" };
var s = "%A, %x:\n";
s += "  Default:\t" + YAHOO.util.Date.format(d, f);
s += "  fr:   \t" + YAHOO.util.Date.format(d, f, "fr");
s += "  fr-CA:\t" + YAHOO.util.Date.format(d, f, "fr-CA");
s += "  fr-CH:\t" + YAHOO.util.Date.format(d, f, "fr-CH");
s += "  de-CH:\t" + YAHOO.util.Date.format(d, f, "de-CH");

alert(s);

Try it out.

Notice that we also try to access fr-CH and de-CH which haven’t been defined. In this case, the Date utility falls back to a less specific locale and tries fr and de instead. Since de hasn’t been defined either, it falls back to en, which is built in.

I’ve included definitions for a few locales as examples. If you’d like to use these locales, it may make more sense to just include the code directly in your HTML pages, or copy the files to your own servers.

  • fr – French, Canadian French and Swiss French
  • de – German, and Swiss German
  • hi – Hindi
  • ko – Korean
 

Analyzing Bandwidth and Latency on YUIBlog.com

10:38 am - April 8, 2010 in Yahoo! User Interface Blog

About the author: Philip Tellis (@bluesmoon) is a geek working with Yahoo!’s Exceptional Performance team where he analyses the impact of various design decisions on web application performance. He also maintains YUI’s date formatter and the YUI port of the flot charting library. You can find him on the web at http://bluesmoon.info/.

A few months ago, Eric helped me run some analysis on the types of network connections that were coming in to YUIBlog. In this article, I’ll analyze the data we collected and look for insights about the tools and infrastructure the YUIBlog readership uses to browse the web. Being the performance guy at Yahoo!, my interest focuses how fast our readers connections are. In particular, with what kind of network bandwidth and latency do YUI geeks experience the web? Although your mileage will vary, YUIBlog is likely to be representative of US tech blogs read by people in the software industry, and the data here may prove useful for those of you catering to a similar audience.

Caveats

Before I go any further, I have a few caveats to list. The tests and analysis we ran were based in the US (since that’s where YUIBlog is hosted), so if you view this site from outside the US, your measured experience will probably be a little slower than what you’d see with local sites. Secondly, while we see traffic from all over the world, I’ll only look at countries with a statistically relevant sample size. Lastly, each individual IP was considered only once. If the same IP accessed the site more than once, we consider the arithmetic mean of values from each access. For overall summaries, we use the median. No statistical filtering was applied to the data before analysis. The total sample size was approximately 27,000 points.

The tests

The test measured HTTP bandwidth and HTTP latency between an end user’s computer (or a proxy somewhere in between) and YUIBlog. Note that I say HTTP bandwidth and latency because these may be different (i.e., worse) from the actual bandwidth that your ISP provides you, but it’s what we, as frontend engineers, care about more. Latency is the time between a request going out and the first byte of the response coming back. It depends on how far you are from the server and the number and quality of hops between you and the server, so if you’re far away, you’ll have high latency. HTTP latency also depends on the overhead of HTTP headers, but we’ve tried to keep that down, and on how long it takes for the web server to respond to a request. We think that’s fairly fast.

Bandwidth depends on the bandwidth at every step between you and the server, and it’s somewhat affected by latency as well. The measured bandwidth is also affected by other applications or devices using the network in parallel. If you’re streaming movies or loading an image heavy site while a measurement is taken, your bandwidth will appear lower than it actually is. However, this is the effective bandwidth that you browse YUIBlog with, so it’s what we care about.

So, what kind of networks do YUI geeks browse through?

Quick summary answer: The bandwidth of YUI geeks is 1 Mbps (that’s Megabits) and the latency is 262 ms. Most of you are either on Broadband, DSL or Cable connections and come in from the US, Canada, Great Britain, Germany and India (ordered by distance from our server). For more detailed results, read on…

Connection types

The type of network connection you have with your ISP determines the primary bandwidth limit and secondary latency limit (the first being distance divided by the 66% speed of light). Not surprisingly most of you are very closely associated with the internet, and use Broadband data connections. Only 2% of you used your mobile phones to reach the site, but that’s still more than the number of people on dial-up.

Connection Type Broadband xDSL Cable T1 Mobile Dial-up Wireless Satellite
Count 14155 4874 4719 880 552 143 86 71
Bandwidth (kbps) 733 953 3,118 3,017 407 356 291 287
Latency (ms) 305 278 165 188 430 436 537 859

Pie chart of data point counts from table #connection-type-table

Of all the connection types we’ve seen, Cable and T1 offer the best bandwidth and latency, going over four times as fast as Broadband with half the latency. This could possibly mean that most of these connections were within the US while the others span the globe. The low latency seems to suggest that, but drilling down reduces the sample size too much to make an accurate assessment. From the data we see a clear inverse relationship between bandwidth and latency.

3D Bar chart of bandwidth and latency data from table #connection-type-table

International data

We expected connections from outside the US to appear slower because the test has an intrinsic geographical bias, but what does the data really tell us?

Countries CA US DK NL NO BE SE CH AT GB FI DE CZ FR RO PT BG HU ES IT PL GR JP UA AR TR KR RU BR TW HK AU SG IN PH TH CN ID
Numrecs 949 9071 141 497 238 218 417 212 181 1833 157 1503 176 811 232 168 129 151 455 496 405 126 340 251 179 224 154 395 482 174 133 659 146 1748 200 147 639 206
Bandwidth (kbps) 3,295 3,206 2,404 2,247 2,144 1,981 1,936 1,807 1,658 1,648 1,599 1,581 1,453 1,420 1,357 1,296 1,267 1,165 893 869 765 560 549 545 498 496 478 477 418 409 405 392 368 238 207 205 181 64
Latency (ms) 108 133 247 230 263 233 264 256 278 242 276 269 256 254 274 297 285 270 335 309 284 363 416 340 387 353 462 359 409 506 516 555 595 588 595 688 649 867

Bar chart of bandwidth data for top 18 countries (CA to HU) in table #international-bandwidth-table

The US and Canada top the list as expected. All other countries with a bandwidth over 1 Mbps are from Europe. Looking at the other end of the spectrum, we see a few surprises.

Bar chart of bandwidth data for bottom 17 countries (GR to ID) in table #international-bandwidth-table

I know from recent experience that Korea and Hong Kong have really high bandwidth network connections. Faster than most of the US in fact. The data puts them at less than one sixth the bandwidth of the US. A possible explanation is the latency. As we already know, there’s a correlation between bandwidth and latency. Looking at the latency numbers for these countries gives us a possible answer.

Bar chart of latency data for bottom 17 countries (GR to ID) in table #international-bandwidth-table

We get hits from many more countries (147 in all). They either fell between these two blocks or had too few data points to be a statistically sound sample.

ISPs

As the final piece of analysis, I looked at ISP information. I only looked at a few countries since there were too many ISPs world-wide to put on a bar chart. Presented in the chart below is data for US and Canadian ISPs.

Country ISP Latency (ms) Bandwidth (kbps)
Germany hansenet telekommunikation gmbh 282 1,849
international ip-backbone of vodafone 263 1,282
deutsche telekom ag 311 1,149
Great Britain avatar broadband limited 229 2,200
btnet uk regional network 256 1,769
ntl group limited 253 1,581
India videsh sanchar nigam ltd. aut 508 355
reliance infocom ltd internet 615 260
bharti airtel ltd. telemedia services 601 226
tata communications 569 195
national internet backbone 609 167
USA cablevision systems corp. 78 4,752
verizon global networks 101 4,218
road runner holdco llc 108 4,093
charter communications 114 3,945
comcast cable communications inc. 123 3,816
att worldnet services 117 3,328
time warner telecom 95 3,304
cox communications inc. 166 3,224
xo communications 140 2,986
qwest 203 1,892
att internet services 204 1,502
bellsouth.net inc. 121 1,446
Canada rogers cable communications inc. 96 4,386
bell canada 103 3,161
shaw communications inc. 203 3,007

I’m not sure what the difference between AT&T Worldnet and AT&T Internet is, but I’ll leave that for you to figure out. We saw more hits from Comcast than from any other ISP on the planet.

Users of different browsers

The browser you use doesn’t affect your bandwidth (at least hasn’t in lab tests) and has a very negligible effect on latency, so I didn’t expect to see any differences here. I was curious though, to see what the data said, and this is what I found. I’ve included iPhones and iPods as well as they made up a sizeable fraction of all data points. Opera’s mobile browsers are included under Opera. The “other” category only included 22 data points.

Browser Latency Bandwidth
Gecko 267 1,105
Webkit 238 1,591
MSIE 285 759
Opera 271 1,174
iPod/iPhone 399 477
other 274 1,289

Radar chart of bandwidth and latency data across user agents from table #useragent-table

The iPhone’s latency is close to other mobile phones. All other browsers have the same latency. This is as expected. Bandwidth tells a different story though. It appears that those of you with the fastest internet connections use a Webkit based browser like Safari, Chrome or Konqueror, while users with the slowest internet connections use Internet Explorer.

Summary

  • The overall bandwidth of YUIBlog readers is 1 Mbps and the overall latency is 262ms
  • We see a definite correlation between latency and bandwidth, so it would be interesting to see what the measured bandwidth is after eliminating delays caused by latency. Perhaps in a future test I’ll do that.
  • Putting a mirror in Asia probably makes sense for a US-based engineering blog like this one. Putting one in Europe probably doesn’t.
  • Mobile users have terrible latency, so a site optimised for mobile should really reduce the number of HTTP requests it makes. Note that mobile users that surf the web over wifi experience far lower latency.
 
 
 
 
 
 
It's All About Search | © clsc.net |
2012.05.1822:55
Tech used here: Valid HTML - Valid CSS - Valid RSS - JavaScript - PHP - Smarty - MySQL - and a partridge in a pear tree.