Files
OpenRA/thirdparty/yaml/Timestamp.cs
2010-01-09 19:03:25 +13:00

357 lines
9.3 KiB
C#

// ====================================================================================================
// YAML Parser for the .NET Framework
// ====================================================================================================
//
// Copyright (c) 2006
// Christophe Lambrechts
// Jonathan Slenders
//
// ====================================================================================================
// This file is part of the .NET YAML Parser.
//
// This .NET YAML parser is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// The .NET YAML parser is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Foobar; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USAusing System.Reflection;
// ====================================================================================================
using System;
namespace Yaml
{
/// <summary>
/// Yaml Timestamp node
/// uri: tag:yaml.org,2002:timestamp
/// </summary>
public class Timestamp : Scalar
{
private System.DateTime content;
/// <summary>
/// Represents the offset from the UTC time in hours
/// </summary>
/// <remarks>
/// We use this extra variable for compatibility with Mono
/// and .NET 1.0. .NET 2.0 has an extra property for
/// System.Datetime for the timezone.
/// </remarks>
private double timezone = 0;
/// <summary>
/// Basic constructor that takes a given datetime
/// </summary>
/// <param name="datetime">A .NET 1.0 datetime</param>
public Timestamp (DateTime datetime) :
base ("tag:yaml.org,2002:timestamp", NodeType.Timestamp)
{
this.content = datetime;
this.timezone = 0;
}
/// <summary>
/// Basic constructor, that also gives the posibility to set a timezone
/// </summary>
/// <param name="datetime">A .NET 1.0 datetime</param>
/// <param name="timezone">The offset, in hours,r to UTC that determine the timezone </param>
public Timestamp (DateTime datetime, double timezone) :
base ("tag:yaml.org,2002:timestamp", NodeType.Timestamp)
{
this.content = datetime;
this.timezone = timezone;
}
/// <summary> Content property </summary>
public System.DateTime Content
{
get { return content; }
set { content = value; }
}
/// <summary> Timezone, an offset in hours </summary>
public double Timezone
{
get { return timezone; }
set { timezone = value; }
}
/// <summary> To String </summary>
public override string ToString ()
{
return "[TIMESTAMP]" + YamlString () + "[/TIMESTAMP]";
}
/// <summary> Parse a DateTime </summary>
public Timestamp (ParseStream stream) :
base ("tag:yaml.org,2002:timestamp", NodeType.Timestamp)
{
int year = 0;
int month = 0;
int day = 0;
int hour = 0;
int minutes = 0;
int seconds = 0;
int ms = 0;
try
{
// Parse year
year = ParseNumber (stream, 4);
SkipChar (stream, '-');
// Parse month
month = ParseNumber (stream, 2);
SkipChar (stream, '-');
// Parse day
day = ParseNumber (stream, 2);
// Additional, the time
if ( ! stream.EOF)
ParseTime (stream, out hour, out minutes, out seconds);
// Additional, milliseconds
if ( ! stream.EOF)
ms = ParseMilliSeconds (stream);
// Additional, the timezone
if ( ! stream.EOF)
timezone = ParseTimezone (stream);
// If there is more, then a format exception
if ( ! stream.EOF)
throw new Exception ("More data then excepted");
content = new DateTime (year, month, day, hour, minutes, seconds, ms);
}
catch (Exception ex)
{
throw new ParseException (stream, ex.ToString ());
}
}
/// <summary>
/// Parse the time (hours, minutes, seconds)
/// </summary>
private void ParseTime (ParseStream stream,
out int hour, out int minutes, out int seconds)
{
if (stream.Char == 't' || stream.Char == 'T')
stream.Next (true);
else
SkipWhitespace (stream);
// Parse hour
// Note: A hour can be represented by one or two digits.
string hulp = "";
while (stream.Char >= '0' && stream.Char <= '9' &&
! stream.EOF && hulp.Length <= 2)
{
hulp += stream.Char;
stream.Next (true);
}
hour = Int32.Parse (hulp);
SkipChar (stream, ':');
// Parse minutes
minutes = ParseNumber (stream, 2);
SkipChar (stream, ':');
// Parse seconds
seconds = ParseNumber (stream, 2);
}
/// <summary>
/// Parse the milliseconds
/// </summary>
private int ParseMilliSeconds (ParseStream stream)
{
int ms = 0;
// Look for fraction
if (stream.Char == '.')
{
stream.Next (true);
// Parse fraction, can consists of an
// unlimited sequence of numbers, we only
// look to the first three (max 1000)
int count = 0;
while (stream.Char >= '0' && stream.Char <= '9' &&
count < 3 && ! stream.EOF)
{
ms *= 10;
ms += stream.Char - '0';
stream.Next (true);
count ++;
}
if (count == 1) ms *= 100;
if (count == 2) ms *= 10;
if (count == 3) ms *= 1;
// Ignore the rest
while (stream.Char >= '0' && stream.Char <= '9' &&
! stream.EOF)
stream.Next (true);
}
return ms;
}
/// <summary>
/// Parse the time zone
/// </summary>
private double ParseTimezone (ParseStream stream)
{
double timezone = 0;
SkipWhitespace (stream);
// Timezone = UTC, use by default 0
if (stream.Char == 'Z')
stream.Next (true);
else
{
// Find the sign of the offset
int sign = 0;
if (stream.Char == '-')
sign = -1;
else if (stream.Char == '+')
sign = +1;
else
throw new Exception ("Invalid time zone: " +
"unexpected character");
// Read next char and test for more chars
stream.Next (true);
if (stream.EOF)
throw new Exception ("Invalid time zone");
// Parse hour offset
// Note: A hour can be represented by one or two digits.
string hulp = "";
while (stream.Char >= '0' &&
stream.Char <= '9' &&
!stream.EOF && hulp.Length <= 2)
{
hulp += (stream.Char);
stream.Next (true);
}
timezone = sign * Double.Parse (hulp);
// Parse the minutes of the timezone
// when there is still more to parse
if ( ! stream.EOF)
{
SkipChar (stream, ':');
int temp = ParseNumber (stream, 2);
timezone += (temp / 60.0);
}
}
return timezone;
}
/// <summary>
/// Parse an integer
/// </summary>
/// <param name="length">
/// The number of characters that the integer is expected to be.
/// </param>
/// <param name="stream"> The stream that will be parsed </param>
private int ParseNumber (ParseStream stream, int length)
{
System.Text.StringBuilder hulp = new System.Text.StringBuilder ();
int i;
for (i = 0; i < length && !stream.EOF; i++)
{
hulp.Append (stream.Char);
stream.Next (true);
}
if (i == length)
return Int32.Parse (hulp.ToString ());
else
throw new Exception ("Can't parse number");
}
/// <summary>
/// Skips a specified char, and throws an exception when
/// another char was found.
/// </summary>
private void SkipChar (ParseStream stream, char toSkip)
{
if (stream.Char == toSkip)
stream.Next (true);
else
throw new Exception ("Unexpected character");
}
/// <summary>
/// Skips the spaces * and tabs * in the current stream
/// </summary>
private void SkipWhitespace (ParseStream stream)
{
while ((stream.Char == ' ' || stream.Char == '\t') && ! stream.EOF)
stream.Next (true);
}
/// <summary> Yaml notation for this datetime </summary>
private string YamlString ()
{
string date = content.ToString ("yyyy-MM-ddTHH:mm:ss");
int ms = content.Millisecond;
if (ms != 0)
{
string hulp = "" + (ms / 1000.0);
hulp = hulp.Substring(2); // Cut of the '0,', first 2 digits
date += "." + hulp;
}
string zone = "";
if (timezone != 0)
{
int timezoneHour = (int) Math.Floor (timezone);
int timezoneMinutes = (int) (60 * (timezone - timezoneHour));
// if positif offset add '+', a '-' is default added
if (timezone > 0)
zone = "+";
zone += timezoneHour.ToString ();
if (timezoneMinutes != 0)
zone += ":" + timezoneMinutes;
}
else
zone = "Z"; // UTC timezone as default and if offset == 0
return date + zone;
}
/// <summary> Write to YAML </summary>
protected internal override void Write (WriteStream stream)
{
stream.Append (YamlString ());
}
}
}