2199 lines
59 KiB
C#
2199 lines
59 KiB
C#
//
|
|
// 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
|
|
}
|
|
}
|