I am using Rust with the Chrono library, and need to find the DateTime for the previous Tuesday at 6 PM UTC.
I have the following code, which seems to be working, but seems more complicated than it should be.
use chrono::{DateTime, Utc, Duration, Datelike};
use chrono::prelude::*;
fn main() {
let now: DateTime<Utc> = Utc::now();
let n_date = Utc::now().date();
let dt = Utc.ymd(n_date.year(), n_date.month(), n_date.day()).and_hms(18, 0, 0);
let current_day = now.weekday().number_from_monday();
let target_dt = if current_day == 2 {
if dt > now {
dt
} else {
dt - Duration::days(7)
}
}
else if current_day > 2 {
dt - Duration::days((current_day - 2) as i64)
} else {
dt - Duration::days(6)
};
println!("{:?}", target_dt);
}
Is there more efficient / better way to find a specific DateTime in the previous week? (In this case, the previous Tuesday at 18:00 UTC). Looking at some other languages, there are one line solutions, and I feel like I must be missing something obvious.
Based on the feedback below, here is the updated code:
let now: DateTime<Utc> = Utc::now();
//use this to test specific dates / times
//let now : DateTime<Utc> = Utc.ymd(2020, 11, 24).and_hms(18, 0, 1);
let n_date = now.date();
//let n_date = Utc.ymd(2020, 11, 24).and_hms(17, 0, 0).date();
let dt = Utc.ymd(n_date.year(), n_date.month(), n_date.day()).and_hms(18, 0, 0);
let w_day = n_date.weekday();
let target_dt = if w_day == Weekday::Tue {
if now > dt {
dt
} else {
dt - Duration::days(7)
}
} else {
let c:i64 = ((w_day.num_days_from_sunday() + 4) % 7 + 1) as i64;
dt - Duration::days(c)
};
println!("{:?}", target_dt);
ok. After thinking about this more, I figured out a more straight forward way, that plays off the fact that I can use a previous know reset data / time as a reference:
pub const WEEK_IN_SECONDS: i64 = 60 * 60 * 24 * 7;
pub fn get_last_reset() -> DateTime<Utc> {
//get a hardcoded past reset date / time
let past_reset : DateTime<Utc> = Utc.ymd(2020, 11, 10).and_hms(18, 0, 0);
let now: DateTime<Utc> = Utc::now();
//get total seconds between now and the past reset
//take the mod of that divided by a week in seconds
//subtract that amount from current date / time to find previous reset
now - Duration::seconds((now - past_reset).num_seconds() % WEEK_IN_SECONDS)
}
By shamelessly stealing ideas of both #MaxV and #Simson you can do the following: Playground
Fixed it to cover time below or above 18 hours on Tuesday
use chrono::{Utc, Duration, NaiveTime};
use chrono::prelude::*;
fn main() {
let now: DateTime<Utc> = Utc.ymd(2020, 11, 25).and_hms(17, 0, 0);
let now_date = now.date();
let current_day = now_date.weekday().number_from_monday() as i64;
let tuesday_correction = if current_day == 2 && now.time() < NaiveTime::from_hms(18, 0, 0) {7} else {0};
let days_to_tuesday = Duration::days((7 + current_day - 2) % 7 + tuesday_correction);
let target_date = (now_date - days_to_tuesday).and_hms(18, 0, 0);
println!("{:?}", target_date);
}
There is my solution for Tuesday 18:00: playground
use chrono::{Utc, Duration, Datelike, Date};
fn main() {
let now: Date<Utc> = Utc::now().date();
let days_since_previous_tue = match now.weekday().number_from_monday() {
/*Mon*/ 1 => 6,
/*Tue*/ 2 => 7,
/*Wed*/ 3 => 1,
/*Thu*/ 4 => 2,
/*Fri*/ 5 => 3,
/*Sat*/ 6 => 4,
/*Sun*/ 7 => 5,
_ => panic!("Incorrect number_from_monday"),
};
let target = (now - Duration::days(days_since_previous_tue)).and_hms(18, 0, 0);
println!("Target date:{:?}", target);
}
It's not generic enough to process any possible day but I believe it shows the idea. Please let me know if you're interested in generic solution for any day/time.
Related
Let's suppose I have a array list with all these following data.
let events = {
["-1 19:00"],
["-1 20:00"],
["-1 17:00", "-1 23:00"],
["1 18:00"],
["2 18:00"],
["3 18:00"],
["4 18:00"],
["5 18:00"],
["6 18:00"],
["7 18:00"],
};
So, Here -1 represents every single day, Like every "sunday, monday" and so on. -1 = everyday, 1 = Monday, 2 = Tuesday and so on.
So, I want to calculate The time left from the current time to the nearest day with hours and mins inside the array. I'm really lacking idea on how I'm supposed to do it.
getTimeLeftTillDay(dayname, time) :any {
let d = new Date();
let coming = parseInt(dayname);
if(coming === -1) {
coming = d.getDay();
}
const day = d.getDay();
const targetDay = coming; // Someday
let dayOffset = targetDay - day;
if (dayOffset < 0) dayOffset += 7;
d = new Date(d.getTime() + (dayOffset * 24 * 3600 * 1000));
let timea = parseInt(time[0]);
let timeb = parseInt(time[1]);
d.setHours(timea);
d.setMinutes(timeb);
return d;
}
I tried to use the above code but it doesn't work as I expected. I'll really like help!
If relying on a 3rd-party library is ok for you, I'd use one of the many date-libs to perform the calculations. Here's how you could do it using date-fns:
import {addDays, formatDuration, intervalToDuration, setHours, setMinutes} from "date-fns";
const getTimeLeftTillDay = (dayName, time) => {
let daysToAdd = 0;
if (dayName !== '-1') {
daysToAdd = Number.parseInt(dayName);
}
const startDate = new Date();
let endDate = addDays(startDate, daysToAdd);
const [minutes, seconds] = time.split(":");
const hoursToSet = Number.parseInt(minutes);
endDate = setHours(endDate, hoursToSet)
const minutesToSet = Number.parseInt(seconds);
endDate = setMinutes(endDate, minutesToSet)
return customFormatDuration(startDate, endDate);
}
export const customFormatDuration = (start, end) => {
const durations = intervalToDuration({start, end})
return formatDuration(durations);
}
console.log(getTimeLeftTillDay("-1", "19:00"));
console.log(getTimeLeftTillDay("1", "02:00"));
console.log(getTimeLeftTillDay("7", "02:00"));
This prints the following on my machine (executed at 2:25 pm, CET):
4 hours 35 minutes
11 hours 35 minutes
6 days 21 hours 35 minutes
If you want to consider cases where the calculated time is before the current time and you want to treat them as "next-day" and handle days in the past in general, you can do:
const getTimeLeftTillDay = (dayName, time) => {
const startDate = new Date();
let dayToSet = getDay(startDate);
if (dayName !== '-1') {
dayToSet = Number.parseInt(dayName) - 1;
}
let endDate = new Date();
const [minutes, seconds] = time.split(":");
const hoursToSet = Number.parseInt(minutes);
endDate = setHours(endDate, hoursToSet)
const minutesToSet = Number.parseInt(seconds);
endDate = setMinutes(endDate, minutesToSet)
endDate = setDay(endDate, dayToSet);
if (isBefore(endDate, startDate)) {
endDate = addDays(endDate, 1);
}
return customFormatDuration(startDate, endDate);
}
I am trying to program a triangle in Rust based on the C++ tutorial at vulkan-tutorial.com. I can see my triangle, but my program stops responding after finishing the drawFrame function 3 times. The drawFrame function:
unsafe {
vkWaitForFences(
self.vulkan.logical_device,
1,
&self.vulkan.in_flight_fences[self.vulkan.current_frame as usize],
VK_TRUE,
18446744073709551615, // uint_max
);
}
let mut image_index = 0;
let wait_stages = VkPipelineStageFlagBits_VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
unsafe {
let result = vkAcquireNextImageKHR(
self.vulkan.logical_device,
self.vulkan.swapchain,
u64::MAX,
self.vulkan.image_available_semaphore[self.vulkan.current_frame as usize],
null_mut(),
&mut image_index,
);
if result != VkResult_VK_SUCCESS {
panic!("Couldn't aquire next image. Error code: {}", result);
}
}
if self.vulkan.images_in_flight[image_index as usize] != null_mut() {
unsafe {
vkWaitForFences(
self.vulkan.logical_device,
1,
&self.vulkan.images_in_flight[image_index as usize],
VK_TRUE,
18446744073709551615, // uint_max
);
}
}
let wait_semaphores =
[self.vulkan.image_available_semaphore[self.vulkan.current_frame as usize]];
let signal_semaphores =
[self.vulkan.render_finished_semaphore[self.vulkan.current_frame as usize]];
self.vulkan.images_in_flight[image_index as usize] =
self.vulkan.in_flight_fences[self.vulkan.current_frame as usize];
let mut submit_info: VkSubmitInfo = unsafe { core::mem::zeroed() };
submit_info.sType = VkStructureType_VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = wait_semaphores.as_ptr();
submit_info.pWaitDstStageMask = &wait_stages;
submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &self.vulkan.command_buffers[image_index as usize];
submit_info.signalSemaphoreCount = 1;
submit_info.pSignalSemaphores = signal_semaphores.as_ptr();
unsafe {
vkResetFences(
self.vulkan.logical_device,
1,
&self.vulkan.in_flight_fences[self.vulkan.current_frame as usize],
);
}
let result = unsafe {
vkQueueSubmit(
self.vulkan.graphics_queue,
1,
&submit_info,
self.vulkan.in_flight_fences[self.vulkan.current_frame as usize],
)
};
if result != VkResult_VK_SUCCESS {
panic!(
"failed to submit draw command buffer. Error code: {}",
result
);
}
let mut present_info: VkPresentInfoKHR = unsafe { core::mem::zeroed() };
present_info.sType = VkStructureType_VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
present_info.waitSemaphoreCount = 1;
present_info.pWaitSemaphores = self.vulkan.render_finished_semaphore.as_ptr();
present_info.swapchainCount = 1;
present_info.pSwapchains = &self.vulkan.swapchain;
present_info.pImageIndices = &image_index;
present_info.pResults = null_mut();
unsafe {
vkQueuePresentKHR(self.vulkan.present_queue, &present_info);
}
self.vulkan.current_frame = (self.vulkan.current_frame + 1) % MAX_FRAMES_IN_FLIGHT;
It gives me errors which mean nothing to me:
UNASSIGNED-CoreValidation-DrawState-QueueForwardProgress and VUID-vkQueuePresentKHR-pWaitSemaphores-03268.
I don't wish to use vulkano or other crates, I just want to know what I did wrong.
Seems to me you are waiting on the wrong semaphore.
You signal self.vulkan.render_finished_semaphore[self.vulkan.current_frame], but then on present wait on self.vulkan.render_finished_semaphore[0].
I wrote this simple test to compare the speeds of serializing an array of objects as binary and JSON.
const { performance } = require('perf_hooks');
let units = [];
let id = 0;
let CHUNK_SIZE = 23;
for (let i = 0; i < 50000; i++) {
let r = Math.random();
let u = {};
u.id = id;
u.rotation = Math.PI * 2 * Math.random();
u.type = 0;
u.x = i;
u.y = i;
u.size = r * 20 + 12;
u.health = 1;
u.int = 1;
units.push(u);
}
[
[
"JSON",
(units) => JSON.stringify(units.map(unit => [
unit.id,
unit.type,
unit.x.toFixed(0),
unit.y.toFixed(0),
unit.rotation.toFixed(4),
unit.health.toFixed(2),
unit.size.toFixed(0),
parseInt(unit.int)
])),
(units) => JSON.parse(units)
],
[
"Binary",
(units) => {
return Buffer.concat(units.map(unit => {
let buf = new Buffer(CHUNK_SIZE);
buf.writeUInt32BE(unit.id);
buf.writeUInt8(unit.type, 4);
buf.writeInt32BE(unit.x, 5);
buf.writeInt32BE(unit.y, 9);
buf.writeFloatBE(unit.rotation, 13);
buf.writeFloatBE(unit.health, 17);
buf.writeUInt8(unit.size, 21);
buf.writeUInt8(unit.attacking ? 1 : 0, 22);
return buf;
}));
},
(units) => {
let u = units.buffer;
let result = [];
for (let offset = 0; offset < u.byteLength; offset += CHUNK_SIZE) {
let view = new DataView(u, offset, CHUNK_SIZE);
result.push([
view.getUint32(0),
view.getUint8(4),
view.getInt32(5),
view.getInt32(9),
view.getFloat32(13),
view.getFloat32(17),
view.getUint8(21),
view.getUint8(22)
]);
}
return result;
}
]
].forEach(([name, compress, decompress]) => {
console.log("Test: " + name);
let t0 = performance.now();
let compressed = compress(units);
let t1 = performance.now();
let decompressed = decompress(compressed);
let t2 = performance.now();
console.log(`Result: ${decompressed.length}`);
console.log(`Compression took: ${t1 - t0}ms`);
console.log(`Compressed length: ${compressed.byteLength || compressed.length}`);
console.log(`Decompression took: ${t2 - t1}ms`);
console.log(`Total time: ${t2 - t0}ms`);
console.log("");
})
Drop that into NodeJS and look at the results, here are mine
Test: JSON
Result: 50000
Compression took: 411.7958119995892ms
Compressed length: 2227781
Decompression took: 134.79507100209594ms
Total time: 546.5908830016851ms
Test: Binary
Result: 50000
Compression took: 612.1825229972601ms
Compressed length: 1150000
Decompression took: 191.14320900291204ms
Total time: 803.3257320001721ms
I'm quite surprised to find that JSON is faster since it is doing considerably more work than the binary counterpart.
Why is that and how can it be improved?
Hope I don't look like a weirdo answering, like a third consecutive, question I asked myself, but hopefully I'm providing content that could be useful for someone.
It appears as if I have been taking way too much advantage of convenience functions. Combine that with not knowing exactly what is happening "under the hood" and you get the results from the question.
Constructing a new buffer for each entry and then concatenating them seems like a costly thing to do. Instead create a single buffer and add to that:
(units) => {
let buf = new Buffer(units.length * CHUNK_SIZE);
units.forEach((unit, i) => {
let offset = i * CHUNK_SIZE;
buf.writeUInt32BE(unit.id, offset);
buf.writeUInt8(unit.type, offset + 4);
buf.writeInt32BE(unit.x, offset + 5);
buf.writeInt32BE(unit.y, offset + 9);
buf.writeFloatBE(unit.rotation, offset + 13);
buf.writeFloatBE(unit.health, offset + 17);
buf.writeUInt8(unit.size, offset + 21);
buf.writeUInt8(unit.attacking ? 1 : 0, offset + 22);
});
return buf;
},
Same goes on the unserializing part, instead of constructing a new DataView, which can mislead you to believe it would be more effective as it's constructor takes an offset and length, construct one and read off of it:
(units) => {
let u = units.buffer;
let result = [];
let view = new DataView(u);
for (let offset = 0; offset < u.byteLength; offset += CHUNK_SIZE) {
result.push([
view.getUint32(offset + 0),
view.getUint8(offset + 4),
view.getInt32(offset + 5),
view.getInt32(offset + 9),
view.getFloat32(offset + 13),
view.getFloat32(offset + 17),
view.getUint8(offset + 21),
view.getUint8(offset + 22)
]);
}
return result;
}
And now we've got more acceptable results:
Test: JSON
Result: 50000
Compression took: 284.3018040023744ms
Compressed length: 2934399
Decompression took: 197.91818399727345ms
Total time: 522.21998799964786ms
Test: Binary
Result: 50000
Compression took: 175.56888100132346ms
Compressed length: 1150000
Decompression took: 79.27483800053596ms
Total time: 254.84371900185943ms
JSON length has increased because I removed the .toFixed calls from the compress function.
I'm interested if there are further improvements that can be done, as I'm sure there are plenty of people who are more competent than me out there.
I'm able to generate the X Axis for one specific month (i.e. February).
func generateDateAxisValues(_ month: Int, year: Int) -> [ChartAxisValueDate] {
let date = dateWithComponents(1, month, year)
let calendar = Calendar.current
let monthDays = calendar.range(of: .day, in: .month, for: date)!
let arr = CountableRange<Int>(monthDays)
return arr.map {day in
let date = dateWithComponents(day, month, year)
let axisValue = ChartAxisValueDate(date: date, formatter: displayFormatter, labelSettings: labelSettings)
axisValue.hidden = !(day % 5 == 0)
return axisValue
}
}
But I want to stretch the X Axis values across the past 30 days; not just one individual month. How can you generate X Axis values for the past 30 days?
I solved this by, first, making an extension on Date:
extension Date {
var day:Int { return Calendar.current.component(.day, from:self) }
var month:Int { return Calendar.current.component(.month, from:self) }
var year:Int { return Calendar.current.component(.year, from:self) }
func priorDates(daysBack: Int) -> [Date] {
let cal = Calendar.current
var date = cal.startOfDay(for: self)
var dates = [Date]()
for _ in 1 ... daysBack {
date = cal.date(byAdding: .day, value: -1, to: date)!
dates.append(date)
}
dates = dates.sorted(by: { $0 < $1 })
return dates
}
}
Which I then use to generate the x axis dates:
func generateDateAxisValues(daysBack: Int) -> [ChartAxisValueDate] {
let priorDaysArray = Date().priorDates(daysBack: daysBack)
return priorDaysArray.map { date in
let axisValue = ChartAxisValueDate(date: date, formatter: displayFormatter, labelSettings: labelSettings)
axisValue.hidden = !(date.day % 5 == 0)
return axisValue
}
}
The way I did this in Swift 2.3 was:
let currentDate = NSDate()
let currentCalendar = NSCalendar.currentCalendar()
var startDate : NSDate?
var endDate : NSDate?
// The following two lines set the `startDate` and `endDate` to the start of the day
currentCalendar.rangeOfUnit(.Day, startDate: &startDate, interval: nil, forDate: currentDate)
currentCalendar.rangeOfUnit(.Day, startDate: &endDate, interval: nil, forDate: self)
let intervalComps = currentCalendar.components([.Day], fromDate: startDate!, toDate: endDate!, options: [])
print(intervalComps.day)
Now this has all changed with Swift 3. I have to either use NSCalendar and NSDate by constantly type casting with as, or find the Swift 3 way of doing it.
What's the right way to do it in Swift 3?
In Swift 5 there is a simple one-liner to get the number of days (or any other DateComponent) between two dates:
let diffInDays = Calendar.current.dateComponents([.day], from: dateA, to: dateB).day
Note: As pointed out in the comments, this solution measures the 24h periods and therefore requires at least 24h between dateA and dateB.
Turns out this is much simpler to do in Swift 3:
extension Date {
func interval(ofComponent comp: Calendar.Component, fromDate date: Date) -> Int {
let currentCalendar = Calendar.current
guard let start = currentCalendar.ordinality(of: comp, in: .era, for: date) else { return 0 }
guard let end = currentCalendar.ordinality(of: comp, in: .era, for: self) else { return 0 }
return end - start
}
}
Edit
Comparing the ordinality of the two dates should be within the same era instead of the same year, since naturally the two dates may fall in different years.
Usage
let yesterday = Date(timeInterval: -86400, since: Date())
let tomorrow = Date(timeInterval: 86400, since: Date())
let diff = tomorrow.interval(ofComponent: .day, fromDate: yesterday)
// return 2
Swift 4 Version
let startDate = "2000-11-22"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let formatedStartDate = dateFormatter.date(from: startDate)
let currentDate = Date()
let components = Set<Calendar.Component>([.second, .minute, .hour, .day, .month, .year])
let differenceOfDate = Calendar.current.dateComponents(components, from: formatedStartDate!, to: currentDate)
print (differenceOfDate)
Printed -
year: 16 month: 10 day: 19 hour: 12 minute: 16 second: 42 isLeapMonth: false
swift4 calendar date
If any one want to do it more specifically follow Below Steps
1.Add this Date Extension
extension Date {
/// Returns the amount of years from another date
func years(from date: Date) -> Int {
return Calendar.current.dateComponents([.year], from: date, to: self).year ?? 0
}
/// Returns the amount of months from another date
func months(from date: Date) -> Int {
return Calendar.current.dateComponents([.month], from: date, to: self).month ?? 0
}
/// Returns the amount of weeks from another date
func weeks(from date: Date) -> Int {
return Calendar.current.dateComponents([.weekOfMonth], from: date, to: self).weekOfMonth ?? 0
}
/// Returns the amount of days from another date
func days(from date: Date) -> Int {
return Calendar.current.dateComponents([.day], from: date, to: self).day ?? 0
}
/// Returns the amount of hours from another date
func hours(from date: Date) -> Int {
return Calendar.current.dateComponents([.hour], from: date, to: self).hour ?? 0
}
/// Returns the amount of minutes from another date
func minutes(from date: Date) -> Int {
return Calendar.current.dateComponents([.minute], from: date, to: self).minute ?? 0
}
/// Returns the amount of seconds from another date
func seconds(from date: Date) -> Int {
return Calendar.current.dateComponents([.second], from: date, to: self).second ?? 0
}
/// Returns the amount of nanoseconds from another date
func nanoseconds(from date: Date) -> Int {
return Calendar.current.dateComponents([.nanosecond], from: date, to: self).nanosecond ?? 0
}
/// Returns the a custom time interval description from another date
func offset(from date: Date) -> String {
var result: String = ""
if years(from: date) > 0 { return "\(years(from: date))y" }
if months(from: date) > 0 { return "\(months(from: date))M" }
if weeks(from: date) > 0 { return "\(weeks(from: date))w" }
if seconds(from: date) > 0 { return "\(seconds(from: date))" }
if days(from: date) > 0 { result = result + " " + "\(days(from: date)) D" }
if hours(from: date) > 0 { result = result + " " + "\(hours(from: date)) H" }
if minutes(from: date) > 0 { result = result + " " + "\(minutes(from: date)) M" }
if seconds(from: date) > 0 { return "\(seconds(from: date))" }
return ""
}
}
2.Define it in globally
fileprivate var timer: Timer?
3.Call this Method in viewDidLoad or where ever you want
override func viewDidLoad() {
super.viewDidLoad()
self.getRemainingTime()
}
4.Usage
fileprivate func getRemainingTime() {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let startDate = "2018-06-02 10:11:12"
let currentDate = dateFormatter.string(from: Date())
if currentDate != startDate {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: (#selector(calculateTime)), userInfo: nil, repeats: true)
RunLoop.current.add(timer!, forMode: RunLoopMode.commonModes)
timer?.fire()
}
else {
self.timer?.invalidate()
self.timer = nil
}
}
func calculateTime() {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let stdate : String = "2018-06-02 10:11:12"
let startDate = dateFormatter.date(from: stdate)!
let currentDate = Date()
let strTimer : String = startDate.offset(from: currentDate)
if !strTimer.isEmpty {
let stDay: String = "\((Int(strTimer)! % 31536000) / 86400)"
let stHour: String = "\((Int(strTimer)! % 86400) / 3600)"
let stMin: String = "\((Int(strTimer)! % 3600) / 60)"
let stSec: String = "\(Int(strTimer)! % 60)"
yourLabelOutlet.text = "Start In :\(stDay) Days \(stHour) Hours \(stMin) Minutes \(stSec) Seconds"
}
}
Works like Charm You can Use every separate string to your UI Side,
Enjoy
private func calculateDaysBetweenTwoDates(start: Date, end: Date) -> Int {
let currentCalendar = Calendar.current
guard let start = currentCalendar.ordinality(of: .day, in: .era, for: start) else {
return 0
}
guard let end = currentCalendar.ordinality(of: .day, in: .era, for: end) else {
return 0
}
return end - start
}
In Swift4 we can easily get no of days between two different calendar dates using below codes.
First one is the difference in days with the current date.
let previousDate = "2017-03-01"
let currentDate = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let previousDateFormated : Date? = dateFormatter.date(from: previousDate)
let difference = currentDate.timeIntervalSince(previousDateFormated!)
var differenceInDays = Int(difference/(60 * 60 * 24 ))
print(differenceInDays)
Continuing with the above code ... Below is for finding no of days for two different dates. the content of previous date is taken from above date
let futureDate = "2017-12-30"
let futureDateFormatted : Date? = dateFormatter.date(from: futureDate)
differenceInDays = (futureDateFormatted?.timeIntervalSince(previousDateFormated!))! / (60 * 60 * 24)
print(differenceInDays)
If someone would need to display all time units e.g "hours minutes seconds" not just "hours". Let's say the time difference between two dates is 1hour 59minutes 20seconds. This function will display "1h 59m 20s".
Here is my code:
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'hh:mm:ss"
let start = dateFormatter.date(from: "2019-01-31T07:45:00")!
let end = dateFormatter.date(from: "2019-03-01T06:30:00")!
print("Date Difference : ", end.offsetFrom(date: start))
Function Definition:
extension Date {
func offsetFrom(date : Date) -> String {
let dayHourMinuteSecond: Set = [.day, .hour, .minute, .second]
let difference = NSCalendar.current.dateComponents(dayHourMinuteSecond, from: date, to: self);
let seconds = "\(difference.second ?? 0)s"
let minutes = "\(difference.minute ?? 0)m" + " " + seconds
let hours = "\(difference.hour ?? 0)h" + " " + minutes
let days = "\(difference.day ?? 0)d" + " " + hours
if let day = difference.day, day > 0 { return days }
if let hour = difference.hour, hour > 0 { return hours }
if let minute = difference.minute, minute > 0 { return minutes }
if let second = difference.second, second > 0 { return seconds }
return ""
}
}
Found this on a different thread, but it was finally the most simple solution for me using Swift 4:
let previousDate = ENTER DATE HERE
let now = Date()
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .brief // May delete the word brief to let Xcode show you the other options
formatter.allowedUnits = [.month, .day, .hour]
formatter.maximumUnitCount = 1 // Show just one unit (i.e. 1d vs. 1d 6hrs)
let stringDate = formatter.string(from: previousDate, to: now)
import Foundation
extension DateComponents {
func dateComponentsToTimeString() -> String {
var hour = "\(self.hour!)"
var minute = "\(self.minute!)"
var second = "\(self.second!)"
if self.hour! < 10 { hour = "0" + hour }
if self.minute! < 10 { minute = "0" + minute }
if self.second! < 10 { second = "0" + second }
let str = "\(hour):\(minute):\(second)"
return str
}
}
extension Date {
func offset(from date: Date)-> DateComponents {
let components = Set<Calendar.Component>([.second, .minute, .hour, .day, .month, .year])
let differenceOfDate = Calendar.current.dateComponents(components, from: date, to: self)
return differenceOfDate
}
}
Use:
var durationString: String {
return self.endTime.offset(from: self.startTime).dateComponentsToTimeString()
}
Updated for Swift 3:
if you want to print the number of days as well as days list between two calendar dates, used below simple code;
// Variable Declaration:
var daysListArray = [String]()
// function Defination:
func printCountBtnTwoDates(mStartDate: Date, mEndDate: Date) -> Int {
let calendar = Calendar.current
let formatter = DateFormatter()
var newDate = mStartDate
daysListArray.removeAll()
while newDate <= mEndDate {
formatter.dateFormat = "yyyy-MM-dd"
daysListArray.append(formatter.string(from: newDate))
newDate = calendar.date(byAdding: .day, value: 1, to: newDate)!
}
// print("daysListArray: \(daysListArray)") // if you want to print list between start date and end date
return daysListArray.count
}
// To call above function:
let count = self.printCountBtnTwoDates(mStartDate: your_start_date, mEndDate: your_end_date)
print("count: \(count)") // date count
// Enjoy coding...!
private func days(actual day1:[Int],expect day2:[Int]) -> Int {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let first = "\(day1[2])-\(day1[1])-\(day1[0])"
let firstDate = dateFormatter.date(from:first)!
let last = "\(day2[2])-\(day2[1])-\(day2[0])"
let lastDate = dateFormatter.date(from:last)!
let currentCalendar = NSCalendar.current
let components = currentCalendar.dateComponents([.day], from: firstDate, to: lastDate)
return components.day!
}
Another approach to compare with components of day month year
Usage:
Input the dates in following format
[dd, mm, yyyy]
[9, 6, 2017]
[6, 6, 2017]