1469 lines
42 KiB
Plaintext
1469 lines
42 KiB
Plaintext
{
|
||
$Project$
|
||
$Workfile$
|
||
$Revision$
|
||
$DateUTC$
|
||
$Id$
|
||
|
||
This file is part of the Indy (Internet Direct) project, and is offered
|
||
under the dual-licensing agreement described on the Indy website.
|
||
(http://www.indyproject.org/)
|
||
|
||
Copyright:
|
||
(c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
|
||
}
|
||
{
|
||
$Log$
|
||
}
|
||
{
|
||
Rev 1.3 2004.02.03 5:45:04 PM czhower
|
||
Name changes
|
||
|
||
Rev 1.2 1/21/2004 1:57:38 PM JPMugaas
|
||
InitComponent
|
||
|
||
Rev 1.1 10/12/2003 2:01:46 PM BGooijen
|
||
Compiles in DotNet
|
||
|
||
Rev 1.0 11/14/2002 02:16:44 PM JPMugaas
|
||
|
||
2002-Feb-07 Pete Mee
|
||
- Modified interface: GetAsRFC882 is now GetAsRFC822. ;-)
|
||
- Fixed GetAsTTimeStamp (was way out).
|
||
|
||
2001-Nov-10 Pete Mee
|
||
- Added SetFromDOSDateTime.
|
||
|
||
2001-Mar-29 Pete Mee
|
||
- Fixed bug in SetFromRFC822. As luck would have it, my PC has changed
|
||
to BST from GMT, so I caught the error. Change use of GMTToLocalDateTime
|
||
to StrInternetToDateTime.
|
||
|
||
2001-Mar-27 Pete Mee
|
||
- Added GetTimeZoneHour, GetTimeZoneMinutes, GetTimeZoneAsString and
|
||
corresponding properties, TimeZoneHour, TimeZoneMinutes and TimeZoneAsString.
|
||
- Added SetFromRFC822 and SetFromISO8601.
|
||
|
||
2001-Mar-26 Pete Mee
|
||
- Fixed bug in AddDays. Was adding an extra day in the wrong centuary.
|
||
- Fixed bug in AddDays. Was not altering the year with large additions.
|
||
|
||
2001-Mar-23 Pete Mee
|
||
- Fixed bug in SubtractMilliseconds.
|
||
- GetBeatOfDay is more accurate (based on milliseconds instead of seconds).
|
||
|
||
2001-Mar-21 Pete Mee
|
||
- Altered Day, Seond and Millisecond properties to use their respective
|
||
Set methods.
|
||
- Added SetTimeZone, Zero, ZeroTime and ZeroDate.
|
||
- Altered SetYear and SetDay to cope with the value 0.
|
||
|
||
2000-Sep-16 Pete Mee
|
||
- SetYear no longer accepts zero but instead simply exits.
|
||
|
||
2000-Aug-01 Pete Mee
|
||
- Fix bugs in AddDays & SubtractDays. Depending on the day of the year, the
|
||
calculations could have been incorrect. Now 'rounds off' to the nearest year
|
||
before any other calculation.
|
||
|
||
2000-Jul-28 Pete Mee
|
||
- Fix bugs in AddDays & SubtractDays. 3 days in 400 years lost, 1 day in 100
|
||
years lost.
|
||
|
||
2000-May-11 Pete Mee
|
||
- Added GetAsRFC822, GetAsISO8601
|
||
|
||
2000-May-03 Pete Mee
|
||
- Added detection of Day, Week and Month (various formats).
|
||
|
||
2000-May-02 Pete Mee
|
||
- Started TIdDateTimeStamp
|
||
}
|
||
|
||
unit IdDateTimeStamp;
|
||
|
||
{
|
||
Development notes:
|
||
|
||
The Calendar used is the Gregorian Calendar (modern western society). This
|
||
Calendar's use started somtime in the 1500s but wasn't adopted by some countries
|
||
until the early 1900s. No attempt is made to cope with any other Calendars.
|
||
|
||
No attempt is made to cope with any Atomic time quantity less than a leap
|
||
year (i.e., an exact number of seconds per day and an exact number of days
|
||
per year / leap year - no leap seconds, no 1/4 days, etc).
|
||
|
||
The implementation revolves around the Milliseconds, Seconds, Days and Years.
|
||
The heirarchy is as follows:
|
||
Milliseconds modify seconds. (0-999 Milliseconds)
|
||
Seconds modify days. (0-59 Seconds)
|
||
Days modify years. (1-365/366 Days)
|
||
Years modify years. (..., -2, -1, 1, ...)
|
||
|
||
All other time units are translated into necessary component parts. I.e.,
|
||
a week is 7 days, and hour is 3600 seconds, a minute is 60 seconds, etc...
|
||
|
||
The implementation could be easily expanded to represent decades, centuries,
|
||
nanoseconds, and beyond in both directions. Milliseconds are included to
|
||
provide easy conversion from TTimeStamp and back (and hence TDateTime). The
|
||
current component is designed to give good functionality for the majority (if
|
||
not all) of Internet component requirements (including Swatch's Internet Time).
|
||
It is also not limited to the 2038 bug of many of today's OSs (32-bit signed
|
||
number of seconds from 1st Jan 1970 = 19th Jan 2038 03:14:07, or there abouts).
|
||
|
||
NB: This implementation is factors slower than those of the TDateTime and
|
||
TTimeStamp components of standard Delphi. It's main use lies in the conversion
|
||
to / from ISO 8601 and RFC 822 formats as well as dates ranging beyond 2037 and
|
||
before 1970 (though TTimeStamp is capable here). It's also the only date component
|
||
I'm aware of that complies with RFC 2550 "Y10K and Beyond"... one of those RFCs in
|
||
the same category as RFC 1149, IP over Avian Carriers. ;-)
|
||
|
||
Pete Mee
|
||
}
|
||
{
|
||
ToDo: Allow localisation date / time strings generated (i.e., to zone name).
|
||
ToDo: Rework SetFromRFC822 as it is (marginally) limited by it's
|
||
conversion to TDateTime.
|
||
ToDo: Conversion between Time Zones.
|
||
}
|
||
|
||
interface
|
||
|
||
{$i IdCompilerDefines.inc}
|
||
|
||
uses
|
||
IdGlobal,
|
||
IdBaseComponent;
|
||
|
||
const
|
||
// Some basic constants
|
||
IdMilliSecondsInSecond = 1000;
|
||
IdSecondsInMinute = 60;
|
||
IdMinutesInHour = 60;
|
||
IdHoursInDay = 24;
|
||
|
||
IdDaysInWeek = 7;
|
||
IdDaysInYear = 365;
|
||
IdDaysInLeapYear = 366;
|
||
IdYearsInShortLeapYearCycle = 4;
|
||
IdDaysInShortLeapYearCycle = IdDaysInLeapYear + (IdDaysInYear * 3);
|
||
IdDaysInShortNonLeapYearCycle = IdDaysInYear * IdYearsInShortLeapYearCycle;
|
||
IdDaysInFourYears = IdDaysInShortLeapYearCycle;
|
||
IdYearsInCentury = 100;
|
||
IdDaysInCentury = (25 * IdDaysInFourYears) - 1;
|
||
IdDaysInLeapCentury = IdDaysInCentury + 1;
|
||
IdYearsInLeapYearCycle = 400;
|
||
IdDaysInLeapYearCycle = IdDaysInCentury * 4 + 1;
|
||
|
||
IdMonthsInYear = 12;
|
||
|
||
// Beat time is Swatch's "Internet Time" http://www.swatch.com/ {Do not Localize}
|
||
IdBeatsInDay = 1000;
|
||
|
||
// Some compound constants
|
||
IdHoursInHalfDay = IdHoursInDay div 2;
|
||
|
||
IdSecondsInHour = IdSecondsInMinute * IdMinutesInHour;
|
||
IdSecondsInDay = IdSecondsInHour * IdHoursInDay;
|
||
IdSecondsInHalfDay = IdSecondsInHour * IdHoursInHalfDay;
|
||
IdSecondsInWeek = IdDaysInWeek * IdSecondsInDay;
|
||
IdSecondsInYear = IdSecondsInDay * IdDaysInYear;
|
||
IdSecondsInLeapYear = IdSecondsInDay * IdDaysInLeapYear;
|
||
|
||
IdMillisecondsInMinute = IdSecondsInMinute * IdMillisecondsInSecond;
|
||
IdMillisecondsInHour = IdSecondsInHour * IdMillisecondsInSecond;
|
||
IdMillisecondsInDay = IdSecondsInDay * IdMillisecondsInSecond;
|
||
IdMillisecondsInWeek = IdSecondsInWeek * IdMillisecondsInSecond;
|
||
|
||
SShortMonthNameJan = 'Jan';
|
||
SShortMonthNameFeb = 'Feb';
|
||
SShortMonthNameMar = 'Mar';
|
||
SShortMonthNameApr = 'Apr';
|
||
SShortMonthNameMay = 'May';
|
||
SShortMonthNameJun = 'Jun';
|
||
SShortMonthNameJul = 'Jul';
|
||
SShortMonthNameAug = 'Aug';
|
||
SShortMonthNameSep = 'Sep';
|
||
SShortMonthNameOct = 'Oct';
|
||
SShortMonthNameNov = 'Nov';
|
||
SShortMonthNameDec = 'Dec';
|
||
|
||
SLongMonthNameJan = 'January';
|
||
SLongMonthNameFeb = 'February';
|
||
SLongMonthNameMar = 'March';
|
||
SLongMonthNameApr = 'April';
|
||
SLongMonthNameMay = 'May';
|
||
SLongMonthNameJun = 'June';
|
||
SLongMonthNameJul = 'July';
|
||
SLongMonthNameAug = 'August';
|
||
SLongMonthNameSep = 'September';
|
||
SLongMonthNameOct = 'October';
|
||
SLongMonthNameNov = 'November';
|
||
SLongMonthNameDec = 'December';
|
||
|
||
SShortDayNameSun = 'Sun';
|
||
SShortDayNameMon = 'Mon';
|
||
SShortDayNameTue = 'Tue';
|
||
SShortDayNameWed = 'Wed';
|
||
SShortDayNameThu = 'Thu';
|
||
SShortDayNameFri = 'Fri';
|
||
SShortDayNameSat = 'Sat';
|
||
|
||
SLongDayNameSun = 'Sunday';
|
||
SLongDayNameMon = 'Monday';
|
||
SLongDayNameTue = 'Tuesday';
|
||
SLongDayNameWed = 'Wednesday';
|
||
SLongDayNameThu = 'Thursday';
|
||
SLongDayNameFri = 'Friday';
|
||
SLongDayNameSat = 'Saturday';
|
||
|
||
IdDaysInMonth : array[1..IdMonthsInYear] of byte =
|
||
(
|
||
31, 28, 31, 30, 31, 30,
|
||
31, 31, 30, 31, 30, 31
|
||
);
|
||
|
||
IdMonthNames : array[0..IdMonthsInYear] of string =
|
||
( '', {Do not Localize}
|
||
SLongMonthNameJan, SLongMonthNameFeb, SLongMonthNameMar,
|
||
SLongMonthNameApr, SLongMonthNameMay, SLongMonthNameJun,
|
||
SLongMonthNameJul, SLongMonthNameAug, SLongMonthNameSep,
|
||
SLongMonthNameOct, SLongMonthNameNov, SLongMonthNameDec );
|
||
|
||
|
||
IdMonthShortNames : array[0..IdMonthsInYear] of string =
|
||
( '', // Used for GetMonth {Do not Localize}
|
||
SShortMonthNameJan, SShortMonthNameFeb, SShortMonthNameMar,
|
||
SShortMonthNameApr, SShortMonthNameMay, SShortMonthNameJun,
|
||
SShortMonthNameJul, SShortMonthNameAug, SShortMonthNameSep,
|
||
SShortMonthNameOct, SShortMonthNameNov, SShortMonthNameDec );
|
||
|
||
IdDayNames : array[0..IdDaysInWeek] of string =
|
||
( '', SLongDayNameSun, SLongDayNameMon, SLongDayNameTue, {Do not Localize}
|
||
SLongDayNameWed, SLongDayNameThu, SLongDayNameFri,
|
||
SLongDayNameSat );
|
||
|
||
IdDayShortNames : array[0..IdDaysInWeek] of string =
|
||
( '', SShortDayNameSun, SShortDayNameMon, SShortDayNameTue, {Do not Localize}
|
||
SShortDayNameWed, SShortDayNameThu, SShortDayNameFri,
|
||
SShortDayNameSat );
|
||
|
||
// Area Time Zones
|
||
TZ_NZDT = 13; // New Zealand Daylight Time
|
||
TZ_IDLE = 12; // International Date Line East
|
||
TZ_NZST = TZ_IDLE;// New Zealand Standard Time
|
||
TZ_NZT = TZ_IDLE; // New Zealand Time
|
||
TZ_EADT = 11; // Eastern Australian Daylight Time
|
||
TZ_GST = 10; // Guam Standard Time / Russia Zone 9
|
||
TZ_JST = 9; // Japan Standard Time / Russia Zone 8
|
||
TZ_CCT = 8; // China Coast Time / Russia Zone 7
|
||
TZ_WADT = TZ_CCT; // West Australian Daylight Time
|
||
TZ_WAST = 7; // West Australian Standard Time / Russia Zone 6
|
||
TZ_ZP6 = 6; // Chesapeake Bay / Russia Zone 5
|
||
TZ_ZP5 = 5; // Chesapeake Bay / Russia Zone 4
|
||
TZ_ZP4 = 4; // Russia Zone 3
|
||
TZ_BT = 3; // Baghdad Time / Russia Zone 2
|
||
TZ_EET = 2; // Eastern European Time / Russia Zone 1
|
||
TZ_MEST = TZ_EET; // Middle European Summer Time
|
||
TZ_MESZ = TZ_EET; // Middle European Summer Zone
|
||
TZ_SST = TZ_EET; // Swedish Summer Time
|
||
TZ_FST = TZ_EET; // French Summer Time
|
||
TZ_CET = 1; // Central European Time
|
||
TZ_FWT = TZ_CET; // French Winter Time
|
||
TZ_MET = TZ_CET; // Middle European Time
|
||
TZ_MEWT = TZ_CET; // Middle European Winter Time
|
||
TZ_SWT = TZ_CET; // Swedish Winter Time
|
||
TZ_GMT = 0; // Greenwich Meanttime
|
||
TZ_UT = TZ_GMT; // Universla Time
|
||
TZ_UTC = TZ_GMT; // Universal Time Co-ordinated
|
||
TZ_WET = TZ_GMT; // Western European Time
|
||
TZ_WAT = -1; // West Africa Time
|
||
TZ_BST = TZ_WAT; // British Summer Time
|
||
TZ_AT = -2; // Azores Time
|
||
TZ_ADT = -3; // Atlantic Daylight Time
|
||
TZ_AST = -4; // Atlantic Standard Time
|
||
TZ_EDT = TZ_AST; // Eastern Daylight Time
|
||
TZ_EST = -5; // Eastern Standard Time
|
||
TZ_CDT = TZ_EST; // Central Daylight Time
|
||
TZ_CST = -6; // Central Standard Time
|
||
TZ_MDT = TZ_CST; // Mountain Daylight Time
|
||
TZ_MST = -7; // Mountain Standard Time
|
||
TZ_PDT = TZ_MST; // Pacific Daylight Time
|
||
TZ_PST = -8; // Pacific Standard Time
|
||
TZ_YDT = TZ_PST; // Yukon Daylight Time
|
||
TZ_YST = -9; // Yukon Standard Time
|
||
TZ_HDT = TZ_YST; // Hawaii Daylight Time
|
||
TZ_AHST = -10; // Alaska-Hawaii Standard Time
|
||
TZ_CAT = TZ_AHST;// Central Alaska Time
|
||
TZ_HST = TZ_AHST; // Hawaii Standard Time
|
||
TZ_EAST = TZ_AHST;// East Australian Standard Time
|
||
TZ_NT = -11; // -None-
|
||
TZ_IDLW = -12; // International Date Line West
|
||
|
||
// Military Time Zones
|
||
TZM_A = TZ_WAT;
|
||
TZM_Alpha = TZM_A;
|
||
TZM_B = TZ_AT;
|
||
TZM_Bravo = TZM_B;
|
||
TZM_C = TZ_ADT;
|
||
TZM_Charlie = TZM_C;
|
||
TZM_D = TZ_AST;
|
||
TZM_Delta = TZM_D;
|
||
TZM_E = TZ_EST;
|
||
TZM_Echo = TZM_E;
|
||
TZM_F = TZ_CST;
|
||
TZM_Foxtrot = TZM_F;
|
||
TZM_G = TZ_MST;
|
||
TZM_Golf = TZM_G;
|
||
TZM_H = TZ_PST;
|
||
TZM_Hotel = TZM_H;
|
||
TZM_J = TZ_YST;
|
||
TZM_Juliet = TZM_J;
|
||
TZM_K = TZ_AHST;
|
||
TZM_Kilo = TZM_K;
|
||
TZM_L = TZ_NT;
|
||
TZM_Lima = TZM_L;
|
||
TZM_M = TZ_IDLW;
|
||
TZM_Mike = TZM_M;
|
||
TZM_N = TZ_CET;
|
||
TZM_November = TZM_N;
|
||
TZM_O = TZ_EET;
|
||
TZM_Oscar = TZM_O;
|
||
TZM_P = TZ_BT;
|
||
TZM_Papa = TZM_P;
|
||
TZM_Q = TZ_ZP4;
|
||
TZM_Quebec = TZM_Q;
|
||
TZM_R = TZ_ZP5;
|
||
TZM_Romeo = TZM_R;
|
||
TZM_S = TZ_ZP6;
|
||
TZM_Sierra = TZM_S;
|
||
TZM_T = TZ_WAST;
|
||
TZM_Tango = TZM_T;
|
||
TZM_U = TZ_CCT;
|
||
TZM_Uniform = TZM_U;
|
||
TZM_V = TZ_JST;
|
||
TZM_Victor = TZM_V;
|
||
TZM_W = TZ_GST;
|
||
TZM_Whiskey = TZM_W;
|
||
TZM_X = TZ_NT;
|
||
TZM_XRay = TZM_X;
|
||
TZM_Y = TZ_IDLE;
|
||
TZM_Yankee = TZM_Y;
|
||
TZM_Z = TZ_GMT;
|
||
TZM_Zulu = TZM_Z;
|
||
|
||
type
|
||
{ TODO: I'm sure these are stored in a unit elsewhere... need to find out } {Do not Localize}
|
||
TDays = (TDaySun, TDayMon, TDayTue, TDayWed, TDayThu, TDayFri, TDaySat);
|
||
TMonths = (TMthJan, TMthFeb, TMthMar, TMthApr, TMthMay, TMthJun,
|
||
TMthJul, TMthAug, TMthSep, TMthOct, TMthNov, TMthDec);
|
||
|
||
TIdDateTimeStamp = class(TIdBaseComponent)
|
||
protected
|
||
FDay : Integer;
|
||
FIsLeapYear : Boolean;
|
||
FMillisecond : Integer;
|
||
FSecond : Integer;
|
||
FTimeZone : Integer; // Number of minutes + / - from GMT / UTC
|
||
FYear : Integer;
|
||
|
||
procedure CheckLeapYear;
|
||
procedure SetDateFromISO8601(AString : String);
|
||
procedure SetTimeFromISO8601(AString : String);
|
||
procedure InitComponent; override;
|
||
public
|
||
procedure AddDays(ANumber : UInt32);
|
||
procedure AddHours(ANumber : UInt32);
|
||
procedure AddMilliseconds(ANumber : UInt32);
|
||
procedure AddMinutes(ANumber : UInt32);
|
||
procedure AddMonths(ANumber : UInt32);
|
||
procedure AddSeconds(ANumber : UInt32);
|
||
procedure AddTDateTime(ADateTime : TDateTime);
|
||
procedure AddTIdDateTimeStamp(AIdDateTime : TIdDateTimeStamp);
|
||
procedure AddTTimeStamp(ATimeStamp : TIdDateTimeStamp);
|
||
procedure AddWeeks(ANumber : UInt32);
|
||
procedure AddYears(ANumber : UInt32);
|
||
|
||
function GetAsISO8601Calendar : String;
|
||
function GetAsISO8601Ordinal : String;
|
||
function GetAsISO8601Week : String;
|
||
function GetAsRFC822 : String;
|
||
{TODO : function GetAsRFC977DateTime : String;}
|
||
function GetAsTDateTime : TDateTime;
|
||
function GetAsTTimeStamp : TIdDateTimeStamp;
|
||
function GetAsTimeOfDay : String; // HH:MM:SS
|
||
|
||
function GetBeatOfDay : Integer;
|
||
function GetDaysInYear : Integer;
|
||
function GetDayOfMonth : Integer;
|
||
function GetDayOfWeek : Integer;
|
||
function GetDayOfWeekName : String;
|
||
function GetDayOfWeekShortName : String;
|
||
function GetHourOf12Day : Integer;
|
||
function GetHourOf24Day : Integer;
|
||
function GetIsMorning : Boolean;
|
||
function GetMinuteOfDay : Integer;
|
||
function GetMinuteOfHour : Integer;
|
||
function GetMonthOfYear : Integer;
|
||
function GetMonthName : String;
|
||
function GetMonthShortName : String;
|
||
function GetSecondsInYear : Integer;
|
||
function GetSecondOfMinute : Integer;
|
||
function GetTimeZoneAsString: String;
|
||
function GetTimeZoneHour: Integer;
|
||
function GetTimeZoneMinutes: Integer;
|
||
function GetWeekOfYear : Integer;
|
||
|
||
procedure SetFromDOSDateTime(ADate, ATime : Word);
|
||
procedure SetFromISO8601(AString : String);
|
||
procedure SetFromRFC822(AString : String);
|
||
procedure SetFromTDateTime(ADateTime : TDateTime);
|
||
procedure SetFromTTimeStamp(ATimeStamp : TIdDateTimeStamp);
|
||
|
||
procedure SetDay(ANumber : Integer);
|
||
procedure SetMillisecond(ANumber : Integer);
|
||
procedure SetSecond(ANumber : Integer);
|
||
procedure SetTimeZone(const Value: Integer);
|
||
procedure SetYear(ANumber : Integer);
|
||
|
||
procedure SubtractDays(ANumber : UInt32);
|
||
procedure SubtractHours(ANumber : UInt32);
|
||
procedure SubtractMilliseconds(ANumber : UInt32);
|
||
procedure SubtractMinutes(ANumber : UInt32);
|
||
procedure SubtractMonths(ANumber : UInt32);
|
||
procedure SubtractSeconds(ANumber : UInt32);
|
||
procedure SubtractTDateTime(ADateTime : TDateTime);
|
||
procedure SubtractTIdDateTimeStamp(AIdDateTime : TIdDateTimeStamp);
|
||
procedure SubtractTTimeStamp(ATimeStamp : TIdDateTimeStamp);
|
||
procedure SubtractWeeks(ANumber : UInt32);
|
||
procedure SubtractYears(ANumber : UInt32);
|
||
|
||
procedure Zero;
|
||
procedure ZeroDate;
|
||
procedure ZeroTime;
|
||
|
||
property AsISO8601Calendar : String read GetAsISO8601Calendar;
|
||
property AsISO8601Ordinal : String read GetAsISO8601Ordinal;
|
||
property AsISO8601Week : String read GetAsISO8601Week;
|
||
property AsRFC822 : String read GetAsRFC822;
|
||
property AsTDateTime : TDateTime read GetAsTDateTime;
|
||
property AsTTimeStamp : TIdDateTimeStamp read GetAsTTimeStamp;
|
||
property AsTimeOfDay : String read GetAsTimeOfDay;
|
||
property BeatOfDay : Integer read GetBeatOfDay;
|
||
property Day : Integer read FDay write SetDay;
|
||
property DaysInYear : Integer read GetDaysInYear;
|
||
property DayOfMonth : Integer read GetDayOfMonth;
|
||
property DayOfWeek : Integer read GetDayOfWeek;
|
||
property DayOfWeekName : String read GetDayOfWeekName;
|
||
property DayOfWeekShortName : String read GetDayOfWeekShortName;
|
||
property HourOf12Day : Integer read GetHourOf12Day;
|
||
property HourOf24Day : Integer read GetHourOf24Day;
|
||
property IsLeapYear : Boolean read FIsLeapYear;
|
||
property IsMorning : Boolean read GetIsMorning;
|
||
property Millisecond : Integer read FMillisecond write SetMillisecond;
|
||
property MinuteOfDay : Integer read GetMinuteOfDay;
|
||
property MinuteOfHour : Integer read GetMinuteOfHour;
|
||
property MonthOfYear : Integer read GetMonthOfYear;
|
||
property MonthName : String read GetMonthName;
|
||
property MonthShortName : String read GetMonthShortName;
|
||
property Second : Integer read FSecond write SetSecond;
|
||
property SecondsInYear : Integer read GetSecondsInYear;
|
||
property SecondOfMinute : Integer read GetSecondOfMinute;
|
||
property TimeZone : Integer read FTimeZone write SetTimeZone;
|
||
property TimeZoneHour : Integer read GetTimeZoneHour;
|
||
property TimeZoneMinutes : Integer read GetTimeZoneMinutes;
|
||
property TimeZoneAsString : String read GetTimeZoneAsString;
|
||
property Year : Integer read FYear write SetYear;
|
||
property WeekOfYear : Integer read GetWeekOfYear;
|
||
end;
|
||
|
||
implementation
|
||
|
||
uses
|
||
IdGlobalProtocols,
|
||
IdStrings,
|
||
SysUtils;
|
||
|
||
const
|
||
MaxWeekAdd : UInt32 = $FFFFFFFF div IdDaysInWeek;
|
||
MaxMinutesAdd : UInt32 = $FFFFFFFF div IdSecondsInMinute;
|
||
DIGITS : String = '0123456789'; {Do not Localize}
|
||
|
||
function LocalDateTimeToTimeStamp(ADateTime: TDateTime): TIdDateTimeStamp;
|
||
var
|
||
Year,
|
||
Month,
|
||
Day,
|
||
Hour,
|
||
Minute,
|
||
Second,
|
||
MSec: Word;
|
||
begin
|
||
DecodeDate(ADateTime, Year, Month, Day);
|
||
DecodeTime(ADateTime, Hour, Minute, Second, MSec);
|
||
Result := TIdDateTimeStamp.Create;
|
||
Result.Zero;
|
||
Result.AddYears(Year);
|
||
Result.AddMonths(Month);
|
||
Result.AddDays(Day);
|
||
Result.AddHours(Hour);
|
||
Result.AddMinutes(Minute);
|
||
Result.AddSeconds(Second);
|
||
Result.AddMilliseconds(MSec);
|
||
end;
|
||
|
||
procedure ValidateTimeStamp(const ATimeStamp: TIdDateTimeStamp);
|
||
begin
|
||
IdGlobal.ToDo('ValidateTimeStamp() function in IdDateTimeStamp.pas is not implemented yet'); {do not localize}
|
||
// if (ATimeStamp.Time < 0) or (ATimeStamp.Date <= 0) then
|
||
// EIdExceptionBase.CreateFmt('''%d.%d'' is not a valid timestamp', [ATimeStamp.Date, ATimeStamp.Time]);
|
||
end;
|
||
|
||
function LocalTimeStampToDateTime(const ATimeStamp: TIdDateTimeStamp): TDateTime;
|
||
begin
|
||
ValidateTimeStamp(ATimeStamp);
|
||
Result := EncodeDate(ATimeStamp.Year, ATimeStamp.MonthOfYear, ATimeStamp.DayOfMonth) +
|
||
EncodeTime(ATimeStamp.HourOf24Day, ATimeStamp.MinuteOfHour, ATimeStamp.SecondOfMinute, ATimeStamp.Millisecond);
|
||
end;
|
||
|
||
///////////////////
|
||
// TIdDateTimeStamp
|
||
///////////////////
|
||
|
||
procedure TIdDateTimeStamp.InitComponent;
|
||
begin
|
||
inherited InitComponent;
|
||
Zero;
|
||
FTimeZone := 0;
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.AddDays;
|
||
var
|
||
i : Integer;
|
||
begin
|
||
// First 'round off' the current day of the year. This is done to prevent {Do not Localize}
|
||
// miscalculations in leap years and also as an optimisation for small
|
||
// increments.
|
||
if (ANumber > UInt32(DaysInYear - FDay)) and (not (FDay = 1)) then begin
|
||
ANumber := ANumber - UInt32(DaysInYear - FDay);
|
||
FDay := 0;
|
||
AddYears(1);
|
||
end else begin
|
||
// The number of days added is contained within this year.
|
||
FDay := FDay + Integer(ANumber);
|
||
if FDay > DaysInYear then
|
||
begin
|
||
ANumber := FDay;
|
||
FDay := 0;
|
||
AddDays(ANumber);
|
||
end;
|
||
Exit;
|
||
end;
|
||
|
||
if ANumber >= IdDaysInLeapYearCycle then begin
|
||
i := ANumber div IdDaysInLeapYearCycle;
|
||
AddYears(i * IdYearsInLeapYearCycle);
|
||
ANumber := ANumber - UInt32(i * IdDaysInLeapYearCycle);
|
||
end;
|
||
|
||
if ANumber >= IdDaysInLeapCentury then begin
|
||
while ANumber >= IDDaysInLeapCentury do begin
|
||
i := FYear div 100;
|
||
if i mod 4 = 3 then begin
|
||
// Going forward through a 'leap' century {Do not Localize}
|
||
AddYears(IdYearsInCentury);
|
||
ANumber := ANumber - UInt32(IdDaysInLeapCentury);
|
||
end else begin
|
||
AddYears(IdYearsInCentury);
|
||
ANumber := ANumber - UInt32(IdDaysInCentury);
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
if ANumber >= IdDaysInShortLeapYearCycle then begin
|
||
i := ANumber div IdDaysInShortLeapYearCycle;
|
||
AddYears(i * IdYearsInShortLeapYearCycle);
|
||
ANumber := ANumber - UInt32(i * IdDaysInShortLeapYearCycle);
|
||
end;
|
||
|
||
i := GetDaysInYear;
|
||
while Integer(ANumber) > i do begin
|
||
AddYears(1);
|
||
Dec(ANumber, i);
|
||
i := GetDaysInYear;
|
||
end;
|
||
|
||
if FDay + Integer(ANumber) > i then begin
|
||
AddYears(1);
|
||
Dec(ANumber, i - FDay);
|
||
FDay := ANumber;
|
||
end else begin
|
||
Inc(FDay, ANumber);
|
||
end;
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.AddHours;
|
||
var
|
||
i : UInt32;
|
||
begin
|
||
i := ANumber div IdHoursInDay;
|
||
AddDays(i);
|
||
Dec(ANumber, i * IdHoursInDay);
|
||
AddSeconds(ANumber * IdSecondsInHour);
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.AddMilliseconds;
|
||
var
|
||
i : UInt32;
|
||
begin
|
||
i := ANumber div IdMillisecondsInDay;
|
||
if i > 0 then begin
|
||
AddDays(i);
|
||
Dec(ANumber, i * IdMillisecondsInDay);
|
||
end;
|
||
|
||
i := ANumber div IdMillisecondsInSecond;
|
||
if i > 0 then begin
|
||
AddSeconds(i);
|
||
Dec(ANumber, i * IdMillisecondsInSecond);
|
||
end;
|
||
|
||
Inc(FMillisecond, ANumber);
|
||
while FMillisecond > IdMillisecondsInSecond do begin
|
||
// Should only happen once...
|
||
AddSeconds(1);
|
||
Dec(FMillisecond, IdMillisecondsInSecond);
|
||
end;
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.AddMinutes;
|
||
begin
|
||
// Convert down to seconds
|
||
while ANumber > MaxMinutesAdd do begin
|
||
AddSeconds(MaxMinutesAdd);
|
||
Dec(ANumber, MaxMinutesAdd);
|
||
end;
|
||
|
||
AddSeconds(ANumber * IdSecondsInMinute);
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.AddMonths;
|
||
var
|
||
i : Integer;
|
||
begin
|
||
i := ANumber div IdMonthsInYear;
|
||
AddYears(i);
|
||
Dec(ANumber, i * IdMonthsInYear);
|
||
|
||
i := MonthOfYear;
|
||
while ANumber > 0 do begin
|
||
if i = 12 then begin
|
||
i := 1;
|
||
end;
|
||
if (i = 2) and (IsLeapYear) then begin
|
||
AddDays(IdDaysInMonth[i] + 1);
|
||
end else begin
|
||
AddDays(IdDaysInMonth[i]);
|
||
end;
|
||
Dec(ANumber);
|
||
Inc(i);
|
||
end;
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.AddSeconds;
|
||
var
|
||
i : UInt32;
|
||
begin
|
||
i := ANumber Div IdSecondsInDay;
|
||
if i > 0 then begin
|
||
AddDays(i);
|
||
ANumber := ANumber - (i * IdSecondsInDay);
|
||
end;
|
||
|
||
Inc(FSecond, ANumber);
|
||
while FSecond > IdSecondsInDay do begin
|
||
// Should only ever happen once...
|
||
AddDays(1);
|
||
Dec(FSecond, IdSecondsInDay);
|
||
end;
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.AddTDateTime;
|
||
begin
|
||
// todo:
|
||
// AddTTimeStamp(DateTimeToTimeStamp(ADateTime));
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.AddTIdDateTimeStamp;
|
||
begin
|
||
{ TODO : Check for accuracy }
|
||
AddYears(AIdDateTime.Year);
|
||
AddDays(AIdDateTime.Day);
|
||
AddSeconds(AIdDateTime.Second);
|
||
AddMilliseconds(AIdDateTime.Millisecond);
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.AddTTimeStamp;
|
||
begin
|
||
AddTIdDateTimeStamp(ATimeStamp);
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.AddWeeks;
|
||
begin
|
||
// Cannot add years as there are not exactly 52 weeks in the year and there
|
||
// is no exact match between weeks and the 400 year leap cycle
|
||
|
||
// Convert down to days...
|
||
while ANumber > MaxWeekAdd do begin
|
||
AddDays(MaxWeekAdd);
|
||
Dec(ANumber, MaxWeekAdd);
|
||
end;
|
||
|
||
AddDays(ANumber * IdDaysInWeek);
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.AddYears;
|
||
begin
|
||
{TODO: Capture overflow because adding UInt32 to Integer }
|
||
if (FYear <= -1) and (Integer(ANumber) >= -FYear) then begin
|
||
Inc(ANumber);
|
||
end;
|
||
Inc(FYear, ANumber);
|
||
CheckLeapYear;
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.CheckLeapYear;
|
||
begin
|
||
// Nested if done to prevent unnecessary calcs on slower machines
|
||
if FYear mod 4 = 0 then begin
|
||
if FYear mod 100 = 0 then begin
|
||
if FYear mod 400 = 0 then begin
|
||
FIsLeapYear := True;
|
||
end else begin
|
||
FIsLeapYear := False;
|
||
end;
|
||
end else begin
|
||
FIsLeapYear := True;
|
||
end;
|
||
end else begin
|
||
FIsLeapYear := False;
|
||
end;
|
||
{TODO : If (FIsLeapYear = false) and (FDay = IdDaysInLeapYear) then begin
|
||
and, do what?
|
||
}
|
||
end;
|
||
|
||
function TIdDateTimeStamp.GetAsISO8601Calendar : String;
|
||
begin
|
||
Result := IntToStr(FYear) + '-' {Do not Localize}
|
||
+ IntToStr(MonthOfYear) + '-' {Do not Localize}
|
||
+ IntToStr(DayOfMonth) + 'T' {Do not Localize}
|
||
+ AsTimeOfDay;
|
||
end;
|
||
|
||
function TIdDateTimeStamp.GetAsISO8601Ordinal : String;
|
||
begin
|
||
Result := IntToStr(FYear) + '-' {Do not Localize}
|
||
+ IntToStr(FDay) + 'T' {Do not Localize}
|
||
+ AsTimeOfDay;
|
||
end;
|
||
|
||
function TIdDateTimeStamp.GetAsISO8601Week : String;
|
||
begin
|
||
Result := IntToStr(FYear) + '-W' {Do not Localize}
|
||
+ IntToStr(WeekOfYear) + '-' {Do not Localize}
|
||
+ IntToStr(DayOfWeek) + 'T' {Do not Localize}
|
||
+ AsTimeOfDay;
|
||
end;
|
||
|
||
function TIdDateTimeStamp.GetAsRFC822 : String;
|
||
begin
|
||
Result := IdDayShortNames[DayOfWeek] + ', ' {Do not Localize}
|
||
+ IntToStr(DayOfMonth) + ' ' {Do not Localize}
|
||
+ IdMonthShortNames[MonthOfYear] + ' ' {Do not Localize}
|
||
+ IntToStr(Year) + ' ' {Do not Localize}
|
||
+ AsTimeOfDay + ' ' {Do not Localize}
|
||
+ TimeZoneAsString;
|
||
end;
|
||
|
||
function TIdDateTimeStamp.GetAsTDateTime : TDateTime;
|
||
begin
|
||
Result := LocalTimeStampToDateTime(GetAsTTimeStamp);
|
||
end;
|
||
|
||
function TIdDateTimeStamp.GetAsTTimeStamp : TIdDateTimeStamp;
|
||
begin
|
||
Result := Self;
|
||
end;
|
||
|
||
function TIdDateTimeStamp.GetAsTimeOfDay : String;
|
||
begin
|
||
Result := IndyFormat('%.2d:%.2d:%.2d', {Do not localize}
|
||
[HourOf24Day, MinuteOfHour, SecondOfMinute]);
|
||
end;
|
||
|
||
function TIdDateTimeStamp.GetBeatOfDay : Integer;
|
||
var
|
||
i64 : Int64;
|
||
DTS : TIdDateTimeStamp;
|
||
begin
|
||
// Check
|
||
if FTimeZone <> TZ_MET then
|
||
begin
|
||
// Rather than messing about with this instance, create
|
||
// a new one.
|
||
DTS := TIdDateTimeStamp.Create;
|
||
try
|
||
DTS.SetYear(FYear);
|
||
DTS.SetDay(FDay);
|
||
DTS.SetSecond(FSecond);
|
||
DTS.SetMillisecond(FMillisecond);
|
||
DTS.SetTimeZone(TZ_MET);
|
||
DTS.AddMinutes( (TZ_MET * IdMinutesInHour) - FTimeZone);
|
||
Result := DTS.GetBeatOfDay;
|
||
finally
|
||
DTS.Free;
|
||
end;
|
||
end else
|
||
begin
|
||
i64 := (FSecond * IdMillisecondsInSecond) + FMillisecond;
|
||
i64 := i64 * IdBeatsInDay;
|
||
i64 := i64 div IdMillisecondsInDay;
|
||
Result := Integer(i64);
|
||
end;
|
||
end;
|
||
|
||
function TIdDateTimeStamp.GetDaysInYear : Integer;
|
||
begin
|
||
if IsLeapYear then begin
|
||
Result := IdDaysInLeapYear;
|
||
end else begin
|
||
Result := IdDaysInYear;
|
||
end;
|
||
end;
|
||
|
||
function TIdDateTimeStamp.GetDayOfMonth : Integer;
|
||
var
|
||
count, mnth, days : Integer;
|
||
begin
|
||
mnth := MonthOfYear;
|
||
if IsLeapYear and (mnth > 2) then begin
|
||
days := 1;
|
||
end else begin
|
||
days := 0;
|
||
end;
|
||
for count := 1 to mnth - 1 do begin
|
||
Inc(days, IdDaysInMonth[count]);
|
||
end;
|
||
days := Day - days;
|
||
if days < 0 then begin
|
||
Result := 0;
|
||
end else begin
|
||
Result := days;
|
||
end;
|
||
end;
|
||
|
||
function TIdDateTimeStamp.GetDayOfWeek : Integer;
|
||
var
|
||
a, y, m, d, mnth : Integer;
|
||
begin
|
||
// Thanks to the "FAQ About Calendars" by Claus T<>ndering for this algorithm
|
||
// http://www.tondering.dk/claus/calendar.html
|
||
mnth := MonthOfYear;
|
||
a := (14 - mnth) div 12;
|
||
y := Year - a;
|
||
m := mnth + (12 * a) - 2;
|
||
d := DayOfMonth + y + (y div 4) - (y div 100) + (y div 400) + ((31 * m) div 12);
|
||
d := d mod 7;
|
||
Result := d + 1;
|
||
end;
|
||
|
||
function TIdDateTimeStamp.GetDayOfWeekName : String;
|
||
begin
|
||
result := IdDayNames[GetDayOfWeek];
|
||
end;
|
||
|
||
function TIdDateTimeStamp.GetDayOfWeekShortName : String;
|
||
begin
|
||
result := IdDayShortNames[GetDayOfWeek];
|
||
end;
|
||
|
||
function TIdDateTimeStamp.GetHourOf12Day : Integer;
|
||
var
|
||
hr : Integer;
|
||
begin
|
||
hr := GetHourOf24Day;
|
||
if hr > IdHoursInHalfDay then begin
|
||
Dec(hr, IdHoursInHalfDay);
|
||
end;
|
||
Result := hr;
|
||
end;
|
||
|
||
function TIdDateTimeStamp.GetHourOf24Day : Integer;
|
||
begin
|
||
Result := Second div IdSecondsInHour;
|
||
end;
|
||
|
||
function TIdDateTimeStamp.GetIsMorning : Boolean;
|
||
begin
|
||
Result := Second <= (IdSecondsInHalfDay + 1);
|
||
end;
|
||
|
||
function TIdDateTimeStamp.GetMinuteOfDay : Integer;
|
||
begin
|
||
Result := Second div IdSecondsInMinute;
|
||
end;
|
||
|
||
function TIdDateTimeStamp.GetMinuteOfHour : Integer;
|
||
begin
|
||
Result := GetMinuteOfDay - (IdMinutesInHour * GetHourOf24Day);
|
||
end;
|
||
|
||
function TIdDateTimeStamp.GetMonthOfYear : Integer;
|
||
var
|
||
AddOne, Count : Byte;
|
||
Today : Integer;
|
||
begin
|
||
Result := 0;
|
||
if IsLeapYear then begin
|
||
AddOne := 1;
|
||
end else begin
|
||
AddOne := 0;
|
||
end;
|
||
Today := Day;
|
||
Count := 1;
|
||
while Count <> 13 do begin
|
||
if Count = 2 then begin
|
||
if Today > IdDaysInMonth[Count] + AddOne then begin
|
||
Dec(Today, IdDaysInMonth[Count] + AddOne);
|
||
end else begin
|
||
Result := Count;
|
||
Break;
|
||
end;
|
||
end else begin
|
||
if Today > IdDaysInMonth[Count] then begin
|
||
Dec(Today, IdDaysInMonth[Count]);
|
||
end else begin
|
||
Result := Count;
|
||
Break;
|
||
end;
|
||
end;
|
||
Inc(Count);
|
||
end;
|
||
end;
|
||
|
||
function TIdDateTimeStamp.GetMonthName : String;
|
||
begin
|
||
Result := IdMonthNames[MonthOfYear];
|
||
end;
|
||
|
||
function TIdDateTimeStamp.GetMonthShortName : String;
|
||
begin
|
||
Result := IdMonthShortNames[MonthOfYear];
|
||
end;
|
||
|
||
function TIdDateTimeStamp.GetSecondsInYear : Integer;
|
||
begin
|
||
if IsLeapYear then begin
|
||
Result := IdSecondsInLeapYear;
|
||
end else begin
|
||
Result := IdSecondsInYear;
|
||
end;
|
||
end;
|
||
|
||
function TIdDateTimeStamp.GetSecondOfMinute : Integer;
|
||
begin
|
||
Result := Second - (GetMinuteOfDay * IdSecondsInMinute);
|
||
end;
|
||
|
||
function TIdDateTimeStamp.GetTimeZoneAsString: String;
|
||
var
|
||
i : Integer;
|
||
begin
|
||
i := GetTimeZoneHour;
|
||
if i < 0 then begin
|
||
if i < -9 then begin
|
||
Result := IntToStr(i);
|
||
end else begin
|
||
Result := '-0' + IntToStr(Abs(i)); {Do not Localize}
|
||
end;
|
||
end
|
||
else if i <= 9 then begin
|
||
Result := '+0' + IntToStr(i); {Do not Localize}
|
||
end else
|
||
begin
|
||
Result := '+' + IntToStr(i); {Do not Localize}
|
||
end;
|
||
i := GetTimeZoneMinutes;
|
||
if i <= 9 then begin
|
||
Result := Result + '0'; {Do not Localize}
|
||
end;
|
||
Result := Result + IntToStr(i);
|
||
end;
|
||
|
||
function TIdDateTimeStamp.GetTimeZoneHour: Integer;
|
||
begin
|
||
Result := FTimeZone div 60;
|
||
end;
|
||
|
||
function TIdDateTimeStamp.GetTimeZoneMinutes: Integer;
|
||
begin
|
||
Result := Abs(FTimeZone) mod 60;
|
||
end;
|
||
|
||
function TIdDateTimeStamp.GetWeekOfYear : Integer;
|
||
var
|
||
w : Integer;
|
||
DT : TIdDateTimeStamp;
|
||
begin
|
||
DT := TIdDateTimeStamp.Create;
|
||
try
|
||
DT.SetYear(Year);
|
||
w := DT.DayOfWeek; // Get the first day of this year & hence number of
|
||
// days of the first week that are in the previous year
|
||
w := w + Day - 2; // Get complete weeks
|
||
w := w div 7;
|
||
Result := w + 1;
|
||
finally
|
||
DT.Free;
|
||
end;
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.SetFromDOSDateTime(ADate, ATime: Word);
|
||
begin
|
||
Zero;
|
||
SetYear(1980);
|
||
AddYears(ADate shr 9);
|
||
AddMonths(((ADate and $1E0) shr 5) - 1);
|
||
AddDays((ADate and $1F) - 1);
|
||
AddHours(ATime shr 11);
|
||
AddMinutes((ATime and $7E0) shr 5);
|
||
AddSeconds((ATime and $1F) - 1);
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.SetDateFromISO8601(AString: String);
|
||
var
|
||
i, week : Integer;
|
||
s : String;
|
||
begin
|
||
// AString should be in one of three formats:
|
||
// Calender - YYYY-MM-DD
|
||
// Ordinal - YYYY-XXX where XXX is the day of the year
|
||
// Week - YYYY-WXX-D where W is a literal and XX is the week of the year.
|
||
i := IndyPos('-', AString); {Do not Localize}
|
||
if i > 0 then
|
||
begin
|
||
s := Trim(Copy(AString, 1, i - 1));
|
||
AString := Trim(Copy(AString, i + 1, MaxInt));
|
||
i := FindFirstNotOf('0123456789', s); {Do not Localize}
|
||
if i = 0 then
|
||
begin
|
||
SetYear(IndyStrToInt(s));
|
||
if Length(AString) > 0 then
|
||
begin
|
||
i := IndyPos('-', AString); {Do not Localize}
|
||
if TextStartsWith(AString, 'W') then {Do not Localize}
|
||
begin
|
||
// Week format
|
||
s := Trim(Copy(AString, 2, i - 2));
|
||
AString := Trim(Copy(AString, i + 1, MaxInt));
|
||
|
||
week := -1;
|
||
i := -1;
|
||
if (Length(AString) > 0) and (FindFirstNotOf(DIGITS, AString) = 0) then
|
||
begin
|
||
i := IndyStrToInt(AString);
|
||
end;
|
||
|
||
if (Length(s) > 0) and (FindFirstNotOf(DIGITS, AString) = 0) then
|
||
begin
|
||
week := IndyStrToInt(s);
|
||
end;
|
||
|
||
if (week > 0) and (i >= 0) then
|
||
begin
|
||
Dec(week);
|
||
FDay := 1 + (IdDaysInWeek * week);
|
||
|
||
// Now have the correct week of the year
|
||
if i < GetDayOfWeek then begin
|
||
SubtractDays(GetDayOfWeek - i);
|
||
end else begin
|
||
AddDays(i - GetDayOfWeek);
|
||
end;
|
||
end;
|
||
end
|
||
else if i > 0 then
|
||
begin
|
||
// Calender format
|
||
s := Trim(Copy(AString, 1, i - 1));
|
||
AString := Trim(Copy(AString, i + 1, MaxInt));
|
||
|
||
// Set the day first due to internal format.
|
||
if (Length(AString) > 0) and (FindFirstNotOf(DIGITS, s) = 0) then
|
||
begin
|
||
SetDay(IndyStrToInt(AString));
|
||
end;
|
||
|
||
// Add the months.
|
||
if (Length(s) > 0) and (FindFirstNotOf(DIGITS, s) = 0) then
|
||
begin
|
||
AddMonths(IndyStrToInt(s) - 1);
|
||
end;
|
||
end else
|
||
begin
|
||
// Ordinal format
|
||
i := FindFirstNotOf(DIGITS, AString);
|
||
if i = 0 then begin
|
||
SetDay(IndyStrToInt(AString));
|
||
end;
|
||
end;
|
||
end;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.SetTimeFromISO8601(AString: String);
|
||
var
|
||
i : Integer;
|
||
Hour, Minute : String;
|
||
begin
|
||
// AString should be in the format of HH:MM:SS where : is a literal.
|
||
i := IndyPos(':', AString); {Do not Localize}
|
||
Hour := Trim(Copy(AString, 1, i - 1));
|
||
AString := Trim(Copy(AString, i + 1, MaxInt));
|
||
|
||
i := IndyPos(':', AString); {Do not Localize}
|
||
Minute := Trim(Copy(AString, 1, i - 1));
|
||
AString := Trim(Copy(AString, i + 1, MaxInt));
|
||
|
||
// Set seconds first due to internal format.
|
||
if (Length(AString) > 0) and (FindFirstNotOf(DIGITS, AString) = 0) then
|
||
begin
|
||
SetSecond(IndyStrToInt(AString));
|
||
end;
|
||
|
||
if (Length(Minute) > 0) and (FindFirstNotOf(DIGITS, Minute) = 0) then
|
||
begin
|
||
AddMinutes(IndyStrToInt(Minute));
|
||
end;
|
||
|
||
if (Length(Hour) > 0) and (FindFirstNotOf(DIGITS, Hour) = 0) then
|
||
begin
|
||
AddHours(IndyStrToInt(Hour));
|
||
end;
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.SetFromISO8601(AString: String);
|
||
var
|
||
i : Integer;
|
||
begin
|
||
Zero;
|
||
i := IndyPos('T', AString); {Do not Localize}
|
||
if i > 0 then
|
||
begin
|
||
SetDateFromISO8601(Trim(Copy(AString, 1, i - 1)));
|
||
SetTimeFromISO8601(Trim(Copy(AString, i + 1, MaxInt)));
|
||
end else
|
||
begin
|
||
SetDateFromISO8601(AString);
|
||
SetTimeFromISO8601(AString);
|
||
end;
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.SetFromRFC822(AString: String);
|
||
begin
|
||
SetFromTDateTime(StrInternetToDateTime(AString))
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.SetFromTDateTime(ADateTime : TDateTime);
|
||
var
|
||
LStamp: TIdDateTimeStamp;
|
||
begin
|
||
LStamp := LocalDateTimeToTimeStamp(ADateTime);
|
||
try
|
||
SetFromTTimeStamp(LStamp);
|
||
finally
|
||
FreeAndNil(LStamp);
|
||
end;
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.SetFromTTimeStamp(ATimeStamp : TIdDateTimeStamp);
|
||
begin
|
||
FDay := ATimeStamp.Day;
|
||
FMillisecond := ATimeStamp.Millisecond;
|
||
FIsLeapYear := ATimeStamp.IsLeapYear;
|
||
FSecond := ATimeStamp.Second;
|
||
FTimeZone := ATimeStamp.TimeZone;
|
||
FYear := ATimeStamp.Year;
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.SetDay(ANumber : Integer);
|
||
begin
|
||
if ANumber > 0 then begin
|
||
FDay := 0;
|
||
AddDays(ANumber);
|
||
end else begin
|
||
FDay := 1;
|
||
end;
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.SetMillisecond(ANumber : Integer);
|
||
begin
|
||
FMillisecond := 0;
|
||
AddMilliseconds(ANumber);
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.SetSecond(ANumber : Integer);
|
||
begin
|
||
FSecond := 0;
|
||
AddSeconds(ANumber);
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.SetTimeZone(const Value: Integer);
|
||
begin
|
||
FTimeZone := Value;
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.SetYear(ANumber : Integer);
|
||
begin
|
||
If ANumber = 0 then begin
|
||
FYear := 1;
|
||
end else begin
|
||
FYear := ANumber;
|
||
end;
|
||
CheckLeapYear;
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.SubtractDays(ANumber : UInt32);
|
||
var
|
||
i : Integer;
|
||
begin
|
||
if ANumber = 0 then begin
|
||
Exit;
|
||
end;
|
||
|
||
// First remove the number of days in this year. As with AddDays this
|
||
// is both an optimisation and a fix for calculations that begin in leap years.
|
||
if ANumber >= UInt32(FDay - 1) then begin
|
||
ANumber := ANumber - UInt32(FDay - 1);
|
||
FDay := 1;
|
||
end else begin
|
||
FDay := FDay - Integer(ANumber);
|
||
end;
|
||
|
||
// Subtract the number of whole leap year cycles = 400 years
|
||
if ANumber >= IdDaysInLeapYearCycle then begin
|
||
i := ANumber div IdDaysInLeapYearCycle;
|
||
SubtractYears(i * IdYearsInLeapYearCycle);
|
||
ANumber := ANumber - UInt32(i * IdDaysInLeapYearCycle);
|
||
end;
|
||
|
||
// Next subtract the centuries, checking for the century that is passed through
|
||
if ANumber >= IdDaysInLeapCentury then begin
|
||
while ANumber >= IdDaysInLeapCentury do begin
|
||
i := FYear div 100;
|
||
if i mod 4 = 0 then begin
|
||
// Going back through a 'leap' century {Do not Localize}
|
||
SubtractYears(IdYearsInCentury);
|
||
ANumber := ANumber - UInt32(IdDaysInLeapCentury);
|
||
end else begin
|
||
SubtractYears(IdYearsInCentury);
|
||
ANumber := ANumber - UInt32(IdDaysInCentury);
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
// Subtract multiples of 4 ("Short" Leap year cycle)
|
||
if ANumber >= IdDaysInShortLeapYearCycle then begin
|
||
while ANumber >= IdDaysInShortLeapYearCycle do begin
|
||
// Round off current year to nearest four.
|
||
i := (FYear shr 2) shl 2;
|
||
if SysUtils.IsLeapYear(i) then begin
|
||
// Normal
|
||
SubtractYears(IdYearsInShortLeapYearCycle);
|
||
ANumber := ANumber - UInt32(IdDaysInShortLeapYearCycle);
|
||
end else begin
|
||
// Subtraction crosses a 100-year (but not 400-year) boundary. Add the
|
||
// same number of years, but one less day.
|
||
SubtractYears(IdYearsInShortLeapYearCycle);
|
||
ANumber := ANumber - UInt32(IdDaysInShortNonLeapYearCycle);
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
// Now the individual years
|
||
while ANumber > UInt32(DaysInYear) do begin
|
||
SubtractYears(1);
|
||
Dec(ANumber, DaysInYear);
|
||
if Self.IsLeapYear then begin
|
||
// Correct the assumption of a non-leap year
|
||
AddDays(1);
|
||
end;
|
||
end;
|
||
|
||
// and finally the remainders
|
||
if ANumber >= UInt32(FDay) then begin
|
||
SubtractYears(1);
|
||
ANumber := ANumber - UInt32(FDay);
|
||
Day := DaysInYear - Integer(ANumber);
|
||
end else begin
|
||
Dec(FDay, ANumber);
|
||
end;
|
||
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.SubtractHours(ANumber : UInt32);
|
||
var
|
||
i : UInt32;
|
||
begin
|
||
i := ANumber div IdHoursInDay;
|
||
SubtractDays(i);
|
||
Dec(ANumber, i * IdHoursInDay);
|
||
SubtractSeconds(ANumber * IdSecondsInHour);
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.SubtractMilliseconds(ANumber : UInt32);
|
||
var
|
||
i : UInt32;
|
||
begin
|
||
if ANumber = 0 then begin
|
||
Exit;
|
||
end;
|
||
|
||
i := ANumber div IdMillisecondsInDay;
|
||
SubtractDays(i);
|
||
Dec(ANumber, i * IdMillisecondsInDay);
|
||
|
||
i := ANumber div IdMillisecondsInSecond;
|
||
SubtractSeconds(i);
|
||
Dec(ANumber, i * IdMillisecondsInSecond);
|
||
|
||
Dec(FMillisecond, ANumber);
|
||
while FMillisecond <= 0 do begin
|
||
SubtractSeconds(1);
|
||
// FMillisecond is already negative, so add it.
|
||
FMillisecond := IdMillisecondsInSecond + FMillisecond;
|
||
end;
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.SubtractMinutes(ANumber : UInt32);
|
||
begin
|
||
// Down size to seconds
|
||
while ANumber > MaxMinutesAdd do begin
|
||
SubtractSeconds(MaxMinutesAdd * IdSecondsInMinute);
|
||
Dec(ANumber, MaxMinutesAdd);
|
||
end;
|
||
SubtractSeconds(ANumber * IdSecondsInMinute);
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.SubtractMonths(ANumber : UInt32);
|
||
var
|
||
i : Integer;
|
||
begin
|
||
i := ANumber div IdMonthsInYear;
|
||
SubtractYears(i);
|
||
Dec(ANumber, i * IdMonthsInYear);
|
||
|
||
while ANumber > 0 do begin
|
||
i := MonthOfYear;
|
||
if i = 1 then begin
|
||
i := 13;
|
||
end;
|
||
if (i = 3) and (IsLeapYear) then begin
|
||
SubtractDays(IdDaysInMonth[2] + 1);
|
||
end else begin
|
||
SubtractDays(IdDaysInMonth[i - 1]);
|
||
end;
|
||
Dec(ANumber);
|
||
end;
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.SubtractSeconds(ANumber : UInt32);
|
||
var
|
||
i : UInt32;
|
||
begin
|
||
if ANumber = 0 then begin
|
||
Exit;
|
||
end;
|
||
|
||
i := ANumber div IdSecondsInDay;
|
||
SubtractDays(i);
|
||
Dec(ANumber, i * IdSecondsInDay);
|
||
|
||
Dec(FSecond, ANumber);
|
||
If FSecond < 0 then begin
|
||
SubtractDays(1);
|
||
FSecond := IdSecondsInDay + FSecond;
|
||
end;
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.SubtractTDateTime(ADateTime : TDateTime);
|
||
var LStamp : TIdDateTimeStamp;
|
||
begin
|
||
LStamp := LocalDateTimeToTimeStamp(ADateTime);
|
||
try
|
||
SubtractTIdDateTimeStamp(LStamp);
|
||
finally
|
||
FreeAndNil(LStamp);
|
||
end;
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.SubtractTIdDateTimeStamp(AIdDateTime : TIdDateTimeStamp);
|
||
begin
|
||
{ TODO : Check for accuracy }
|
||
SubtractYears(AIdDateTime.Year);
|
||
SubtractDays(AIdDateTime.Day);
|
||
SubtractSeconds(AIdDateTime.Second);
|
||
SubtractMilliseconds(AIdDateTime.Millisecond);
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.SubtractTTimeStamp(ATimeStamp : TIdDateTimeStamp);
|
||
begin
|
||
SubtractTIdDateTimeStamp(ATimeStamp);
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.SubtractWeeks(ANumber : UInt32);
|
||
begin
|
||
if ANumber = 0 then begin
|
||
Exit;
|
||
end;
|
||
|
||
// Down size to subtracting Days
|
||
while ANumber > MaxWeekAdd do begin
|
||
SubtractDays(MaxWeekAdd * IdDaysInWeek);
|
||
Dec(ANumber, MaxWeekAdd * IdDaysInWeek);
|
||
end;
|
||
SubtractDays(ANumber * IdDaysInWeek);
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.SubtractYears(ANumber : UInt32);
|
||
begin
|
||
if (FYear > 0) and (ANumber >= UInt32(FYear)) then begin
|
||
Inc(ANumber);
|
||
end;
|
||
FYear := FYear - Integer(ANumber);
|
||
CheckLeapYear;
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.Zero;
|
||
begin
|
||
ZeroDate;
|
||
ZeroTime;
|
||
FTimeZone := 0;
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.ZeroDate;
|
||
begin
|
||
SetYear(1);
|
||
SetDay(1);
|
||
end;
|
||
|
||
procedure TIdDateTimeStamp.ZeroTime;
|
||
begin
|
||
SetSecond(0);
|
||
SetMillisecond(0);
|
||
end;
|
||
|
||
end.
|