aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar dmaclach <dmaclach@gmail.com>2019-01-03 08:22:28 -0800
committerGravatar GitHub <noreply@github.com>2019-01-03 08:22:28 -0800
commit1057c99ca375a6b53ca079cede47fe2e9dce4d8b (patch)
treec4dec2f1575c27eba1121b4f7e54187e2ecb15be
parentf18111ecb67a4a33f2f4d91705d032dcf00b76f5 (diff)
Add GTMTimeUtils (#229)
Utilities for relatively common desire to know the launch time of an app, or the boot time of a device.
-rw-r--r--Foundation/GTMTimeUtils.h35
-rw-r--r--Foundation/GTMTimeUtils.m72
-rw-r--r--Foundation/GTMTimeUtilsTest.m45
-rw-r--r--GTM.xcodeproj/project.pbxproj12
-rw-r--r--GTMiPhone.xcodeproj/project.pbxproj10
-rw-r--r--GoogleToolboxForMac.podspec4
6 files changed, 178 insertions, 0 deletions
diff --git a/Foundation/GTMTimeUtils.h b/Foundation/GTMTimeUtils.h
new file mode 100644
index 0000000..9b49532
--- /dev/null
+++ b/Foundation/GTMTimeUtils.h
@@ -0,0 +1,35 @@
+//
+// GTMTimeUtils.h
+//
+// Copyright 2018 Google LLC
+//
+// 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 <Foundation/Foundation.h>
+
+// Return the date that the app was launched.
+NSDate *GTMAppLaunchDate(void);
+
+// Return the date that the device was started. Note on the simulator that this
+// returns the date that the computer was started, not the simulator.
+NSDate *GTMBootDate(void);
+
+// Convert a timeval to NSTimeInterval.
+NSTimeInterval GTMTimeValToNSTimeInterval(struct timeval time);
+
+// Timeval versions of the functions above if timevals are a more useful
+// structure to work with.
+struct timeval GTMBootTimeRelativeTo1970(void);
+struct timeval GTMAppLaunchTimeRelativeTo1970(void);
+
diff --git a/Foundation/GTMTimeUtils.m b/Foundation/GTMTimeUtils.m
new file mode 100644
index 0000000..2b551e4
--- /dev/null
+++ b/Foundation/GTMTimeUtils.m
@@ -0,0 +1,72 @@
+//
+// GTMTimeUtils.m
+//
+// Copyright 2018 Google LLC
+//
+// 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 "GTMTimeUtils.h"
+
+#include <sys/sysctl.h>
+
+#import "GTMDefines.h"
+
+NSTimeInterval GTMTimeValToNSTimeInterval(struct timeval time) {
+ return time.tv_sec + (time.tv_usec / (double)USEC_PER_SEC);
+}
+
+struct timeval GTMBootTimeRelativeTo1970(void) {
+ struct timeval bootTime = { 0, 0 };
+ int mib[2] = { CTL_KERN, KERN_BOOTTIME };
+ size_t bootTimeSize = sizeof(bootTime);
+ if (sysctl(mib, 2, &bootTime, &bootTimeSize, NULL, 0) != 0) {
+ _GTMDevAssert(errno == 0, @"sysctl error - %d", errno);
+ struct timeval invalid = { 0, 0 };
+ return invalid;
+ }
+ return bootTime;
+}
+
+struct timeval GTMAppLaunchTimeRelativeTo1970(void) {
+ id_t pid = getpid();
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, (int)pid };
+ const size_t mibSize = sizeof(mib) / sizeof(mib[0]);
+ size_t infoSize = 0;
+
+ // Get initial size of KERN_PROC data structure.
+ if (sysctl(mib, mibSize, NULL, &infoSize, NULL, 0) != 0) {
+ _GTMDevAssert(errno == 0, @"sysctl error - %d", errno);
+ struct timeval invalid = { 0, 0 };
+ return invalid;
+ }
+ struct kinfo_proc info;
+ if (sysctl(mib, mibSize, &info, &infoSize, NULL, 0) != 0) {
+ _GTMDevAssert(errno == 0, @"sysctl error - %d", errno);
+ struct timeval invalid = { 0, 0 };
+ return invalid;
+ }
+ return info.kp_proc.p_starttime;
+}
+
+NSDate *GTMAppLaunchDate() {
+ NSTimeInterval ti =
+ GTMTimeValToNSTimeInterval(GTMAppLaunchTimeRelativeTo1970());
+ return [NSDate dateWithTimeIntervalSince1970:ti];
+}
+
+NSDate *GTMBootDate() {
+ NSTimeInterval ti =
+ GTMTimeValToNSTimeInterval(GTMBootTimeRelativeTo1970());
+ return [NSDate dateWithTimeIntervalSince1970:ti];
+}
diff --git a/Foundation/GTMTimeUtilsTest.m b/Foundation/GTMTimeUtilsTest.m
new file mode 100644
index 0000000..471cad0
--- /dev/null
+++ b/Foundation/GTMTimeUtilsTest.m
@@ -0,0 +1,45 @@
+//
+// GTMTimeUtilsTest.m
+//
+// Copyright 2018 Google LLC
+//
+// 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 "GTMSenTestCase.h"
+#import "GTMTimeUtils.h"
+
+@interface GTMTimeUtilsTest : GTMTestCase
+@end
+
+@implementation GTMTimeUtilsTest
+
+- (void)testAppLaunchDate {
+ // Basic test to verify that "now" is after appLaunch.
+ NSDate *now = [NSDate date];
+ NSDate *appLaunch = GTMAppLaunchDate();
+
+ XCTAssertEqual([now compare:appLaunch], NSOrderedDescending,
+ @"now: %@ appLaunch: %@", now, appLaunch);
+}
+
+- (void)testBootDate {
+ // Basic test to verify that appLaunch occurred after boot.
+ NSDate *appLaunch = GTMAppLaunchDate();
+ NSDate *boot = GTMBootDate();
+
+ XCTAssertEqual([appLaunch compare:boot], NSOrderedDescending,
+ @"appLaunch: %@ boot: %@", appLaunch, boot);
+}
+
+@end
diff --git a/GTM.xcodeproj/project.pbxproj b/GTM.xcodeproj/project.pbxproj
index 6ab0a65..148019d 100644
--- a/GTM.xcodeproj/project.pbxproj
+++ b/GTM.xcodeproj/project.pbxproj
@@ -57,6 +57,9 @@
8B45A2AC0DA49C47001148C5 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B45A2A80DA49C47001148C5 /* main.m */; };
8B45A2B30DA49CA9001148C5 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; };
8B4D78080E40AFFA00EFEDD8 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B45A1990DA46AAA001148C5 /* QuartzCore.framework */; };
+ 8B5769A721CD77D600D924D3 /* GTMTimeUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B5769A521CD77D600D924D3 /* GTMTimeUtils.h */; };
+ 8B5769A821CD77D600D924D3 /* GTMTimeUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B5769A621CD77D600D924D3 /* GTMTimeUtils.m */; };
+ 8B5769AB21CD7ACF00D924D3 /* GTMTimeUtilsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B5769A921CD798000D924D3 /* GTMTimeUtilsTest.m */; };
8B61FDC00E4CDB8000FF9C21 /* GTMStackTrace.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B61FDBF0E4CDB8000FF9C21 /* GTMStackTrace.m */; };
8B6C15930F356E6400E51E5D /* GTMNSObject+KeyValueObserving.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B6C15910F356E6400E51E5D /* GTMNSObject+KeyValueObserving.h */; settings = {ATTRIBUTES = (Public, ); }; };
8B6C15940F356E6400E51E5D /* GTMNSObject+KeyValueObserving.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6C15920F356E6400E51E5D /* GTMNSObject+KeyValueObserving.m */; };
@@ -278,6 +281,9 @@
8B45A28A0DA49B99001148C5 /* GTMUIUnitTestingHarness.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GTMUIUnitTestingHarness.app; sourceTree = BUILT_PRODUCTS_DIR; };
8B45A2A70DA49C47001148C5 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
8B45A2A80DA49C47001148C5 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+ 8B5769A521CD77D600D924D3 /* GTMTimeUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GTMTimeUtils.h; sourceTree = "<group>"; };
+ 8B5769A621CD77D600D924D3 /* GTMTimeUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GTMTimeUtils.m; sourceTree = "<group>"; };
+ 8B5769A921CD798000D924D3 /* GTMTimeUtilsTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GTMTimeUtilsTest.m; sourceTree = "<group>"; };
8B5B4ABC15BF31050081A96C /* CodeCoverage.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = CodeCoverage.xcconfig; sourceTree = "<group>"; };
8B5B4ABD15BF31050081A96C /* CodeCoverageStatic.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = CodeCoverageStatic.xcconfig; sourceTree = "<group>"; };
8B5B4ABE15BF31050081A96C /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
@@ -719,6 +725,9 @@
F48FE2920D198D24009257D2 /* GTMSystemVersion.h */,
F48FE2930D198D24009257D2 /* GTMSystemVersion.m */,
F48FE2E10D198E4C009257D2 /* GTMSystemVersionTest.m */,
+ 8B5769A521CD77D600D924D3 /* GTMTimeUtils.h */,
+ 8B5769A621CD77D600D924D3 /* GTMTimeUtils.m */,
+ 8B5769A921CD798000D924D3 /* GTMTimeUtilsTest.m */,
);
path = Foundation;
sourceTree = "<group>";
@@ -787,6 +796,7 @@
F93207DE0F4B82DB005F37EA /* GTMSQLite.h in Headers */,
F42E094C0D199BBF00D5DDE0 /* GTMGeometryUtils.h in Headers */,
F42E094F0D199BBF00D5DDE0 /* GTMNSBezierPath+RoundRect.h in Headers */,
+ 8B5769A721CD77D600D924D3 /* GTMTimeUtils.h in Headers */,
F42E09510D199BBF00D5DDE0 /* GTMNSString+HTML.h in Headers */,
F42E09540D199BBF00D5DDE0 /* GTMSystemVersion.h in Headers */,
F428FF030D48E55E00382ED1 /* GTMNSBezierPath+CGPath.h in Headers */,
@@ -1122,6 +1132,7 @@
8BFE6E911282371200B5C894 /* GTMNSObject+KeyValueObservingTest.m in Sources */,
8BFE6E921282371200B5C894 /* GTMNSScanner+JSONTest.m in Sources */,
8BFE6E941282371200B5C894 /* GTMNSString+FindFolderTest.m in Sources */,
+ 8B5769AB21CD7ACF00D924D3 /* GTMTimeUtilsTest.m in Sources */,
8BFE6E951282371200B5C894 /* GTMNSString+HTMLTest.m in Sources */,
8BFE6E971282371200B5C894 /* GTMNSString+URLArgumentsTest.m in Sources */,
8BFE6E981282371200B5C894 /* GTMNSString+XMLTest.m in Sources */,
@@ -1154,6 +1165,7 @@
33C374390DD8D44800E97817 /* GTMNSDictionary+URLArguments.m in Sources */,
8B7DCBBD0DFF0F5D0017E983 /* GTMMethodCheck.m in Sources */,
F41A6F830E02EC3600788A6C /* GTMSignalHandler.m in Sources */,
+ 8B5769A821CD77D600D924D3 /* GTMTimeUtils.m in Sources */,
F425977A0E23FE3A003BEA3E /* GTMNSString+FindFolder.m in Sources */,
F98680C30E2C163D00CEE8BF /* GTMLogger.m in Sources */,
F98681970E2C20C800CEE8BF /* GTMLogger+ASL.m in Sources */,
diff --git a/GTMiPhone.xcodeproj/project.pbxproj b/GTMiPhone.xcodeproj/project.pbxproj
index 82f5de5..5974e05 100644
--- a/GTMiPhone.xcodeproj/project.pbxproj
+++ b/GTMiPhone.xcodeproj/project.pbxproj
@@ -14,6 +14,8 @@
64D0F5ED0FD3E68400506CC7 /* GTMUIImage+Resize_100x100.png in Resources */ = {isa = PBXBuildFile; fileRef = 64D0F5DB0FD3E68400506CC7 /* GTMUIImage+Resize_100x100.png */; };
67A7820C0E00927400EBF506 /* GTMIPhoneUnitTestDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 67A7820B0E00927400EBF506 /* GTMIPhoneUnitTestDelegate.m */; };
8B2C72EB1D9EBBA10027BD14 /* GTMTestTimerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B2C72EA1D9EBBA10027BD14 /* GTMTestTimerTest.m */; };
+ 8B5769B121CD7C8300D924D3 /* GTMTimeUtilsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B5769AD21CD7C7600D924D3 /* GTMTimeUtilsTest.m */; };
+ 8B5769B221CD7C8600D924D3 /* GTMTimeUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B5769AC21CD7C7600D924D3 /* GTMTimeUtils.m */; };
8B7651D81D9C872F00DB2C59 /* GTMLogger+ASL.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B7651D61D9C872C00DB2C59 /* GTMLogger+ASL.m */; };
8B7651D91D9C873200DB2C59 /* GTMLogger+ASLTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B7651D71D9C872C00DB2C59 /* GTMLogger+ASLTest.m */; };
8B7651E11D9C89B800DB2C59 /* GTMRoundedRectPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B7651E01D9C89B800DB2C59 /* GTMRoundedRectPath.m */; };
@@ -135,6 +137,9 @@
8B3AA8F10E032FC7007E31B5 /* GTMNSString+URLArguments.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+URLArguments.m"; sourceTree = "<group>"; };
8B3AA8F20E032FC7007E31B5 /* GTMNSString+URLArgumentsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+URLArgumentsTest.m"; sourceTree = "<group>"; };
8B3AA9330E0336AC007E31B5 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; };
+ 8B5769AC21CD7C7600D924D3 /* GTMTimeUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMTimeUtils.m; sourceTree = "<group>"; };
+ 8B5769AD21CD7C7600D924D3 /* GTMTimeUtilsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMTimeUtilsTest.m; sourceTree = "<group>"; };
+ 8B5769AE21CD7C7600D924D3 /* GTMTimeUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMTimeUtils.h; sourceTree = "<group>"; };
8B6C18710F3769D200E51E5D /* GTMNSObject+KeyValueObserving.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSObject+KeyValueObserving.h"; sourceTree = "<group>"; };
8B6C18720F3769D200E51E5D /* GTMNSObject+KeyValueObserving.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSObject+KeyValueObserving.m"; sourceTree = "<group>"; };
8B6C18730F3769D200E51E5D /* GTMNSObject+KeyValueObservingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSObject+KeyValueObservingTest.m"; sourceTree = "<group>"; };
@@ -346,6 +351,9 @@
8BC047760DAE928A00C2D1CA /* Foundation */ = {
isa = PBXGroup;
children = (
+ 8B5769AE21CD7C7600D924D3 /* GTMTimeUtils.h */,
+ 8B5769AC21CD7C7600D924D3 /* GTMTimeUtils.m */,
+ 8B5769AD21CD7C7600D924D3 /* GTMTimeUtilsTest.m */,
0BBC768810FEF61D0006FABE /* GTMStringEncoding.h */,
0BBC768910FEF61D0006FABE /* GTMStringEncoding.m */,
0BBC768A10FEF61D0006FABE /* GTMStringEncodingTest.m */,
@@ -627,6 +635,7 @@
8BF753DB1D9DB3080010A295 /* GTMSQLite.m in Sources */,
8B82CF0C1D9C1C3B007182AA /* GTMNSNumber+64Bit.m in Sources */,
8B82CF041D9C1C3B007182AA /* GTMLightweightProxy.m in Sources */,
+ 8B5769B221CD7C8600D924D3 /* GTMTimeUtils.m in Sources */,
8B82CF0B1D9C1C3B007182AA /* GTMNSFileHandle+UniqueName.m in Sources */,
8B7651E11D9C89B800DB2C59 /* GTMRoundedRectPath.m in Sources */,
8B82CF021D9C1C3B007182AA /* GTMStringEncoding.m in Sources */,
@@ -666,6 +675,7 @@
8B82CF391D9C2373007182AA /* GTMLoggerRingBufferWriterTest.m in Sources */,
8B7651D91D9C873200DB2C59 /* GTMLogger+ASLTest.m in Sources */,
8B82CF351D9C2353007182AA /* GTMMethodCheckTest.m in Sources */,
+ 8B5769B121CD7C8300D924D3 /* GTMTimeUtilsTest.m in Sources */,
8B82CF481D9C2373007182AA /* GTMSystemVersionTest.m in Sources */,
8B82CF2C1D9C1CC5007182AA /* GTMStringEncodingTest.m in Sources */,
8B82CF4D1D9C2385007182AA /* GTMUIFont+LineHeightTest.m in Sources */,
diff --git a/GoogleToolboxForMac.podspec b/GoogleToolboxForMac.podspec
index 2a78a00..2d850c0 100644
--- a/GoogleToolboxForMac.podspec
+++ b/GoogleToolboxForMac.podspec
@@ -151,6 +151,10 @@ Pod::Spec.new do |s|
sp.dependency 'GoogleToolboxForMac/Defines', "#{s.version}"
end
+ s.subspec 'TimeUtils' do |sp|
+ sp.source_files = 'Foundation/GTMTimeUtils.{h,m}'
+ end
+
s.subspec 'iPhone' do |sp|
sp.platform = :ios, '5.0'
sp.source_files =