/*
AtomicTime utility

    by: Shawn A. Van Ness
    rev: 2002.07.28

This simple little C# app demonstrates a broad range of .NET functionality.  
It grabs HTML from http://www.time.gov, then scrapes the current date and 
time out of it via a pair of simple regular expressions.

Then it sets the current time, via p/invoke to kernel32!SetSystemTime.
(Funny that the CLR doesn't seem to expose that, directly...?)
*/

using System;
using System.Net; // for HttpWebRequest

using System.IO; // for Stream, StreamReader

using System.Text; // for Encoding

using System.Text.RegularExpressions; // for Regex (duh)

using System.Globalization; // for DateTimeFormatInfo

using System.Runtime.InteropServices; // for DllImport

using System.ComponentModel; // for Win32Exception



// This class wraps Kernel32!SetSystemTime

//

internal class TimeSetter
{
    public static void SetSystemTime( DateTime dt)
    {
        SystemTime st;
        st.wYear = (ushort)dt.Year;
        st.wMonth = (ushort)dt.Month;
        st.wDayOfWeek = (ushort)dt.DayOfWeek;
        st.wDay = (ushort)dt.Day;
        st.wHour = (ushort)dt.Hour;
        st.wMinute = (ushort)dt.Minute;
        st.wSecond = (ushort)dt.Second;
        st.wMilliseconds = (ushort)dt.Millisecond;

        bool b = SetSystemTime( ref st);
        if ( !b)
            throw new Win32Exception( Marshal.GetLastWin32Error());
    }

    [StructLayout(LayoutKind.Sequential)]
    protected struct SystemTime // Win32's SYSTEMTIME

    {
        public ushort wYear; 
        public ushort wMonth;
        public ushort wDayOfWeek; 
        public ushort wDay; 
        public ushort wHour; 
        public ushort wMinute; 
        public ushort wSecond; 
        public ushort wMilliseconds; 
    }

    [ DllImport("kernel32.dll", SetLastError=true) ]
    protected static extern 
        bool SetSystemTime( ref SystemTime systemTime);
}


// The main entry point for our console-mode executable

//

class MainModule
{
    public static void Main( string[] args)
    {
        // Formulate the web request

        string url = "http://time.gov/timezone.cgi?UTC/s/0";

        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
        request.Timeout = 7000// 7 seconds


        // Send the request, and await response

        Console.WriteLine("Connecting to {0}...", url);

        HttpWebResponse response = null;
        TimeSpan roundTrip;
        try
        {
            // We send the request twice, to get accurate timing (the first roundtrip is 

            // just to wake up the JIT compiler, and the network hardware, etc.

            DateTime temp = DateTime.Now;
            request.GetResponse();
            roundTrip = DateTime.Now-temp;

            temp = DateTime.Now;
            response = (HttpWebResponse)request.GetResponse();
            roundTrip = DateTime.Now-temp;
        }
        catch ( WebException e)
        {
            // By rethrowing the exception, we can add a little descriptive info

            throw new WebException( "Failed to connect to "+url+".", e);
        }

        // Process response

        Stream responseStream = response.GetResponseStream();
        StreamReader streamReader = new StreamReader( responseStream, Encoding.UTF8, true);
        string s = streamReader.ReadToEnd();

        streamReader.Close();
        response.Close();

        // Scrape the time and date

        Console.WriteLine("Reading the time...", url);

        Regex dateRE = new Regex( @">(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday),\s+(January|February|March|April|May|June|July|August|September|October|November|December)\s+[1-3]?[0-9],\s+[1-2][0-9][0-9][0-9]<", RegexOptions.ExplicitCapture);
        // example match: ">Saturday, July 27, 2002<"

        // >dddd, MMMM dd, yyyy<


        Regex timeRE = new Regex( @">[0-2][0-9]:[0-5][0-9]:[0-5][0-9]<", RegexOptions.ExplicitCapture);
        // example match: ">15:45:48<"

        // >HH:mm:ss<


        Match dateMatch = dateRE.Match(s);
        Match timeMatch = timeRE.Match(s);

        if ( !dateMatch.Success || !timeMatch.Success)
            throw new ApplicationException( "Could not determine the current date and/or time.");

        DateTimeFormatInfo dtfi = new DateTimeFormatInfo();
        dtfi.FullDateTimePattern = ">HH:mm:ss<>dddd, MMMM dd, yyyy<";

        DateTime dt = DateTime.Parse( timeMatch.Value+dateMatch.Value, dtfi);

        // The HTML doesn't specify milliseconds, so let's guess 500

        dt = dt.AddMilliseconds(500-dt.Millisecond);

        // Add in one half of the network roundtrip time

        dt = dt.AddMilliseconds(roundTrip.Milliseconds/2);

        // Last but not least, set the clock!

        Console.WriteLine("Setting current system time to {0} (UTC).", dt);

        TimeSetter.SetSystemTime(dt);
    }
}