There are a lot of apps that allow the user to use a functionality a certain number of times per month. And when the quota is reached that functionality is blocked till the end of the month.
I am trying to do the same thing and I seem to have run into a bit of a problem.
struct Data{
var text:String
var date:Date
}
var array : [Data] = [
Data(text: "Cars", date: Date()),
Data(text: "Bikes", date: Date()),
Data(text: "Trucks", date: Date())
]
func countForCurrentMonth(date: Date) -> Int{
let currentMonth = Calendar.current.component(.month, from: date)
print(currentMonth)
var datesArray = [Int]()
for item in array{
let month = Calendar.current.component(.month, from: item.date)
if month == currentMonth{
datesArray.append(month)
}
}
let itemCount = datesArray.count
return itemCount
}
func addItem(){
if countForCurrentMonth(date:Date()) > 120{
//functionality locked
print("subscribe to unlock")
}else{
//allowed
print("allowed")
}
}
The problem is here, the date property is compared with Date() which gives the users system date. As the users system date can be manually set, one could easily by bypass this limitation.
So my question is, how can I implement the same thing without using the system date and which doesn't require an internet connection.
One of the things that I tried was keeping a stored date on the device and every time the user opens the app it checks the system date and if it's not equal to the stored date incrementing the day count and replacing the stored date with the new date. However, this would only work if the user opens the app everyday of the month.
I don't think that there is a proper solution for this without an internet connection but you cloud also save the latest registered date your app has been used with. This way you could check if the current date is earlier and lock your app for instance and try to fetch the correct timestamp online, if possible.
But it may be possible this leads to other issues when the user travels and changes his timezone for example but generally this solution should work.
One option would be to store the date and have it be a crucial component. That way if a user was cheating, when compared to other users the date would be too far in the future to count in the app against the competition. Then you would just store the latest date and any earlier date would invalid the session or lock the progress of the app. For instance you could say apply changes to the user account if current date < item date. This could be a problem if you update all users every few seconds. But, in that case you'd probably be online. In this case it seems you want to limit by month though. So even if the user applied 120 changes this month and another 120 changes next month, the changes would only be valid in that next month. So they still get the number of changes applied in that month. Or the first 120 of them. Also, you may want to check the year while you're looking for valid dates.
Related
So I currently have a now playing swift app, that requests to the server every 30 seconds, and seems to be overloading the server.
Since we request data from iTunes API about the current song that is playing, I was thinking that I could possibly limit the request if a result was found by iTunes API.
Using trackTimeMillis I could add that trackTimeMillis to the current time and if not greater than the time don't ping the server. If greater than ping server for latest track.
I am thinking I will need to wrap the function that pings the server in a
Calendar.current.dateComponents([.hour], from: Date(), to: Date()), diff > Date())+ trackTimeMillis {
But that does not work
basically I need to add trackTimeMillis to the current Date() and if that users current timestamp (date) is larger than the Date() + trackTimeMillis then allow request.
I don't really understand what you are trying to do with the code snippet you put in your question.
Focusing on how you add some number of milliseconds to a Date():
Date has the function addingTimeInterval(_:)
If you need to add a certain number of milliseconds do a date, use this:
newDate = Date().addingTimeInterval( Double(millis) / 1000)
Edit:
As Matt pointed out, you can do that even more simply by writing
newDate = Date() + Double(millis) / 1000
Since there is an override of the + operator that takes a Date and a Double and returns a Date.
Both versions of this add a Double containing a specified number of seconds to the Date. Since Doubles can represent very small and very large numbers you can add or subtract values ranging from tiny fractions of a second to thousands of years to any Date.
A simpler alternative. When I am doing a task like this, I usually store the Date of the previous request, e.g.:
let lastRequestTime = Date()
and then simply check the elapsed time:
if Date().timeIntervalSince(lastRequestTime) > 30 {
...
}
Another option is to use Timer. Basically, you can schedule a Timer that will send the request after X seconds. If you receive a response from a different source first, you simply cancel the Timer and possibly the request if it is already running.
I am creating a social media app. You can only post once every 24 hours, and I'm using it with firebase so all of the post' data goes to the firebase database.
When creating a post I have the variable Date, this is how you track if it's been 24 hours since the last post...
The only problem is that the user can just change the clock on their device and post as many times as they want. I need to get a date variable that is the local time of my timezone (EST) so I know that the data is accurate, but I'm not sure how to do it...
This is how I would usually get the current date:
let date = Date()
let currentTime = date.timeIntervalSince1970
So, I just need to get currentTime to be the current Eastern Standard Time (EST)
Thanks in advance!
Get the current timestamp form the firebase server and compare to the date from the last post
firebase.database.ServerValue.TIMESTAMP
Go here for reference firebase.database
for firestore
firebase.firestore.Timestamp.now()
Go here for reference firebase.firestore
If you want to make sure that 24 hours have passed, you need to compare the date in UTC, not in EST, UTC is a date without any time zone
and here
let date = Date()
let currentTime = date.timeIntervalSince1970
you have UTC timestamp
UPD: the fact of publication itself should be checked exclusively on the server, while the server should take its UTC time and check the time of the last publication from the database, before publishing.
If you are not trusting the device time, you could probably use an NTP client to get the date through an internet connection.
There are some implementations written in Swift like TrueTime and Kronos.
Both appears to use by default time.apple.com.
Their respective guides (on README files) seems to be pretty straightforward.
Example using TrueTime:
let client = TrueTimeClient()
client.start()
client.fetchIfNeeded { result in
switch result {
case let .success(referenceTime):
// Current date
let now = referenceTime.now()
client.pause()
case let .failure(error):
print("Error: \(error)")
}
}
Example using Kronos:
Clock.sync { date, offset in
// Current date
print(date)
}
Currently I am facing issue, We need to find weekly Users list. There is a one field in MongoDB
Profile Collection caled lastActive. We need to print last week user count, what happens is this
lastActive field is updating every users session, For example: User X lastActive value is '2020-07-
14T10:10:10:12.000' If users logged in today 2020-07-21T09:10:12.000, then what happens lastActive
field is updating with today's Date based on session details, can someone please let me know how can I
calculate the exact count value of weekly users list, based on latest Updated value. Hope u clear my
issue.
LocalDate today = LocalDate.now();
LocalDate lastweek = LocalDate.now().minusDays(7);
String start = today.toString();
String end = lastweek.toString();
long weekly_userlist = profile_collection.countDocuments(Filters.and(Filters.gte("lastActive",end),Filters.lte("lastActive",start)));
I'm writing a web app using Firestore that needs to be able to show "Today's Popular Posts" and I'm having trouble getting the queries right when considering users in different timezones.
The dates are stored in the DB as UTC 0, then adjusted to the current user's UTC offset in the client via Moment.js. This works correctly.
When adding a new post I use firebase.firestore.FieldValue.serverTimestamp() to store the current server timestamp in a field called timestamp, like so:
const collectionRef = db.collection('posts');
collectionRef.add({
name: "Test Post",
content: "Blah blah blah",
timestamp: firebase.firestore.FieldValue.serverTimestamp(),
likeCount: 0
});
Then on the server I have a Cloud Function that runs on create and adds another field to the document called datestamp which is the the UTC 0 timestamp, but adjusted so that the time is the beginning of the day. The function looks like this:
exports.updatePostDate = functions.firestore
.document('posts/{postID}')
.onCreate((event) => {
const db = admin.firestore();
const postRef = db.doc('post/'+event.params.postID);
const postData = event.data.data();
const startOfDay = moment(postData.timestamp).startOf('day').toDate();
return postRef.update({
datestamp: startOfDay
});
});
Storing a timestamp where the time is always the beginning of the day enables me to write a query like this for finding all posts and ordering by popularity on a given day:
const startOfDayUTC = moment.utc().startOf('day').toDate();
const postQuery = db.collection('posts')
.orderBy('likeCount', 'desc')
.orderBy('timestamp', 'desc')
.where('datestamp', '==', startOfDayUTC)
.limit(25);
The problem is, depending on the user's UTC offset, this can display posts with two different dates when parsing the post's timestamp field. So even though the query is correctly fetching all the posts where the datestamp is say, 2018-01-30T00:00:00Z, the timestamp's date might not be the same once parsed. Here's an example of two posts:
Post 2:
likeCount: 1
timestamp (UTC 0): 2018-01-30T06:41:58Z
timestamp (parsed to UTC-8): 2018-01-29T22:41:58-08:00
datestamp (UTC 0): 2018-01-30T00:00:00Z
Post 1:
likeCount: 0
timestamp (UTC 0): 2018-01-30T10:44:35Z
timestamp (parsed to UTC-8): 2018-01-30T02:44:35-08:00
datestamp (UTC 0): 2018-01-30T00:00:00Z
So you can see, while the posts have the same datestamp, after adjusting the timestamp to the local UTC, the timestamp fields can end up being on two different days.
If anyone has a solution to this I would be very grateful.
I think it is better to avoid functions in this case as you can perform compound queries now. You can simply use
query.where(date > lastMidnight).where(data < now).get().then(...)
so to speak to limit data which only belongs to one day and try to keep all your time variables in UTC 0 and just find the start point and the current time both client side and convert them to UTC0.
//get local time from midnight to now (local)
const now = new Date();
const lastMidnight = now.setHours(0,0,0,0);
//then convert those to UTC0 to pass on in your query to firestore
const lastMidNightUTC = new Date(lastMidnight + now.getTimezoneOffset() * 60000).toString();
const nowInUTC = new Date(now + now.getTimezoneOffset() * 60000).toString();
and you can get your data (remember you need to make an index or just run the query once and firebase SDK will generate a link to create the index in dev tools -> console , for you)
query.where(date > lastMidNightUTC).where(data < now).get().then(...)
I came up with a solution that I'm really not happy with... But it works!
The problem is fundamentally one post can be on more than one date, depending on the user's location. And since for this case we also want to order by a field other than timestamp we can't use a range query to select posts on a given date, because your first .orderBy must be on the field you're using a range query on (see Firestore docs).
My solution is to map localized datestamps to their corresponding UTC offset. The object contains every UTC offset as a key, and the post's datestamp in that offset's time.
An example post looks like this:
posts/{somepostid}
{
name: "Test Post",
content: "Blah blah blah",
timestamp: Mon Jan 29 2018 21:37:21 GMT-0800 (PST),
likeCount: 0,
utcDatemap: {
0: "2018-01-30,
...
-480: "2018-01-29",
...
}
}
The field utcDatemap is the the one we use in our queries now:
const now = moment();
const datestamp = now.format("YYYY-MM-DD");
const utcOffset = now.utcOffset();
const utcDatemapField = 'utcDatemap.'+utcOffset;
const postQuery = db.collection('posts')
.orderBy('likeCount', 'desc')
.orderBy('timestamp', 'desc')
.where(utcDatemapField, '==', datestamp)
.limit(25);
Now posts can show up on two different days, depending on where the user is querying from. And we can still convert the regular old timestamp to the user's local time on the client.
This is definitely not an ideal solution. For the above query I needed to create composite indexes for every single key in utcDatemap. I'm not sure what the rules of thumb are with composite indexes, but I'm thinking having 39 indexes for something simple like this is probably not great.
Additionally I checked it out using the roughSizeOfObject function from thomas-peter's answer on this post and the utcDatemap object, with all it's string datestamps clocked in at roughly 780 bytes, and it's not like 0.78kb is a lot, but you do need to be mindful of how much data you're transferring with a service like Firestore (0.78kb is a lot for a date).
I'm learning/reading up on Firestore and have Google'd to see how it deals with times, so discount my answer appropriately.
It looks as though Firestore converts times to UTC and stores them as its own Timestamp datatype. If so, then it's critical to know that this is a destructive conversion.
Though UTC is useful for comparing instants in time, it means that the wall-clock time as observed by the app user is lost forever. Some countries like the UK are in one of two timezones during the year, Daylight Savings Time and British Summer Time.
You can convert back to the user's observed time, but the problem is that the rules change over the years. You'd have to keep a record of all the different rule changes for all the timezones and countries of the world.
What was the time at the time?
The question is, what time did the user think an event happened ...at the time. This can have legal ramifications. You may need to go back through historic data to prove a person acted at a certain time as they observed it.
The solution is to capture the user's observed offset in an additional field. That way, you can always use this to convert.
Regarding the OPs problem, this seems somewhat philosophical for a web app. Does "today" mean the event, such as a post, must have happened within the user's Monday? Or just posts on today's date? Or posts within the last 24h?
An important thing to remember is that dates are the same all around the world, even when they begin and end at different instants.
What's Elvis got to do with all this?
Christmas Day is 25th everywhere. If I say something happened on Christmas Day and I'm in the USA, and then someone in Australia wants to see all the world's posts made on Christmas Day, then they need to query for posts where observedDate == 25th Dec.
Think about it. Such posts were all made on Christmas Day, even though it might have been Boxing Day for me in England at the instant that they posted.
Elvis died on 16th August. In the UK our radio stations don't all wait until it's the 16th in the timezone of the place of his death to start playing his records.
Another interesting one is whether something happened in Q1 or Q2 of a company's reporting year. Is a sale recognised as on the date at the point-of-sale in the store in New York, or in the database in LA?
The observed date is interesting.
My point is, think deeply about this and store both a normalised instant like UTC, but also the user's observed date, time and offset. Then you have all the information you'll need for the future.
Finally, consider adding observed year or week numbers, or day ordinals, H1/H2 for financial data, since it can be super useful for composing rapid queries, depending on your use-cases.
I have a list that tracks activity for my team. Everyone puts their information in with a Start Date and Due Date. On the first day of every month I pull a report to see what was accomplished in the previous month. Nothing is ever deleted from the list so there are activities from a year ago in there. I would like to export a report of only activities from the previous month. I have tried to create a view for this and another column that would populate yes or no depending on whether the last month fell between the start and end date but neither worked.
ex. for the month of September YES indicates I want it in the report, NO I do not want it in the report
Case 1: Start Date = 9/2/16, Due Date = 9/30/16, YES
Case 2: Start Date = 9/16/16, Due Date = 10/5/16, YES
Case 3: Start Date = 8/7/16, Due Date = 9/14/16, YES
Case 4: Start Date = 6/6/16, Due Date = 7/7/16, NO
Case 5: Start Date = 10/1/16, Due Date = 10/12/16, NO
The calculated column I tried to create as a quick fix for looked like this
=IF(OR([Start Date]>=9/1/16,[Due Date]>=9/1/16),"YES","NO")
I then planned to filter it on YES. I know that's not a good code for the long run but I am by no means a SharePoint expert so I was just trying to figure something out as a sort of band aid.
The problem with your code above Is that you will need to update your formula everytime you want to use the view.
You want a formula similar to the one below so on the first of every month it should relate correctly
=IF(OR((MONTH([Due Date])=MONTH(TODAY())-1),(MONTH([Start Date])=MONTH(TODAY())-1)),"YES","NO")
Cheers
Truez