JLChen
2021-11-04 d3713a9e02760ac9f5c0551ca72be0bdda3ba91c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/*
 * Copyright 2012 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
#import "ZXCalendarParsedResult.h"
 
static NSRegularExpression *ZX_DATE_TIME = nil;
static NSRegularExpression *ZX_RFC2445_DURATION = nil;
 
const long ZX_RFC2445_DURATION_FIELD_UNITS[] = {
  7 * 24 * 60 * 60 * 1000, // 1 week
  24 * 60 * 60 * 1000, // 1 day
  60 * 60 * 1000, // 1 hour
  60 * 1000, // 1 minute
  1000, // 1 second
};
 
@implementation ZXCalendarParsedResult
 
+ (void)initialize {
  if ([self class] != [ZXCalendarParsedResult class]) return;
 
  ZX_DATE_TIME = [[NSRegularExpression alloc] initWithPattern:@"[0-9]{8}(T[0-9]{6}Z?)?"
                                                      options:0
                                                        error:nil];
 
  ZX_RFC2445_DURATION = [[NSRegularExpression alloc] initWithPattern:@"P(?:(\\d+)W)?(?:(\\d+)D)?(?:T(?:(\\d+)H)?(?:(\\d+)M)?(?:(\\d+)S)?)?"
                                                             options:NSRegularExpressionCaseInsensitive
                                                               error:nil];
}
 
- (id)initWithSummary:(NSString *)summary startString:(NSString *)startString endString:(NSString *)endString
       durationString:(NSString *)durationString location:(NSString *)location organizer:(NSString *)organizer
            attendees:(NSArray *)attendees description:(NSString *)description latitude:(double)latitude
            longitude:(double)longitude {
  if (self = [super initWithType:kParsedResultTypeCalendar]) {
    _summary = summary;
    _start = [self parseDate:startString];
 
    if (endString == nil) {
      long durationMS = [self parseDurationMS:durationString];
      _end = durationMS < 0 ? nil : [NSDate dateWithTimeIntervalSince1970:[_start timeIntervalSince1970] + durationMS / 1000];
    } else {
      _end = [self parseDate:endString];
    }
 
    _startAllDay = startString.length == 8;
    _endAllDay = endString != nil && endString.length == 8;
 
    _location = location;
    _organizer = organizer;
    _attendees = attendees;
    _resultDescription = description;
    _latitude = latitude;
    _longitude = longitude;
  }
  return self;
}
 
+ (id)calendarParsedResultWithSummary:(NSString *)summary startString:(NSString *)startString
                            endString:(NSString *)endString durationString:(NSString *)durationString
                             location:(NSString *)location organizer:(NSString *)organizer
                            attendees:(NSArray *)attendees description:(NSString *)description latitude:(double)latitude
                            longitude:(double)longitude {
  return [[self alloc] initWithSummary:summary startString:startString endString:endString durationString:durationString
                              location:location organizer:organizer attendees:attendees description:description
                              latitude:latitude longitude:longitude];
}
 
- (NSString *)displayResult {
  NSMutableString *result = [NSMutableString stringWithCapacity:100];
  [ZXParsedResult maybeAppend:self.summary result:result];
  [ZXParsedResult maybeAppend:[self format:self.startAllDay date:self.start] result:result];
  [ZXParsedResult maybeAppend:[self format:self.endAllDay date:self.end] result:result];
  [ZXParsedResult maybeAppend:self.location result:result];
  [ZXParsedResult maybeAppend:self.organizer result:result];
  [ZXParsedResult maybeAppendArray:self.attendees result:result];
  [ZXParsedResult maybeAppend:self.description result:result];
  return result;
}
 
/**
 * Parses a string as a date. RFC 2445 allows the start and end fields to be of type DATE (e.g. 20081021)
 * or DATE-TIME (e.g. 20081021T123000 for local time, or 20081021T123000Z for UTC).
 */
- (NSDate *)parseDate:(NSString *)when {
  NSArray *matches = [ZX_DATE_TIME matchesInString:when options:0 range:NSMakeRange(0, when.length)];
  if (matches.count == 0) {
    [NSException raise:NSInvalidArgumentException
                format:@"Invalid date"];
  }
  if (when.length == 8) {
    // Show only year/month/day
    return [[self buildDateFormat] dateFromString:when];
  } else {
    // The when string can be local time, or UTC if it ends with a Z
    if (when.length == 16 && [when characterAtIndex:15] == 'Z') {
      return [[self buildDateTimeFormat] dateFromString:[when substringToIndex:15]];
    } else {
      return [[self buildDateTimeFormat] dateFromString:when];
    }
  }
}
 
- (NSString *)format:(BOOL)allDay date:(NSDate *)date {
  if (date == nil) {
    return nil;
  }
  NSDateFormatter *format = [[NSDateFormatter alloc] init];
  format.dateFormat = allDay ? @"MMM d, yyyy" : @"MMM d, yyyy hh:mm:ss a";
  return [format stringFromDate:date];
}
 
- (long)parseDurationMS:(NSString *)durationString {
  if (durationString == nil) {
    return -1;
  }
  NSArray *m = [ZX_RFC2445_DURATION matchesInString:durationString options:0 range:NSMakeRange(0, durationString.length)];
  if (m.count == 0) {
    return -1;
  }
  long durationMS = 0;
  NSTextCheckingResult *match = m[0];
  for (int i = 0; i < sizeof(ZX_RFC2445_DURATION_FIELD_UNITS) / sizeof(long); i++) {
    if ([match rangeAtIndex:i + 1].location != NSNotFound) {
      NSString *fieldValue = [durationString substringWithRange:[match rangeAtIndex:i + 1]];
      if (fieldValue != nil) {
        durationMS += ZX_RFC2445_DURATION_FIELD_UNITS[i] * [fieldValue intValue];
      }
    }
  }
  return durationMS;
}
 
- (NSDateFormatter *)buildDateFormat {
  NSDateFormatter *format = [[NSDateFormatter alloc] init];
  format.dateFormat = @"yyyyMMdd";
  format.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
  return format;
}
 
- (NSDateFormatter *)buildDateTimeFormat {
  NSDateFormatter *format = [[NSDateFormatter alloc] init];
  format.dateFormat = @"yyyyMMdd'T'HHmmss";
  return format;
}
 
- (NSString *)description {
  return self.resultDescription;
}
 
@end