PageRenderTime 77ms CodeModel.GetById 18ms app.highlight 55ms RepoModel.GetById 1ms app.codeStats 0ms

/core/externals/update-engine/externals/gdata-objectivec-client/Source/Elements/GDataDateTime.m

http://macfuse.googlecode.com/
Objective C | 434 lines | 299 code | 88 blank | 47 comment | 76 complexity | bdbebcf4e944a2d0974ac28e7c3b2a0b MD5 | raw file
  1/* Copyright (c) 2007 Google Inc.
  2*
  3* Licensed under the Apache License, Version 2.0 (the "License");
  4* you may not use this file except in compliance with the License.
  5* You may obtain a copy of the License at
  6*
  7*     http://www.apache.org/licenses/LICENSE-2.0
  8*
  9* Unless required by applicable law or agreed to in writing, software
 10* distributed under the License is distributed on an "AS IS" BASIS,
 11* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12* See the License for the specific language governing permissions and
 13* limitations under the License.
 14*/
 15
 16//
 17//  GDataDateTime.m
 18//
 19
 20#import "GDataDateTime.h"
 21
 22static NSMutableDictionary *gCalendarsForTimeZones = nil;
 23
 24@implementation GDataDateTime
 25
 26+ (void)initialize {
 27  // Note that initialize is guaranteed by the runtime to be called in a
 28  // thread-safe manner.
 29  gCalendarsForTimeZones = [[NSMutableDictionary alloc] init];
 30}
 31
 32+ (GDataDateTime *)dateTimeWithRFC3339String:(NSString *)str {
 33  return [[[GDataDateTime alloc] initWithRFC3339String:str] autorelease];
 34}
 35
 36+ (GDataDateTime *)dateTimeWithDate:(NSDate *)date timeZone:(NSTimeZone *)tz {
 37 return [[[GDataDateTime alloc] initWithDate:date
 38                                    timeZone:tz] autorelease];
 39}
 40
 41- (id)initWithRFC3339String:(NSString *)str {
 42
 43  self = [super init];
 44  if (self) {
 45    [self setFromRFC3339String:str];
 46  }
 47  return self;
 48}
 49
 50- (id)initWithDate:(NSDate *)date timeZone:(NSTimeZone *)tz {
 51
 52  self = [super init];
 53  if (self) {
 54    [self setFromDate:date timeZone:tz];
 55  }
 56  return self;
 57}
 58
 59- (void)dealloc {
 60  [dateComponents_ release];
 61  [timeZone_ release];
 62  [super dealloc];
 63}
 64
 65- (id)copyWithZone:(NSZone *)zone {
 66
 67  GDataDateTime *newObj = [[GDataDateTime alloc] init];
 68
 69  [newObj setIsUniversalTime:[self isUniversalTime]];
 70  [newObj setTimeZone:[self timeZone] withOffsetSeconds:[self offsetSeconds]];
 71
 72  NSDateComponents *newDateComponents;
 73  NSDateComponents *oldDateComponents = [self dateComponents];
 74
 75// TODO: Experiments show it lies in 10.4.8 commented out for now.
 76  if (NO && [NSDateComponents conformsToProtocol:@protocol(NSCopying)]) {
 77
 78    newDateComponents = [[oldDateComponents copyWithZone:zone] autorelease];
 79
 80  } else {
 81    // NSDateComponents doesn't implement NSCopying in 10.4. We'll just retain
 82    // it, which is fine since we never set individual components
 83    // except after allocating a new NSDateCompoents instance.
 84    newDateComponents = oldDateComponents;
 85  }
 86  [newObj setDateComponents:newDateComponents];
 87
 88  return newObj;
 89}
 90
 91// until NSDateComponent implements isEqual, we'll use this
 92- (BOOL)doesDateComponents:(NSDateComponents *)dc1
 93       equalDateComponents:(NSDateComponents *)dc2 {
 94
 95  return [dc1 era] == [dc2 era]
 96          && [dc1 year] == [dc2 year]
 97          && [dc1 month] == [dc2 month]
 98          && [dc1 day] == [dc2 day]
 99          && [dc1 hour] == [dc2 hour]
100          && [dc1 minute] == [dc2 minute]
101          && [dc1 second] == [dc2 second]
102          && [dc1 week] == [dc2 week]
103          && [dc1 weekday] == [dc2 weekday]
104          && [dc1 weekdayOrdinal] == [dc2 weekdayOrdinal];
105}
106
107- (BOOL)isEqual:(GDataDateTime *)other {
108
109  if (self == other) return YES;
110  if (![other isKindOfClass:[GDataDateTime class]]) return NO;
111
112  return [self offsetSeconds] == [other offsetSeconds]
113    && [self isUniversalTime] == [other isUniversalTime]
114    && [self timeZone] == [other timeZone]
115    && [self doesDateComponents:[self dateComponents]
116            equalDateComponents:[other dateComponents]];
117}
118
119- (NSString *)description {
120  return [NSString stringWithFormat:@"%@ %p: {%@}",
121    [self class], self, [self RFC3339String]];
122}
123
124- (NSTimeZone *)timeZone {
125  if (timeZone_) {
126    return timeZone_;
127  }
128
129  if ([self isUniversalTime]) {
130    NSTimeZone *ztz = [NSTimeZone timeZoneWithName:@"Universal"];
131    return ztz;
132  }
133
134  NSInteger offsetSeconds = [self offsetSeconds];
135
136  if (offsetSeconds != NSUndefinedDateComponent) {
137    NSTimeZone *tz = [NSTimeZone timeZoneForSecondsFromGMT:offsetSeconds];
138    return tz;
139  }
140  return nil;
141}
142
143- (void)setTimeZone:(NSTimeZone *)timeZone {
144  [timeZone_ release];
145  timeZone_ = [timeZone retain];
146
147  if (timeZone) {
148    NSInteger offsetSeconds = [timeZone secondsFromGMTForDate:[self date]];
149    [self setOffsetSeconds:offsetSeconds];
150  } else {
151    [self setOffsetSeconds:NSUndefinedDateComponent];
152  }
153}
154
155- (void)setTimeZone:(NSTimeZone *)timeZone withOffsetSeconds:(NSInteger)val {
156  [timeZone_ release];
157  timeZone_ = [timeZone retain];
158
159  offsetSeconds_ = val;
160}
161
162- (NSCalendar *)calendarForTimeZone:(NSTimeZone *)tz {
163  NSCalendar *cal = nil;
164  @synchronized(gCalendarsForTimeZones) {
165    id tzKey = (tz ? tz : [NSNull null]);
166    cal = [gCalendarsForTimeZones objectForKey:tzKey];
167    if (cal == nil) {
168      cal = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
169      if (tz) {
170        [cal setTimeZone:tz];
171      }
172      [gCalendarsForTimeZones setObject:cal forKey:tzKey];
173    }
174  }
175  return cal;
176}
177
178- (NSCalendar *)calendar {
179  NSTimeZone *tz = self.timeZone;
180  return [self calendarForTimeZone:tz];
181}
182
183- (NSDate *)date {
184  NSDateComponents *dateComponents = [self dateComponents];
185  NSCalendar *cal;
186
187  if (![self hasTime]) {
188    // we're not keeping track of a time, but NSDate always is based on
189    // an absolute time. We want to avoid returning an NSDate where the
190    // calendar date appears different from what was used to create our
191    // date-time object.
192    //
193    // We'll make a copy of the date components, setting the time on our
194    // copy to noon GMT, since that ensures the date renders correctly for
195    // any time zone
196    //
197    // Note that on 10.4 NSDateComponents does not implement NSCopying, so we'll
198    // assemble an NSDateComponents manually here
199    NSDateComponents *noonDateComponents = [[[NSDateComponents alloc] init] autorelease];
200    [noonDateComponents setYear:[dateComponents year]];
201    [noonDateComponents setMonth:[dateComponents month]];
202    [noonDateComponents setDay:[dateComponents day]];
203    [noonDateComponents setHour:12];
204    [noonDateComponents setMinute:0];
205    [noonDateComponents setSecond:0];
206    dateComponents = noonDateComponents;
207
208    NSTimeZone *gmt = [NSTimeZone timeZoneWithName:@"Universal"];
209    cal = [self calendarForTimeZone:gmt];
210  } else {
211    cal = self.calendar;
212  }
213
214  NSDate *date = [cal dateFromComponents:dateComponents];
215  return date;
216}
217
218- (NSString *)stringValue {
219  return [self RFC3339String];
220}
221
222- (NSString *)RFC3339String {
223  NSDateComponents *dateComponents = [self dateComponents];
224  NSInteger offset = [self offsetSeconds];
225
226  NSString *timeString = @""; // timeString like "T15:10:46-08:00"
227
228  if ([self hasTime]) {
229
230    NSString *timeOffsetString; // timeOffsetString like "-08:00"
231
232    if ([self isUniversalTime]) {
233     timeOffsetString = @"Z";
234    } else if (offset == NSUndefinedDateComponent) {
235      // unknown offset is rendered as -00:00 per
236      // http://www.ietf.org/rfc/rfc3339.txt section 4.3
237      timeOffsetString = @"-00:00";
238    } else {
239      NSString *sign = @"+";
240      if (offset < 0) {
241        sign = @"-";
242        offset = -offset;
243      }
244      timeOffsetString = [NSString stringWithFormat:@"%@%02ld:%02ld",
245        sign, (long)(offset/(60*60)) % 24, (long)(offset / 60) % 60];
246    }
247    timeString = [NSString stringWithFormat:@"T%02ld:%02ld:%02ld%@",
248      (long)[dateComponents hour], (long)[dateComponents minute],
249      (long)[dateComponents second], timeOffsetString];
250  }
251
252  // full dateString like "2006-11-17T15:10:46-08:00"
253  NSString *dateString = [NSString stringWithFormat:@"%04ld-%02ld-%02ld%@",
254    (long)[dateComponents year], (long)[dateComponents month],
255    (long)[dateComponents day], timeString];
256
257  return dateString;
258}
259
260- (void)setFromDate:(NSDate *)date timeZone:(NSTimeZone *)tz {
261  NSCalendar *cal = [self calendarForTimeZone:tz];
262  if (tz) {
263    [cal setTimeZone:tz];
264  }
265
266  NSUInteger const kComponentBits = (NSYearCalendarUnit | NSMonthCalendarUnit
267    | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit
268    | NSSecondCalendarUnit);
269
270  NSDateComponents *components = [cal components:kComponentBits fromDate:date];
271  [self setDateComponents:components];
272
273  [self setIsUniversalTime:NO];
274
275  NSInteger offset = NSUndefinedDateComponent;
276
277  if (tz) {
278    offset = [tz secondsFromGMTForDate:date];
279
280    if (offset == 0 && [tz isEqualToTimeZone:[NSTimeZone timeZoneWithName:@"Universal"]]) {
281      [self setIsUniversalTime:YES];
282    }
283  }
284  [self setOffsetSeconds:offset];
285
286  // though offset seconds are authoritative, we'll retain the time zone
287  // since we can't regenerate it reliably from just the offset
288  timeZone_ = [tz retain];
289}
290
291static inline BOOL ScanInteger(NSScanner *scanner, NSInteger *targetInteger) {
292  return [scanner scanInteger:targetInteger];
293}
294
295- (void)setFromRFC3339String:(NSString *)str {
296
297  NSInteger year = NSUndefinedDateComponent;
298  NSInteger month = NSUndefinedDateComponent;
299  NSInteger day = NSUndefinedDateComponent;
300  NSInteger hour = NSUndefinedDateComponent;
301  NSInteger minute = NSUndefinedDateComponent;
302  NSInteger sec = NSUndefinedDateComponent;
303  float secFloat = -1.0f;
304  NSString* sign = nil;
305  NSInteger offsetHour = 0;
306  NSInteger offsetMinute = 0;
307
308  NSScanner* scanner = [NSScanner scannerWithString:str];
309
310  NSCharacterSet* dashSet = [NSCharacterSet characterSetWithCharactersInString:@"-"];
311  NSCharacterSet* tSet = [NSCharacterSet characterSetWithCharactersInString:@"Tt "];
312  NSCharacterSet* colonSet = [NSCharacterSet characterSetWithCharactersInString:@":"];
313  NSCharacterSet* plusMinusZSet = [NSCharacterSet characterSetWithCharactersInString:@"+-zZ"];
314
315  // for example, scan 2006-11-17T15:10:46-08:00
316  //                or 2006-11-17T15:10:46Z
317  if (// yyyy-mm-dd
318      ScanInteger(scanner, &year) &&
319      [scanner scanCharactersFromSet:dashSet intoString:NULL] &&
320      ScanInteger(scanner, &month) &&
321      [scanner scanCharactersFromSet:dashSet intoString:NULL] &&
322      ScanInteger(scanner, &day) &&
323      // Thh:mm:ss
324      [scanner scanCharactersFromSet:tSet intoString:NULL] &&
325      ScanInteger(scanner, &hour) &&
326      [scanner scanCharactersFromSet:colonSet intoString:NULL] &&
327      ScanInteger(scanner, &minute) &&
328      [scanner scanCharactersFromSet:colonSet intoString:NULL] &&
329      [scanner scanFloat:&secFloat] &&
330      // Z or +hh:mm
331      [scanner scanCharactersFromSet:plusMinusZSet intoString:&sign] &&
332      ScanInteger(scanner, &offsetHour) &&
333      [scanner scanCharactersFromSet:colonSet intoString:NULL] &&
334      ScanInteger(scanner, &offsetMinute)) {
335  }
336
337  NSDateComponents *dateComponents = [[[NSDateComponents alloc] init] autorelease];
338  [dateComponents setYear:year];
339  [dateComponents setMonth:month];
340  [dateComponents setDay:day];
341  [dateComponents setHour:hour];
342  [dateComponents setMinute:minute];
343
344  if (secFloat < -1.0f || secFloat > -1.0f) sec = (NSInteger)secFloat;
345  [dateComponents setSecond:sec];
346
347  [self setDateComponents:dateComponents];
348
349  // determine the offset, like from Z, or -08:00:00.0
350
351  [self setTimeZone:nil];
352
353  NSInteger totalOffset = NSUndefinedDateComponent;
354  [self setIsUniversalTime:NO];
355
356  if ([sign caseInsensitiveCompare:@"Z"] == NSOrderedSame) {
357
358    [self setIsUniversalTime:YES];
359    totalOffset = 0;
360
361  } else if (sign != nil) {
362
363    totalOffset = (60 * offsetMinute) + (60 * 60 * offsetHour);
364
365    if ([sign isEqual:@"-"]) {
366
367      if (totalOffset == 0) {
368        // special case: offset of -0.00 means undefined offset
369        totalOffset = NSUndefinedDateComponent;
370      } else {
371        totalOffset *= -1;
372      }
373    }
374  }
375
376  [self setOffsetSeconds:totalOffset];
377}
378
379- (BOOL)hasTime {
380  NSDateComponents *dateComponents = [self dateComponents];
381
382  BOOL hasTime = ([dateComponents hour] != NSUndefinedDateComponent
383                  && [dateComponents minute] != NSUndefinedDateComponent);
384
385  return hasTime;
386}
387
388- (void)setHasTime:(BOOL)shouldHaveTime {
389
390  // we'll set time values to zero or NSUndefinedDateComponent as appropriate
391  BOOL hadTime = [self hasTime];
392
393  if (shouldHaveTime && !hadTime) {
394    [dateComponents_ setHour:0];
395    [dateComponents_ setMinute:0];
396    [dateComponents_ setSecond:0];
397    offsetSeconds_ = NSUndefinedDateComponent;
398    isUniversalTime_ = NO;
399
400  } else if (hadTime && !shouldHaveTime) {
401    [dateComponents_ setHour:NSUndefinedDateComponent];
402    [dateComponents_ setMinute:NSUndefinedDateComponent];
403    [dateComponents_ setSecond:NSUndefinedDateComponent];
404    offsetSeconds_ = NSUndefinedDateComponent;
405    isUniversalTime_ = NO;
406    [self setTimeZone:nil];
407  }
408}
409
410- (NSInteger)offsetSeconds {
411  return offsetSeconds_;
412}
413
414- (void)setOffsetSeconds:(NSInteger)val {
415  offsetSeconds_ = val;
416}
417
418- (BOOL)isUniversalTime {
419  return isUniversalTime_;
420}
421
422- (void)setIsUniversalTime:(BOOL)flag {
423  isUniversalTime_ = flag;
424}
425
426- (NSDateComponents *)dateComponents {
427  return dateComponents_;
428}
429
430- (void)setDateComponents:(NSDateComponents *)dateComponents {
431  [dateComponents_ autorelease];
432  dateComponents_ = [dateComponents retain]; // NSDateComponents doesn't implement NSCopying in 10.4
433}
434@end