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]
Related
So, I managed to get it to work but the year 2004 returns under 18.
Let's say I set the year to 01/13/2004 it returns the age as 18 but also returns they're under 18. This also goes with any day within the month of January that meets the requirements for being 18. Here's the function for checking age & date. My best guess at this point is possibly timezone issue?
GetAge(date: string): number {
let today = new Date();
let birthDate = new Date(date);
let age = today.getFullYear() - birthDate.getFullYear();
let month = today.getMonth() - birthDate.getMonth();
if (month < 0 || (month === 0 && today.getDate() < birthDate.getDate())) {
age--;
}
console.log('Age: ' + age + '\nBirthday: ' + birthDate);
return age;
}
AgeCheck(controlName: string): ValidatorFn {
return (controls: AbstractControl) => {
const control = controls.get(controlName);
if (control?.errors && !control.errors['under18']) {
return null;
}
if (this.GetAge(control?.value) <= 18) {
return { under18: true }
} else {
return null;
}
}
}
I'm using this as the date selection input
<ion-input type="date" formControlName="dateofbirth"></ion-input>
And here's an example of the FormBuilder
this.credentials = this.fb.group({
dateofbirth: ['', [Validators.required]],
}, {validator: [this.AgeCheck('dateofbirth')]});
Screenshot of console out put
The BEST way is to use Moment.js like:
npm install moment --save
and
import * as moment from 'moment';
...
age18Check(birthday: Date) {
return moment(birthday).add(18, 'years') <= moment();
}
In this way you can also check the hour if you insert it in the birthday(Date). But if you have problems with hours you can also this:
age18Check(birthday: Date) {
return
moment(birthday).add(18, 'years').format('DD/MM/YYYY') <=
moment().format('DD/MM/YYYY');
}
I hope you was looking for this!
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 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.
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
}
}
I am populating a datagridview with dates for the last 3 days starting
from Yesterday. Sunday date should not be included. The code below works
great but I think this can be improved with limited amount of code like writing
just one function
How can I improve on this?
private void button1_Click(object sender, EventArgs e)
{
var rangeList = new List<MyDates>();
//first date
var yesterday = DateTime.Today.AddDays(-1);
string dayoftheweek = yesterday.DayOfWeek.ToString();
DateTime storefirstdate;
if (dayoftheweek == "Sunday")
{
var firstdate = DateTime.Today.AddDays(-2);
storefirstdate = firstdate;
rangeList.Add(new MyDates() {Dateseries = storefirstdate});
}
else
{
storefirstdate = yesterday;
rangeList.Add(new MyDates() {Dateseries = storefirstdate});
}
//second date
var twodaysago = DateTime.Today.AddDays(-2);
string dayoftheweek2 = twodaysago.DayOfWeek.ToString();
DateTime storeseconddate;
if (dayoftheweek2 == "Sunday")
{
var seconddate = DateTime.Today.AddDays(-3);
storeseconddate = seconddate;
rangeList.Add(new MyDates() {Dateseries = storeseconddate});
}
else
{
storeseconddate = twodaysago;
rangeList.Add(new MyDates() {Dateseries = storeseconddate});
}
//Third date
var threedaysago = DateTime.Today.AddDays(-3);
string dayoftheweek3 = threedaysago.DayOfWeek.ToString();
DateTime storethirddate;
if (dayoftheweek3 == "Sunday")
{
var thirddate = DateTime.Today.AddDays(-4);
storethirddate = thirddate;
rangeList.Add(new MyDates() {Dateseries = storethirddate});
}
else
{
storethirddate = threedaysago;
rangeList.Add(new MyDates() {Dateseries = storethirddate});
}
dataGridView1.DataSource = rangeList.OrderByDescending(j=>j.Dateseries).ToList();
}
public class MyDates
{
public DateTime Dateseries { get; set; }
}
The result should look like this if processed today- Tuesday-11/08/2016,Monday -11/07/2016,Saturday - 11/05/2016
Well you can do it using linq.
The integer array is not exactelly beautiful, perhaps someone can sugest a more elegant solution for that.
IEnumerable<MyDates> rangeList = new [] {1, 2, 3, 4}.Select(n => DateTime.Now.Subtract(TimeSpan.FromDays(n))) // Transforming the integer (kind of index of days) array into dates.
.Where(n => n.DayOfWeek != DayOfWeek.Sunday).Take(3) // Removing the Sunday and taking 3 days only.
.Select(n => new MyDates() { Dateseries = n }); // Converting the date in your MyDates structure.