[Mono-list] [PATCH] DateTime.cs
Martin Baulig
martin@gnome.org
23 Feb 2002 23:02:57 +0100
--=-=-=
Hi,
here's a larger patch for DateTime.cs which makes it use TimeSpan for its `ticks'
field and also implements a lot of the missing methods:
=====
2002-02-23 Martin Baulig <martin@gnome.org>
* DateTime.cs: Change type of the `ticks' field to System.TimeSpan,
cleaned up constructors, added support for UTC/local time, implemented
a lot of the missing methods, added support for printing.
=====
--=-=-=
Content-Type: application/octet-stream
Content-Disposition: attachment; filename=DateTime.cs
//
// System.DateTime.cs
//
// author:
// Marcel Narings (marcel@narings.nl)
//
// (C) 2001 Marcel Narings
using System;
using System.Globalization;
using System.Runtime.CompilerServices;
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>
///
public struct DateTime : IComparable , IFormattable , IConvertible
{
private TimeSpan ticks;
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;
//
// The UnixEpoch, it begins on Jan 1, 1970 at 0:0:0, expressed
// in Ticks
//
internal const long UnixEpoch = 621355968000000000L;
public static readonly DateTime MaxValue = new DateTime (false,TimeSpan.MaxValue);
public static readonly DateTime MinValue = new DateTime (false,TimeSpan.MinValue);
private enum Which
{
Day,
DayYear,
Month,
Year
};
private static int[] daysmonth = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
private static 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 = this.ticks.Days;
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;
}
// Constructors
/// <summary>
/// Constructs a DateTime for specified ticks
/// </summary>
///
public DateTime (long newticks)
// `local' must default to false here to avoid
// a recursion loop.
: this (false, newticks) {}
internal DateTime (bool local, long newticks)
: this (true, new TimeSpan (newticks))
{
if (local) {
TimeZone tz = TimeZone.CurrentTimeZone;
TimeSpan offset = tz.GetUtcOffset (this);
ticks = ticks + offset;
}
}
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 )
throw new ArgumentOutOfRangeException() ;
ticks = new TimeSpan (AbsoluteDays(year,month,day), hour, minute, second, millisecond);
}
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)
: this(year, month, day, hour, minute, second, millisecond)
{
if ( calendar == null)
throw new ArgumentNullException();
}
internal DateTime (bool check, TimeSpan value)
{
if (check && (value.Ticks < MinValue.Ticks || value.Ticks > MaxValue.Ticks))
throw new ArgumentOutOfRangeException ();
ticks = value;
}
/* Properties */
public DateTime Date
{
get
{
return new DateTime (Year, Month, Day);
}
}
public int Month
{
get
{
return FromTicks(Which.Month);
}
}
public int Day
{
get
{
return FromTicks(Which.Day);
}
}
public DayOfWeek DayOfWeek
{
get
{
return ( (DayOfWeek) ((ticks.Days+1) % 7) );
}
}
public int DayOfYear
{
get
{
return FromTicks(Which.DayYear);
}
}
public TimeSpan TimeOfDay
{
get
{
return new TimeSpan(ticks.Ticks % TimeSpan.TicksPerDay );
}
}
public int Hour
{
get
{
return ticks.Hours;
}
}
public int Minute
{
get
{
return ticks.Minutes;
}
}
public int Second
{
get
{
return ticks.Seconds;
}
}
public int Millisecond
{
get
{
return ticks.Milliseconds;
}
}
[MethodImplAttribute(MethodImplOptions.InternalCall)]
private static extern long GetNow ();
public static DateTime Now
{
get
{
return new DateTime (true, GetNow ());
}
}
public long Ticks
{
get
{
return ticks.Ticks;
}
}
public static DateTime Today
{
get
{
return new DateTime (true, (GetNow () / TimeSpan.TicksPerDay) * TimeSpan.TicksPerDay);
}
}
public static DateTime UtcNow
{
get {
return new DateTime (GetNow ());
}
}
public int Year
{
get
{
return FromTicks(Which.Year);
}
}
/* methods */
public DateTime Add (TimeSpan ts)
{
return new DateTime (true, ticks) + ts;
}
public DateTime AddDays (double days)
{
return AddMilliseconds (days * 86400000);
}
public DateTime AddTicks (long t)
{
return Add (new TimeSpan (t));
}
public DateTime AddHours (double hours)
{
return AddMilliseconds (hours * 3600000);
}
public DateTime AddMilliseconds (double ms)
{
long msticks;
msticks = (long) (ms += ms > 0 ? 0.5 : -0.5) * TimeSpan.TicksPerMillisecond ;
return AddTicks (msticks);
}
public DateTime AddMinutes (double minutes)
{
return AddMilliseconds (minutes * 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);
return temp.Add (this.TimeOfDay);
}
public DateTime AddSeconds (double seconds)
{
return AddMilliseconds (seconds*1000);
}
public DateTime AddYears (int years )
{
return AddMonths(years * 12);
}
public static int Compare (DateTime t1, DateTime t2)
{
if (t1.ticks < t2.ticks)
return -1;
else if (t1.ticks > t2.ticks)
return 1;
else
return 0;
}
public int CompareTo (object v)
{
if ( v == null)
return 1;
if (!(v is System.DateTime))
throw new ArgumentException (Locale.GetText (
"Value is not a System.DateTime"));
return Compare (this, (DateTime) v);
}
public static int DaysInMonth (int year, int month)
{
int[] days ;
if (month < 1 || month >12)
throw new ArgumentOutOfRangeException ();
days = (IsLeapYear(year) ? daysmonthleap : daysmonth);
return days[month];
}
public override bool Equals (object o)
{
if (!(o is System.DateTime))
return false;
return ((DateTime) o).ticks == ticks;
}
public static bool Equals (DateTime t1, DateTime t2 )
{
return (t1.ticks == t2.ticks );
}
public static DateTime FromFileTime (long fileTime)
{
return new DateTime (w32file_epoch + fileTime);
}
// TODO: Implement me.
[MonoTODO]
public static DateTime FromOADate (double d)
{
return new DateTime(0);
}
// TODO: Implement me.
[MonoTODO]
public string[] GetDateTimeFormats()
{
return null;
}
//TODO: implement me
[MonoTODO]
public string[] GetDateTimeFormats(char format)
{
return null;
}
// TODO: implement me
[MonoTODO]
public string[] GetDateTimeFormats(IFormatProvider provider)
{
return null;
}
//TODO: implement me
[MonoTODO]
public string[] GetDateTimeFormats(char format,IFormatProvider provider )
{
return null;
}
public override int GetHashCode ()
{
return (int) ticks.Ticks;
}
public TypeCode GetTypeCode ()
{
return TypeCode.DateTime;
}
public static bool IsLeapYear (int year)
{
return ( (year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ;
}
[MonoTODO]
public static DateTime Parse (string s)
{
// TODO: Implement me
return new DateTime (0);
}
[MonoTODO]
public static DateTime Parse (string s, IFormatProvider fp)
{
// TODO: Implement me
return new DateTime (0);
}
[MonoTODO]
public static DateTime Parse (string s, NumberStyles style, IFormatProvider fp)
{
// TODO: Implement me
return new DateTime (0);
}
[MonoTODO]
public static DateTime ParseExact(string s, string format, IFormatProvider provider )
{
// TODO: Implement me
return new DateTime (0);
}
[MonoTODO]
public static DateTime ParseExact(string s, string format, IFormatProvider provider, DateTimeStyles style )
{
// TODO: Implement me
return new DateTime (0);
}
[MonoTODO]
public static DateTime ParseExact( string s, string[] formats, IFormatProvider provider,
DateTimeStyles style )
{
// TODO: Implement me
return new DateTime (0);
}
public TimeSpan Subtract(DateTime dt)
{
return new TimeSpan(ticks.Ticks) - dt.ticks;
}
public DateTime Subtract(TimeSpan ts)
{
TimeSpan newticks;
newticks = (new TimeSpan (ticks.Ticks)) - ts;
return new DateTime(true,newticks);
}
public long ToFileTime()
{
if(ticks.Ticks < w32file_epoch) {
throw new ArgumentOutOfRangeException("file time is not valid");
}
return(ticks.Ticks - w32file_epoch);
}
public string ToLongDateString()
{
return ToString ("D");
}
public string ToLongTimeString()
{
return ToString ("T");
}
[MonoTODO]
public double ToOADate()
{
// TODO implement me
return 0;
}
public string ToShortDateString()
{
return ToString ("d");
}
public string ToShortTimeString()
{
return ToString ("t");
}
public override string ToString ()
{
return ToString (null, null);
}
public string ToString (IFormatProvider fp)
{
return ToString (null, fp);
}
public string ToString (string format)
{
return ToString (format, null);
}
[MonoTODO]
public string ToString (string format, IFormatProvider fp)
{
DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance(fp);
if (format == null)
{
format = dfi.FullDateTimePattern;
}
String str = null, result = null;
char[] chars = format.ToCharArray ();
int len = format.Length, pos = 0, num = 0;
bool use_utc = false;
if (len == 1)
{
switch (chars[0])
{
case 'd':
format = dfi.ShortDatePattern;
break;
case 'D':
format = dfi.LongDatePattern;
break;
case 'f':
String f1 = dfi.LongDatePattern;
String f2 = dfi.ShortTimePattern;
format = String.Concat (f1, " ");
format = String.Concat (format, f2);
break;
case 'F':
format = dfi.FullDateTimePattern;
break;
case 'g':
String f1 = dfi.ShortDatePattern;
String f2 = dfi.ShortTimePattern;
format = String.Concat (f1, " ");
format = String.Concat (format, f2);
break;
case 'G':
String f1 = dfi.ShortDatePattern;
String f2 = dfi.LongTimePattern;
format = String.Concat (f1, " ");
format = String.Concat (format, f2);
break;
case 'm':
case 'M':
format = dfi.MonthDayPattern;
break;
case 'r':
case 'R':
format = dfi.RFC1123Pattern;
break;
case 's':
format = dfi.SortableDateTimePattern;
break;
case 't':
format = dfi.ShortTimePattern;
break;
case 'T':
format = dfi.LongTimePattern;
break;
case 'u':
format = dfi.UniversalSortableDateTimePattern;
useutc = true;
break;
case 'U':
String f1 = dfi.LongDatePattern;
String f2 = dfi.LongTimePattern;
format = String.Concat (f1, " ");
format = String.Concat (format, f2);
useutc = true;
break;
case 'y':
case 'Y':
format = dfi.YearMonthPattern;
break;
}
Console.Write ("Pattern: ");
Console.WriteLine (format);
chars = format.ToCharArray ();
len = format.Length;
}
// FIXME: Do printing in UTC if requested.
// FIXME: Do time zone and era.
while (pos+num < len)
{
if (chars[pos] == '\'')
{
num = 1;
while (pos+num < len)
{
if (chars[pos+num] == '\'')
break;
result = String.Concat (result, chars[pos+num]);
num = num + 1;
}
if (pos+num > len)
throw new FormatException (Locale.GetText ("The specified format is invalid"));
pos = pos + num + 1;
num = 0;
continue;
}
else if (chars[pos] == '\\')
{
if (pos+1 >= len)
throw new FormatException (Locale.GetText ("The specified format is invalid"));
result = String.Concat (result, chars[pos]);
pos = pos + 1;
continue;
}
else if (chars[pos] == '%')
{
pos = pos + 1;
continue;
}
if ((pos+num+1 < len) && (chars[pos+num+1] == chars[pos+num]))
{
num = num + 1;
continue;
}
switch (chars[pos])
{
case 'd':
if (num == 0)
str = Day.ToString ("d");
else if (num == 1)
str = Day.ToString ("d02");
else if (num == 2)
str = dfi.GetAbbreviatedDayName (DayOfWeek);
else
{
str = dfi.GetDayName (DayOfWeek);
num = 3;
}
break;
case 'M':
if (num == 0)
str = Month.ToString ("d");
else if (num == 1)
str = Month.ToString ("d02");
else if (num == 2)
str = dfi.GetAbbreviatedMonthName (Month);
else
{
str = dfi.GetMonthName (Month);
num = 3;
}
break;
case 'y':
if (num == 0)
{
int shortyear = Year % 100;
str = shortyear.ToString ("d");
}
else if (num < 3)
{
int shortyear = Year % 100;
str = shortyear.ToString ("d02");
num = 1;
}
else
{
str = Year.ToString ("d");
num = 3;
}
break;
case 'g':
// FIXME
break;
case 'f':
num = Math.Min (num, 6);
long ms = (long) Millisecond;
long exp = 10;
for (int i = 0; i < num; i++)
exp = exp * 10;
long maxexp = TimeSpan.TicksPerMillisecond;
exp = Math.Min (exp, maxexp);
ms = ms * exp / maxexp;
String prec = (num+1).ToString ("d02");
str = ms.ToString (String.Concat ("d", prec));
break;
case 'h':
if (num == 0)
{
int shorthour = Hour % 12;
str = shorthour.ToString ("d");
}
else
{
int shorthour = Hour % 12;
str = shorthour.ToString ("d02");
num = 1;
}
break;
case 'H':
if (num == 0)
str = Hour.ToString ("d");
else
{
str = Hour.ToString ("d02");
num = 1;
}
break;
case 'm':
if (num == 0)
str = Minute.ToString ("d");
else
{
str = Minute.ToString ("d02");
num = 1;
}
break;
case 's':
if (num == 0)
str = Second.ToString ("d");
else
{
str = Second.ToString ("d02");
num = 1;
}
break;
case 't':
if (Hour < 12)
str = dfi.AMDesignator;
else
str = dfi.PMDesignator;
if (num == 0)
str = str.Substring (0,1);
else
num = 1;
break;
case 'z':
// FIXME;
break;
case ':':
str = dfi.TimeSeparator;
num = 1;
break;
case '/':
str = dfi.DateSeparator;
num = 1;
break;
default:
str = String.Concat (chars [pos]);
num = 0;
break;
}
result = String.Concat (result, str);
pos = pos + num + 1;
num = 0;
}
return String.Format (dfi, "{0}", result);
}
public DateTime ToLocalTime()
{
TimeZone tz = TimeZone.CurrentTimeZone;
TimeSpan offset = tz.GetUtcOffset (this);
return new DateTime (true, ticks + offset);
}
public DateTime ToUniversalTime()
{
TimeZone tz = TimeZone.CurrentTimeZone;
TimeSpan offset = tz.GetUtcOffset (this);
return new DateTime (true, ticks - offset);
}
/* OPERATORS */
public static DateTime operator +(DateTime d, TimeSpan t)
{
return new DateTime (true, d.ticks + t);
}
public static bool operator ==(DateTime d1, DateTime d2)
{
return (d1.ticks == d2.ticks);
}
public static bool operator >(DateTime t1,DateTime t2)
{
return (t1.ticks > t2.ticks);
}
public static bool operator >=(DateTime t1,DateTime t2)
{
return (t1.ticks >= t2.ticks);
}
public static bool operator !=(DateTime d1, DateTime d2)
{
return (d1.ticks != d2.ticks);
}
public static bool operator <(DateTime t1, DateTime t2)
{
return (t1.ticks < t2.ticks );
}
public static bool operator <=(DateTime t1,DateTime t2)
{
return (t1.ticks <= t2.ticks);
}
public static TimeSpan operator -(DateTime d1,DateTime d2)
{
return new TimeSpan((d1.ticks - d2.ticks).Ticks);
}
public static DateTime operator -(DateTime d,TimeSpan t)
{
return new DateTime (true, d.ticks - t);
}
public bool ToBoolean(IFormatProvider provider)
{
throw new InvalidCastException();
}
public byte ToByte(IFormatProvider provider)
{
throw new InvalidCastException();
}
public char ToChar(IFormatProvider provider)
{
throw new InvalidCastException();
}
// TODO Implement me
[MonoTODO]
public System.DateTime ToDateTime(IFormatProvider provider)
{
return new System.DateTime(true,this.ticks);
}
public decimal ToDecimal(IFormatProvider provider)
{
throw new InvalidCastException();
}
public double ToDouble(IFormatProvider provider)
{
throw new InvalidCastException();
}
public Int16 ToInt16(IFormatProvider provider)
{
throw new InvalidCastException();
}
public Int32 ToInt32(IFormatProvider provider)
{
throw new InvalidCastException();
}
public Int64 ToInt64(IFormatProvider provider)
{
throw new InvalidCastException();
}
[CLSCompliant(false)]
public SByte ToSByte(IFormatProvider provider)
{
throw new InvalidCastException();
}
public Single ToSingle(IFormatProvider provider)
{
throw new InvalidCastException();
}
public object ToType(Type conversionType,IFormatProvider provider)
{
throw new InvalidCastException();
}
UInt16 System.IConvertible.ToUInt16(IFormatProvider provider)
{
throw new InvalidCastException();
}
[CLSCompliant(false)]
public UInt32 ToUInt32(IFormatProvider provider)
{
throw new InvalidCastException();
}
[CLSCompliant(false)]
public UInt64 ToUInt64(IFormatProvider provider)
{
throw new InvalidCastException();
}
}
}
namespace System
{
public enum DayOfWeek
{
Sunday,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
}
}
--=-=-=
--
Martin Baulig
martin@gnome.org
--=-=-=--