Making NSTimeZone Trouble Free in Unit Tests - objective-c

So here's the problem with doing unit tests of calendar code in Objective-C: the Timezone contains the information about daylight savings, so you write a test in the summer, expecting sunrise on June 10th to occur at 6:02 am. Your test passes, later, you are running the test when it's not DST and you get a failure, because when you get the timezone, daylightsavings is turned off.
There doesn't seem to be a simple way to just tell it to give you the timezone with dst turned on?
I was thinking about doing a category so that I would intercept the timezone call but that sounds super messy as I don't know what date you are manipulating.
Of course, I could write all my tests to check the timezone setting and then just shift all my expectations but that sounds like the worst of all possible choices.

Region-specific timezones must take into account daylight saving time in order to accurately calculate intervals between two dates (and times). If you aren't interested in this, perhaps you could use a UTC “timezone” instead, which don't change at all.
For example, New Zealand Standard Time is defined as UTC+12:00, and New Zealand Daylight Saving Time is defined as UTC+13:00. Although the local time in New Zealand differs during Daylight Saving Time, the times in UTC+12:00 remain the same (that is, every other country that also uses UTC+12:00 don't magically move forward just because Daylight Saving Time has commenced in New Zealand).
You can achieve this simply by providing that UTC offset as the name:
NSTimeZone *utc_plus12 = [NSTimeZone timeZoneWithName:#"UTC+12:00"];
Find out what UTC offset your region's daylight saving time is based on and use that.

I encountered the similar problem. Finally I found OCMock and saved my life.
If you are using Cocoapods, that will be great! Here is the steps:
Edit your Podfile and add a lines to import OCMock.
target 'YourProjectTests' do
pod 'OCMock'
end
Add import in your unit test class
#import <OCMock/OCMock.h>
Write your test case like this
- (void)testLocalTimezoneFromPDT {
NSTimeZone* timeZone = [NSTimeZone timeZoneWithAbbreviation:#"PDT"];
id timeZoneMock = OCMClassMock([NSTimeZone class]);
OCMStub([timeZoneMock localTimeZone]).andReturn(timeZone);
// Implement your test case
// XCTAssertEqual(...);
}
That code will mock the original [NSTimeZone localTimeZone] method and return
the static value.
In this example, we return the timezone from Pacific Daylight Time (PDT) [GMT-07:00].
When your class call [NSTimeZone localTimeZone], your class will get the timezone we set with OCMock.
Hopes this answer will help you.

Related

ASP.NET Core API ISO8601 not parsed in local time when suffix is 'Z'

Scenario:
I have a REST-API that takes a POST-Request. As Body-Data there is passed a Datetime in ISO8601 Format.
{
"validate": "2019-12-02T23:00:00Z",
"configuration": {},
"sortId": 1
}
With Modelbinding in MVC the Datetime gets parsed automaticly. The variable should be in the local timezone of the api-server. In this case Europe/Berlin. I would expect the time to be (refering to the example) to be on 2019-12-03:00:00:00. But this is not the case. It is still one hour off.
But when i post the following:
{
"validate": "2019-12-02T23:00:00+00:00",
"configuration": {},
"sortId": 1
}
The parsing into the local timezone works as expected. Because the Client posting the data is written in JS and uses the default Date.toISOString() function, i always get a 'Z' in the ending. According to the ISO8601 this is totally fine.
Z explicitly means UTC. +00:00 doesn't. The UK is at 00:00 now, but 01:00 in the summer. In 1970, summer time (01:00) was used for the entire year.
There are a couple of concepts involved here, and a bit of history. First of all, DateTime has no concept of offset or timezone. A DateTime can only be UTC, Local or Undefined, according to its Kind property.
Using DateTime means that the offset information is lost. The resulting value needs to be converted to something. To do that, the machine's offset is used. That is, the web service machine's offset, not the database server's.
And then, our container or application fails over to a machine with a default UTC timezone instead of our configured timezone. Over the weekend.
It's worth reading Falsehoods programmers believe about time, especially 8. The machine that a program runs on will always be in the GMT time zone.
A better solution would be to use DateTimeOffset, although even that won't be able to handle DST rule changes.
An even better solution would be to use IANA timezone names and pass Europe/Berlin instead of offsets. That's not common usage though. Airlines at least post flight times both with offsets and timezone names.
DateTime Parsing rules
DateTime parsing rules convert Z or offsets to Local with conversion and anything else to Unspecified without conversion. This sounds strange but consider that DateTime was built to work on desktop applications, where Local time makes sense.
This code
var values=new[]{
"2019-12-02T23:00:00+00:00",
"2019-12-02T23:00:00Z",
"2019-12-02T23:00:00"
};
foreach(var value in values)
{
var dt=DateTime.Parse(value);
Console.WriteLine($"{dt:s}\t{dt.Kind}");
}
Produces :
2019-12-03T01:00:00 Local
2019-12-03T01:00:00 Local
2019-12-02T23:00:00 Unspecified
The UTC kind is lost here and, as a developer of flight reservation systems, I don't like that at all. Flight times are in the airport's local time, not my servers. Now I'll have to convert that value back to UTC or something, before saving it to a database. I have to convert it back to the original airport offset to print the tickets and send notifications.
And I'll have to reimburse you guys if there's any error, even if it's due to an airline's error.
JSON.NET (really API) parsing rules
JSON.NET on the other hand, parses strings with Z to UTC, Offsets to Local with conversion and no offset to Undefined. For an API that receives requests from anywhere, UTC is far more useful. Most hosters and cloud services provide UTC machines by default too.
This code :
class It
{
public DateTime Dt{get;set;}
}
var values=new[]{
"{'dt':'2019-12-02T23:00:00+00:00'}",
"{'dt':'2019-12-02T23:00:00Z'}",
"{'dt':'2019-12-02T23:00:00'}"
};
foreach(var value in values)
{
var dt=JsonConvert.DeserializeObject<It>(value).Dt;
Console.WriteLine($"{dt:s}\t{dt.Kind}");
}
Produces :
2019-12-03T01:00:00 Local
2019-12-02T23:00:00 Utc
2019-12-02T23:00:00 Unspecified
Better, but I don't like it. I still lose information.
JSON with DateTimeOffset
DateTimeOffset includes the offset so no information is lost. Unspecified offsets are treated as Local time. This snippet :
class It
{
public DateTimeOffset Dt{get;set;}
}
void Main()
{
var values=new[]{
"{'dt':'2019-12-02T23:00:00+00:00'}",
"{'dt':'2019-12-02T23:00:00Z'}",
"{'dt':'2019-12-02T23:00:00'}"
};
foreach(var value in values)
{
var dt=JsonConvert.DeserializeObject<It>(value).Dt;
Console.WriteLine($"{dt:o}");
}
}
Produces :
2019-12-02T23:00:00.0000000+00:00
2019-12-02T23:00:00.0000000+00:00
2019-12-02T23:00:00.0000000+02:00
Why not UTC everywhere?
Because that loses a lot of information that could easily make that time value unusable. There are a lot of SO questions where the poster tried to compare UTC time and got unexpected results because of DST effects or even DST changes.
A few years back Egypt changed its DST rules with a couple of weeks notice. Airlines and online agents weren't thrilled.
Besides, what if there are more than two timezones in play? International flights rarely land in the same timezone, so storing UTC doesn't work. Airlines don't publish schedules in UTC, they publish them as local time, with offset and IANA TZ names as additional info.
It's worth reading Falsehoods programmers believe about time, especially the parts that refer to UTC or GMT.

TimeZoneInfo which was defined by local machine not found by local machine?

I'm having trouble with timezones, a class I've built finds the local timezone of the user using:
Dim Timezone As String = TimeZoneInfo.Local.ToString
This is then stored in a MySQL DB.
When I pull the timezone, I compare it once again with the local timezone of the user to convert the time to the local timezone:
Dim D_0 As DateTime
Dim D_1 As DateTime
Dim Event_Timezone As TimeZoneInfo
Dim User_Timezone As TimeZoneInfo
Event_Timezone = TimeZoneInfo.FindSystemTimeZoneById(U_1(5).ToString)
User_Timezone = TimeZoneInfo.Local()
D_0 = TimeZoneInfo.ConvertTime(U_1(i + 4), Event_Timezone, User_Timezone)
D_1 = TimeZoneInfo.ConvertTime(U_1(i + 8), Event_Timezone, User_Timezone)
This returns the following error:
The time zone ID '(UTC-05:00) Eastern Time (US & Canada)' was not found on the local computer.
This is a confusing error because this is the timezone the local computer specified only seconds earlier. It works with nearly every other timezone. Is there a better way I should be doing this? Does anyone know why a timezone defined by the local machine is not found by the local machine seconds later?
You're calling ToString() on TimeZoneInfo - that doesn't give the ID, it gives the display name. Often they're the same in English cultures, but they don't have to be, and usually won't be in non-English cultures.
Basically you should persist TimeZoneInfo.Local.Id instead of TimeZoneInfo.Local.ToString().
(Note that using the Windows system time zone identifiers pins you down to Windows pretty heavily. You might want to consider using TZDB time zone information instead, e.g. via my Noda Time project. That's a separate decision though.)

Unstable NSTimer causes fluctuations in counting

I use NSTimer to count from a certain moment.
int totalSeconds;
int totalMinutes;
int totalHours;
If the totalSeconds are 60, totalMinuts become +1. Its very simple and should work.
For example i started the NSTimer together with the clock of my mac. (running on simulator).
When i look at the clock of my mac and the timer and compare the time the first 10-20 seconds its counting perfectly synchronous. After that it fluctuates or goes ahead 5 seconds or more.
So i output my timer and found this:
2012-10-24 14:45:44.002 xxApp driveTime: 0:0:44
2012-10-24 14:45:45.002 xxApp driveTime: 0:0:45
2012-10-24 14:45:45.813 xxApp driveTime: 0:0:46
2012-10-24 14:45:46.002 xxApp driveTime: 0:0:47
The milliseconds are timed at 002 as you see. But at the third row its 813. This happens very randomly and causes the fluctuations.
Is there a more stable way to count?
From the NSTimer documentation
A timer is not a real-time mechanism; it fires only when one of the run loop modes to which the timer has been added is running and able to check if the timer’s firing time has passed. Because of the various input sources a typical run loop manages, the effective resolution of the time interval for a timer is limited to on the order of 50-100 milliseconds.
If your goal is to compute the total time that has passed since your program has started running, this is quite easy. As soon as you want to begin keeping track of time, store -[NSDate date] into a variable. Whenever you want to compute how much time has passed, call -[NSDate date again and do the following, assuming originalDate is a property where you stored the result of the first call to -[NSDate date]:
NSDate *presentDate = [NSDate date];
NSTimeInterval runningTime = [presentDate timeIntervalSinceDate:originalDate];
runningTime will be the total number of seconds that have elapsed since you started keeping track of time. In order to get the number of hours, minutes, seconds, and so on, an NSDateComponents object should be used.
This mechanism will allow you to use a timer to update your total running time "just about once a second" without losing accuracy.

How to change time and timezone in iPhone simulator?

How do I change time and time zone in the iPhone simulator?
I'm guessing it uses your system timezone, so changing the TZ in System Preferences would probably do the trick
You can set the TZ environment variable in an Xcode Scheme to set the time zone just for that app.
You can use UTC, PST, EST, as well as place-based timezone names such as America/Los_Angeles. It's not well documented, but I suspect any time zone name should work.
It's not well documented, but the source is an Apple Developer Support rep on the developer forums.
Restart the Simulator after changing the system date time preferences and you shall see the changes reflected. It worked for me.
For the purpose of taking a screenshot, Apple finally made it possible to override time on the status bar of iOS simulator (since Xcode 11) by using simctl tool:
xcrun simctl status_bar "iPhone Xs" override --time "21:08"
Here is a solution available from iOS 13 and Xcode 11 at least. (did not test with previous versions)
[Edit] This will change only the status bar as in the comments!
By default the iOS Simulator shows whatever the time is on your Mac, however, you can use Xcode’s command line to override that with this command in the terminal:
xcrun simctl status_bar "iPhone 11 Pro Max" override --time '9:41'
Replace the simulator name with the device you want to change.
For the status bar you have this overrides:
You may specify any combination of these flags (at least one is required):
--time <string>
Set the date or time to a fixed value.
If the string is a valid ISO date string it will also set the date on relevant devices.
--dataNetwork <dataNetworkType>
If specified must be one of 'wifi', '3g', '4g', 'lte', 'lte-a', or 'lte+'.
--wifiMode <mode>
If specified must be one of 'searching', 'failed', or 'active'.
--wifiBars <int>
If specified must be 0-3.
--cellularMode <mode>
If specified must be one of 'notSupported', 'searching', 'failed', or 'active'.
--cellularBars <int>
If specified must be 0-4.
--batteryState <state>
If specified must be one of 'charging', 'charged', or 'discharging'.
--batteryLevel <int>
If specified must be 0-100.
The time can be any string. But if you want the device to show the date you will need use the ISO format.
For example a valid ISO date string would be '2007-01-09T10:41:00+01:00'
Otherwise you can use the time parameter as a string and it will display whatever you pass in it.
With thanks to the original post by Paul Hudson Here's the link!
This is an old thread but it is closest to my question. I need to simulate time zone for Europe, this method works for me. Go to 'TimeZone' tap instead of 'Date&Time' tap. Uncheck the 'Set time zone automatically using current location' box and slide the vertical rectangle bar (with blue dot on it) to simulate your system time.
When changing the timezone, I found the easiest way to do it was by clicking the clock in the menubar. And then selecting "Open Date & Time Preferences" then select the tab Time Zone.
Alternatively System Preferences -> Date and Time and select the tab Time Zone.
Just a pointer for anyone that might not know their way around OSX.
I have proposed an automatic solution to the problem of changing the time that includes hacky method swizzling: https://stackoverflow.com/a/34793193/829338. I assume that should also work for changing the time zone accordingly.
I needed to test my app automatically, which required changing the sys time. I did, what Tom suggested: happy hacky method swizzling.
For demonstrative purposes, I only change [NSDate date] but not [NSDate dateWithTimeIntervalSince1970:].
First your need to create your class method that serves as the new [NSDate date]. I implemented it to simply shift the time by a constant timeDifference.
int timeDifference = 60*60*24; //shift by one day
NSDate* (* original)(Class,SEL) = nil;
+(NSDate*)date{
NSDate* date = original([NSDate class], #selector(date));
return [date dateByAddingTimeInterval:timeDifference];
}
So far, pretty easy. Now comes the fun part. We get the methods from both classes and exchange implementation (it worked for me in the AppDelegate, but not in my UITests class). For this you will need to import objc/runtime.h.
Method originalMethod = class_getClassMethod([NSDate class], #selector(date));
Method newMethod = class_getClassMethod([self class], #selector(date));
//save the implementation of NSDate to use it later
original = (NSDate* (*)(Class,SEL)) [NSDate methodForSelector:#selector(date)];
//happy swapping
method_exchangeImplementations(originalMethod, newMethod);
My build server is UTC and some of my unit tests needed the timezone to be PST. Using a category on NSTimeZone you can override Apple's implementation to use your code. Works for swift only projects too.
//NSTimeZone+DefaultTimeZone.h
#import <Foundation/Foundation.h>
#interface NSTimeZone (DefaultTimeZone)
+(NSTimeZone *)defaultTimeZone;
#end
//NSTimeZone+DefaultTimeZone.m
#import "NSTimeZone+DefaultTimeZone.h"
#implementation NSTimeZone (DefaultTimeZone)
+(NSTimeZone *)defaultTimeZone
{
return [NSTimeZone timeZoneWithName:#"America/Los_Angeles"];
}
#end
Only working solution for now. XCode does provide option to set a Time Zone for a particular app.
In XCode, Click on your app, Edit Scheme -> Run configuration -> Arguments Tab -> Add Environment Variables
Create a variable with Name: TZ, Value: CST (Any other standard format. XCode didn't explicitly mention the allowed values. But you can use America/Chicago too)
After changing the system date time preferences I had to choose Hardware > Reset All Content And Settings.
Only this worked for me in Version 10.3 (SimulatorApp-880.5 CoreSimulator-681.5.4).

Rudimentary ways to measure execution time of a method

What object/method would I call to get current time in milliseconds (or great precision) to help measure how long a method took to execute?
NSDate's timeIntervalSinceDate will return NSInterval which is measured in seconds. I am looking for something finer grained, something similar to Java's System.currentTimeMillis.
Is there an equivalent version in objective-c/CocoaTouch?
For very fine-grained timings on OS X, I use mach_absolute_time( ), which is defined in <mach/mach_time.h>. You can use it as follows:
#include <mach/mach_time.h>
#include <stdint.h>
static double ticksToNanoseconds = 0.0;
uint64_t startTime = mach_absolute_time( );
// Do some stuff you want to time here
uint64_t endTime = mach_absolute_time( );
// Elapsed time in mach time units
uint64_t elapsedTime = endTime - startTime;
// The first time we get here, ask the system
// how to convert mach time units to nanoseconds
if (0.0 == ticksToNanoseconds) {
mach_timebase_info_data_t timebase;
// to be completely pedantic, check the return code of this next call.
mach_timebase_info(&timebase);
ticksToNanoseconds = (double)timebase.numer / timebase.denom;
}
double elapsedTimeInNanoseconds = elapsedTime * ticksToNanoseconds;
Actually, +[NSDate timeIntervalSinceReferenceDate] returns an NSTimeInterval, which is a typedef for a double. The docs say
NSTimeInterval is always specified in seconds; it yields sub-millisecond precision over a range of 10,000 years.
So it's safe to use for millisecond-precision timing. I do so all the time.
Do not use NSDate for this. You're loosing a lot of precision to call methods and instantiate objects, maybe even releasing something internal. You just don't have enough control.
Use either time.h or as Stephen Canon suggested mach/mach_time.h. They are both much more accurate.
The best way to do this is to fire up Instruments or Shark, attach them to your process (works even if it's already running) and let them measure the time a method takes.
After you're familiar with it this takes even less time than any put-in-mach-time-functions-and-recompile-the-whole-application solution. You even get a lot of information extra. I wouldn't settle for anything less.
timeIntervalSinceReferenceDate is perfectly fine.
However, unless it's a long-running method, this won't bear much fruit. Execution times can vary wildly when you're talking about a few millisecond executions. If your thread/process gets preempted mid-way through, you'll have non-deterministic spikes. Essentially, your sample size is too small. Either use a profiler or run 100,000 iterations to get total time and divide by 100,000 to get average run-time.
If you're trying to tune your code's performance, you would do better to use Instruments or Shark to get an overall picture of where your app is spending its time.
I will repost my answer from another post here. Note that my admittedly simple solution to this complex problem uses NSDate and NSTimeInterval as its foundation:
I know this is an old one but even I found myself wandering past it again, so I thought I'd submit my own option here.
Best bet is to check out my blog post on this:
Timing things in Objective-C: A stopwatch
Basically, I wrote a class that does stop watching in a very basic way but is encapsulated so that you only need to do the following:
[MMStopwatchARC start:#"My Timer"];
// your work here ...
[MMStopwatchARC stop:#"My Timer"];
And you end up with:
MyApp[4090:15203] -> Stopwatch: [My Timer] runtime: [0.029]
in the log...
Again, check out my post for a little more or download it here:
MMStopwatch.zip
#bladnman I love your stopwatch thing.. I use it all the time.. Here's a little block I wrote that eliminates the need for the closing call, and makes it even EASIER (if that even seemed possible) to use, lol.
+(void)stopwatch:(NSString*)name timing:(void(^)())block {
[MMStopwatch start:name];
block();
[MMStopwatch stop: name];
}
then you can just call it wherever..
[MMStopwatch stopwatch:#"slowAssFunction" timing:^{
NSLog(#"%#",#"someLongAssFunction");
}];
↪someLongAssFunction
-> Stopwatch: [slowAssFunction] runtime:[0.054435]
You should post that sucker to github - so people can find it easily / contribute, etc. it's great. thanks.