Skip to content

13-Random 和 DateTime

一、Random(随机数)

Random 类用于生成随机数,位于 System 命名空间。


1. Random 的两种创建方式

csharp
// 方式一:无种子——基于当前时间自动生成种子
Random rnd = new Random();
// 每次运行产生不同的随机序列

// 方式二:指定种子——相同种子产生相同的序列
Random rnd2 = new Random(12345);
// 调试和测试时使用,保证可重复性

种子选择的重要性:

  • 无种子:基于系统时钟,每次运行结果不同
  • 固定种子:结果可重现,适合测试场景
  • 不要在循环中重复创建:同一时钟滴答内创建的多个对象会产生相同序列
csharp
// ❌ 错误:循环中重复创建
for (int i = 0; i < 5; i++)
{
    Random bad = new Random();  // 可能使用相同的时间种子
    Console.WriteLine(bad.Next(100));  // 可能打印相同的数字
}

// ✅ 正确:只创建一次,重复使用
Random good = new Random();
for (int i = 0; i < 5; i++)
{
    Console.WriteLine(good.Next(100));  // 每次不同
}

2. 常用方法详解

方法签名范围示例
Next()int Next()0 ~ int.MaxValuernd.Next() → 某个正随机整数
Next(Int32)int Next(int maxValue)0 ~ (maxValue-1)rnd.Next(100) → 0~99
Next(Int32,Int32)int Next(int min, int max)min ~ (max-1)rnd.Next(1,7) → 1~6(掷骰子)
NextDouble()double NextDouble()0.0 ~ 1.0rnd.NextDouble() → 0.0~1.0
NextBytes()void NextBytes(byte[])0 ~ 255填充字节数组
csharp
Random rnd = new Random();

// 各种范围的随机数
int dice = rnd.Next(1, 7);                    // 1~6(模拟骰子)
int score = rnd.Next(101);                    // 0~100(随机分数)
int year = rnd.Next(2020, 2025);              // 2020~2024(随机年份)

double percent = rnd.NextDouble();             // 0.0~1.0(百分比)
double price = Math.Round(rnd.NextDouble() * 1000, 2);  // 0.00~1000.00(价格)
double temperature = Math.Round(rnd.NextDouble() * 60 - 20, 1);  // -20~40(温度)

// 随机布尔值
bool coinFlip = rnd.Next(2) == 0;
bool lucky = rnd.NextDouble() < 0.3;  // 30% 的概率

3. 实际应用场景

场景一:随机抽奖

csharp
static string LotteryDraw(string[] participants, int winnerCount)
{
    if (participants.Length < winnerCount)
        return "参与人数不足";

    Random rnd = new Random();
    // 打乱顺序
    string[] shuffled = participants.OrderBy(_ => rnd.Next()).ToArray();

    return $"中奖者:{string.Join("、", shuffled.Take(winnerCount))}";
}

string[] names = { "张三", "李四", "王五", "赵六", "钱七" };
Console.WriteLine(LotteryDraw(names, 2));

场景二:模拟掷骰子

csharp
static int RollDice(int sides = 6)
{
    return new Random().Next(1, sides + 1);
}

// 多次掷骰子统计
Random rnd = new Random();
int[] counts = new int[6];

for (int i = 0; i < 6000; i++)
{
    int result = rnd.Next(1, 7);  // 1~6
    counts[result - 1]++;
}

for (int i = 0; i < 6; i++)
{
    Console.WriteLine($"点数 {i + 1}:{counts[i]} 次({counts[i] / 60.0:F1}%)");
}

场景三:生成验证码

csharp
static string GenerateCaptcha(int length)
{
    const string chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";  // 排除易混淆字符(0,O,1,I)
    Random rnd = new Random();
    char[] result = new char[length];

    for (int i = 0; i < length; i++)
        result[i] = chars[rnd.Next(chars.Length)];

    return new string(result);
}

Console.WriteLine($"验证码:{GenerateCaptcha(4)}");
Console.WriteLine($"验证码:{GenerateCaptcha(6)}");

场景四:洗牌算法(Fisher-Yates)

csharp
static void Shuffle<T>(T[] array)
{
    Random rnd = new Random();
    for (int i = array.Length - 1; i > 0; i--)
    {
        int j = rnd.Next(0, i + 1);
        // 交换 array[i] 和 array[j]
        (array[i], array[j]) = (array[j], array[i]);
    }
}

int[] cards = Enumerable.Range(1, 52).ToArray();  // 52 张牌
Shuffle(cards);
Console.WriteLine($"洗牌后前5张:{string.Join(", ", cards.Take(5))}");

二、DateTime(日期时间)

DateTime 用于处理日期和时间,支持时区、运算、格式化等操作。


1. 创建 DateTime 对象

csharp
// 获取当前时间
DateTime now = DateTime.Now;        // 本地时间:2024-10-01 14:30:00
DateTime utcNow = DateTime.UtcNow;  // UTC 时间:2024-10-01 06:30:00
DateTime today = DateTime.Today;    // 当天日期(时间部分为 00:00:00)

// 指定日期时间
DateTime d1 = new DateTime(2024, 1, 1);               // 2024-01-01 00:00:00
DateTime d2 = new DateTime(2024, 10, 1, 14, 30, 0);  // 2024-10-01 14:30:00
DateTime d3 = new DateTime(2024, 12, 31, 23, 59, 59, 999);  // 含毫秒

// 从字符串解析
DateTime parsed = DateTime.Parse("2024-10-01");
DateTime parsed2 = DateTime.Parse("2024/10/01 14:30:00");

// 安全解析(推荐,避免异常)
if (DateTime.TryParse("2024-13-01", out DateTime result))
{
    Console.WriteLine($"解析成功:{result}");
}
else
{
    Console.WriteLine("日期格式无效");  // 会执行这里
}

DateTime 与 DateTimeOffset

csharp
// DateTime:可以包含时区信息,但容易混淆
DateTime localTime = DateTime.Now;          // 本地时间
DateTime utcTime = DateTime.UtcNow;         // UTC 时间

// DateTimeOffset:明确包含时区偏移
DateTimeOffset nowWithOffset = DateTimeOffset.Now;
Console.WriteLine(nowWithOffset);           // 2024-10-01 14:30:00 +08:00

// 跨时区存储时推荐 DateTimeOffset

2. 常用属性

csharp
DateTime now = DateTime.Now;

Console.WriteLine($"完整时间:{now}");         // 2024-10-01 14:30:00
Console.WriteLine($"年:{now.Year}");           // 2024
Console.WriteLine($"月:{now.Month}");          // 10
Console.WriteLine($"日:{now.Day}");            // 1
Console.WriteLine($"时:{now.Hour}");           // 14
Console.WriteLine($"分:{now.Minute}");         // 30
Console.WriteLine($"秒:{now.Second}");         // 0
Console.WriteLine($"毫秒:{now.Millisecond}");  // 123
Console.WriteLine($"星期:{now.DayOfWeek}");    // Tuesday
Console.WriteLine($"一年中第几天:{now.DayOfYear}");  // 275
Console.WriteLine($"日期部分:{now.Date}");     // 2024-10-01 00:00:00
Console.WriteLine($"时间部分:{now.TimeOfDay}"); // 14:30:00.1230000

3. 日期运算

添加时间间隔

csharp
DateTime today = DateTime.Today;

DateTime tomorrow    = today.AddDays(1);       // 明天
DateTime yesterday   = today.AddDays(-1);      // 昨天
DateTime nextWeek    = today.AddDays(7);       // 一周后
DateTime lastMonth   = today.AddMonths(-1);    // 上个月
DateTime nextYear    = today.AddYears(1);      // 明年
DateTime noon        = today.AddHours(12);     // 今天中午
DateTime midNight    = today.AddMinutes(1);    // 明天 00:01

// 计算月末最后一天
DateTime firstDay = new DateTime(2024, 2, 1);
DateTime lastDay = firstDay.AddMonths(1).AddDays(-1);
Console.WriteLine(lastDay.ToString("yyyy-MM-dd"));  // 2024-02-29(闰年)

日期差值计算

csharp
DateTime start = new DateTime(2024, 1, 1);
DateTime end = new DateTime(2024, 12, 31);

// 计算差值(TimeSpan)
TimeSpan diff = end - start;
Console.WriteLine($"总天数:{diff.Days}");               // 365
Console.WriteLine($"总小时数:{diff.TotalHours}");       // 8760
Console.WriteLine($"总分钟数:{diff.TotalMinutes}");     // 525600

// 计算两个日期之间的工作日数
static int WorkDaysBetween(DateTime start, DateTime end)
{
    int workDays = 0;
    for (DateTime d = start; d <= end; d = d.AddDays(1))
    {
        if (d.DayOfWeek != DayOfWeek.Saturday && d.DayOfWeek != DayOfWeek.Sunday)
            workDays++;
    }
    return workDays;
}

Console.WriteLine($"工作日:{WorkDaysBetween(start, end)}");

4. 日期格式化

csharp
DateTime now = DateTime.Now;

// 常用标准格式
string[] formats =
{
    now.ToString("yyyy-MM-dd"),                     // 2024-10-01(ISO 格式)
    now.ToString("yyyy/MM/dd"),                     // 2024/10/01
    now.ToString("yyyyMMdd"),                       // 20241001(紧凑格式)
    now.ToString("yyyy-MM-dd HH:mm:ss"),            // 2024-10-01 14:30:00
    now.ToString("yyyy-MM-dd HH:mm:ss.fff"),        // 2024-10-01 14:30:00.123(含毫秒)
    now.ToString("HH:mm:ss"),                       // 14:30:00(仅时间)
    now.ToString("dddd"),                           // 星期二
    now.ToString("ddd"),                            // 周二
    now.ToString("yyyy年MM月dd日 dddd"),            // 2024年10月01日 星期二
    now.ToString("hh:mm tt"),                       // 02:30 下午(12小时制)
    now.ToString("yyyy-MM-ddTHH:mm:ssZ"),           // 2024-10-01T14:30:00Z(ISO 8601)
};

// 标准格式说明符
Console.WriteLine(now.ToString("d"));   // 短日期:2024/10/1
Console.WriteLine(now.ToString("D"));   // 长日期:2024年10月1日
Console.WriteLine(now.ToString("t"));   // 短时间:14:30
Console.WriteLine(now.ToString("T"));   // 长时间:14:30:00
Console.WriteLine(now.ToString("f"));   // 完整日期+短时间:2024年10月1日 14:30
Console.WriteLine(now.ToString("F"));   // 完整日期+长时间:2024年10月1日 14:30:00
Console.WriteLine(now.ToString("g"));   // 短日期+短时间:2024/10/1 14:30
Console.WriteLine(now.ToString("G"));   // 短日期+长时间:2024/10/1 14:30:00

三、TimeSpan(时间间隔)

TimeSpan 表示一段时间间隔,常用于计算耗时、日期差等。

创建方式

csharp
// 通过构造函数
TimeSpan ts1 = new TimeSpan(1, 30, 0);           // 1小时30分钟
TimeSpan ts2 = new TimeSpan(1, 2, 30, 0);         // 1天2小时30分钟
TimeSpan ts3 = new TimeSpan(1, 2, 30, 0, 500);    // 1天2小时30分钟500毫秒

// 通过静态方法(更直观)
TimeSpan fromDays = TimeSpan.FromDays(7);         // 7天
TimeSpan fromHours = TimeSpan.FromHours(2.5);     // 2.5小时
TimeSpan fromMinutes = TimeSpan.FromMinutes(90);  // 90分钟
TimeSpan fromSeconds = TimeSpan.FromSeconds(45);  // 45秒
TimeSpan fromMs = TimeSpan.FromMilliseconds(500); // 500毫秒

属性和方法

csharp
TimeSpan duration = new TimeSpan(2, 5, 30, 15);  // 2天5小时30分钟15秒

// 组成属性
Console.WriteLine($"天:{duration.Days}");              // 2
Console.WriteLine($"小时:{duration.Hours}");           // 5
Console.WriteLine($"分钟:{duration.Minutes}");         // 30
Console.WriteLine($"秒:{duration.Seconds}");           // 15

// 总属性(以该单位表示的完整时间)
Console.WriteLine($"总天数:{duration.TotalDays}");            // 2.229...
Console.WriteLine($"总小时数:{duration.TotalHours}");          // 53.504...
Console.WriteLine($"总分钟数:{duration.TotalMinutes}");        // 3210.25
Console.WriteLine($"总秒数:{duration.TotalSeconds}");           // 192615
Console.WriteLine($"总毫秒数:{duration.TotalMilliseconds}");    // 192615000

// 运算
TimeSpan add = duration.Add(TimeSpan.FromHours(1));
TimeSpan sub = duration.Subtract(TimeSpan.FromDays(1));

计时器:测量代码执行时间

csharp
// 使用 Stopwatch(推荐)——更精准
Stopwatch sw = Stopwatch.StartNew();

// 模拟耗时操作
Thread.Sleep(100);

sw.Stop();
Console.WriteLine($"耗时:{sw.ElapsedMilliseconds} 毫秒");
Console.WriteLine($"耗时:{sw.Elapsed.TotalSeconds:F3} 秒");

// 使用 DateTime
DateTime start = DateTime.Now;
Thread.Sleep(100);
DateTime end = DateTime.Now;
Console.WriteLine($"耗时:{(end - start).TotalMilliseconds:F0} 毫秒");

四、综合案例

案例一:年龄计算器

csharp
static int CalculateAge(DateTime birthDate)
{
    DateTime today = DateTime.Today;
    int age = today.Year - birthDate.Year;

    // 如果今年生日还没过,减一岁
    if (birthDate > today.AddYears(-age))
        age--;

    return age;
}

// 计算到生日的天数
static int DaysToBirthday(DateTime birthDate)
{
    DateTime today = DateTime.Today;
    DateTime nextBirthday = new DateTime(today.Year, birthDate.Month, birthDate.Day);

    if (nextBirthday < today)
        nextBirthday = nextBirthday.AddYears(1);

    return (nextBirthday - today).Days;
}

// 使用
DateTime birth = new DateTime(1990, 5, 15);
Console.WriteLine($"年龄:{CalculateAge(birth)}");         // 输出:34
Console.WriteLine($"距离下次生日:{DaysToBirthday(birth)}天");

案例二:随机数据生成器

csharp
class RandomDataGenerator
{
    private static readonly Random rnd = new Random();
    private static readonly string[] LastNames = { "张", "李", "王", "赵", "刘", "陈" };
    private static readonly string[] FirstNames = { "伟", "强", "丽", "敏", "静", "涛" };

    public static string GenerateName()
    {
        return LastNames[rnd.Next(LastNames.Length)] + FirstNames[rnd.Next(FirstNames.Length)];
    }

    public static DateTime GenerateBirthDate(int minAge = 18, int maxAge = 60)
    {
        int age = rnd.Next(minAge, maxAge + 1);
        return DateTime.Today.AddYears(-age).AddDays(-rnd.Next(365));
    }

    public static string GenerateIdNumber()
    {
        return string.Join("", Enumerable.Range(0, 18).Select(_ => rnd.Next(10)));
    }

    public static decimal GenerateSalary(decimal min = 3000, decimal max = 30000)
    {
        return Math.Round((decimal)(min + rnd.NextDouble() * (double)(max - min)), 2);
    }

    public static (string Name, int Age, DateTime Birth, decimal Salary) GenerateEmployee()
    {
        DateTime birth = GenerateBirthDate();
        return (GenerateName(), CalculateAge(birth), birth, GenerateSalary());
    }

    // 复用年龄计算方法
    static int CalculateAge(DateTime birth) => new DateTime(2024, 1, 1).Year - birth.Year;
}

// 生成员工数据
for (int i = 0; i < 5; i++)
{
    var emp = RandomDataGenerator.GenerateEmployee();
    Console.WriteLine($"{emp.Name}\t{emp.Age}岁\t{emp.Salary:C}");
}

案例三:日志时间戳

csharp
class Logger
{
    public static void Info(string message)
    {
        string timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
        Console.WriteLine($"[{timestamp}] [INFO] {message}");
    }

    public static void Error(string message)
    {
        string timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
        Console.WriteLine($"[{timestamp}] [ERROR] {message}");
    }

    public static void Measure(string taskName, Action action)
    {
        Stopwatch sw = Stopwatch.StartNew();
        action();
        sw.Stop();
        Info($"{taskName} 耗时 {sw.ElapsedMilliseconds}ms");
    }
}

// 使用
Logger.Info("系统启动");
Logger.Measure("数据处理", () => Thread.Sleep(200));
Logger.Error("数据库连接超时");

核心知识点总结

Random

方法作用范围
Next()随机整数0 ~ int.MaxValue
Next(max)随机整数0 ~ max-1
Next(min, max)随机整数min ~ max-1
NextDouble()随机浮点数0.0 ~ 1.0

注意事项:

  1. 一个程序只创建一个 Random 实例,不要重复创建
  2. 固定种子用于测试,无种子用于正式运行
  3. 不是密码学安全的随机数——需要安全随机时用 System.Security.Cryptography.RandomNumberGenerator

DateTime

属性/方法说明
Now当前本地日期时间
UtcNow当前 UTC 日期时间
Today当天日期(00:00)
AddDays/Months/Years/Hours添加时间间隔
ToString(format)格式化为字符串
TryParse(str, out dt)安全解析字符串

时间标准选择:

  • 用户界面显示:DateTime.Now
  • 数据存储/跨时区:DateTime.UtcNowDateTimeOffset
  • 纯日期(无时间):DateTime.Today
  • 文件命名:yyyy-MM-dd-HH-mm-ss

TimeSpan

  • 表示两个时间点之间的间隔
  • Days / Hours / Minutes / Seconds 返回组成部分
  • TotalDays / TotalHours 等返回以该单位表示的完整间隔
  • DateTime 相减得到 TimeSpan
  • Stopwatch 是测量代码执行时间的最佳选择

Released under the MIT License.