I am getting date from server: 2022-12-14 20:59:59 +0000
I have view with promo codes, so according to received date I should show after how many seconds it will be expired.
For example if my current date is 2022-12-14 19:59:59 +0000, I must show 60:00 seconds.
If the users timezone is GMT+4, he must see 60:00 seconds as well.
So I use this code:
let currentTime = Int(Date().timeIntervalSince1970)
let expirationDate = model.expirationDate
expiredText = expirationDate.dateStringWithFormat("dd.MM.yyyy")
timeLeft = Int(expirationDate.timeIntervalSince1970) - currentTime
Result is:
currentTime: 2022-12-14 13:48:16 +0000 // my current time is 16:48:16
expirationDate: 2022-12-14 20:59:59 +0000
timeLeft: "0d. 07:11:43"
So Date() returns wrong date.
I tried to use calendar, but result is the same.
let calendar = Calendar(identifier: .gregorian)
let currentDate = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss:Z"
dateFormatter.timeZone = calendar.timeZone
How can I correctly convert my timezone to timezone of received date or vice versa?
Thanks
I think you are making this more complicated than it is.
We have a server timestamp
let serverDate = "2022-12-14 20:59:59 +0000"
that we convert to a Date using a formatter
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ssZ"
Then we want to calculate the difference between the server data and now
if let expirationDate = dateFormatter.date(from: serverDate) {
let components = Calendar.current.dateComponents([.day, .hour, .minute, .second], from: .now, to: expirationDate)
}
and then we can use a DateComponentsFormatter to get the difference as a string
let compFormatter = DateComponentsFormatter()
print(compFormatter.string(from: components))
This question already has answers here:
TimeZone changed while converting string to Date
(2 answers)
NSDate() or Date() shows the wrong time
(2 answers)
Getting date from [NSDate date] off by a few hours
(3 answers)
Swift convert string to date output wrong date
(4 answers)
Closed 1 year ago.
my string value is "2021.09.28 23:39".
but converted date is Optional(2021-09-28 14:39:00 +0000)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy.MM.dd HH:mm"
dateFormatter.locale = Locale(identifier: "ko_kr")
dateFormatter.timeZone = TimeZone(abbreviation: "KST")
let date = dateFormatter.date(from: "2021.09.28 23:39")
let dateComponents = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute], from: date!)
This is working correctly.
The KST timezone is UTC +9.
So... a time of 23:39 in the time zone KST is the same moment in time as the time 14:39 in UTC.
You output of Optional(2021-09-28 14:39:00 +0000) shows the timezone of +0000 and so is a displayed as a UTC time.
Show time for multiple countries with native time format.
Trying to Display time of Netherland eg: 22.37 uur
With Locale: nl_NL
For Date: 2019-09-17 17:07:00 +0000
I have tried the code below:
let formatter = DateComponentsFormatter()
var calendar = Calendar.gregorianCalendar()
calendar.locale = apiDate.locale // nl_NL
formatter.calendar = calendar
formatter.unitsStyle = .short
formatter.allowedUnits = [.hour, .minute]
formatter.zeroFormattingBehavior = [.dropLeading, .pad]
let dateComponent = calendar.dateComponents([.hour, .minute], from: apiDate.absoluteDate) //apiDate.absoluteDate: Date : 2019-09-17 17:07:00 +0000
print(formatter.string(from: dateComponent) as Any) // prints 22 uur, 37 min
Expected: 22.37 uur
Actual: 22 uur, 37 min
I'm trying to print a given date in the format of "[week in a year] [year]" combination using a DateFormatter. I have given it a pattern of "w yyyy" but it returns an empty string.
let date = DateComponents(calendar: Calendar.current, year: 2018, month: 9, day: 28).date
let formatter = DateFormatter()
formatter.locale = Locale.current
formatter.setLocalizedDateFormatFromTemplate("w yyyy")
formatter.string(from: date!) // this returns ""
formatter.setLocalizedDateFormatFromTemplate("w")
formatter.string(from: date!) // this returns the correct number, like "36"
According to the Date Formatting Guide, iOS 5 uses version tr35-19. I assume that it hasn't changed since then.
In the unicode.org documentation, there is no special mention of how week of year is behaving in conjunction with a year format. What am I missing here?
EDIT I understand that I can use DateComponents to get the numbers and formatting them that way, but this question is more about why the format "w yyyy" is special.
Don't use setLocalizedDateFormatFromTemplate(). I'm not familiar with that method, but it sounds like it expects a pre-defined date string template, and your format string must not match any known templates. If you change your line
formatter.setLocalizedDateFormatFromTemplate("w yyyy")
to
formatter.dateFormat = "w yyyy"
It works as expected.
Edit:
It seems you should use:
formatter.dateFormat = "w Y"
y and yyyy will not give the correct results with certain dates.
Try this :
let date = Date()
let calendar = Calendar.current
let year = calendar.component(.year, from: date)
let month = calendar.component(.month, from: date)
let day = calendar.component(.day, from: date)
let weekOfYear = calendar.component(.weekOfYear, from: Date.init(timeIntervalSinceNow: 0))
OR
let date = Date()
let calendar = Calendar.current
let year = calendar.component(.year, from: date)
let month = calendar.component(.month, from: date)
let day = calendar.component(.day, from: date)
let weekOfYear = calendar.component(.weekOfYear, from: Date())
I want to get date of particular day for every week.
Suppose I have a date: 2017-04-13. It is an April, and 13 April is Thursday. I need to get every date in April which is Thursday.
How can I do this?
The output should be: 2017-04-06, 2017-04-13, 2017-04-20, 2017-04-27
Short solution:
// Get current calendar and current date
let calendar = Calendar.current
let now = Date()
// Get the current date components for year, month, weekday and weekday ordinal
var components = calendar.dateComponents([.year, .month, .weekdayOrdinal, .weekday], from: now)
// get the range (number of occurrences) of the particular weekday in the month
let range = calendar.range(of: .weekdayOrdinal, in: .month, for: now)!
// Loop thru the range, set the components to the appropriate weekday ordinal and get the date
for ordinal in range.lowerBound..
Be aware that print prints dates always in UTC.
Edit:
range(of: .weekdayOrdinal, in: .month does not work, it returns 1..<6 regardless of the date.
This is a working alternative. It checks if the date exceeds the month bounds
// Get current calendar and date for 2017/4/13
let calendar = Calendar.current
let april13Components = DateComponents(year:2017, month:4, day:13)
let april13Date = calendar.date(from: april13Components)!
// Get the current date components for year, month, weekday and weekday ordinal
var components = calendar.dateComponents([.year, .month, .weekdayOrdinal, .weekday], from: april13Date)
// Loop thru the range, set the components to the appropriate weekday ordinal and get the date
for ordinal in 1..<6 { // maximum 5 occurrences
components.weekdayOrdinal = ordinal
let date = calendar.date(from: components)!
if calendar.component(.month, from: date) != components.month! { break }
print(calendar.date(from: components)!)
}
Try this playground:
import UIKit
let dateString = "2017-04-13"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let referenceDate = dateFormatter.date(from: dateString)!
let calendar = Calendar.current
let firstDayComponents = calendar.dateComponents([.year, .month], from: referenceDate)
let monthFirst = calendar.date(from: firstDayComponents)!
let weekDay = calendar.component(.weekday, from: referenceDate)
var oneDay = DateComponents()
oneDay.day = 1
var checkDate = monthFirst
while calendar.component(.month, from: checkDate) == calendar.component(.month, from: referenceDate) {
if calendar.component(.weekday, from: checkDate) == weekDay {
let thisDay = dateFormatter.string(from: checkDate)
print(thisDay)
}
checkDate = calendar.date(byAdding: oneDay, to: checkDate)!
}
This code does the job. I added some logs to understand some logic behind it.
You can set dateInit as you wish, the rest of the code will find all the days that have the same weekday in the same year of the same month.
I printed two versions of date representations (NSDate objects and NSString objects), for the one having issue with timezones and "it's not the same day" cries.
It uses enumerateDatesStartingAfterDate:matchingComponents:options:usingBlock:
NSCalendar *calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
NSString *dateStr = #"2017-04-13";
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd"];
NSDate *dateInit = [dateFormatter dateFromString:dateStr];
NSLog(#"dateInit: %#", dateInit);
NSDateComponents *componentsToMatch = [calendar components:(NSCalendarUnitMonth|NSCalendarUnitWeekday) fromDate:dateInit];
NSDate *startOfMonth = [calendar dateFromComponents:[calendar components:(NSCalendarUnitYear|NSCalendarUnitMonth) fromDate:dateInit]];
NSLog(#"StartOfTheMonth: %#", startOfMonth);
NSArray *daysToFind = #[#"2017-04-06", #"2017-04-13", #"2017-04-20", #"2017-04-27"]; //According to author
NSLog(#"DaysToFind: %#", daysToFind);
NSMutableArray *allDaysInMonthMatchingWeekDay = [[NSMutableArray alloc] init];
[calendar enumerateDatesStartingAfterDate:startOfMonth
matchingComponents:componentsToMatch
options:NSCalendarMatchStrictly
usingBlock:^(NSDate * _Nullable date, BOOL exactMatch, BOOL * _Nonnull stop) {
NSLog(#"DateBlock: %#", date);
[allDaysInMonthMatchingWeekDay addObject:date];
}];
NSLog(#"allDaysInMonthMatchingWeekDay: %#",allDaysInMonthMatchingWeekDay);
for (NSDate *aDate in allDaysInMonthMatchingWeekDay)
{
NSLog(#"Found: %#", [dateFormatter stringFromDate:aDate]);
}
The logs:
$>dateInit: 2017-04-12 22:00:00 +0000
$>StartOfTheMonth: 2017-03-31 22:00:00 +0000
$>DaysToFind: (
"2017-04-06",
"2017-04-13",
"2017-04-20",
"2017-04-27"
)
$>DateBlock: 2017-04-05 22:00:00 +0000
$>DateBlock: 2017-04-12 22:00:00 +0000
$>DateBlock: 2017-04-19 22:00:00 +0000
$>DateBlock: 2017-04-26 22:00:00 +0000
$>allDaysInMonthMatchingWeekDay: (
"2017-04-05 22:00:00 +0000",
"2017-04-12 22:00:00 +0000",
"2017-04-19 22:00:00 +0000",
"2017-04-26 22:00:00 +0000"
)
$>Found: 2017-04-06
$>Found: 2017-04-13
$>Found: 2017-04-20
$>Found: 2017-04-27
Note: For the componentsToMatch, I tried to set the Year/Month/WeekDay flags unit, but the enumeration stopped at the first occurence, didn't search long why, I came up with only month and weekday flag to get it work. Maybe some little issue that I missed.
EDIT:
In Swift 3 (it works, but since I'm an Objective-C developer and not a Swift one, it may have issues, like wrapping/unwrapping etc)
let calendar = NSCalendar.init(calendarIdentifier: .gregorian)
let dateStr = "2017-04-13"
let dateFormatter = DateFormatter.init()
dateFormatter.dateFormat = "yyyy-MM-dd"
let dateInit = dateFormatter.date(from: dateStr)!
print("dateInit: \(dateInit)")
let componentsToMatch = calendar?.components([.month,.weekday], from: dateInit)
let startOfMonth = calendar?.date(from: (calendar?.components([.year,.month], from: dateInit))!)
print("StartOfTheMonth:\(startOfMonth)")
calendar?.enumerateDates(startingAfter: startOfMonth!, matching: componentsToMatch!, options: .matchStrictly, using: { (date, extactMatch, stop) in
print("DateBlock: \(date)")
})
I would write an extension for Calendar for any given time span and use an enum to name the weekdays
enum WeekDay: Int {
case sunday = 1
case monday
case tuesday
case wednesday
case thursday
case friday
case saturday
}
struct TimeSpan {
let startDate: Date
let endDate: Date
}
extension Calendar {
func allOccurrenceOf(day: WeekDay, in timeSpan:TimeSpan) -> [Date] {
let startDateWeekDay = Int(self.component(.weekday, from: timeSpan.startDate))
let desiredDay = day.rawValue
let offset = (desiredDay - startDateWeekDay + 7) % 7
let firstOccurrence = self.startOfDay(for:self.date(byAdding: DateComponents(day:offset), to: timeSpan.startDate)!)
guard firstOccurrence.timeIntervalSince1970 < timeSpan.endDate.timeIntervalSince1970 else {
return []
}
var filtered = [firstOccurrence]
while true {
let nextDate = self.date(byAdding: DateComponents(day: 7), to: filtered.last!)!
if nextDate < timeSpan.endDate {
filtered.append(nextDate)
break
}
}
return filtered
}
}
Beware that I hacked this could rather fast. I am sure that this can be expressed swiftier. In real production code I would also try to eliminate all ! from it.
usage:
let tuesdays = Calendar.autoupdatingCurrent.allOccurrenceOf(day: .tuesday, in: TimeSpan(startDate: Date(), endDate: Calendar.autoupdatingCurrent.date(byAdding: DateComponents(month:1), to: Date())!))
As Suggested in comment. see updated code. updated with week day
func getNumberOfDaysInMonth (month : Int , Year : Int, weekday: Int) -> [String]{
let dateComponents = NSDateComponents()
dateComponents.year = Year
dateComponents.month = month
let calendar = Calendar.current
let date = calendar.date(from: dateComponents as DateComponents)
let range = calendar.range(of: .day, in: .month, for: date!)
let numDays:Int = (range?.upperBound)!
let thuFormatter = DateFormatter()
var dateArray:[String] = [String]()
thuFormatter.dateFormat = "yyyy-MM-dd"
for day in 1...numDays {
dateComponents.day = day
let date2 = calendar.date(from: dateComponents as DateComponents)
print(calendar.component(.weekday, from: date2!))
if calendar.component(.weekday, from: date2!) == weekday
{
let dateThu = thuFormatter.string(from: date2!)
dateArray.append(dateThu)
}
}
return dateArray
}
and then call it like
let myThu:[String] = getNumberOfDaysInMonth(month: 4, Year: 2017,weekday: 3)
print(myThu)