如何在Objective-C中生成随机日期?

12

我想要生成两个日期之间的随机日期--例如从今天到未来60天之间的一个随机日期。我该如何做到?

更新

根据答案提供的信息,我想出了这个方法,我经常使用:

// Generate a random date sometime between now and n days before day.
// Also, generate a random time to go with the day while we are at it.
- (NSDate *) generateRandomDateWithinDaysBeforeToday:(NSInteger)days
{
    int r1 = arc4random_uniform(days);
    int r2 = arc4random_uniform(23);
    int r3 = arc4random_uniform(59);

    NSDate *today = [NSDate new];
    NSCalendar *gregorian = 
             [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];

    NSDateComponents *offsetComponents = [NSDateComponents new];
    [offsetComponents setDay:(r1*-1)];
    [offsetComponents setHour:r2];
    [offsetComponents setMinute:r3];

    NSDate *rndDate1 = [gregorian dateByAddingComponents:offsetComponents 
                                                  toDate:today options:0];

    return rndDate1;
}

我不知道你在想什么,@lulius Cæsar,但这个问题与在两个日期之间循环根本没有任何相似之处... - memmons
同样的代码,重构并进行了文档记录 https://gist.github.com/eirnym/c9526a045556e4d8464b41a367843e3c - Eir Nym
6个回答

17

获取一个随机数并将其用作时间间隔,然后将其添加到起始日期。例如:

NSTimeInterval timeBetweenDates = [endDate timeIntervalSinceDate:startDate];
NSTimeInterval randomInterval = ((NSTimeInterval)arc4random() / ARC4RANDOM_MAX) * timeBetweenDates;

NSDate *randomDate = [startDate dateByAddingTimeInterval:randomInterval];

3
如果您遇到有关ARC4RANDOM_MAX的错误,请将其替换为0x100000000或使用#define ARC4RANDOM_MAX 0x100000000。 - budiDino
1
顺便说一句,我认为这应该成为被接受的答案,因为它解决了比被接受的答案更多的随机日期用例。 - budiDino

9
  1. Generate a random number between 1 and 60

    int r = arc4random_uniform(60) + 1;
    
    // Usage : arc4random_uniform(hi - lo + 1) + lo
    
  2. Get current date

    [NSDate date];
    
  3. Use NSDateComponents to subtract the random number from your days component and generate a new date.


3
在这里可能不是什么大问题,但使用百分号(%)几乎总是会对随机数产生偏差。你需要使用的函数是arc4random_uniform()。 - Rob Napier
4
arc4random_uniform(upper_bound) 返回一个大于等于0且小于upper_bound的整数,使得分布是均匀的。也就是说,范围内的所有值都有同样的可能性。 - Rob Napier
@RobNapier: 感谢您启发我 :) - Legolas
非常有用的信息。谢谢!我之前不知道如何解决这个问题。现在,我不仅有了一个好的解决方案,而且还学到了一些关于随机数生成的知识!arc4random()的非均匀分布(在创建BOOL时特别糟糕)一直让我感到困扰。 - memmons

6
这是一个适用于Swift 4.x的扩展,它允许您指定一段时间(以天为单位),然后使用它来查找当前日期之前或之后的随机Date
extension Date {
    static func randomDate(range: Int) -> Date {
        // Get the interval for the current date
        let interval =  Date().timeIntervalSince1970
        // There are 86,400 milliseconds in a day (ignoring leap dates)
        // Multiply the 86,400 milliseconds against the valid range of days
        let intervalRange = Double(86_400 * range)
        // Select a random point within the interval range
        let random = Double(arc4random_uniform(UInt32(intervalRange)) + 1)
        // Since this can either be in the past or future, we shift the range
        // so that the halfway point is the present
        let newInterval = interval + (random - (intervalRange / 2.0))
        // Initialize a date value with our newly created interval
        return Date(timeIntervalSince1970: newInterval)
    }
}

你这样称呼它:
Date.randomDate(range: 500) // Any date that is +/- 500 days from the current date

运行这个程序10次会产生:
2019-03-15 01:45:52 +0000
2018-12-20 02:09:51 +0000
2018-06-28 10:28:31 +0000
2018-08-02 08:13:01 +0000
2019-01-25 07:04:18 +0000
2018-08-30 22:37:52 +0000
2018-10-05 19:38:22 +0000
2018-11-30 04:51:18 +0000
2019-03-24 07:27:39 +0000

5

这个框架在生成随机日期方面做得很好。但是在Swift中: https://github.com/thellimist/SwiftRandom/blob/master/SwiftRandom/Randoms.swift

public extension NSDate {
    /// SwiftRandom extension
    public static func randomWithinDaysBeforeToday(days: Int) -> NSDate {
        let today = NSDate()

        guard let gregorian = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian) else {
            print("no calendar \"NSCalendarIdentifierGregorian\" found")
            return today
        }

        let r1 = arc4random_uniform(UInt32(days))
        let r2 = arc4random_uniform(UInt32(23))
        let r3 = arc4random_uniform(UInt32(23))
        let r4 = arc4random_uniform(UInt32(23))

        let offsetComponents = NSDateComponents()
        offsetComponents.day = Int(r1) * -1
        offsetComponents.hour = Int(r2)
        offsetComponents.minute = Int(r3)
        offsetComponents.second = Int(r4)

        guard let rndDate1 = gregorian.dateByAddingComponents(offsetComponents, toDate: today, options: []) else {
            print("randoming failed")
            return today
        }
        return rndDate1
    }

    /// SwiftRandom extension
    public static func random() -> NSDate {
        let randomTime = NSTimeInterval(arc4random_uniform(UInt32.max))
        return NSDate(timeIntervalSince1970: randomTime)
    }

}

不错的扩展!r3和r4随机数应该随机到59吗? - Felipe Ferri
嗯,是的 :D 欢迎在 GitHub 上发送拉取请求。 - Esqarrouth
好的,拉取请求已发送。顺便说一下,SwiftRandom非常有用,感谢您的建议!您是作者吗? - Felipe Ferri
链接失效!@Esqarrouth - Hemang

2
使用秒。伪代码如下:
1 Generate a random integer between 0 and (60 * 60 * 24 * 60)
2 Get the unixtime in seconds for the current time
3 Add your random integer
4 Convert this integer back to a date

1
一天的长度可能比24小时长或短,这是由于夏令时的开始和结束所致。 - Ken Thomases
1
啊,是的,那是正确的。这个方法将在60天的范围内产生一个随机日期,加减一个小时,这在大多数情况下都是可以接受的。 - Albert Veli

-1

Swift 3.x +

public extension Date {
    /// SwiftRandom extension
    public static func randomWithinDaysBeforeToday(days: Int) -> Date {
        let today = Date()
        let gregorian = Calendar(identifier: .gregorian)

        let r1 = arc4random_uniform(UInt32(days))
        let r2 = arc4random_uniform(UInt32(23))
        let r3 = arc4random_uniform(UInt32(23))
        let r4 = arc4random_uniform(UInt32(23))

        let offsetComponents = NSDateComponents()
        offsetComponents.day = Int(r1) * -1
        offsetComponents.hour = Int(r2)
        offsetComponents.minute = Int(r3)
        offsetComponents.second = Int(r4)

        let rndDate1 = gregorian.date(byAdding: offsetComponents as DateComponents, to: today)
        return rndDate1!
    }

    /// SwiftRandom extension
    public static func random() -> Date {
        let randomTime = TimeInterval(arc4random_uniform(UInt32.max))
        return Date(timeIntervalSince1970: randomTime)
    }
}

附言:Esqarrouth答案已经修正为最新的Swift。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接