Files
cappuccino/Foundation/CPDate.j
T
2016-11-02 16:45:01 +01:00

298 lines
7.8 KiB
Plaintext

/*
* CPDate.j
* Foundation
*
* Created by Thomas Robinson.
* Copyright 2008, 280 North, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import "CPObject.j"
@import "CPString.j"
@import "CPException.j"
var CPDateReferenceDate = new Date(Date.UTC(2001, 0, 1, 0, 0, 0, 0));
/*!
@class CPDate
@ingroup foundation
@brief A representation of a single point in time.
*/
@implementation CPDate : CPObject
{
}
+ (id)alloc
{
var result = new Date;
result.isa = [self class];
return result;
}
+ (id)date
{
return [[self alloc] init];
}
+ (id)dateWithTimeIntervalSinceNow:(CPTimeInterval)seconds
{
return [[CPDate alloc] initWithTimeIntervalSinceNow:seconds];
}
+ (id)dateWithTimeIntervalSince1970:(CPTimeInterval)seconds
{
return [[CPDate alloc] initWithTimeIntervalSince1970:seconds];
}
+ (id)dateWithTimeIntervalSinceReferenceDate:(CPTimeInterval)seconds
{
return [[CPDate alloc] initWithTimeIntervalSinceReferenceDate:seconds];
}
+ (id)distantPast
{
return [CPDate dateWithTimeIntervalSinceReferenceDate:-63113817600.0];
}
+ (id)distantFuture
{
return [CPDate dateWithTimeIntervalSinceReferenceDate:63113990400.0];
}
- (id)initWithTimeIntervalSinceNow:(CPTimeInterval)seconds
{
if (!_isNumberType(seconds))
CPLog.warn(@"The parameter of the method initWithTimeIntervalSinceNow: should be an integer or a float");
self = new Date((new Date()).getTime() + seconds * 1000);
return self;
}
- (id)initWithTimeIntervalSince1970:(CPTimeInterval)seconds
{
if (!_isNumberType(seconds))
CPLog.warn(@"The parameter of the method initWithTimeIntervalSince1970: should be an integer or a float");
self = new Date(seconds * 1000);
return self;
}
- (id)initWithTimeIntervalSinceReferenceDate:(CPTimeInterval)seconds
{
if (!_isNumberType(seconds))
CPLog.warn(@"The parameter of the method initWithTimeIntervalSinceReferenceDate: should be an integer or a float");
self = [self initWithTimeInterval:seconds sinceDate:CPDateReferenceDate];
return self;
}
- (id)initWithTimeInterval:(CPTimeInterval)seconds sinceDate:(CPDate)refDate
{
if (!_isNumberType(seconds))
CPLog.warn(@"The parameter of the method initWithTimeInterval:sinceDate: should be an integer or a float");
self = new Date(refDate.getTime() + seconds * 1000);
return self;
}
/*!
Returns a CPDate initialized with a date and time specified by the given
string in international date format YYYY-MM-DD HH:MM:SS ±HHMM (e.g.
2009-11-17 17:52:04 +0000).
*/
- (id)initWithString:(CPString)description
{
var format = new RegExp("(\\d{4})-(\\d{2})-(\\d{2}) (\\d{2}):(\\d{2}):(\\d{2}) ([-+])(\\d{2})(\\d{2})"),
d = description.match(new RegExp(format));
if (!d || d.length != 10)
[CPException raise:CPInvalidArgumentException
reason:"initWithString: the string must be in YYYY-MM-DD HH:MM:SS ±HHMM format"];
var date = new Date(d[1], d[2] - 1, d[3]),
timeZoneOffset = (Number(d[8]) * 60 + Number(d[9])) * (d[7] === '-' ? 1 : -1);
date.setHours(d[4]);
date.setMinutes(d[5]);
date.setSeconds(d[6]);
self = new Date(date.getTime() + (timeZoneOffset - date.getTimezoneOffset()) * 60 * 1000);
return self;
}
- (CPTimeInterval)timeIntervalSinceDate:(CPDate)anotherDate
{
return (self.getTime() - anotherDate.getTime()) / 1000.0;
}
- (CPTimeInterval)timeIntervalSinceNow
{
return [self timeIntervalSinceDate:[CPDate date]];
}
- (CPTimeInterval)timeIntervalSince1970
{
return self.getTime() / 1000.0;
}
- (CPTimeInterval)timeIntervalSinceReferenceDate
{
return (self.getTime() - CPDateReferenceDate.getTime()) / 1000.0;
}
+ (CPTimeInterval)timeIntervalSinceReferenceDate
{
return [[CPDate date] timeIntervalSinceReferenceDate];
}
/**
Return a new date representing the receiver's time plus the given interval into the future,
or into the past for a negative interval.
*/
- (id)dateByAddingTimeInterval:(CPTimeInterval)seconds
{
return [[CPDate alloc] initWithTimeInterval:seconds sinceDate:self];
}
- (BOOL)isEqual:(CPDate)aDate
{
if (self === aDate)
return YES;
if (!aDate || ![aDate isKindOfClass:[CPDate class]])
return NO;
return [self isEqualToDate:aDate];
}
- (BOOL)isEqualToDate:(CPDate)aDate
{
if (!aDate)
return NO;
return !(self < aDate || self > aDate);
}
- (CPComparisonResult)compare:(CPDate)anotherDate
{
return (self > anotherDate) ? CPOrderedDescending : ((self < anotherDate) ? CPOrderedAscending : CPOrderedSame);
}
- (CPDate)earlierDate:(CPDate)anotherDate
{
return (self < anotherDate) ? self : anotherDate;
}
- (CPDate)laterDate:(CPDate)anotherDate
{
return (self > anotherDate) ? self : anotherDate;
}
/*!
Returns timezone offset as a string in ±HHMM format
*/
+ (CPString)timezoneOffsetString:(int)timezoneOffset
{
var offset = -timezoneOffset,
positive = offset >= 0,
hours = positive ? FLOOR(offset / 60) : CEIL(offset / 60),
minutes = offset - hours * 60;
return [CPString stringWithFormat:@"%s%02d%02d", positive ? "+" : "-", ABS(hours), ABS(minutes)];
}
/*!
Returns the date as a string in the international format
YYYY-MM-DD HH:MM:SS ±HHMM.
*/
- (CPString)description
{
return [CPString stringWithFormat:@"%04d-%02d-%02d %02d:%02d:%02d %s", self.getFullYear(), self.getMonth() + 1, self.getDate(), self.getHours(), self.getMinutes(), self.getSeconds(), [CPDate timezoneOffsetString:self.getTimezoneOffset()]];
}
- (id)copy
{
return new Date(self.getTime());
}
@end
var CPDateTimeKey = @"CPDateTimeKey";
@implementation CPDate (CPCoding)
- (id)initWithCoder:(CPCoder)aCoder
{
if (self)
{
self.setTime([aCoder decodeIntForKey:CPDateTimeKey]);
}
return self;
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
[aCoder encodeInt:self.getTime() forKey:CPDateTimeKey];
}
@end
// Based on 'Universal JavaScript Date.parse for ISO 8601' available at https://github.com/csnover/js-iso8601.
var numericKeys = [1, 4, 5, 6, 7, 10, 11];
Date.parseISO8601 = function (date)
{
var timestamp,
struct,
minutesOffset = 0;
// First, check for native parsing.
timestamp = Date.parse(date);
if (isNaN(timestamp) && (struct = new RegExp("^(\\d{4}|[+\\-]\\d{6})(?:-(\\d{2})(?:-(\\d{2}))?)?(?:T(\\d{2}):(\\d{2})(?::(\\d{2})(?:\\.(\\d{3}))?)?(?:(Z)|([+\\-])(\\d{2})(?::(\\d{2}))?)?)?$").exec(date)))
{
// avoid NaN timestamps caused by "undefined" values being passed to Date.UTC
for (var i = 0, k; (k = numericKeys[i]); ++i)
struct[k] = +struct[k] || 0;
// allow undefined days and months
struct[2] = (+struct[2] || 1) - 1;
struct[3] = +struct[3] || 1;
if (struct[8] !== 'Z' && struct[9] !== undefined)
{
minutesOffset = struct[10] * 60 + struct[11];
if (struct[9] === '+')
minutesOffset = 0 - minutesOffset;
}
return Date.UTC(struct[1], struct[2], struct[3], struct[4], struct[5] + minutesOffset, struct[6], struct[7]);
}
return timestamp;
};
Date.prototype.isa = CPDate;
function _isNumberType(value)
{
if (typeof value === 'number')
return YES;
else
return NO;
}