How can I use relative dates like "Today" or "Tomorrow" in a watchOS complication? - cocoa-touch

I have a watchOS complication that shows information about what's happening either on the current day or on some future day. As with any complication, I have a CLKComplicationDataSource set up with various methods including getCurrentTimelineEntryForComplication:withHandler: and getTimelineEntriesForComplication:afterDate:limit:withHandler:. When these methods are called, I create a CLKComplicationTimelineEntry using a CLKComplicationTemplate subclass that's appropriate for the complication type.
The "Modular Large" complication type (CLKComplicationFamilyModularLarge) is the one that's giving me the most trouble. With this type I want the heading to say "Today" or "Tomorrow" when it's appropriate. Below that will be some text about what's happening that day. Currently I'm using CLKRelativeDateTextProvider for this:
template.headerTextProvider = [CLKRelativeDateTextProvider textProviderWithDate:date style:CLKRelativeDateStyleNatural units:NSCalendarUnitDay];
This doesn't quite do what I want though—it shows something like "0DAYS" or "1DAY". That's acceptable for a smaller complication, but with Modular Large it looks especially awkward. You don't even get a space after the number, no matter how much extra room there is.
I thought I could fix this by just using an NSDateFormatter to generate the string, then using a CLKSimpleTextProvider to display it. Seemed simple enough:
NSDateFormatter *formatter = [NSDateFormatter new];
formatter.doesRelativeDateFormatting = YES;
formatter.dateStyle = NSDateFormatterShortStyle;
formatter.timeStyle = NSDateFormatterNoStyle;
NSString *string = [formatter stringFromDate:date];
But there's a problem: a watchOS complication needs to be able to supply data for dates in the future. As far as I can tell, NSDateFormatter has no way to tell it what date you want the output to be relative to—it's always relative to the current date. So if the date for the complication data is tomorrow, and getTimelineEntriesForComplication:afterDate:limit:withHandler: is called, then all the timeline entries I generate will say "Tomorrow". That will be right for the current day, but when midnight hits, it'll be wrong. It will still say "Tomorrow" until all the complication data is generated again.
I've only been able to think of a couple solutions to this problem, and they both have drawbacks:
I could calculate how many days away the date is and just give CLKSimpleTextProvider the string "Today" or "Tomorrow" when it's appropriate. My main concern here is that I use NSDateFormatter elsewhere in my app, and I'd like to be consistent with that. With various languages to account for that's not trivial. Just to give one simple example, French uses "après-demain", meaning "the day after tomorrow", which has no real English equivalent.
I could give NSDateFormatter the wrong date, forcing it to give me the output I want. Let's say the complication date is June 14th, and it's currently June 13th. When my app is creating a timeline entry for the current day, it calls my date formatter using with the date June 14th ("Tomorrow"). But when it needs to create timeline data past midnight, it calls the date formatter with the date June 13th ("Today"). This seems like a terrible idea that's bound to break in some situation like a leap year or time change or something. I already don't like using anything other than CLKRelativeDateTextProvider, but I really don't like this.
That said, I'm not sure what else I could do. I have already filed a Radar (27267550: CLKRelativeDateTextProvider should offer more control) asking for more date formatting options. It's a year old and shows no sign of being addressed in watchOS 4.
What's the best way to solve this?

If you go the route of calculating how many days away it is, you could use a localized .stringsdict and get a localized string based on the number. For English it could be:
<key>zero</key>
<string>Today</string>
<key>one</key>
<string>Tomorrow</string>
...
<key>other</key>
<string>%d days</string>
And then for other languages you can add specific rules, like French's "après-demain" for two.
Feels like a pretty unsatisfying option since you're now taking on the responsibility of managing every language and each possible way of describing future days, but it could work.

I think an NSDateFormatter is the best option here. If you try to schedule a timeline refresh at midnight, then you should be able to avoid the problem with dates past the current day. No guarantees on people traveling across the international date line! :)

Related

Is there a bug with the rally API when querying release dates?

I think there is a bug when querying a release's ReleaseDate field. Let's say I have a release with a ReleaseDate of 04/24/2017 EDT, when I get the response from the API request, and take a peek at the ReleaseDate field for that release, it comes off as 04/25/2017 (in date time format obviously), one day ahead of the actual date. I am building a calendar app for inside use using full calendar, and this bug is really annoying. Am I correct to say that this is a bug? Are there any workarounds that do not involve writing code to fix the date after querying it? thanks.
Pretty sure this is timezone related. Dates in WSAPI are always UTC. Assuming your workspace is configured for Eastern the dates returned probably are the next day:
A release with an end date of 04/24/2017 is probably being returned from WSAPI with a date like 2017-04-25T03:59:59.000Z, which when converted to EDT would be the end of the day on the 24th.
You'll also want to be specific when setting dates since the backend will again try to coerce the date into UTC if there is no time specified. The current behavior or the detail pages is to set it to noon on that day for that timezone. So something like this: 2017-04-24T12:00:00-05:00
Hope that helps. We did a bunch of work across the product and the api's last year to try to provide a more consistent experience working with timezones. It still can be confusing, but a good rule of thumb is to always be specific and include a time and timezone offset when working with dates using the api.
If you're using App SDK, the moment.js library is already included and is super handy for working with dates:
https://help.rallydev.com/apps/2.1/doc/#!/guide/third_party_libs-section-moment.js-2.10.3

search by date and a time span core data

I'm working on a Core Data project, I have a table with a modification_date field,
I need to extract all the entries of the last n days that also have a modification_date between (let's say) 1PM and 9PM.
I've already sorted out part of the solution to extract just the entries in the last n days
like suggested in: Core Data- predicate with dates
NSDate *today = [NSDate date];
NSDate *end_day = [today addTimeInterval: (days*86400.0f)];
day_st = [NSString stringWithFormat: #"(timestamp >= %#) AND (timestamp <= %#) AND ", today, end_day];
My only problem now, would be adding the filter by time spans.
But, except for making a foreach to check the time of every single element in the result,
I can't come up with anything clever.
If you're storing date-type fields in your data model, there's no good way to do that. Those get stored as the number of seconds since a reference date-- so filtering on an NSDate works, since it's the same thing. You can't build a predicate based on date components (like hour of the day) though, because the data store doesn't know anything about those. Filtering the results after fetching them from the data store is a reasonable approach in most cases.
If this is something you'll do a lot, you might consider adding one or more new attributes-- for example, an integer field where you store the hour of the day. Then you could include those fields in your NSPredicate. Set the value for these fields any time the date field changes. And, be damn sure you know what time zone those fields are in. UTC is preferable. Local time is asking for trouble. With UTC you'd covert "1PM", "9PM", etc to their equivalents in UTC before setting values for the fields and before fetching.
Finally (and you may have realized this, but it's not obvious from your question), note that the other question you link to is building an NSPredicate while you have an NSString there.
I'd say you're looking to end up with a predicate like:
((timestamp >= <startTimeOnDayOne>) AND (timestamp <= <endTimeOnDayOne>)) OR
((timestamp >= <startTimeOnDayTwo>) AND (timestamp <= <endTimeOnDayTwo>)) OR
[...]
In terms of how you'd formulate the appropriate Cocoa date objects, you'd probably want to seed two NSDateComponents, one for the start time and one for the end time, and a third that simply says 'one day'. Then use an NSCalendar (just the current calendar if you're being that vague about it) and its dateFromComponents: and dateByAddingComponents:toDate:options: methods — the former to get the dates for the first day and then the latter to advance each by one day.
If you attempt just to add 24 hours at each juncture you'll get the wrong result twice a year in any territory with daylight savings.
You can build up the NSPredicates for each relevant day into an array and then use NSCompoundPredicate +orPredicateWithSubpredicates: to glue them all together into a single predicate at the end, allowing you to worry about how many is n days at runtime.

Converting string with US date and time format to UK format

I have an application that stores date and time in a string field in an SQL Server 2008 table.
The application stores the date and time according to the regional settings of the PC that is running and we can’t change this behavior.
The problem is that some PCs have to be in UK date format with 12h time (eg. 22/10/2011 1:22:35 pm) some with UK date format with 24h time (eg. 22/10/2011 13:22:25) and some have to be US date format (eg. 10/22/2011 1:22:35 pm) and (eg. 10/22/2011 13:22:25).
Is there any automatic way to change the string every time it changing/added to the table to UK 24h format so it will be always the same format in the database?
Can it be done using some trigger on update or insert? Is there any built-in function that already does that?
Even a script to run it from time to time may be do the job...
I’m thinking to break apart the string to day, month , year, hour, minute, second , AM/PM and then put the day and month part in dd/mm order and somehow change the hour part to 24h if PM, get rid of the “am” and “pm” and then put the modified date/time back to the table.
For example the table has
id datestring value Location
1 15/10/2011 11:55:01 pm BLAHBLAH UK
2 15/10/2011 13:12:20 BLAKBLAK GR
3 10/15/2011 6:00:01 pm SOMESTUFF US
4 10/15/2011 20:16:43 SOMEOTHERSTUFF US
and we want it to be
id datestring value Location
1 15/10/2011 23:55:01 BLAHBLAH UK
2 15/10/2011 13:12:20 BLAKBLAK GR
3 15/10/2011 18:00:01 SOMESTUFF US
4 15/10/2011 20:16:43 SOMEOTHERSTUFF US
We can display the date parts (day,month,year) correctly using the datepart function but with the time part we have problems because it changes too many ways.
Edited to explain some more
mr. p.campbell thanks for the edit .. i didn't know how to beautify it :)
and mr. Matthew, thank you for your quick reply..
We can tell if it is UK date or US date because we have another field i didn't mention with the text "US", "UK", "GR", "IT" according to where the PLC machine is located.
I'm sorry i didn't explain it to well. My english are not so good.
There are two different and independent applications. And they don't have direct relation with the sql server.
The application that only writes data to the database ..lets call it "the writer" for short.. and a different application that reads the data .. lets call it "the reader".
"The writer" is an internal application of a PLC machine that stores values every 1 min to the database that's why we can't change its behavior. It uses the string data type to store the date and the time at the same field according to the regional settings of the pc that a daemon application runs and does the communication between the pc and the PLC machine.
Now "the reader" expects the date and time to be in the format "dd/mm/yyyy 23:23:01" or "yyyy/mm/dd 23:23:01" and the only thing it does for now is doing some calculations with the data in the value field between given dates. eg. from 10/09/2011 10:00:00 to 15/09/2011 14:00:00.
we just need to do something like this ...
select * from table1 where datestring between "10/09/2011 10:00:00" and "15/09/2011 14:00:00"
I could post some of the code but it will be very long post.
At first, I agreed with Matthew, but then I realized that, given the information presented, this actually was possible (well, sorta).
However, some caveats;
You are doing nobody any favors by storing and maintaining the database this way. Your best bet is to change the application to have it give an actual Datetime value, not this mangled string.
This data CANNOT be meaningfully sorted by date or time (not without performing expensive string manipulation).
You appear to be storing all times as local times, but do not appear to be storing a TimeZone or related information. Without this information, you will NOT be able to (completely) correctly translate times 'globally'. For instance, which is later - 4PM in London, or 11AM in New York (for, say, an international conference call)? The answer is that you don't know: it depends on the time of year.
You are storing local times, period. This only works so long as local time is correct. What happens when somebody sets their clock to 1900? You should be storing time based off of the SERVER'S clock.
Your stored timestamp is based on a formatted string. If the user changes how their time is displayed, your data correctness (potentially) goes out the window. For instance, what if somebody removes the am/pm symbols, thinking "I'll look out the window - if the sun is out, it's 'am'"?
Please keep all of that in mind.
As to how to do this....
I'm not going to actually write out the SQL statement for this. Mostly because storing the information this way is pretty terrible. But also because it's going to take a lot of work I'd rather not do. I really recommend stressing to whomever has the keys at your place to get that application changed.
So instead, I'm going to give you a really big clue - and this will only work for so long as your timestamp format remains the same; You should be able to tell what format the date and time are in based on the presence and absence of 'am' and 'pm' in the string (if you don't have both, you're flat-out toast). As Matthew has pointed out, the formatting is also likely different for the date, as well as the time - you will need to translate both. However, this will immediately give you problems due to comparative timestamps (please see point 4, above); any attempt to run scheduling or auditing with this data id pretty much doomed to failure ("When did that happen?" "Well, it's in the UK date format, so..." "But that makes it 1AM here, and he was dead then!").
Most beneficial answer: Change how the information is stored in the database
EDIT:
And then it hits me (especially in light of the new edits) - there are potentially other possibilities that could actually make this work....
First, change your database to actually store some sort of 'globalized' timestamp, based off of the server's clock.
This will of course break your existing application code - it would get a data-type mismatch error. To fix that, rename the table, then create a view, named the same as the original table, that will return the string formatted as indicated in the 'source' column. You'll need to create instead-of triggers for the view, to translate the formatted string to an actual datetime value. The best part is, the application code should never notice the difference. You seem to have indicated that you have sufficient control over the database to allow this to happen; this should allow you to 'fix' the data transparently.
This of course works best if the incoming datetime values are absolute (not local). Hopefully, the values are actually supposed to be 'insert time' - these could likely be safely ignored, in favor of using a special register (like NOW or CURRENT DATE or whatever).
Can't believe this didn't hit me earlier...
You stated that you cannot change the application behavior, thus this is not possible.
Your problem is that your database doesn't know the culture / timezone settings of the client and your client doesn't report it.
You will need to report this data or think of clever ways to infer this information before you can act on it.
EDIT: For example, without knowledge of the client's details how could you tell the difference between the strings:
10/1/2011 12:00:00 (October First, noon, US)
10/1/2011 12:00:00 (January Tenth, noon, UK)
?

Most efficient way to convert a NSDate object with a given time to one with the same time and the current date

I'm currently creating a scheduling application that deals with schools schedules. Each schedule has an array of period objects that contains both a start and an end time. Currently I have these times stored as NSDate objects, where only the time is really relevant and each NSDate object has some arbitrary date associated with it that is largely insignificant to my application (it's whatever the UIDatePicker assigns to it, but I only use the TimePickerMode). I do a number of things with these dates. First, I use an NSDateFormatter to output a string representation of the time for each period in a UITableView. I then also attempt to find the current period in the day by comparing the current time, fetched by [NSDate date], to the start and end time. But I am having trouble deciding the best way to do this.
Currently, I use an NSDateFormatter to convert the start and end times to strings, and then back to dates that now have a date the same as today's date. Then after that I can do my date comparisons using - (NSComparisonResult)compare: comparing the start and end dates to the current date. However, I could see this as being very inefficient.
Other possible ways could be to convert each NSDate to NSDateComponents, then use NSCalendar to convert that back into an NSDate with the current date and the same original time.
Or, I could take a different approach and ditch the NSDate storage technique altogether. Instead I could store each start and end time as NSDateComponents, then it would be simple enough to output a formatted version of the time for the UITableView, and then I could convert the date obtained by [NSDate date] to NSDateComponents to be used for logical comparisons to obtain the current period.
I see three or four different ways of going about this, and actually after talking it out I I'm pretty confident the final way I discussed would be the most efficient as it requires the least amount of runtime conversions between different types of date objects. I'm just looking for other people's inputs.
The problem with using NSDate to store a time-of-day is that its value is dependent on the time zone. If the time zone or DST changes while your app is running in the foreground or background, then all of your times will suddenly be wrong. Ditto if you actually save NSDate values to disk. (One option is to set your app's default time zone to UTC, but then your app's idea of "now" will be incorrect to the user).
I would store the time separately and implement methods to convert to and from NSDate. You could store the time using NSDateComponents, or you could just store it as a single integer (say, minutes past midnight) which makes it trivial to compare two times.
When converting minutes to and from NSDate you use NSDateComponents, and you need to make sure that your UIDatePickers, NSCalendars, and NSDateFormatters are using the same time zone, which by default is the local time zone.
It seems like you're worried about the overhead that NSDateFormatter currently levies on your program to synchronize the day of your periods and the current date. But really, you don't care about the date at all, you just want the time since the beginning of the day.
Why not cut out the middleman? Store the start and end times as NSTimeIntervals, which are really just doubles. Mod the start and end times by 86,400, the seconds in a day, in order to distill the time down to simply the time in seconds after the start of a new day. You don't really care what day it represents, except that that day is represented as second 0.
Then whenever you pull the current NSDate, mod its NSTimeInterval by 86,400 to obtain the time in seconds and compare it against the stored period values. The only object conversion involved in the whole process would be using the primitive timeIntervalSinceReferenceDate. The rest are simple mod and comparison operators, and you get to store your period numbers as simple NSTimeIntervals.

Adding two NSDate

I've got two NSDate, date and time. I'd like to add them in such a manner that I get the date from date and time from time. Any suggestions on how I do this?
Cheers
Nik
If I got you right NSDates -dateByAddingTimeInterval: together with -timeIntervalSinceDate: are what you looking for.
hth
–f
Use NSCalendars components:fromDate: to get the components of the two dates.
Then reassemble them as needed using dateFromComponents:.
I've got two NSDate, date and time.
Sounds like you're going about it wrong.
An NSDate represents a specific moment in time (X seconds since the epoch). An NSDate is not simply “x o'clock” or “this date on the calendar”, and you shouldn't attempt to combine them as if they were because effects such as DST may make your computation wrong (in some time zones, some dates have two 1:00 hours, and some have no 2:00 hours).
Consider using an NSDatePicker or UIDatePicker (as appropriate) to let the user enter the date and time from a single place. Not only is this easier for you to do, it'll also give more correct results.
If you're reading the two pieces separately from a file or similar source, and you don't control the format (so you can't order the generating side to emit dates with their times in one value), you'll need to do one of two things:
If possible, combine the two strings. For example, an ISO 8601 date (e.g., “2010-05-10”) and an ISO 8601 time (e.g., “23:20:19-0700”) can be concatenated with a ‘T’ between them to form a valid, fully-specified ISO 8601 date (“2010-05-10T23:20:19-0700”). You can then pass this single string to a properly-configured NSDateFormatter.
If, for some reason, you can't combine the strings meaningfully, parse them yourself and fill out a single NSDateComponents object yourself.
This will not be pleasant, but correctness is important, and a bug (incorrect date parsing) that only happens in two hours out of every year for only some of your users will be maddening.
The goal is to produce exactly one NSDate object that completely describes the date and time in question. This is the only way to ensure that, in all circumstances, you get the correct NSDate value.