/*----------------------------------------------------------------------
|   TestDateFromTimeStringANSI
+---------------------------------------------------------------------*/
static void
TestDateFromTimeStringANSI()
{
    NPT_DateTime date;

    /* Valid date */
    SHOULD_SUCCEED(date.FromString("Fri Apr 14 12:01:10 2006", NPT_DateTime::FORMAT_ANSI));
    SHOULD_EQUAL_I(date.m_Year         , 2006);
    SHOULD_EQUAL_I(date.m_Month        , 4);
    SHOULD_EQUAL_I(date.m_Day          , 14);
    SHOULD_EQUAL_I(date.m_Hours        , 12);
    SHOULD_EQUAL_I(date.m_Minutes      , 1);
    SHOULD_EQUAL_I(date.m_Seconds      , 10);
    SHOULD_EQUAL_I(date.m_NanoSeconds  , 0);
    SHOULD_EQUAL_I(date.m_TimeZone     , 0);

    /* Valid date with space in the days */
    SHOULD_SUCCEED(date.FromString("Fri Apr  7 12:01:10 2006", NPT_DateTime::FORMAT_ANSI));
    SHOULD_EQUAL_I(date.m_Year         , 2006);
    SHOULD_EQUAL_I(date.m_Month        , 4);
    SHOULD_EQUAL_I(date.m_Day          , 7);
    SHOULD_EQUAL_I(date.m_Hours        , 12);
    SHOULD_EQUAL_I(date.m_Minutes      , 1);
    SHOULD_EQUAL_I(date.m_Seconds      , 10);
    SHOULD_EQUAL_I(date.m_NanoSeconds  , 0);
    SHOULD_EQUAL_I(date.m_TimeZone     , 0);

    /* Wrong weekday */
    SHOULD_FAIL(date.FromString("Wed Apr 14 12:01:10 2006", NPT_DateTime::FORMAT_ANSI));

    /* Wrong year length */
    SHOULD_FAIL(date.FromString("Mon Apr 14 12:01:10 95", NPT_DateTime::FORMAT_ANSI));
}
Beispiel #2
0
/*----------------------------------------------------------------------
|   PLT_HttpServer::ServeFile
+---------------------------------------------------------------------*/
NPT_Result 
PLT_HttpServer::ServeFile(const NPT_HttpRequest&        request, 
                          const NPT_HttpRequestContext& context,
                          NPT_HttpResponse&             response,
                          NPT_String                    file_path) 
{
    NPT_InputStreamReference stream;
    NPT_File                 file(file_path);
    NPT_FileInfo             file_info;
    
    // prevent hackers from accessing files outside of our root
    if ((file_path.Find("/..") >= 0) || (file_path.Find("\\..") >= 0) ||
        NPT_FAILED(NPT_File::GetInfo(file_path, &file_info))) {
        return NPT_ERROR_NO_SUCH_ITEM;
    }
    
    // check for range requests
    const NPT_String* range_spec = request.GetHeaders().GetHeaderValue(NPT_HTTP_HEADER_RANGE);
    
    // handle potential 304 only if range header not set
    NPT_DateTime  date;
    NPT_TimeStamp timestamp;
    if (NPT_SUCCEEDED(PLT_UPnPMessageHelper::GetIfModifiedSince((NPT_HttpMessage&)request, date)) &&
        !range_spec) {
        date.ToTimeStamp(timestamp);
        
        NPT_LOG_INFO_5("File %s timestamps: request=%d (%s) vs file=%d (%s)", 
                       (const char*)request.GetUrl().GetPath(),
                       (NPT_UInt32)timestamp.ToSeconds(),
                       (const char*)date.ToString(),
                       (NPT_UInt32)file_info.m_ModificationTime,
                       (const char*)NPT_DateTime(file_info.m_ModificationTime).ToString());
        
        if (timestamp >= file_info.m_ModificationTime) {
            // it's a match
            NPT_LOG_FINE_1("Returning 304 for %s", request.GetUrl().GetPath().GetChars());
            response.SetStatus(304, "Not Modified", NPT_HTTP_PROTOCOL_1_1);
            return NPT_SUCCESS;
        }
    }
    
    // open file
    if (NPT_FAILED(file.Open(NPT_FILE_OPEN_MODE_READ)) || 
        NPT_FAILED(file.GetInputStream(stream))        ||
        stream.IsNull()) {
        return NPT_ERROR_NO_SUCH_ITEM;
    }
    
    // set Last-Modified and Cache-Control headers
    if (file_info.m_ModificationTime) {
        NPT_DateTime last_modified = NPT_DateTime(file_info.m_ModificationTime);
        response.GetHeaders().SetHeader("Last-Modified", last_modified.ToString(NPT_DateTime::FORMAT_RFC_1123), true);
        response.GetHeaders().SetHeader("Cache-Control", "max-age=0,must-revalidate", true);
        //response.GetHeaders().SetHeader("Cache-Control", "max-age=1800", true);
    }
    
    PLT_HttpRequestContext tmp_context(request, context);
    return ServeStream(request, context, response, stream, PLT_MimeType::GetMimeType(file_path, &tmp_context));
}
/*----------------------------------------------------------------------
|   TestDateFromTimeStringRFC_1123
+---------------------------------------------------------------------*/
static void
TestDateFromTimeStringRFC_1123()
{
    NPT_DateTime date;

    /* Valid date */
    SHOULD_SUCCEED(date.FromString("Fri, 14 Apr 2006 12:01:10 UT", NPT_DateTime::FORMAT_RFC_1123));
    SHOULD_EQUAL_I(date.m_Year         , 2006);
    SHOULD_EQUAL_I(date.m_Month        , 4);
    SHOULD_EQUAL_I(date.m_Day          , 14);
    SHOULD_EQUAL_I(date.m_Hours        , 12);
    SHOULD_EQUAL_I(date.m_Minutes      , 1);
    SHOULD_EQUAL_I(date.m_Seconds      , 10);
    SHOULD_EQUAL_I(date.m_NanoSeconds  , 0);
    SHOULD_EQUAL_I(date.m_TimeZone     , 0);

    /* Valid date with timezone*/
    SHOULD_SUCCEED(date.FromString("Fri, 14 Apr 2006 12:01:10 GMT", NPT_DateTime::FORMAT_RFC_1123));
    SHOULD_EQUAL_I(date.m_Year         , 2006);
    SHOULD_EQUAL_I(date.m_Month        , 4);
    SHOULD_EQUAL_I(date.m_Day          , 14);
    SHOULD_EQUAL_I(date.m_Hours        , 12);
    SHOULD_EQUAL_I(date.m_Minutes      , 1);
    SHOULD_EQUAL_I(date.m_Seconds      , 10);
    SHOULD_EQUAL_I(date.m_NanoSeconds  , 0);
    SHOULD_EQUAL_I(date.m_TimeZone     , 0);

    /* Valid date with timezone*/
    SHOULD_SUCCEED(date.FromString("Fri, 14 Apr 2006 12:01:10 +0800", NPT_DateTime::FORMAT_RFC_1123));
    SHOULD_EQUAL_I(date.m_Year         , 2006);
    SHOULD_EQUAL_I(date.m_Month        , 4);
    SHOULD_EQUAL_I(date.m_Day          , 14);
    SHOULD_EQUAL_I(date.m_Hours        , 12);
    SHOULD_EQUAL_I(date.m_Minutes      , 1);
    SHOULD_EQUAL_I(date.m_Seconds      , 10);
    SHOULD_EQUAL_I(date.m_NanoSeconds  , 0);
    SHOULD_EQUAL_I(date.m_TimeZone     , 8*60);

    /* Valid date, short year */
    SHOULD_SUCCEED(date.FromString("Fri, 14 Apr 95 12:01:10 GMT", NPT_DateTime::FORMAT_RFC_1123));
    SHOULD_EQUAL_I(date.m_Year         , 1995);
    SHOULD_EQUAL_I(date.m_Month        , 4);
    SHOULD_EQUAL_I(date.m_Day          , 14);
    SHOULD_EQUAL_I(date.m_Hours        , 12);
    SHOULD_EQUAL_I(date.m_Minutes      , 1);
    SHOULD_EQUAL_I(date.m_Seconds      , 10);
    SHOULD_EQUAL_I(date.m_NanoSeconds  , 0);
    SHOULD_EQUAL_I(date.m_TimeZone     , 0);

    /* Wrong day name */
    SHOULD_FAIL(date.FromString("Friday, 14 Apr 95 12:01:10 GMT", NPT_DateTime::FORMAT_RFC_1123));

    /* Wrong weekday */
    SHOULD_FAIL(date.FromString("Wed, 14 Apr 2006 12:01:10 GMT", NPT_DateTime::FORMAT_RFC_1123));

    /* Wrong year length */
    SHOULD_FAIL(date.FromString("Mon, 14 Apr 95 12:01:10 GMT", NPT_DateTime::FORMAT_RFC_1123));
}
Beispiel #4
0
void Server::sendDiscover(std::string host, int port, std::string st)
{
    std::string udn = gMS->udnString();
    std::string serverHost = "192.168.1.103";
    int serverPort = 8082;

    NPT_DateTime date;
    NPT_TimeStamp now;

    NPT_System::GetCurrentTimeStamp(now);
    date.FromTimeStamp(now);
    NPT_String date_str = date.ToString(NPT_DateTime::FORMAT_RFC_1123);

    if (st.compare(udn) == 0)
    {
        udn = "";
    }
    else
    {
        udn += "::";
    }

    std::string discovery;
    discovery.reserve(1024);
    discovery.append("HTTP/1.1 200 OK").append(CRLF);
	discovery.append("CACHE-CONTROL: max-age=1200").append(CRLF);
	discovery.append("DATE: ").append(date_str.GetChars()).append(" GMT").append(CRLF);
	discovery.append("LOCATION: http://").append(serverHost).append(":").append(std::to_string(serverPort)).append("/description/fetch").append(CRLF);
	discovery.append("SERVER: ").append(gMS->name()).append(CRLF);
	discovery.append("ST: ").append(st).append(CRLF);
	discovery.append("EXT: ").append(CRLF);
	discovery.append("USN: ").append(udn).append(st).append(CRLF);
	discovery.append("Content-Length: 0").append(CRLF).append(CRLF);

    sendReply(host, port, discovery);
}
Beispiel #5
0
/*----------------------------------------------------------------------
|   PLT_MediaObject::FromDidl
+---------------------------------------------------------------------*/
NPT_Result
PLT_MediaObject::FromDidl(NPT_XmlElementNode* entry)
{
    NPT_String str, xml;
    NPT_Array<NPT_XmlElementNode*> children;
    NPT_Result res;

    // check if item is restricted (is default true?)
    if (NPT_SUCCEEDED(PLT_XmlHelper::GetAttribute(entry, "restricted", str, "", 5))) {
        m_Restricted = PLT_Service::IsTrue(str);
    }
    
    // read non-required elements
    PLT_XmlHelper::GetChildText(entry, "creator", m_Creator, didl_namespace_dc, 256);
    PLT_XmlHelper::GetChildText(entry, "date", m_Date, didl_namespace_dc, 256);
    
    // parse date and make sure it's valid
    NPT_String parsed_date;
    for (int format=0; format<=NPT_DateTime::FORMAT_RFC_1036; format++) {
        NPT_DateTime date;
        if (NPT_SUCCEEDED(date.FromString(m_Date, (NPT_DateTime::Format)format))) {
            parsed_date = date.ToString((NPT_DateTime::Format)format);
            break;
        }
    }
    m_Date = parsed_date;

    res = PLT_XmlHelper::GetAttribute(entry, "id", m_ObjectID);
    NPT_CHECK_SEVERE(res);

    res = PLT_XmlHelper::GetAttribute(entry, "parentID", m_ParentID);
    NPT_CHECK_SEVERE(res);

    PLT_XmlHelper::GetAttribute(entry, "refID", m_ReferenceID);

    res = PLT_XmlHelper::GetChildText(entry, "title", m_Title, didl_namespace_dc);
    NPT_CHECK_SEVERE(res);
    
    res = PLT_XmlHelper::GetChildText(entry, "class", m_ObjectClass.type, didl_namespace_upnp);
    NPT_CHECK_SEVERE(res);
    
    // DLNA 7.3.17.3 max bytes for dc:title and upnp:class is 256 bytes
    m_Title = m_Title.SubString(0, 256);    
    m_ObjectClass.type =  m_ObjectClass.type.SubString(0, 256);

    children.Clear();
    PLT_XmlHelper::GetChildren(entry, children, "artist", didl_namespace_upnp);
    m_People.artists.FromDidl(children);
    
    children.Clear();
    PLT_XmlHelper::GetChildren(entry, children, "author", didl_namespace_upnp);
    m_People.authors.FromDidl(children);
    
    children.Clear();
    PLT_XmlHelper::GetChildren(entry, children, "actors", didl_namespace_upnp);
    m_People.actors.FromDidl(children);

    children.Clear();
    PLT_XmlHelper::GetChildren(entry, children, "director", didl_namespace_upnp);
    m_People.directors.FromDidl(children);

    PLT_XmlHelper::GetChildText(entry, "album", m_Affiliation.album, didl_namespace_upnp, 256);
    PLT_XmlHelper::GetChildText(entry, "programTitle", m_Recorded.program_title, didl_namespace_upnp);
    PLT_XmlHelper::GetChildText(entry, "seriesTitle", m_Recorded.series_title, didl_namespace_upnp);
    PLT_XmlHelper::GetChildText(entry, "episodeNumber", str, didl_namespace_upnp);
    NPT_UInt32 value;
    if (NPT_FAILED(str.ToInteger(value))) value = 0;
    m_Recorded.episode_number = value;

    children.Clear();
    PLT_XmlHelper::GetChildren(entry, children, "genre", didl_namespace_upnp);
    for (NPT_Cardinal i=0; i<children.GetItemCount(); i++) {
        if (children[i]->GetText()) {
            m_Affiliation.genres.Add(children[i]->GetText()->SubString(0, 256));
        }
    }
    
    PLT_XmlHelper::GetChildText(entry, "description", m_Description.description, didl_namespace_dc);
    PLT_XmlHelper::GetChildText(entry, "longDescription", m_Description.long_description, didl_namespace_upnp);
    PLT_XmlHelper::GetChildText(entry, "icon", m_Description.icon_uri, didl_namespace_upnp);
    PLT_XmlHelper::GetChildText(entry, "rating", m_Description.rating, didl_namespace_upnp);
	PLT_XmlHelper::GetChildText(entry, "toc", m_MiscInfo.toc, didl_namespace_upnp);
    
    // album arts
    children.Clear();
    PLT_XmlHelper::GetChildren(entry, children, "albumArtURI", didl_namespace_upnp);
    for (NPT_Cardinal i=0; i<children.GetItemCount(); i++) {
        if (children[i]->GetText()) {
            PLT_AlbumArtInfo info;
            info.uri = children[i]->GetText()->SubString(0, 1024);
            PLT_XmlHelper::GetAttribute(children[i], "profileID", info.dlna_profile, didl_namespace_dlna);
            m_ExtraInfo.album_arts.Add(info);
        }
    }
    
    PLT_XmlHelper::GetChildText(entry, "originalTrackNumber", str, didl_namespace_upnp);
    if (NPT_FAILED(str.ToInteger(value))) value = 0;
    m_MiscInfo.original_track_number = value;

    PLT_XmlHelper::GetChildText(entry, "lastPlaybackPosition", str, didl_namespace_upnp);
    if (NPT_FAILED(str.ToInteger(value))) value = 0;
    m_MiscInfo.last_position = value;

    PLT_XmlHelper::GetChildText(entry, "lastPlaybackTime", m_MiscInfo.last_time, didl_namespace_dc, 256);
    NPT_String parsed_last_time;
    for (int format=0; format<=NPT_DateTime::FORMAT_RFC_1036; format++) {
        NPT_DateTime date;
        if (NPT_SUCCEEDED(date.FromString(m_MiscInfo.last_time, (NPT_DateTime::Format)format))) {
            parsed_last_time = date.ToString((NPT_DateTime::Format)format);
            break;
        }
    }
    m_MiscInfo.last_time = parsed_last_time;

    PLT_XmlHelper::GetChildText(entry, "playbackCount", str, didl_namespace_upnp);
    if (NPT_FAILED(str.ToInteger(value))) value = -1;
    m_MiscInfo.play_count = value;

    children.Clear();
    PLT_XmlHelper::GetChildren(entry, children, "res");
    for (NPT_Cardinal i=0; i<children.GetItemCount(); i++) {
        PLT_MediaItemResource resource;

        // extract url
        if (children[i]->GetText() == NULL) {
            NPT_LOG_WARNING_1("No resource text found in: %s", (const char*)PLT_XmlHelper::Serialize(*children[i]));
        } else {
            resource.m_Uri = children[i]->GetText()->SubString(0, 1024);

            // basic uri validation, ignoring scheme (could be rtsp)
            NPT_HttpUrl url(resource.m_Uri, true);
            if (!url.IsValid()) {
                NPT_LOG_WARNING_1("Invalid resource uri: %s", (const char*)resource.m_Uri);
                continue;
            }
        }

        // extract protocol info
        NPT_String protocol_info;
        res = PLT_XmlHelper::GetAttribute(children[i], "protocolInfo", protocol_info, "", 256);
        if (NPT_FAILED(res)) {
            NPT_LOG_WARNING_1("No protocol info found in: %s", (const char*)PLT_XmlHelper::Serialize(*children[i]));
        } else {
            resource.m_ProtocolInfo = PLT_ProtocolInfo(protocol_info);
            if (!resource.m_ProtocolInfo.IsValid()) {
                NPT_LOG_WARNING_1("Invalid resource protocol info: %s", (const char*)protocol_info);
            }
        }

        // extract known attributes
        PLT_XmlHelper::GetAttribute(children[i], "protection", resource.m_Protection, "", 256);
        PLT_XmlHelper::GetAttribute(children[i], "resolution", resource.m_Resolution, "", 256);

        if (NPT_SUCCEEDED(PLT_XmlHelper::GetAttribute(children[i], "size", str, "", 256))) {
            if (NPT_FAILED(str.ToInteger64(resource.m_Size))) resource.m_Size = (NPT_Size)-1;
        }

        if (NPT_SUCCEEDED(PLT_XmlHelper::GetAttribute(children[i], "duration", str, "", 256))) {
            if (NPT_FAILED(PLT_Didl::ParseTimeStamp(str, resource.m_Duration))) {
                // if error while converting, ignore and set to -1 to indicate we don't know the duration
                resource.m_Duration = (NPT_UInt32)-1;
                PLT_XmlHelper::RemoveAttribute(children[i], "duration");
            } else {
                // DLNA: reformat duration in case it was not compliant
                str = PLT_Didl::FormatTimeStamp(resource.m_Duration);
                PLT_XmlHelper::SetAttribute(children[i], "duration", str); 
            }
        }    
        m_Resources.Add(resource);
    }

    // re serialize the entry didl as a we might need to pass it to a renderer
    // we may have modified the tree to "fix" issues, so as not to break a renderer
    // (don't write xml prefix as this didl could be part of a larger document)
    //res = PLT_XmlHelper::Serialize(*entry, xml, false);
    m_Didl = "";
    res = ToDidl(PLT_FILTER_MASK_ALL, m_Didl);
    NPT_CHECK_SEVERE(res);
    
    m_Didl = didl_header + m_Didl + didl_footer;    
    return NPT_SUCCESS;
}
/*----------------------------------------------------------------------
|   TestMisc
+---------------------------------------------------------------------*/
static void
TestMisc()
{
    NPT_DateTime  date;
    NPT_TimeStamp ts;
    NPT_String    s;
    
    NPT_System::GetCurrentTimeStamp(ts);
    SHOULD_SUCCEED(date.FromTimeStamp(ts, false));
    s = date.ToString(NPT_DateTime::FORMAT_W3C);
    NPT_Console::OutputF("%s\n", s.GetChars());
    s = date.ToString(NPT_DateTime::FORMAT_ANSI);
    NPT_Console::OutputF("%s\n", s.GetChars());
    s = date.ToString(NPT_DateTime::FORMAT_RFC_1036);
    NPT_Console::OutputF("%s\n", s.GetChars());
    s = date.ToString(NPT_DateTime::FORMAT_RFC_1123);
    NPT_Console::OutputF("%s\n", s.GetChars());
    SHOULD_SUCCEED(date.FromTimeStamp(ts, true));
    s = date.ToString(NPT_DateTime::FORMAT_W3C);
    NPT_Console::OutputF("%s\n", s.GetChars());
    s = date.ToString(NPT_DateTime::FORMAT_ANSI);
    NPT_Console::OutputF("%s\n", s.GetChars());
    s = date.ToString(NPT_DateTime::FORMAT_RFC_1036);
    NPT_Console::OutputF("%s\n", s.GetChars());
    s = date.ToString(NPT_DateTime::FORMAT_RFC_1123);
    NPT_Console::OutputF("%s\n", s.GetChars());

    ts = 0.0;
    SHOULD_SUCCEED(date.FromTimeStamp(ts, false));
    s = date.ToString(NPT_DateTime::FORMAT_W3C);
    SHOULD_EQUAL_S(s.GetChars(), "1970-01-01T00:00:00Z");
    s = date.ToString(NPT_DateTime::FORMAT_ANSI);
    SHOULD_EQUAL_S(s.GetChars(), "Thu Jan  1 00:00:00 1970");
    s = date.ToString(NPT_DateTime::FORMAT_RFC_1036);
    SHOULD_EQUAL_S(s.GetChars(), "Thursday, 01-Jan-70 00:00:00 GMT");
    s = date.ToString(NPT_DateTime::FORMAT_RFC_1123);
    SHOULD_EQUAL_S(s.GetChars(), "Thu, 01 Jan 1970 00:00:00 GMT");
    
    ts.SetSeconds(0xFFFFFFFF);
    SHOULD_SUCCEED(date.FromTimeStamp(ts, false));
    s = date.ToString(NPT_DateTime::FORMAT_W3C, false);
    SHOULD_EQUAL_S(s.GetChars(), "2106-02-07T06:28:15Z");

    NPT_TimeStamp now;
    NPT_System::GetCurrentTimeStamp(now);
    NPT_DateTime now_local(now, true);
    NPT_DateTime now_utc(now, false);
    SHOULD_EQUAL_I(now_utc.m_TimeZone, 0);
    NPT_TimeStamp ts1, ts2;
    now_local.ToTimeStamp(ts1);
    now_utc.ToTimeStamp(ts2);
    SHOULD_EQUAL_I((int)ts1.ToSeconds(), (int)ts2.ToSeconds());
    
    ts.SetSeconds(0);
    NPT_DateTime d1(ts);
    ts.SetSeconds(ts.ToSeconds()-3600);
    NPT_DateTime d2(ts);
    d1.ToTimeStamp(ts1);
    d2.ToTimeStamp(ts2);
    SHOULD_EQUAL_I((int)ts1.ToSeconds(), (int)ts2.ToSeconds()+3600);
}
/*----------------------------------------------------------------------
|   TestRandom
+---------------------------------------------------------------------*/
static void
TestRandom()
{
    for (unsigned int i=0; i<10000; i++) {
        NPT_TimeStamp ts((double)NPT_System::GetRandomInteger());
        NPT_TimeStamp ts2;
        NPT_DateTime date;
        SHOULD_SUCCEED(date.FromTimeStamp(ts, false));
        SHOULD_SUCCEED(date.ToTimeStamp(ts2));
        NPT_String ds;
        NPT_DateTime ndate;
        ds = date.ToString(NPT_DateTime::FORMAT_ANSI);
        ndate.FromString(ds);
        //SHOULD_EQUAL(date, ndate);
        SHOULD_SUCCEED(ndate.ToTimeStamp(ts2));
        SHOULD_EQUAL_F((double)ts2.ToSeconds(), (double)ts.ToSeconds());
        
        ds = date.ToString(NPT_DateTime::FORMAT_W3C);
        ndate.FromString(ds);
        //SHOULD_EQUAL(date, ndate);
        SHOULD_SUCCEED(ndate.ToTimeStamp(ts2));
        SHOULD_EQUAL_F((double)ts2.ToSeconds(), (double)ts.ToSeconds());

        ds = date.ToString(NPT_DateTime::FORMAT_RFC_1123);
        ndate.FromString(ds);
        //SHOULD_EQUAL(date, ndate);
        SHOULD_SUCCEED(ndate.ToTimeStamp(ts2));
        SHOULD_EQUAL_F((double)ts2.ToSeconds(), (double)ts.ToSeconds());

        ds = date.ToString(NPT_DateTime::FORMAT_RFC_1036);
        ndate.FromString(ds);
        //SHOULD_EQUAL(date, ndate);
        SHOULD_SUCCEED(ndate.ToTimeStamp(ts2));
        SHOULD_EQUAL_F((double)ts2.ToSeconds(), (double)ts.ToSeconds());
    }
}
/*----------------------------------------------------------------------
|   TestDateFromTimeStringW3C
+---------------------------------------------------------------------*/
static void
TestDateFromTimeStringW3C()
{
    NPT_DateTime date;

    /* Valid date */
    SHOULD_SUCCEED(date.FromString("2006-04-14T12:01:10.003Z", NPT_DateTime::FORMAT_W3C));
    SHOULD_EQUAL_I(date.m_Year         , 2006);
    SHOULD_EQUAL_I(date.m_Month        , 4);
    SHOULD_EQUAL_I(date.m_Day          , 14);
    SHOULD_EQUAL_I(date.m_Hours        , 12);
    SHOULD_EQUAL_I(date.m_Minutes      , 1);
    SHOULD_EQUAL_I(date.m_Seconds      , 10);
    SHOULD_EQUAL_I(date.m_NanoSeconds  , 3000000);
    SHOULD_EQUAL_I(date.m_TimeZone     , 0);

    /* Valid date, 2 characters milliseconds */
    SHOULD_SUCCEED(date.FromString("2006-04-14T12:01:10.02Z", NPT_DateTime::FORMAT_W3C));
    SHOULD_EQUAL_I(date.m_Year         , 2006);
    SHOULD_EQUAL_I(date.m_Month        , 4);
    SHOULD_EQUAL_I(date.m_Day          , 14);
    SHOULD_EQUAL_I(date.m_Hours        , 12);
    SHOULD_EQUAL_I(date.m_Minutes      , 1);
    SHOULD_EQUAL_I(date.m_Seconds      , 10);
    SHOULD_EQUAL_I(date.m_NanoSeconds  , 20000000);
    SHOULD_EQUAL_I(date.m_TimeZone     , 0);

    /* Valid date, 1 character milliseconds */
    SHOULD_SUCCEED(date.FromString("2006-04-14T12:01:10.9Z", NPT_DateTime::FORMAT_W3C));
    SHOULD_EQUAL_I(date.m_Year         , 2006);
    SHOULD_EQUAL_I(date.m_Month        , 4);
    SHOULD_EQUAL_I(date.m_Day          , 14);
    SHOULD_EQUAL_I(date.m_Hours        , 12);
    SHOULD_EQUAL_I(date.m_Minutes      , 1);
    SHOULD_EQUAL_I(date.m_Seconds      , 10);
    SHOULD_EQUAL_I(date.m_NanoSeconds  , 900000000);
    SHOULD_EQUAL_I(date.m_TimeZone     , 0);

    /* Valid date, no millimseconds */
    SHOULD_SUCCEED(date.FromString("2006-04-14T12:01:10Z", NPT_DateTime::FORMAT_W3C));
    SHOULD_EQUAL_I(date.m_Year         , 2006);
    SHOULD_EQUAL_I(date.m_Month        , 4);
    SHOULD_EQUAL_I(date.m_Day          , 14);
    SHOULD_EQUAL_I(date.m_Hours        , 12);
    SHOULD_EQUAL_I(date.m_Minutes      , 1);
    SHOULD_EQUAL_I(date.m_Seconds      , 10);
    SHOULD_EQUAL_I(date.m_NanoSeconds  , 0);
    SHOULD_EQUAL_I(date.m_TimeZone     , 0);

    /* Valid date with microseconds, 'Z' */
    SHOULD_SUCCEED(date.FromString("2005-09-06T17:16:10.003498Z", NPT_DateTime::FORMAT_W3C));
    SHOULD_EQUAL_I(date.m_Year         , 2005);
    SHOULD_EQUAL_I(date.m_Month        , 9);
    SHOULD_EQUAL_I(date.m_Day          , 6);
    SHOULD_EQUAL_I(date.m_Hours        , 17);
    SHOULD_EQUAL_I(date.m_Minutes      , 16);
    SHOULD_EQUAL_I(date.m_Seconds      , 10);
    SHOULD_EQUAL_I(date.m_NanoSeconds  , 3498000);
    SHOULD_EQUAL_I(date.m_TimeZone     , 0);

    /* Valid date, no milliseconds, with timezone offset */
    SHOULD_SUCCEED(date.FromString("2006-04-14T12:01:10+03:00", NPT_DateTime::FORMAT_W3C));
    SHOULD_EQUAL_I(date.m_Year         , 2006);
    SHOULD_EQUAL_I(date.m_Month        , 4);
    SHOULD_EQUAL_I(date.m_Day          , 14);
    SHOULD_EQUAL_I(date.m_Hours        , 12);
    SHOULD_EQUAL_I(date.m_Minutes      , 1);
    SHOULD_EQUAL_I(date.m_Seconds      , 10);
    SHOULD_EQUAL_I(date.m_NanoSeconds  , 0);
    SHOULD_EQUAL_I(date.m_TimeZone     , 180);

    /* Valid date, no milliseconds, with negative m_TimeZone offset */
    SHOULD_SUCCEED(date.FromString("2006-04-14T12:01:10-05:00", NPT_DateTime::FORMAT_W3C));
    SHOULD_EQUAL_I(date.m_Year         , 2006);
    SHOULD_EQUAL_I(date.m_Month        , 4);
    SHOULD_EQUAL_I(date.m_Day          , 14);
    SHOULD_EQUAL_I(date.m_Hours        , 12);
    SHOULD_EQUAL_I(date.m_Minutes      , 1);
    SHOULD_EQUAL_I(date.m_Seconds      , 10);
    SHOULD_EQUAL_I(date.m_NanoSeconds  , 0);
    SHOULD_EQUAL_I(date.m_TimeZone     , -300);

    /* Valid date, with milliseconds, with positive m_TimeZone offset */
    SHOULD_SUCCEED(date.FromString("2006-04-14T12:01:10.200+03:00", NPT_DateTime::FORMAT_W3C));
    SHOULD_EQUAL_I(date.m_Year         , 2006);
    SHOULD_EQUAL_I(date.m_Month        , 4);
    SHOULD_EQUAL_I(date.m_Day          , 14);
    SHOULD_EQUAL_I(date.m_Hours        , 12);
    SHOULD_EQUAL_I(date.m_Minutes      , 1);
    SHOULD_EQUAL_I(date.m_Seconds      , 10);
    SHOULD_EQUAL_I(date.m_NanoSeconds  , 200000000);
    SHOULD_EQUAL_I(date.m_TimeZone     , 180);

    /* Valid date, with milliseconds, with negative m_TimeZone offset */
    SHOULD_SUCCEED(date.FromString("2006-04-14T12:01:10.030-05:00", NPT_DateTime::FORMAT_W3C));
    SHOULD_EQUAL_I(date.m_Year         , 2006);
    SHOULD_EQUAL_I(date.m_Month        , 4);
    SHOULD_EQUAL_I(date.m_Day          , 14);
    SHOULD_EQUAL_I(date.m_Hours        , 12);
    SHOULD_EQUAL_I(date.m_Minutes      , 1);
    SHOULD_EQUAL_I(date.m_Seconds      , 10);
    SHOULD_EQUAL_I(date.m_NanoSeconds  , 30000000);
    SHOULD_EQUAL_I(date.m_TimeZone     , -300);

    /* Valid date with microseconds and negative m_TimeZone offset */
    SHOULD_SUCCEED(date.FromString("2005-09-06T17:16:10.001822-05:00", NPT_DateTime::FORMAT_W3C));
    SHOULD_EQUAL_I(date.m_Year         , 2005);
    SHOULD_EQUAL_I(date.m_Month        , 9);
    SHOULD_EQUAL_I(date.m_Day          , 6);
    SHOULD_EQUAL_I(date.m_Hours        , 17);
    SHOULD_EQUAL_I(date.m_Minutes      , 16);
    SHOULD_EQUAL_I(date.m_Seconds      , 10);
    SHOULD_EQUAL_I(date.m_NanoSeconds  , 1822000);
    SHOULD_EQUAL_I(date.m_TimeZone     , -300);
    
    /* Valid date with microseconds and positive m_TimeZone offset */
    SHOULD_SUCCEED(date.FromString("2005-09-06T17:16:10.001822+05:00", NPT_DateTime::FORMAT_W3C));
    SHOULD_EQUAL_I(date.m_Year         , 2005);
    SHOULD_EQUAL_I(date.m_Month        , 9);
    SHOULD_EQUAL_I(date.m_Day          , 6);
    SHOULD_EQUAL_I(date.m_Hours        , 17);
    SHOULD_EQUAL_I(date.m_Minutes      , 16);
    SHOULD_EQUAL_I(date.m_Seconds      , 10);
    SHOULD_EQUAL_I(date.m_NanoSeconds  , 1822000);
    SHOULD_EQUAL_I(date.m_TimeZone     , 300);

    /* Invalid date with 3 digit year */
    SHOULD_FAIL(date.FromString("206-04-14T12:01:10.003Z", NPT_DateTime::FORMAT_W3C));

    /* Invalid date with 5 digit year */
    SHOULD_FAIL(date.FromString("20076-04-14T12:01:10.003Z", NPT_DateTime::FORMAT_W3C));

    /* Invalid date with 5 digit year */
    SHOULD_FAIL(date.FromString("20076-04-14T12:01:10.003Z", NPT_DateTime::FORMAT_W3C));

    /* Invalid date with garbage in the end */
    SHOULD_FAIL(date.FromString("2006-04-14T12:01:10.003+69:696", NPT_DateTime::FORMAT_W3C));

    /* Invalid date with bad month */
    SHOULD_FAIL(date.FromString("2006-010-14T12:01:10.003", NPT_DateTime::FORMAT_W3C));

    /* Invalid date with bad month, right overall length */
    SHOULD_FAIL(date.FromString("2063-0--14T12:01:10.003", NPT_DateTime::FORMAT_W3C));

    /* Invalid date with bad year-month separator */
    SHOULD_FAIL(date.FromString("2063Y08-14T12:01:10.003", NPT_DateTime::FORMAT_W3C));

    /* Invalid date with bad time separator */
    SHOULD_FAIL(date.FromString("2063-08-14t12:01:10.003", NPT_DateTime::FORMAT_W3C));

    /* Invalid date with bad hour */
    SHOULD_FAIL(date.FromString("2063-08-14T012:01:10.003", NPT_DateTime::FORMAT_W3C));

    /* Invalid date with bad GMT indicator */
    SHOULD_FAIL(date.FromString("2063-08-14T12:01:10.003z", NPT_DateTime::FORMAT_W3C));

    /* Invalid date with bad GMT indicator */
    SHOULD_FAIL(date.FromString("2063-08-14T12:01:10.003g", NPT_DateTime::FORMAT_W3C));

    /* Invalid date with millisecond separator but no digits */
    SHOULD_FAIL(date.FromString("2063-08-14T12:01:10.", NPT_DateTime::FORMAT_W3C));

    /* Invalid date with millisecond separator but no digits */
    SHOULD_FAIL(date.FromString("2063-08-14T12:01:10.Z", NPT_DateTime::FORMAT_W3C));

    /* Invalid date with millisecond separator but no digits */
    SHOULD_FAIL(date.FromString("2063-08-14T12:01:10.+10:38", NPT_DateTime::FORMAT_W3C));

    /* Invalid date with bad m_TimeZone offset */
    SHOULD_FAIL(date.FromString("2063-08-14T12:01:10+10:338", NPT_DateTime::FORMAT_W3C));

    /* Invalid date with bad m_TimeZone offset */
    SHOULD_FAIL(date.FromString("2063-08-14T12:01:10+001:38", NPT_DateTime::FORMAT_W3C));

    /* Invalid date with bad m_TimeZone offset */
    SHOULD_FAIL(date.FromString("2063-08-14T12:01:10+10:33Z", NPT_DateTime::FORMAT_W3C));

    /* Invalid date with bad m_TimeZone offset */
    SHOULD_FAIL(date.FromString("2063-08-14T12:01:10.08+10:33Z", NPT_DateTime::FORMAT_W3C));

    /* Invalid date with bad m_TimeZone offset with m_Seconds*/
    SHOULD_FAIL(date.FromString("2063-08-14T12:01:10.08+10:33:30", NPT_DateTime::FORMAT_W3C));

    /* Invalid date with m_TimeZone offset too big*/
    SHOULD_FAIL(date.FromString("2063-08-14T12:01:10.08+14:33", NPT_DateTime::FORMAT_W3C));
}