sugarcube 0.8.7 → 0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|