newspaint

Documenting Problems That Were Difficult To Find The Answer To

Setting Linux Time Without NTP Using HTTP

Overview

The motivation behind this post is because I couldn’t access UDP NTP time servers from behind a corporate firewall (and UDP cannot be tunnelled through a SSH tunnel).

If you put the below into a script (and call it, say, get_time_from_http.pl) and call it as follows:

date `perl get_time_from_http.pl http://www.bbc.co.uk/`

…this will set the time on your computer wherever you are in the world (give or take one second plus the round-trip latency to the website you specified).

Why You Can Use The BBC Website From Anywhere In The World

The BBC website returns a date stamp in the following format:

Date: Fri, 21 Sep 2012 07:43:37 GMT

As the timestamp is in GMT the Perl functions Time::Local::timegm and localtime can be used to convert that time to a local time.

The output of this script is a string of the form MMDDhhmmCCYY.ss as per the man page for date.

get_time_from_http.pl

#!/usr/bin/perl -w

use Time::Local;
use LWP::UserAgent;
use Getopt::Std;

use strict;

my %opts = ();
Getopt::Std::getopts( 'c', \%opts );

my $url = shift or die( "Must provide URL to sync to" );

my $ua = LWP::UserAgent->new();
my $response = $ua->get( $url );
if ( ! $response->is_success() ) {
    die( "Failed to fetch URL: " . $response->status_line() );
}

my $datestr = $response->header( 'Date' );
die( "No date found" ) if ( ! $datestr );

sub getdatestr {
    my ( $epoch ) = @_;

    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($epoch);
    #my $str = sprintf( "%04d-%02d-%02d %02d:%02d:%02d", $year + 1900, $mon + 1, $mday, $hour, $min, $sec );
    my $str = sprintf( "%02d%02d%02d%02d%04d.%02d", $mon + 1, $mday, $hour, $min, $year + 1900, $sec );
    return( $str );
}

if ( $datestr =~ m{(\d+)\s+(\S+)\s+(\d{4})\s+(\d\d):(\d\d):(\d\d)(\s+(\S+))?} ) {
    my ( $weekday, $monthstr, $year, $hour, $min, $sec ) = ( $1, $2, $3, $4, $5, $6 );
    my ( $tzone ) = $8;

    my %months = qw(jan 1 feb 2 mar 3 apr 4 may 5 jun 6 jul 7 aug 8 sep 9 oct 10 nov 11 dec 12);
    my $month = $months{lc($monthstr)};

    my $tfunc = $tzone eq "GMT" ? \&Time::Local::timegm : \&Time::Local::timelocal;
    my $epoch = $tfunc->( $sec, $min, $hour, $weekday, $month - 1, $year );

    printf( "%s\n", getdatestr( $epoch ) );
} else {
    die( "Unknown date format \"" . $datestr . "\"" );
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: