2199 lines
59 KiB
C#
Raw Normal View History

//
// System.DateTime.cs
//
// Authors:
// Marcel Narings (marcel@narings.nl)
// Martin Baulig (martin@gnome.org)
// Atsushi Enomoto (atsushi@ximian.com)
// Marek Safar (marek.safar@gmail.com)
//
// (C) 2001 Marcel Narings
// Copyright (C) 2004-2006 Novell, Inc (http://www.novell.com)
// Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Runtime.Serialization;
namespace System
{
/// <summary>
/// The DateTime structure represents dates and time ranging from
/// 1-1-0001 12:00:00 AM to 31-12-9999 23:59:00 Common Era.
/// </summary>
///
[Serializable]
[StructLayout (LayoutKind.Auto)]
public struct DateTime : IFormattable, IConvertible, IComparable, ISerializable, IComparable<DateTime>, IEquatable <DateTime>
{
//
// Encodes the DateTime in 64 bits, top two bits contain the DateTimeKind,
// the rest contains the 62 bit value for the ticks. This reduces the
// memory usage from 16 to 8 bytes, see bug: 592221. This also fixes the
// 622127 issue and simplifies the code in reflection.c to encode DateTimes
//
long encoded;
const long TicksMask = 0x3fffffffffffffff;
const long KindMask = unchecked ((long) 0xc000000000000000);
const int KindShift = 62;
private const int dp400 = 146097;
private const int dp100 = 36524;
private const int dp4 = 1461;
// w32 file time starts counting from 1/1/1601 00:00 GMT
// which is the constant ticks from the .NET epoch
private const long w32file_epoch = 504911232000000000L;
//private const long MAX_VALUE_TICKS = 3155378975400000000L;
// -- Microsoft .NET has this value.
private const long MAX_VALUE_TICKS = 3155378975999999999L;
//
// The UnixEpoch, it begins on Jan 1, 1970 at 0:0:0, expressed
// in Ticks
//
internal const long UnixEpoch = 621355968000000000L;
// for OLE Automation dates
private const long ticks18991230 = 599264352000000000L;
private const double OAMinValue = -657435.0d;
private const double OAMaxValue = 2958466.0d;
public static readonly DateTime MaxValue = new DateTime (3155378975999999999);
public static readonly DateTime MinValue = new DateTime (0);
// DateTime.Parse patterns
// Patterns are divided to date and time patterns. The algorithm will
// try combinations of these patterns. The algorithm also looks for
// day of the week, AM/PM GMT and Z independently of the patterns.
private static readonly string[] ParseTimeFormats = new string [] {
"H:m:s.fff zzz",
"H:m:s.fffffffzzz",
"H:m:s.fffffff",
"H:m:s.ffffff",
"H:m:s.ffffffzzz",
"H:m:s.fffff",
"H:m:s.ffff",
"H:m:s.fff",
"H:m:s.ff",
"H:m:s.f",
"H:m:s tt zzz",
"H:m:szzz",
"H:m:s",
"H:mzzz",
"H:m",
"H tt", // Specifies AM to disallow '8'.
"H'\u6642'm'\u5206's'\u79D2'",
};
// DateTime.Parse date patterns extend ParseExact patterns as follows:
// MMM - month short name or month full name
// MMMM - month number or short name or month full name
// Parse behaves differently according to the ShorDatePattern of the
// DateTimeFormatInfo. The following define the date patterns for
// different orders of day, month and year in ShorDatePattern.
// Note that the year cannot go between the day and the month.
private static readonly string[] ParseYearDayMonthFormats = new string [] {
"yyyy/M/dT",
"M/yyyy/dT",
"yyyy'\u5E74'M'\u6708'd'\u65E5",
"yyyy/d/MMMM",
"yyyy/MMM/d",
"d/MMMM/yyyy",
"MMM/d/yyyy",
"d/yyyy/MMMM",
"MMM/yyyy/d",
"yy/d/M",
};
private static readonly string[] ParseYearMonthDayFormats = new string [] {
"yyyy/M/dT",
"M/yyyy/dT",
"yyyy'\u5E74'M'\u6708'd'\u65E5",
"yyyy/MMMM/d",
"yyyy/d/MMM",
"MMMM/d/yyyy",
"d/MMM/yyyy",
"MMMM/yyyy/d",
"d/yyyy/MMM",
"yy/MMMM/d",
"yy/d/MMM",
"MMM/yy/d",
};
private static readonly string[] ParseDayMonthYearFormats = new string [] {
"yyyy/M/dT",
"M/yyyy/dT",
"yyyy'\u5E74'M'\u6708'd'\u65E5",
"yyyy/MMMM/d",
"yyyy/d/MMM",
"d/MMMM/yyyy",
"MMM/d/yyyy",
"MMMM/yyyy/d",
"d/yyyy/MMM",
"d/MMMM/yy",
"yy/MMM/d",
"d/yy/MMM",
"yy/d/MMM",
"MMM/d/yy",
"MMM/yy/d",
};
private static readonly string[] ParseMonthDayYearFormats = new string [] {
"yyyy/M/dT",
"M/yyyy/dT",
"yyyy'\u5E74'M'\u6708'd'\u65E5",
"yyyy/MMMM/d",
"yyyy/d/MMM",
"MMMM/d/yyyy",
"d/MMM/yyyy",
"MMMM/yyyy/d",
"d/yyyy/MMM",
"MMMM/d/yy",
"MMM/yy/d",
"d/MMM/yy",
"yy/MMM/d",
"d/yy/MMM",
"yy/d/MMM",
};
private static readonly string[] ParseGenericYearMonthDayFormats = new string [] {
"yyyy/M/dT",
"yyyy/M/d",
"M/yyyy/dT",
"M/yyyy/d",
"yyyy'\u5E74'M'\u6708'd'\u65E5",
"yyyy'-'M'-'dT",
"yyyy'-'M'-'d",
};
// Patterns influenced by the MonthDayPattern in DateTimeFormatInfo.
// Note that these patterns cannot be followed by the time.
private static readonly string[] MonthDayShortFormats = new string [] {
"MMMM/d",
"d/MMM",
"yyyy/MMMM",
};
private static readonly string[] DayMonthShortFormats = new string [] {
"d/MMMM",
"MMM/yy",
"yyyy/MMMM",
};
private enum Which
{
Day,
DayYear,
Month,
Year
};
private static readonly int[] daysmonth = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
private static readonly int[] daysmonthleap = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
private static int AbsoluteDays (int year, int month, int day)
{
int[] days;
int temp = 0, m=1 ;
days = (IsLeapYear(year) ? daysmonthleap : daysmonth);
while (m < month)
temp += days[m++];
return ((day-1) + temp + (365* (year-1)) + ((year-1)/4) - ((year-1)/100) + ((year-1)/400));
}
private int FromTicks(Which what)
{
int num400, num100, num4, numyears;
int M =1;
int[] days = daysmonth;
int totaldays = (int) ((encoded & TicksMask) / TimeSpan.TicksPerDay);
num400 = (totaldays / dp400);
totaldays -= num400 * dp400;
num100 = (totaldays / dp100);
if (num100 == 4) // leap
num100 = 3;
totaldays -= (num100 * dp100);
num4 = totaldays / dp4;
totaldays -= (num4 * dp4);
numyears = totaldays / 365 ;
if (numyears == 4) //leap
numyears =3 ;
if (what == Which.Year )
return num400*400 + num100*100 + num4*4 + numyears + 1;
totaldays -= (numyears * 365) ;
if (what == Which.DayYear )
return totaldays + 1;
if ((numyears==3) && ((num100 == 3) || !(num4 == 24)) ) //31 dec leapyear
days = daysmonthleap;
while (totaldays >= days[M])
totaldays -= days[M++];
if (what == Which.Month )
return M;
return totaldays +1;
}
static void InvalidTickValue (long ticks)
{
string msg = Locale.GetText ("Value {0} is outside the valid range [0,{1}].", ticks, MAX_VALUE_TICKS);
throw new ArgumentOutOfRangeException ("ticks", msg);
}
// Constructors
/// <summary>
/// Constructs a DateTime for specified ticks
/// </summary>
///
public DateTime (long ticks)
{
if (ticks < 0 || ticks > MAX_VALUE_TICKS)
InvalidTickValue (ticks);
encoded = ticks;
}
public DateTime (int year, int month, int day)
: this (year, month, day,0,0,0,0) {}
public DateTime (int year, int month, int day, int hour, int minute, int second)
: this (year, month, day, hour, minute, second, 0) {}
public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond)
{
if (year < 1 || year > 9999 ||
month < 1 || month >12 ||
day < 1 || day > DaysInMonth(year, month) ||
hour < 0 || hour > 23 ||
minute < 0 || minute > 59 ||
second < 0 || second > 59 ||
millisecond < 0 || millisecond > 999)
throw new ArgumentOutOfRangeException ("Parameters describe an " +
"unrepresentable DateTime.");
encoded = new TimeSpan (AbsoluteDays (year,month,day), hour, minute, second, millisecond).Ticks;
}
public DateTime (int year, int month, int day, Calendar calendar)
: this (year, month, day, 0, 0, 0, 0, calendar)
{
}
public DateTime (int year, int month, int day, int hour, int minute, int second, Calendar calendar)
: this (year, month, day, hour, minute, second, 0, calendar)
{
}
public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar)
{
if (calendar == null)
throw new ArgumentNullException ("calendar");
encoded = calendar.ToDateTime (year, month, day, hour, minute, second, millisecond).encoded;
}
public DateTime (long ticks, DateTimeKind kind)
{
if (ticks < 0 || ticks > MAX_VALUE_TICKS)
InvalidTickValue (ticks);
if (kind < 0 || kind > DateTimeKind.Local)
throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
encoded = ((long)kind << KindShift) | ticks;
}
public DateTime (int year, int month, int day, int hour, int minute, int second, DateTimeKind kind)
: this (year, month, day, hour, minute, second)
{
if (kind < 0 || kind > DateTimeKind.Local)
throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
encoded |= ((long)kind << KindShift);
}
public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, DateTimeKind kind)
: this (year, month, day, hour, minute, second, millisecond)
{
if (kind < 0 || kind > DateTimeKind.Local)
throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
encoded |= ((long)kind << KindShift);
}
public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar, DateTimeKind kind)
: this (year, month, day, hour, minute, second, millisecond, calendar)
{
if (kind < 0 || kind > DateTimeKind.Local)
throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
encoded |= ((long)kind << KindShift);
}
//
// Not visible, but can be invoked during deserialization
//
DateTime (SerializationInfo info, StreamingContext context)
{
if (info.HasKey ("dateData")){
encoded = (Int64)info.GetUInt64 ("dateData");
} else if (info.HasKey ("ticks")){
encoded = info.GetInt64 ("ticks") & TicksMask;
} else {
encoded = 0;
}
}
/* Properties */
public DateTime Date {
get {
DateTime ret = new DateTime (Year, Month, Day);
ret.encoded |= encoded & KindMask;
return ret;
}
}
public int Month {
get {
return FromTicks (Which.Month);
}
}
public int Day {
get {
return FromTicks (Which.Day);
}
}
public DayOfWeek DayOfWeek {
get {
return (DayOfWeek) ((((encoded & TicksMask)/TimeSpan.TicksPerDay)+1) % 7);
}
}
public int DayOfYear {
get {
return FromTicks (Which.DayYear);
}
}
public TimeSpan TimeOfDay {
get {
return new TimeSpan ((encoded & TicksMask) % TimeSpan.TicksPerDay);
}
}
public int Hour {
get {
return (int) ((encoded & TicksMask) % TimeSpan.TicksPerDay / TimeSpan.TicksPerHour);
}
}
public int Minute {
get {
return (int) ((encoded & TicksMask) % TimeSpan.TicksPerHour / TimeSpan.TicksPerMinute);
}
}
public int Second {
get {
return (int) ((encoded & TicksMask) % TimeSpan.TicksPerMinute / TimeSpan.TicksPerSecond);
}
}
public int Millisecond {
get {
return (int) ((encoded & TicksMask) % TimeSpan.TicksPerSecond / TimeSpan.TicksPerMillisecond);
}
}
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern long GetTimeMonotonic ();
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern long GetNow ();
//
// To reduce the time consumed by DateTime.Now, we keep
// the difference to map the system time into a local
// time into `to_local_time_span', we record the timestamp
// for this in `last_now'
//
static object to_local_time_span_object;
static long last_now;
public static DateTime Now {
get {
long now = GetNow ();
DateTime dt = new DateTime (now);
if (Math.Abs (now - last_now) > TimeSpan.TicksPerMinute){
to_local_time_span_object = TimeZone.CurrentTimeZone.GetLocalTimeDiff (dt);
last_now = now;
}
// This is boxed, so we avoid locking.
DateTime ret = dt + (TimeSpan) to_local_time_span_object;
ret.encoded |= ((long)DateTimeKind.Local << KindShift);
return ret;
}
}
public long Ticks {
get {
return encoded & TicksMask;
}
}
public static DateTime Today {
get {
DateTime now = Now;
DateTime today = new DateTime (now.Year, now.Month, now.Day);
today.encoded |= ((long)DateTimeKind.Local << KindShift);
return today;
}
}
public static DateTime UtcNow {
get {
return new DateTime (GetNow (), DateTimeKind.Utc);
}
}
public int Year {
get {
return FromTicks (Which.Year);
}
}
public DateTimeKind Kind {
get {
return (DateTimeKind) ((ulong)encoded >> KindShift);
}
}
/* methods */
public DateTime Add (TimeSpan value)
{
DateTime ret = AddTicks (value.Ticks);
return ret;
}
public DateTime AddDays (double value)
{
return AddMilliseconds (Math.Round (value * 86400000));
}
public DateTime AddTicks (long value)
{
long res = value + (encoded & TicksMask);
if (res < 0 || res > MAX_VALUE_TICKS)
throw new ArgumentOutOfRangeException();
DateTime ret = new DateTime (res);
ret.encoded |= (encoded & KindMask);
return ret;
}
public DateTime AddHours (double value)
{
return AddMilliseconds (value * 3600000);
}
public DateTime AddMilliseconds (double value)
{
if ((value * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
(value * TimeSpan.TicksPerMillisecond) < long.MinValue) {
throw new ArgumentOutOfRangeException();
}
long msticks = (long) Math.Round (value * TimeSpan.TicksPerMillisecond);
return AddTicks (msticks);
}
// required to match MS implementation for OADate (OLE Automation)
private DateTime AddRoundedMilliseconds (double ms)
{
if ((ms * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
(ms * TimeSpan.TicksPerMillisecond) < long.MinValue) {
throw new ArgumentOutOfRangeException ();
}
long msticks = (long) (ms += ms > 0 ? 0.5 : -0.5) * TimeSpan.TicksPerMillisecond;
return AddTicks (msticks);
}
public DateTime AddMinutes (double value)
{
return AddMilliseconds (value * 60000);
}
public DateTime AddMonths (int months)
{
int day, month, year, maxday ;
DateTime temp ;
day = this.Day;
month = this.Month + (months % 12);
year = this.Year + months/12 ;
if (month < 1)
{
month = 12 + month ;
year -- ;
}
else if (month>12)
{
month = month -12;
year ++;
}
maxday = DaysInMonth(year, month);
if (day > maxday)
day = maxday;
temp = new DateTime (year, month, day);
temp.encoded |= encoded & KindMask;
return temp.Add (this.TimeOfDay);
}
public DateTime AddSeconds (double value)
{
return AddMilliseconds (value * 1000);
}
public DateTime AddYears (int value)
{
return AddMonths (value * 12);
}
public static int Compare (DateTime t1, DateTime t2)
{
long t1t = t1.encoded & TicksMask;
long t2t = t2.encoded & TicksMask;
if (t1t < t2t)
return -1;
else if (t1t > t2t)
return 1;
else
return 0;
}
public int CompareTo (object value)
{
if (value == null)
return 1;
if (!(value is System.DateTime))
throw new ArgumentException (Locale.GetText (
"Value is not a System.DateTime"));
return Compare (this, (DateTime) value);
}
public bool IsDaylightSavingTime ()
{
if ((int)((ulong)encoded >> KindShift) == (int) DateTimeKind.Utc)
return false;
return TimeZone.CurrentTimeZone.IsDaylightSavingTime (this);
}
public int CompareTo (DateTime value)
{
return Compare (this, value);
}
public bool Equals (DateTime value)
{
return (value.encoded & TicksMask) == (encoded & TicksMask);
}
public long ToBinary ()
{
if ((encoded & ((long)DateTimeKind.Local << KindShift)) != 0)
return (long) ((ulong) ToUniversalTime ().Ticks | 0x8000000000000000);
return encoded;
}
public static DateTime FromBinary (long dateData)
{
switch ((ulong)dateData >> KindShift) {
case 1: // Utc
return new DateTime (dateData & TicksMask, DateTimeKind.Utc);
case 0: // Unspecified
return new DateTime (dateData, DateTimeKind.Unspecified);
default: // Local
return new DateTime (dateData & TicksMask, DateTimeKind.Utc).ToLocalTime ();
}
}
public static DateTime SpecifyKind (DateTime value, DateTimeKind kind)
{
return new DateTime (value.Ticks, kind);
}
public static int DaysInMonth (int year, int month)
{
int[] days ;
if (month < 1 || month >12)
throw new ArgumentOutOfRangeException ();
if (year < 1 || year > 9999)
throw new ArgumentOutOfRangeException ();
days = (IsLeapYear(year) ? daysmonthleap : daysmonth);
return days[month];
}
public override bool Equals (object value)
{
if (!(value is System.DateTime))
return false;
return (((DateTime) value).encoded & TicksMask) == (encoded & TicksMask);
}
public static bool Equals (DateTime t1, DateTime t2 )
{
return (t1.encoded & TicksMask) == (t2.encoded & TicksMask);
}
public static DateTime FromFileTime (long fileTime)
{
if (fileTime < 0)
throw new ArgumentOutOfRangeException ("fileTime", "< 0");
return new DateTime (w32file_epoch + fileTime).ToLocalTime ();
}
public static DateTime FromFileTimeUtc (long fileTime)
{
if (fileTime < 0)
throw new ArgumentOutOfRangeException ("fileTime", "< 0");
return new DateTime (w32file_epoch + fileTime, DateTimeKind.Utc);
}
public static DateTime FromOADate (double d)
{
// An OLE Automation date is implemented as a floating-point number
// whose value is the number of days from midnight, 30 December 1899.
// d must be negative 657435.0 through positive 2958466.0.
if ((d <= OAMinValue) || (d >= OAMaxValue))
throw new ArgumentException ("d", "[-657435,2958466]");
DateTime dt = new DateTime (ticks18991230);
if (d < 0.0d) {
Double days = Math.Ceiling (d);
// integer part is the number of days (negative)
dt = dt.AddRoundedMilliseconds (days * 86400000);
// but decimals are the number of hours (in days fractions) and positive
Double hours = (days - d);
dt = dt.AddRoundedMilliseconds (hours * 86400000);
}
else {
dt = dt.AddRoundedMilliseconds (d * 86400000);
}
return dt;
}
public string[] GetDateTimeFormats()
{
return GetDateTimeFormats (CultureInfo.CurrentCulture);
}
public string[] GetDateTimeFormats(char format)
{
if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
throw new FormatException ("Invalid format character.");
string[] result = new string[1];
result[0] = this.ToString(format.ToString());
return result;
}
public string[] GetDateTimeFormats(IFormatProvider provider)
{
DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
// return GetDateTimeFormats (info.GetAllDateTimePatterns ());
var l = new List<string> ();
foreach (char c in "dDgGfFmMrRstTuUyY")
l.AddRange (GetDateTimeFormats (c, info));
return l.ToArray ();
}
public string[] GetDateTimeFormats(char format,IFormatProvider provider )
{
if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
throw new FormatException ("Invalid format character.");
// LAMESPEC: There is NO assurance that 'U' ALWAYS
// euqals to 'F', but since we have to iterate all
// the pattern strings, we cannot just use
// ToString("U", provider) here. I believe that the
// method's behavior cannot be formalized.
bool adjustutc = false;
switch (format) {
case 'U':
// case 'r':
// case 'R':
// case 'u':
adjustutc = true;
break;
}
DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
return GetDateTimeFormats (adjustutc, info.GetAllRawDateTimePatterns (format), info);
}
private string [] GetDateTimeFormats (bool adjustutc, string [] patterns, DateTimeFormatInfo dfi)
{
string [] results = new string [patterns.Length];
DateTime val = adjustutc ? ToUniversalTime () : this;
for (int i = 0; i < results.Length; i++)
results [i] = DateTimeUtils.ToString (val, patterns [i], dfi);
return results;
}
public override int GetHashCode ()
{
return (int) encoded;
}
public TypeCode GetTypeCode ()
{
return TypeCode.DateTime;
}
public static bool IsLeapYear (int year)
{
if (year < 1 || year > 9999)
throw new ArgumentOutOfRangeException ();
return ( (year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ;
}
public static DateTime Parse (string s)
{
return Parse (s, null);
}
public static DateTime Parse (string s, IFormatProvider provider)
{
return Parse (s, provider, DateTimeStyles.AllowWhiteSpaces);
}
public static DateTime Parse (string s, IFormatProvider provider, DateTimeStyles styles)
{
if (s == null)
throw new ArgumentNullException ("s");
DateTime res;
DateTimeOffset dto;
Exception exception = null;
if (!CoreParse (s, provider, styles, out res, out dto, true, ref exception))
throw exception;
return res;
}
const string formatExceptionMessage = "String was not recognized as a valid DateTime.";
internal static bool CoreParse (string s, IFormatProvider provider, DateTimeStyles styles,
out DateTime result, out DateTimeOffset dto, bool setExceptionOnError, ref Exception exception)
{
dto = new DateTimeOffset (0, TimeSpan.Zero);
if (s == null || s.Length == 0) {
if (setExceptionOnError)
exception = new FormatException (formatExceptionMessage);
result = MinValue;
return false;
}
if (provider == null)
provider = CultureInfo.CurrentCulture;
DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
// Try first all the combinations of ParseAllDateFormats & ParseTimeFormats
string[] allDateFormats = YearMonthDayFormats (dfi);
if (allDateFormats == null){
result = MinValue;
return false;
}
bool longYear = false;
for (int i = 0; i < allDateFormats.Length; i++) {
string firstPart = allDateFormats [i];
bool incompleteFormat = false;
if (_DoParse (s, firstPart, "", false, out result, out dto, dfi, styles, true, ref incompleteFormat, ref longYear))
return true;
if (!incompleteFormat)
continue;
for (int j = 0; j < ParseTimeFormats.Length; j++) {
if (_DoParse (s, firstPart, ParseTimeFormats [j], false, out result, out dto, dfi, styles, true, ref incompleteFormat, ref longYear))
return true;
}
}
//
// Month day formats
//
int dayIndex = dfi.MonthDayPattern.IndexOf('d');
int monthIndex = dfi.MonthDayPattern.IndexOf('M');
if (dayIndex == -1 || monthIndex == -1){
result = MinValue;
if (setExceptionOnError)
exception = new FormatException (Locale.GetText("Order of month and date is not defined by {0}", dfi.MonthDayPattern));
return false;
}
bool is_day_before_month = dayIndex < monthIndex;
string[] monthDayFormats = is_day_before_month ? DayMonthShortFormats : MonthDayShortFormats;
for (int i = 0; i < monthDayFormats.Length; i++) {
bool incompleteFormat = false;
if (_DoParse (s, monthDayFormats[i], "", false, out result, out dto, dfi, styles, true, ref incompleteFormat, ref longYear))
return true;
}
for (int j = 0; j < ParseTimeFormats.Length; j++) {
string firstPart = ParseTimeFormats [j];
bool incompleteFormat = false;
if (_DoParse (s, firstPart, "", false, out result, out dto, dfi, styles, false, ref incompleteFormat, ref longYear))
return true;
if (!incompleteFormat)
continue;
for (int i = 0; i < monthDayFormats.Length; i++) {
if (_DoParse (s, firstPart, monthDayFormats [i], false, out result, out dto, dfi, styles, false, ref incompleteFormat, ref longYear))
return true;
}
for (int i = 0; i < allDateFormats.Length; i++) {
string dateFormat = allDateFormats [i];
if (dateFormat[dateFormat.Length - 1] == 'T')
continue; // T formats must be before the time part
if (_DoParse (s, firstPart, dateFormat, false, out result, out dto, dfi, styles, false, ref incompleteFormat, ref longYear))
return true;
}
}
// Try as a last resort all the patterns
if (ParseExact (s, dfi.GetAllDateTimePatternsInternal (), dfi, styles, out result, false, ref longYear, setExceptionOnError, ref exception))
return true;
if (!setExceptionOnError)
return false;
// .NET 2.x does not throw an ArgumentOutOfRangeException, but .NET 1.1 does.
exception = new FormatException (formatExceptionMessage);
return false;
}
public static DateTime ParseExact (string s, string format, IFormatProvider provider)
{
return ParseExact (s, format, provider, DateTimeStyles.None);
}
private static string[] YearMonthDayFormats (DateTimeFormatInfo dfi)
{
int dayIndex = dfi.ShortDatePattern.IndexOf('d');
int monthIndex = dfi.ShortDatePattern.IndexOf('M');
int yearIndex = dfi.ShortDatePattern.IndexOf('y');
if (dayIndex == -1 || monthIndex == -1 || yearIndex == -1)
return ParseGenericYearMonthDayFormats;
if (yearIndex < monthIndex)
if (monthIndex < dayIndex)
return ParseYearMonthDayFormats;
else if (yearIndex < dayIndex)
return ParseYearDayMonthFormats;
else {
// The year cannot be between the date and the month
return ParseGenericYearMonthDayFormats;
}
else if (dayIndex < monthIndex)
return ParseDayMonthYearFormats;
else if (dayIndex < yearIndex)
return ParseMonthDayYearFormats;
else {
// The year cannot be between the month and the date
return ParseGenericYearMonthDayFormats;
}
}
private static int _ParseNumber (string s, int valuePos,
int min_digits,
int digits,
bool leadingzero,
bool sloppy_parsing,
out int num_parsed)
{
int number = 0, i;
if (sloppy_parsing)
leadingzero = false;
if (!leadingzero) {
int real_digits = 0;
for (i = valuePos; i < s.Length && i < digits + valuePos; i++) {
if (!Char.IsDigit (s[i]))
break;
real_digits++;
}
digits = real_digits;
}
if (digits < min_digits) {
num_parsed = -1;
return 0;
}
if (s.Length - valuePos < digits) {
num_parsed = -1;
return 0;
}
for (i = valuePos; i < digits + valuePos; i++) {
char c = s[i];
if (!Char.IsDigit (c)) {
num_parsed = -1;
return 0;
}
number = number * 10 + (byte) (c - '0');
}
num_parsed = digits;
return number;
}
private static int _ParseEnum (string s, int sPos, string[] values, string[] invValues, bool exact, out int num_parsed)
{
// FIXME: I know this is somehow lame code. Probably
// it should iterate all the enum value and return
// the longest match. However right now I don't see
// anything but "1" and "10" - "12" that might match
// two or more values. (They are only abbrev month
// names, so do reverse order search). See bug #80094.
for (int i = values.Length - 1; i >= 0; i--) {
if (!exact && invValues [i].Length > values[i].Length) {
if (invValues [i].Length > 0 && _ParseString (s, sPos, 0, invValues [i], out num_parsed))
return i;
if (values [i].Length > 0 && _ParseString (s, sPos, 0, values [i], out num_parsed))
return i;
}
else {
if (values [i].Length > 0 && _ParseString (s, sPos, 0, values [i], out num_parsed))
return i;
if (!exact && invValues [i].Length > 0 && _ParseString (s, sPos, 0, invValues [i], out num_parsed))
return i;
}
}
num_parsed = -1;
return -1;
}
private static bool _ParseString (string s, int sPos, int maxlength, string value, out int num_parsed)
{
if (maxlength <= 0)
maxlength = value.Length;
if (sPos + maxlength <= s.Length && String.CompareOrdinalCaseInsensitive (s, sPos, value, 0, maxlength) == 0) {
num_parsed = maxlength;
return true;
}
num_parsed = -1;
return false;
}
// Note that in case of Parse (exact == false) we check both for AM/PM
// and the culture spcific AM/PM strings.
private static bool _ParseAmPm(string s,
int valuePos,
int num,
DateTimeFormatInfo dfi,
bool exact,
out int num_parsed,
ref int ampm)
{
num_parsed = -1;
if (ampm != -1)
return false;
if (!IsLetter (s, valuePos)) {
if (dfi.AMDesignator != "")
return false;
if (exact)
ampm = 0;
num_parsed = 0;
return true;
}
DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
if (!exact && _ParseString (s, valuePos, num, invInfo.PMDesignator, out num_parsed) ||
dfi.PMDesignator != "" && _ParseString(s, valuePos, num, dfi.PMDesignator, out num_parsed))
ampm = 1;
else if (!exact && _ParseString (s, valuePos, num, invInfo.AMDesignator, out num_parsed) ||
_ParseString (s, valuePos, num, dfi.AMDesignator, out num_parsed)) {
if (exact || num_parsed != 0)
ampm = 0;
}
else
return false;
return true;
}
// Note that in case of Parse (exact == false) we check both for ':'
// and the culture spcific TimeSperator
private static bool _ParseTimeSeparator (string s, int sPos, DateTimeFormatInfo dfi, bool exact, out int num_parsed)
{
return _ParseString (s, sPos, 0, dfi.TimeSeparator, out num_parsed) ||
!exact && _ParseString (s, sPos, 0, ":", out num_parsed);
}
// Accept any character for DateSeparator, except TimeSeparator,
// a digit or a letter.
// Not documented, but seems to be MS behaviour here. See bug 54047.
private static bool _ParseDateSeparator (string s, int sPos, DateTimeFormatInfo dfi, bool exact, out int num_parsed)
{
num_parsed = -1;
if (exact && s [sPos] != '/')
return false;
if (_ParseTimeSeparator (s, sPos, dfi, exact, out num_parsed) ||
Char.IsDigit (s [sPos]) || Char.IsLetter (s [sPos]))
return(false);
num_parsed = 1;
return true;
}
private static bool IsLetter (string s, int pos)
{
return pos < s.Length && Char.IsLetter (s [pos]);
}
// To implement better DateTime.Parse we use two format strings one
// for Date and one for Time. This allows us to define two different
// arrays of formats for Time and Dates and to combine them more or less
// efficiently. When this mode is used flexibleTwoPartsParsing is true.
private static bool _DoParse (string s,
string firstPart,
string secondPart,
bool exact,
out DateTime result,
out DateTimeOffset dto,
DateTimeFormatInfo dfi,
DateTimeStyles style,
bool firstPartIsDate,
ref bool incompleteFormat,
ref bool longYear)
{
bool useutc = false;
bool use_invariant = false;
bool sloppy_parsing = false;
dto = new DateTimeOffset (0, TimeSpan.Zero);
bool flexibleTwoPartsParsing = !exact && secondPart != null;
incompleteFormat = false;
int valuePos = 0;
string format = firstPart;
bool afterTFormat = false;
DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
if (format.Length == 1)
format = DateTimeUtils.GetStandardPattern (format [0], dfi, out useutc, out use_invariant);
result = new DateTime (0);
if (format == null)
return false;
if (s == null)
return false;
if ((style & DateTimeStyles.AllowLeadingWhite) != 0) {
format = format.TrimStart (null);
s = s.TrimStart (null); // it could be optimized, but will make little good.
}
if ((style & DateTimeStyles.AllowTrailingWhite) != 0) {
format = format.TrimEnd (null);
s = s.TrimEnd (null); // it could be optimized, but will make little good.
}
if (use_invariant)
dfi = invInfo;
if ((style & DateTimeStyles.AllowInnerWhite) != 0)
sloppy_parsing = true;
string chars = format;
int len = format.Length, pos = 0, num = 0;
if (len == 0)
return false;
int day = -1, dayofweek = -1, month = -1, year = -1;
int hour = -1, minute = -1, second = -1;
double fractionalSeconds = -1;
int ampm = -1;
int tzsign = -1, tzoffset = -1, tzoffmin = -1;
bool isFirstPart = true;
bool format_with_24_hours = false;
for (; ; )
{
if (valuePos == s.Length)
break;
int num_parsed = 0;
if (flexibleTwoPartsParsing && pos + num == 0)
{
bool isLetter = IsLetter(s, valuePos);
if (isLetter) {
if (s [valuePos] == 'Z')
num_parsed = 1;
else
_ParseString (s, valuePos, 0, "GMT", out num_parsed);
if (num_parsed > 0 && !IsLetter (s, valuePos + num_parsed)) {
valuePos += num_parsed;
useutc = true;
continue;
}
}
if (!afterTFormat && _ParseAmPm (s, valuePos, 0, dfi, exact, out num_parsed, ref ampm)) {
if (IsLetter (s, valuePos + num_parsed))
ampm = -1;
else if (num_parsed > 0) {
valuePos += num_parsed;
continue;
}
}
if (!afterTFormat && dayofweek == -1 && isLetter) {
dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
if (dayofweek == -1)
dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
if (dayofweek != -1 && !IsLetter (s, valuePos + num_parsed)) {
valuePos += num_parsed;
continue;
}
else
dayofweek = -1;
}
if (char.IsWhiteSpace (s [valuePos]) || s [valuePos] == ',') {
valuePos += 1;
continue;
}
num_parsed = 0;
}
if (pos + num >= len)
{
if (flexibleTwoPartsParsing && num == 0) {
afterTFormat = isFirstPart && firstPart [firstPart.Length - 1] == 'T';
if (!isFirstPart && format == "")
break;
pos = 0;
if (isFirstPart)
format = secondPart;
else
format = "";
chars = format;
len = chars.Length;
isFirstPart = false;
continue;
}
break;
}
bool leading_zeros = true;
if (chars[pos] == '\'') {
num = 1;
while (pos+num < len) {
if (chars[pos+num] == '\'')
break;
if (valuePos == s.Length || s [valuePos] != chars [pos + num])
return false;
valuePos++;
num++;
}
pos += num + 1;
num = 0;
continue;
} else if (chars[pos] == '"') {
num = 1;
while (pos+num < len) {
if (chars[pos+num] == '"')
break;
if (valuePos == s.Length || s [valuePos] != chars[pos+num])
return false;
valuePos++;
num++;
}
pos += num + 1;
num = 0;
continue;
} else if (chars[pos] == '\\') {
pos += num + 1;
num = 0;
if (pos >= len)
return false;
if (s [valuePos] != chars [pos])
return false;
valuePos++;
pos++;
continue;
} else if (chars[pos] == '%') {
pos++;
continue;
} else if (char.IsWhiteSpace (s [valuePos]) ||
s [valuePos] == ',' && (!exact && chars [pos] == '/' || Char.IsWhiteSpace (chars [pos]))) {
valuePos++;
num = 0;
if (exact && (style & DateTimeStyles.AllowInnerWhite) == 0) {
if (!Char.IsWhiteSpace (chars[pos]))
return false;
pos++;
continue;
}
int ws = valuePos;
while (ws < s.Length) {
if (Char.IsWhiteSpace (s [ws]) || s [ws] == ',')
ws++;
else
break;
}
valuePos = ws;
ws = pos;
while (ws < chars.Length) {
if (Char.IsWhiteSpace (chars [ws]) || chars [ws] == ',')
ws++;
else
break;
}
pos = ws;
// A whitespace may match a '/' in the pattern.
if (!exact && pos < chars.Length && chars[pos] == '/')
if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
pos++;
continue;
}
if ((pos+num+1 < len) && (chars[pos+num+1] == chars[pos+num])) {
num++;
continue;
}
switch (chars[pos])
{
case 'd':
if (num < 2 && day != -1 || num >= 2 && dayofweek != -1)
return false;
if (num == 0)
day = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
else if (num == 1)
day = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
else if (num == 2)
dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
else
dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
break;
case 'M':
if (month != -1)
return false;
if (flexibleTwoPartsParsing) {
num_parsed = -1;
if (num == 0 || num == 3)
month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
if (num > 1 && num_parsed == -1)
month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
if (num > 1 && num_parsed == -1)
month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
break;
}
if (num == 0)
month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
else if (num == 1)
month = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
else if (num == 2)
month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
else
month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
break;
case 'y':
if (year != -1)
return false;
if (num == 0) {
year = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
} else if (num < 3) {
year = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
} else {
year = _ParseNumber (s, valuePos, exact ? 4 : 3, 4, false, sloppy_parsing, out num_parsed);
if ((year >= 1000) && (num_parsed == 4) && (!longYear) && (s.Length > 4 + valuePos)) {
int np = 0;
int ly = _ParseNumber (s, valuePos, 5, 5, false, sloppy_parsing, out np);
longYear = (ly > 9999);
}
num = 3;
}
//FIXME: We should do use dfi.Calendat.TwoDigitYearMax
if (num_parsed <= 2)
year += (year < 30) ? 2000 : 1900;
break;
case 'h':
if (hour != -1)
return false;
if (num == 0)
hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
else
hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
if (hour > 12)
return false;
if (hour == 12)
hour = 0;
break;
case 'H':
if (hour != -1 || !flexibleTwoPartsParsing && ampm >= 0)
return false;
if (num == 0)
hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
else
hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
if (hour >= 24)
return false;
format_with_24_hours = true;
break;
case 'm':
if (minute != -1)
return false;
if (num == 0)
minute = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
else
minute = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
if (minute >= 60)
return false;
break;
case 's':
if (second != -1)
return false;
if (num == 0)
second = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
else
second = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
if (second >= 60)
return false;
break;
case 'F':
leading_zeros = false;
goto case 'f';
case 'f':
if (num > 6 || fractionalSeconds != -1)
return false;
double decimalNumber = (double) _ParseNumber (s, valuePos, 0, num+1, leading_zeros, sloppy_parsing, out num_parsed);
if (num_parsed == -1)
return false;
fractionalSeconds = decimalNumber / Math.Pow(10.0, num_parsed);
break;
case 't':
if (!_ParseAmPm (s, valuePos, num > 0 ? 0 : 1, dfi, exact, out num_parsed, ref ampm))
return false;
break;
case 'z':
if (tzsign != -1)
return false;
if (s [valuePos] == '+')
tzsign = 0;
else if (s [valuePos] == '-')
tzsign = 1;
else
return false;
valuePos++;
if (num == 0)
tzoffset = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
else if (num == 1)
tzoffset = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
else {
tzoffset = _ParseNumber (s, valuePos, 1, 2, true, /*sloppy_parsing*/true, out num_parsed);
valuePos += num_parsed;
if (num_parsed < 0)
return false;
num_parsed = 0;
if (valuePos < s.Length && Char.IsDigit (s [valuePos]) ||
_ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed)) {
valuePos += num_parsed;
tzoffmin = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
if (num_parsed < 0)
return false;
}
else if (!flexibleTwoPartsParsing)
return false;
else
num_parsed = 0;
}
break;
case 'K':
if (s [valuePos] == 'Z') {
valuePos++;
useutc = true;
}
else if (s [valuePos] == '+' || s [valuePos] == '-') {
if (tzsign != -1)
return false;
if (s [valuePos] == '+')
tzsign = 0;
else if (s [valuePos] == '-')
tzsign = 1;
valuePos++;
// zzz
tzoffset = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
valuePos += num_parsed;
if (num_parsed < 0)
return false;
if (Char.IsDigit (s [valuePos]))
num_parsed = 0;
else if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
return false;
valuePos += num_parsed;
tzoffmin = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
num = 2;
if (num_parsed < 0)
return false;
}
break;
// LAMESPEC: This should be part of UTCpattern
// string and thus should not be considered here.
//
// Note that 'Z' is not defined as a pattern
// character. Keep it for X509 certificate
// verification. Also, "Z" != "'Z'" under MS.NET
// ("'Z'" is just literal; handled above)
case 'Z':
if (s [valuePos] != 'Z')
return false;
num = 0;
num_parsed = 1;
useutc = true;
break;
case 'G':
if (s [valuePos] != 'G')
return false;
if ((pos + 2 < len) && (valuePos + 2 < s.Length) &&
(chars [pos + 1] == 'M') && (s[valuePos + 1] == 'M') &&
(chars [pos + 2] == 'T') && (s[valuePos + 2] == 'T'))
{
useutc = true;
num = 2;
num_parsed = 3;
}
else {
num = 0;
num_parsed = 1;
}
break;
case ':':
if (!_ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed))
return false;
break;
case '/':
if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
return false;
num = 0;
break;
case '.':
if (s[valuePos] == '.') {
num = 0;
num_parsed = 1;
break;
}
// '.FFF....' can be mapped to nothing
if (pos + 1 < len && chars[pos + 1] == 'F') {
++pos;
while (pos + 1 < len && chars[pos + 1] == 'F') {
++pos;
}
num = 0;
num_parsed = 0;
break;
}
return false;
default:
if (s [valuePos] != chars [pos])
return false;
num = 0;
num_parsed = 1;
break;
}
if (num_parsed < 0)
return false;
valuePos += num_parsed;
if (!exact && !flexibleTwoPartsParsing) {
switch (chars [pos]) {
case 'm':
case 's':
case 'F':
case 'f':
case 'z':
if (s.Length > valuePos && s [valuePos] == 'Z' &&
(pos + 1 == chars.Length || chars [pos + 1] != 'Z')) {
useutc = true;
valuePos++;
}
break;
}
}
pos = pos + num + 1;
num = 0;
}
if (pos + 1 < len && chars [pos] == '.' && chars [pos + 1] == 'F') {
pos++;
while (pos < len && chars [pos] == 'F') // '.FFF....' can be mapped to nothing. See bug #444103
pos++;
}
while (pos < len && chars [pos] == 'K') // 'K' can be mapped to nothing
pos++;
if (pos < len)
return false;
if (s.Length > valuePos) // extraneous tail.
{
if (valuePos == 0)
return false;
if (Char.IsDigit (s [valuePos]) && Char.IsDigit (s [valuePos - 1]))
return false;
if (Char.IsLetter (s [valuePos]) && Char.IsLetter (s [valuePos - 1]))
return false;
incompleteFormat = true;
return false;
}
if (hour == -1)
hour = 0;
if (minute == -1)
minute = 0;
if (second == -1)
second = 0;
if (fractionalSeconds == -1)
fractionalSeconds = 0;
// If no date was given
if ((day == -1) && (month == -1) && (year == -1)) {
if ((style & DateTimeStyles.NoCurrentDateDefault) != 0) {
day = 1;
month = 1;
year = 1;
} else {
day = DateTime.Today.Day;
month = DateTime.Today.Month;
year = DateTime.Today.Year;
}
}
if (day == -1)
day = 1;
if (month == -1)
month = 1;
if (year == -1) {
if ((style & DateTimeStyles.NoCurrentDateDefault) != 0)
year = 1;
else
year = DateTime.Today.Year;
}
if (ampm == 0) { // AM designator
if (hour >= 12 && format_with_24_hours && exact)
return false;
if (hour == 12)
hour = 0;
} else if (ampm == 1) { // PM designator
if (hour < 12) {
if (format_with_24_hours && exact)
return false;
hour += 12;
}
}
try {
result = dfi.Calendar.ToDateTime (year, month, day, hour, minute, second, 0);
} catch {
return false;
}
result = result.AddSeconds(fractionalSeconds);
if (dayofweek != -1 && dayofweek != (int) result.DayOfWeek)
return false;
if (tzsign == -1) {
if (result != DateTime.MinValue) {
try {
dto = new DateTimeOffset (result);
} catch { } // We handle this error in DateTimeOffset.Parse
}
} else {
if (tzoffmin == -1)
tzoffmin = 0;
if (tzoffset == -1)
tzoffset = 0;
if (tzsign == 1) {
tzoffset = -tzoffset;
tzoffmin = -tzoffmin;
}
try {
dto = new DateTimeOffset (result, new TimeSpan (tzoffset, tzoffmin, 0));
} catch {} // We handle this error in DateTimeOffset.Parse
}
bool adjustToUniversal = (style & DateTimeStyles.AdjustToUniversal) != 0;
if (tzsign != -1) {
long newticks = (result - dto.Offset).Ticks;
if (newticks < 0)
newticks += TimeSpan.TicksPerDay;
result = new DateTime (newticks, DateTimeKind.Utc);
if ((style & DateTimeStyles.RoundtripKind) != 0)
result = result.ToLocalTime ();
} else if (useutc || ((style & DateTimeStyles.AssumeUniversal) != 0))
result.encoded |= ((long) DateTimeKind.Utc << KindShift);
else if ((style & DateTimeStyles.AssumeLocal) != 0)
result.encoded |= ((long) DateTimeKind.Local << KindShift);
bool adjustToLocal = !adjustToUniversal && (style & DateTimeStyles.RoundtripKind) == 0;
if ((DateTimeKind)(((ulong) result.encoded >> KindShift)) != DateTimeKind.Unspecified) {
if (adjustToUniversal)
result = result.ToUniversalTime ();
else if (adjustToLocal)
result = result.ToLocalTime ();
}
return true;
}
public static DateTime ParseExact (string s, string format,
IFormatProvider provider, DateTimeStyles style)
{
if (format == null)
throw new ArgumentNullException ("format");
string [] formats = new string [1];
formats[0] = format;
return ParseExact (s, formats, provider, style);
}
public static DateTime ParseExact (string s, string[] formats,
IFormatProvider provider,
DateTimeStyles style)
{
DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
CheckStyle (style);
if (s == null)
throw new ArgumentNullException ("s");
if (formats == null)
throw new ArgumentNullException ("formats");
if (formats.Length == 0)
throw new FormatException ("Format specifier was invalid.");
DateTime result;
bool longYear = false;
Exception e = null;
if (!ParseExact (s, formats, dfi, style, out result, true, ref longYear, true, ref e))
throw e;
return result;
}
private static void CheckStyle (DateTimeStyles style)
{
if ( (style & DateTimeStyles.RoundtripKind) != 0)
{
if ((style & DateTimeStyles.AdjustToUniversal) != 0 || (style & DateTimeStyles.AssumeLocal) != 0 ||
(style & DateTimeStyles.AssumeUniversal) != 0)
throw new ArgumentException ("The DateTimeStyles value RoundtripKind cannot be used with the values AssumeLocal, AssumeUniversal or AdjustToUniversal.", "style");
}
if ((style & DateTimeStyles.AssumeUniversal) != 0 && (style & DateTimeStyles.AssumeLocal) != 0)
throw new ArgumentException ("The DateTimeStyles values AssumeLocal and AssumeUniversal cannot be used together.", "style");
}
public static bool TryParse (string s, out DateTime result)
{
if (s != null){
try {
Exception exception = null;
DateTimeOffset dto;
return CoreParse (s, null, DateTimeStyles.AllowWhiteSpaces, out result, out dto, false, ref exception);
} catch { }
}
result = MinValue;
return false;
}
public static bool TryParse (string s, IFormatProvider provider, DateTimeStyles styles, out DateTime result)
{
if (s != null){
try {
Exception exception = null;
DateTimeOffset dto;
return CoreParse (s, provider, styles, out result, out dto, false, ref exception);
} catch {}
}
result = MinValue;
return false;
}
public static bool TryParseExact (string s, string format,
IFormatProvider provider,
DateTimeStyles style,
out DateTime result)
{
string[] formats;
formats = new string [1];
formats[0] = format;
return TryParseExact (s, formats, provider, style, out result);
}
public static bool TryParseExact (string s, string[] formats,
IFormatProvider provider,
DateTimeStyles style,
out DateTime result)
{
try {
DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
bool longYear = false;
Exception e = null;
return ParseExact (s, formats, dfi, style, out result, true, ref longYear, false, ref e);
} catch {
result = MinValue;
return false;
}
}
private static bool ParseExact (string s, string [] formats,
DateTimeFormatInfo dfi, DateTimeStyles style, out DateTime ret,
bool exact, ref bool longYear,
bool setExceptionOnError, ref Exception exception)
{
int i;
bool incompleteFormat = false;
for (i = 0; i < formats.Length; i++)
{
DateTime result;
string format = formats[i];
if (format == null || format == String.Empty)
break;
DateTimeOffset dto;
if (_DoParse (s, formats[i], null, exact, out result, out dto, dfi, style, false, ref incompleteFormat, ref longYear)) {
ret = result;
return true;
}
}
if (setExceptionOnError)
exception = new FormatException ("Invalid format string");
ret = DateTime.MinValue;
return false;
}
public TimeSpan Subtract (DateTime value)
{
return new TimeSpan (Ticks) - new TimeSpan (value.Ticks);
}
public DateTime Subtract(TimeSpan value)
{
long newticks;
newticks = Ticks - value.Ticks;
if (newticks < 0 || newticks > MAX_VALUE_TICKS)
throw new ArgumentOutOfRangeException ();
DateTime ret = new DateTime (newticks);
ret.encoded |= (encoded & KindMask);
return ret;
}
public long ToFileTime()
{
DateTime universalTime = ToUniversalTime();
if (universalTime.Ticks < w32file_epoch) {
throw new ArgumentOutOfRangeException("file time is not valid");
}
return(universalTime.Ticks - w32file_epoch);
}
public long ToFileTimeUtc()
{
if (Kind == DateTimeKind.Local)
return ToFileTime ();
if (Ticks < w32file_epoch) {
throw new ArgumentOutOfRangeException("file time is not valid");
}
return (Ticks - w32file_epoch);
}
public string ToLongDateString()
{
return ToString ("D");
}
public string ToLongTimeString()
{
return ToString ("T");
}
public double ToOADate ()
{
long t = this.Ticks;
// uninitialized DateTime case
if (t == 0)
return 0;
// we can't reach minimum value
if (t < 31242239136000000)
return OAMinValue + 0.001;
TimeSpan ts = new TimeSpan (this.Ticks - ticks18991230);
double result = ts.TotalDays;
// t < 0 (where 599264352000000000 == 0.0d for OA)
if (t < 599264352000000000) {
// negative days (int) but decimals are positive
double d = Math.Ceiling (result);
result = d - 2 - (result - d);
}
else {
// we can't reach maximum value
if (result >= OAMaxValue)
result = OAMaxValue - 0.00000001d;
}
return result;
}
public string ToShortDateString()
{
return ToString ("d");
}
public string ToShortTimeString()
{
return ToString ("t");
}
public override string ToString ()
{
return ToString ("G", null);
}
public string ToString (IFormatProvider provider)
{
return ToString (null, provider);
}
public string ToString (string format)
{
return ToString (format, null);
}
public string ToString (string format, IFormatProvider provider)
{
DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
if (format == null || format == String.Empty)
format = "G";
if (format.Length == 1) {
char fchar = format [0];
bool use_invariant, useutc;
format = DateTimeUtils.GetStandardPattern (fchar, dfi, out useutc, out use_invariant);
if (fchar == 'U')
return DateTimeUtils.ToString (ToUniversalTime (), format, dfi);
// return ToUniversalTime()._ToString (format, dfi);
if (format == null)
throw new FormatException ("format is not one of the format specifier characters defined for DateTimeFormatInfo");
if (use_invariant)
dfi = DateTimeFormatInfo.InvariantInfo;
}
// Don't convert UTC value. It just adds 'Z' for
// 'u' format, for the same ticks.
return DateTimeUtils.ToString (this, format, dfi);
}
public DateTime ToLocalTime ()
{
return TimeZone.CurrentTimeZone.ToLocalTime (this);
}
public DateTime ToUniversalTime()
{
return TimeZone.CurrentTimeZone.ToUniversalTime (this);
}
/* OPERATORS */
public static DateTime operator +(DateTime d, TimeSpan t)
{
try {
long res = checked ((d.encoded & TicksMask) + t.Ticks);
if (res < 0 || res > MAX_VALUE_TICKS){
throw new ArgumentOutOfRangeException ();
}
return new DateTime (res, d.Kind);
} catch (OverflowException){
throw new ArgumentOutOfRangeException ();
}
}
public static bool operator ==(DateTime d1, DateTime d2)
{
return ((d1.encoded & TicksMask) == (d2.encoded & TicksMask));
}
public static bool operator >(DateTime t1,DateTime t2)
{
return ((t1.encoded & TicksMask) > (t2.encoded & TicksMask));
}
public static bool operator >=(DateTime t1,DateTime t2)
{
return ((t1.encoded & TicksMask) >= (t2.encoded & TicksMask));
}
public static bool operator !=(DateTime d1, DateTime d2)
{
return ((d1.encoded & TicksMask) != (d2.encoded & TicksMask));
}
public static bool operator <(DateTime t1, DateTime t2)
{
return ((t1.encoded & TicksMask) < (t2.encoded & TicksMask));
}
public static bool operator <=(DateTime t1, DateTime t2)
{
return ((t1.encoded & TicksMask) <= (t2.encoded & TicksMask));
}
public static TimeSpan operator -(DateTime d1, DateTime d2)
{
return new TimeSpan ((d1.encoded & TicksMask) - (d2.encoded & TicksMask));
}
public static DateTime operator -(DateTime d, TimeSpan t)
{
try {
long res = checked ((d.encoded & TicksMask) - t.Ticks);
if (res < 0 || res > MAX_VALUE_TICKS)
throw new ArgumentOutOfRangeException ();
return new DateTime (res, d.Kind);
} catch (OverflowException){
throw new ArgumentOutOfRangeException ();
}
}
bool IConvertible.ToBoolean (IFormatProvider provider)
{
throw new InvalidCastException();
}
byte IConvertible.ToByte (IFormatProvider provider)
{
throw new InvalidCastException();
}
char IConvertible.ToChar (IFormatProvider provider)
{
throw new InvalidCastException();
}
System.DateTime IConvertible.ToDateTime (IFormatProvider provider)
{
return this;
}
decimal IConvertible.ToDecimal (IFormatProvider provider)
{
throw new InvalidCastException();
}
double IConvertible.ToDouble (IFormatProvider provider)
{
throw new InvalidCastException();
}
Int16 IConvertible.ToInt16 (IFormatProvider provider)
{
throw new InvalidCastException();
}
Int32 IConvertible.ToInt32 (IFormatProvider provider)
{
throw new InvalidCastException();
}
Int64 IConvertible.ToInt64 (IFormatProvider provider)
{
throw new InvalidCastException();
}
SByte IConvertible.ToSByte (IFormatProvider provider)
{
throw new InvalidCastException();
}
Single IConvertible.ToSingle (IFormatProvider provider)
{
throw new InvalidCastException();
}
object IConvertible.ToType (Type targetType, IFormatProvider provider)
{
if (targetType == null)
throw new ArgumentNullException ("targetType");
if (targetType == typeof (DateTime))
return this;
else if (targetType == typeof (String))
return this.ToString (provider);
else if (targetType == typeof (Object))
return this;
else
throw new InvalidCastException();
}
UInt16 IConvertible.ToUInt16 (IFormatProvider provider)
{
throw new InvalidCastException();
}
UInt32 IConvertible.ToUInt32 (IFormatProvider provider)
{
throw new InvalidCastException();
}
UInt64 IConvertible.ToUInt64 (IFormatProvider provider)
{
throw new InvalidCastException();
}
void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
{
long t = Ticks;
info.AddValue ("ticks", t);
// This is the new .NET format, encodes the kind on the top bits
info.AddValue ("dateData", (UInt64)encoded);
}
#if MONOTOUCH
static DateTime () {
if (MonoTouchAOTHelper.FalseFlag) {
var comparer = new System.Collections.Generic.GenericComparer <DateTime> ();
var eqcomparer = new System.Collections.Generic.GenericEqualityComparer <DateTime> ();
}
}
#endif
}
}