sugarcube 0.8.7 → 0.9
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +98 -42
- data/lib/sugarcube/nsdate.rb +24 -37
- data/lib/sugarcube/nsdate_delta.rb +114 -0
- data/lib/sugarcube/numeric.rb +4 -0
- data/lib/sugarcube/version.rb +1 -1
- metadata +7 -6
data/README.md
CHANGED
@@ -88,43 +88,38 @@ end
|
|
88
88
|
NSDate
|
89
89
|
--------
|
90
90
|
|
91
|
-
NSDate
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
(
|
91
|
+
`NSDate` objects are converted to `Time` objects automatically by rubymotion.
|
92
|
+
That's the good news. The bad news? That still doesn't help a lot with some of
|
93
|
+
the everyday date&time crap we have to deal with. (I hate dates, especially
|
94
|
+
recurring events)
|
95
|
+
|
96
|
+
1. Adds the following methods to get date and time components: `date, time, datetime`.
|
97
|
+
|
98
|
+
These methods return arrays. Comparing dates, times, or both become
|
99
|
+
simple `date1.date == date2.date`.
|
100
|
+
2. While I would love to support `date + 1.month` and have that support "smart"
|
101
|
+
calendar math (e.g. "2/13/2013" + 1.month => "3/13/2013"), I can't fudge with
|
102
|
+
the return value of `1.month` (=> `Fixnum`), and I won't make the terrible
|
103
|
+
assumption that "30 days of seconds is *about* one month". So instead, a new
|
104
|
+
method that accepts date components as options is introduced. `date.delta(month:1)`
|
105
|
+
3. Something that is often done is checking whether two dates are the same,
|
106
|
+
ignoring the time components. `start_of_day` and `end_of_day` methods help
|
107
|
+
you here. They are akin to `floor` and `ceil`, if you consider the time to
|
108
|
+
be the "floating" component, and the date to be the nearest "integer".
|
109
|
+
|
110
|
+
```
|
111
|
+
(main)> now = NSDate.new # Time.new is the same thing
|
103
112
|
=> 2012-09-13 09:19:06 -0600
|
104
|
-
(main)> now.
|
105
|
-
=> 2012
|
106
|
-
(main)> now.month
|
107
|
-
=> 9
|
108
|
-
(main)> now.day
|
109
|
-
=> 13
|
110
|
-
(main)> now.hour
|
111
|
-
=> 9
|
112
|
-
(main)> now.minute
|
113
|
-
=> 19
|
114
|
-
(main)> now.second
|
115
|
-
=> 6
|
116
|
-
(main)> now.weekday
|
117
|
-
=> 5
|
118
|
-
(main)> now.ymd
|
113
|
+
(main)> now.date
|
119
114
|
=> [2012, 9, 13]
|
120
|
-
(main)> now.
|
115
|
+
(main)> now.time
|
121
116
|
=> [9, 19, 6]
|
122
117
|
(main)> now.datetime
|
123
118
|
=> [2012, 9, 13, 9, 19, 6]
|
124
119
|
```
|
125
120
|
|
126
|
-
And it is easy to add seconds to the date
|
127
|
-
|
121
|
+
And it is easy to add seconds to the date using the time-related methods added
|
122
|
+
to `Numeric`, and the useful `start_of_day`/`end_of_day` methods.
|
128
123
|
|
129
124
|
```ruby
|
130
125
|
(main)> now + 5
|
@@ -135,11 +130,15 @@ time-related methods added to `Numeric`!
|
|
135
130
|
=> 2012-09-13 09:24:06 -0600
|
136
131
|
(main)> now + 5.days
|
137
132
|
=> 2012-09-18 09:19:06 -0600
|
133
|
+
(main)> now.start_of_day
|
134
|
+
=> 2012-09-13 00:00:00 -0600
|
135
|
+
(main)> now.end_of_day
|
136
|
+
=> 2012-09-14 00:00:00 -0600
|
138
137
|
```
|
139
138
|
|
140
|
-
Time zone objects are available, but the `utc_offset` is a little
|
141
|
-
|
142
|
-
|
139
|
+
Time zone objects are available, but the `Time#utc_offset` method is a little
|
140
|
+
more useful. It returns the offset *in seconds*, so divide by `1.0.hour` to get
|
141
|
+
the offset in hours. `utc_offset` is built into `Time`, not added by SugarCube.
|
143
142
|
|
144
143
|
```ruby
|
145
144
|
(main)> now.timezone
|
@@ -148,10 +147,63 @@ to get the offset in hours.
|
|
148
147
|
=> "America/Denver"
|
149
148
|
(main)> now.utc_offset
|
150
149
|
=> -21600
|
151
|
-
(main)> now.utc_offset / 1.
|
152
|
-
=> -6
|
150
|
+
(main)> now.utc_offset / 1.hour
|
151
|
+
=> -6
|
153
152
|
```
|
154
153
|
|
154
|
+
The `delta` method is smart.
|
155
|
+
|
156
|
+
```ruby
|
157
|
+
(main)> feb_28_2012_stamp = 1330473600 # what, you don't have this memorized?
|
158
|
+
=> 1330473600
|
159
|
+
(main)> feb_28_2012 = Time.at(feb_28_2012_stamp)
|
160
|
+
=> 2012-02-28 17:00:00 -0700
|
161
|
+
|
162
|
+
(main)> feb_28_2012.delta(hours:1)
|
163
|
+
=> 2012-02-28 18:00:00 -0700
|
164
|
+
(main)> feb_28_2012.delta(hours:2)
|
165
|
+
=> 2012-02-28 19:00:00 -0700
|
166
|
+
|
167
|
+
(main)> feb_28_2012.delta(days:1)
|
168
|
+
=> 2012-02-29 17:00:00 -0700
|
169
|
+
(main)> feb_28_2012.delta(days:2)
|
170
|
+
=> 2012-03-01 17:00:00 -0700
|
171
|
+
|
172
|
+
(main)> feb_28_2012.delta(months:1)
|
173
|
+
=> 2012-03-28 17:00:00 -0600 # look, the time didn't change, event though there was a DST change!
|
174
|
+
(main)> feb_28_2012.delta(months:1, hours:0)
|
175
|
+
=> 2012-03-28 18:00:00 -0600 # disable the DST fix by specifying hours, minutes, or seconds (a "precise" delta)
|
176
|
+
|
177
|
+
(main)> feb_28_2012.delta(years:1)
|
178
|
+
=> 2013-02-28 17:00:00 -0700
|
179
|
+
(main)> feb_28_2012.delta(days:1, years:1)
|
180
|
+
=> 2013-02-28 17:00:00 -0700
|
181
|
+
(main)> feb_28_2012.delta(days:1, years:1, months:1)
|
182
|
+
=> 2013-03-29 17:00:00 -0600
|
183
|
+
|
184
|
+
(main)> jan_29_2013 = feb_28_2012.delta(days:1, months:11)
|
185
|
+
=> 2013-01-29 17:00:00 -0700
|
186
|
+
|
187
|
+
# what is 1/29/2013 plus two months? easy! march 29, 2013
|
188
|
+
(main)> jan_29_2013.delta(months:2)
|
189
|
+
=> 2013-03-29 17:00:00 -0600
|
190
|
+
|
191
|
+
# yeah, smart guy? well then what is 1/29/2013 plus ONE month.
|
192
|
+
# it's feb 28th. trust me. when someone says "see you in a month!"
|
193
|
+
# they mean "next month", not "in the early part of two months in the future",
|
194
|
+
# which is where the math will take you if you don't add a "day of month" correction.
|
195
|
+
(main)> jan_29_2013.delta(months:1)
|
196
|
+
=> 2013-02-28 17:00:00 -0700
|
197
|
+
|
198
|
+
# does it work in reverse? fuuuuuu...
|
199
|
+
(main)> jan_29_2013.delta(months:-11)
|
200
|
+
=> 2012-02-29 17:00:00 -0700
|
201
|
+
# ...ck yeah! :-)
|
202
|
+
|
203
|
+
# unfortunately you will end up with stuff like this:
|
204
|
+
(main)> feb_28_2012 == feb_28_2012.delta(days:1, months:12).delta(months:-12)
|
205
|
+
=> true
|
206
|
+
```
|
155
207
|
|
156
208
|
NSURL
|
157
209
|
-------
|
@@ -465,16 +517,20 @@ after 1.minute do
|
|
465
517
|
end
|
466
518
|
|
467
519
|
# other time-related methods
|
520
|
+
# for compatibility with Time methods, the mins/secs (and min/sec) aliases are provided. Personally,
|
521
|
+
# I like the more verbose minutes/seconds.
|
468
522
|
1.second || 2.seconds
|
469
|
-
1.
|
470
|
-
1.
|
471
|
-
1.
|
472
|
-
1.
|
473
|
-
#
|
523
|
+
1.sec || 2.secs # aliases
|
524
|
+
1.minute || 2.minutes # 1.minute = 60 seconds
|
525
|
+
1.min || 2.mins # aliases
|
526
|
+
1.hour || 2.hours # 1.hour = 60 minutes
|
527
|
+
1.day || 2.days # 1.day = 24 hours
|
528
|
+
1.week || 2.weeks # 1.week = 7 days
|
529
|
+
# sensible values for 'month' and 'year', even though we all know you can't
|
474
530
|
# **really** define them this way (go back to python if you find your brain
|
475
531
|
# hemorrhaging):
|
476
|
-
1.month || 2.months # 30 days
|
477
|
-
1.year || 2.years # 365 days
|
532
|
+
1.month || 2.months # 1.month = 30 days
|
533
|
+
1.year || 2.years # 1.year = 365 days
|
478
534
|
```
|
479
535
|
|
480
536
|
NSUserDefaults
|
data/lib/sugarcube/nsdate.rb
CHANGED
@@ -1,62 +1,49 @@
|
|
1
1
|
class NSDate
|
2
|
-
def
|
3
|
-
return
|
2
|
+
def timezone
|
3
|
+
return _calendar_components(NSTimeZoneCalendarUnit).timeZone
|
4
4
|
end
|
5
|
+
alias timeZone timezone
|
5
6
|
|
6
|
-
def
|
7
|
-
return
|
7
|
+
def utc_offset
|
8
|
+
return self.timezone.secondsFromGMT
|
8
9
|
end
|
9
10
|
|
10
|
-
def
|
11
|
-
|
11
|
+
def leap?
|
12
|
+
self.year % 4 == 0 and self.year % 100 != 0 or self.year % 400 == 0
|
12
13
|
end
|
13
14
|
|
14
|
-
def
|
15
|
+
def date
|
15
16
|
return [self.year, self.month, self.day]
|
16
17
|
end
|
17
18
|
|
18
|
-
def
|
19
|
-
return
|
20
|
-
end
|
21
|
-
|
22
|
-
def hour
|
23
|
-
return components(NSHourCalendarUnit).hour
|
24
|
-
end
|
25
|
-
|
26
|
-
def minute
|
27
|
-
return components(NSMinuteCalendarUnit).minute
|
19
|
+
def time
|
20
|
+
return [self.hour, self.min, self.sec]
|
28
21
|
end
|
29
22
|
|
30
|
-
def
|
31
|
-
return
|
32
|
-
end
|
33
|
-
|
34
|
-
def timezone
|
35
|
-
return components(NSTimeZoneCalendarUnit).timeZone
|
23
|
+
def datetime
|
24
|
+
return [self.year, self.month, self.day, self.hour, self.min, self.sec]
|
36
25
|
end
|
37
|
-
alias timeZone timezone
|
38
26
|
|
39
|
-
def
|
40
|
-
|
27
|
+
def start_of_day
|
28
|
+
time_interval = self.hour.hours + self.min.minutes + self.sec
|
29
|
+
return self - time_interval
|
41
30
|
end
|
42
31
|
|
43
|
-
def
|
44
|
-
|
32
|
+
def end_of_day
|
33
|
+
time_interval = (23 - self.hour).hours + (59 - self.min).minutes + 60 - self.sec
|
34
|
+
return self + time_interval
|
45
35
|
end
|
46
36
|
|
47
|
-
def
|
48
|
-
|
37
|
+
def days_in_month
|
38
|
+
NSCalendar.currentCalendar.rangeOfUnit(NSDayCalendarUnit, inUnit:NSMonthCalendarUnit, forDate:self).length
|
49
39
|
end
|
50
40
|
|
51
|
-
def
|
52
|
-
|
41
|
+
def days_in_year
|
42
|
+
NSCalendar.currentCalendar.rangeOfUnit(NSDayCalendarUnit, inUnit:NSYearCalendarUnit, forDate:self).length
|
53
43
|
end
|
54
44
|
|
55
45
|
private
|
56
|
-
def
|
57
|
-
|
58
|
-
@@calendar = NSCalendar.alloc.initWithCalendarIdentifier(NSGregorianCalendar)
|
59
|
-
end
|
60
|
-
return @@calendar.components(components, fromDate:self)
|
46
|
+
def _calendar_components(components)
|
47
|
+
return NSCalendar.currentCalendar.components(components, fromDate:self)
|
61
48
|
end
|
62
49
|
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
class NSDate
|
2
|
+
|
3
|
+
def +(val)
|
4
|
+
if val.is_a?(Hash)
|
5
|
+
return self.delta(val)
|
6
|
+
end
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def delta(_components)
|
11
|
+
components = {}.update(_components)
|
12
|
+
is_very_specific = components.has_key?(:seconds)
|
13
|
+
is_very_specific ||= components.has_key?(:minutes)
|
14
|
+
is_very_specific ||= components.has_key?(:hours)
|
15
|
+
|
16
|
+
y = components.delete(:years) || 0
|
17
|
+
mo = components.delete(:months) || 0
|
18
|
+
d = components.delete(:days) || 0
|
19
|
+
h = components.delete(:hours) || 0
|
20
|
+
mi = components.delete(:minutes) || 0
|
21
|
+
s = components.delete(:seconds) || 0
|
22
|
+
w = components.delete(:weeks) || 0
|
23
|
+
raise "Unknown arguments #{components.keys}" unless components.empty?
|
24
|
+
|
25
|
+
is_dst = self.dst?
|
26
|
+
|
27
|
+
delta = s
|
28
|
+
# todo: leap second adjustment? can leap seconds be detected?
|
29
|
+
delta += mi.minutes
|
30
|
+
delta += h.hours
|
31
|
+
|
32
|
+
delta += d.days
|
33
|
+
delta += w.weeks
|
34
|
+
return_date = self + delta
|
35
|
+
|
36
|
+
# using days_in_month, this is pretty easy. 12 mos per year IS a constant,
|
37
|
+
# and then we just keep adding the number of days in the month (or last month
|
38
|
+
# if we're going backwards). The curve ball is that when we are in day
|
39
|
+
# 29,30,31, we might jump forward a month and "fall off" to the next month.
|
40
|
+
# In this case, we add a correction. We will always move forwards or
|
41
|
+
# backwards until the return_date.day is correct.
|
42
|
+
mo += y * 12
|
43
|
+
if mo != 0
|
44
|
+
if return_date.day > 28
|
45
|
+
# we will try and preserve this day
|
46
|
+
correct_day_of_month = return_date.day
|
47
|
+
else
|
48
|
+
correct_day_of_month = nil
|
49
|
+
end
|
50
|
+
|
51
|
+
if mo > 0
|
52
|
+
mo.times do
|
53
|
+
delta = return_date.days_in_month
|
54
|
+
return_date += delta.days
|
55
|
+
|
56
|
+
# if the day_of_month is wrong, it must be because we either added PAST
|
57
|
+
# the correct month (so roll back), or because we WERE rolled back and
|
58
|
+
# when we moved forward a month, we were left back at the smaller day.
|
59
|
+
if correct_day_of_month
|
60
|
+
if return_date.day < 28
|
61
|
+
return_date -= return_date.day.days
|
62
|
+
elsif return_date.day < correct_day_of_month
|
63
|
+
fix = correct_day_of_month > return_date.days_in_month ? return_date.days_in_month : correct_day_of_month
|
64
|
+
return_date += (fix - return_date.day).days
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
elsif mo < 0
|
69
|
+
(-mo).times do
|
70
|
+
# subtract *last* months number of days.
|
71
|
+
# there is a REALLY rare case where subtracting return_date.day is one
|
72
|
+
# hour short of "last month" and so you end up with THIS month. there
|
73
|
+
# is NEVER a case when subtracting return_date.day+1 days is NOT
|
74
|
+
# "previous month". dates. :-| f-em.
|
75
|
+
delta = (return_date - (return_date.day+1).days).days_in_month
|
76
|
+
return_date -= delta.days
|
77
|
+
# same correction as above
|
78
|
+
if correct_day_of_month
|
79
|
+
if return_date.day < 28
|
80
|
+
return_date -= return_date.day.days
|
81
|
+
elsif return_date.day < correct_day_of_month
|
82
|
+
fix = correct_day_of_month > return_date.days_in_month ? return_date.days_in_month : correct_day_of_month
|
83
|
+
return_date += (fix - return_date.day).days
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# DST adjustment, unless minutes, hours, or seconds were specified.
|
91
|
+
#
|
92
|
+
# the thinking here is that if they WERE specified, the delta should be
|
93
|
+
# accurate to that granularity. if they were omitted, the hour component
|
94
|
+
# should not change, even though an off-by-one adjustment is needed
|
95
|
+
#
|
96
|
+
# for instance. 3/10/2012 is not in DST. 3/11/2012 IS.
|
97
|
+
# Time.at(3/10/2012) # => 2012-03-10 17:00:00 -0700
|
98
|
+
# Time.at(3/10/2012).delta(days:1) # => 2012-03-11 17:00:00 -0600
|
99
|
+
#
|
100
|
+
# notice the time is the SAME, even though the time zone is different. BUT:
|
101
|
+
# Time.at(3/10/2012).delta(hours:24) # => 2012-03-11 17:00:00 -0600
|
102
|
+
# Time.at(3/10/2012).delta(hours:25) # => 2012-03-11 18:00:00 -0600
|
103
|
+
unless return_date.dst? == is_dst or is_very_specific
|
104
|
+
if is_dst
|
105
|
+
return_date += 1.hour
|
106
|
+
else
|
107
|
+
return_date -= 1.hour
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
return return_date
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
data/lib/sugarcube/numeric.rb
CHANGED
data/lib/sugarcube/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sugarcube
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: '0.9'
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,11 +10,11 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-09-
|
13
|
+
date: 2012-09-14 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rake
|
17
|
-
requirement: &
|
17
|
+
requirement: &70323060274480 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ! '>='
|
@@ -22,10 +22,10 @@ dependencies:
|
|
22
22
|
version: '0'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements: *
|
25
|
+
version_requirements: *70323060274480
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: rspec
|
28
|
-
requirement: &
|
28
|
+
requirement: &70323060273940 !ruby/object:Gem::Requirement
|
29
29
|
none: false
|
30
30
|
requirements:
|
31
31
|
- - ! '>='
|
@@ -33,7 +33,7 @@ dependencies:
|
|
33
33
|
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
|
-
version_requirements: *
|
36
|
+
version_requirements: *70323060273940
|
37
37
|
description: ! '== Description
|
38
38
|
|
39
39
|
|
@@ -75,6 +75,7 @@ files:
|
|
75
75
|
- lib/sugarcube/fixnum.rb
|
76
76
|
- lib/sugarcube/notifications.rb
|
77
77
|
- lib/sugarcube/nsdate.rb
|
78
|
+
- lib/sugarcube/nsdate_delta.rb
|
78
79
|
- lib/sugarcube/nsindexpath.rb
|
79
80
|
- lib/sugarcube/nsindexset.rb
|
80
81
|
- lib/sugarcube/nsstring.rb
|