加载中...
基础工具:日期与时间
第1节:在Windows桌面打个叉
第2节:在窗口上跟踪输出鼠标坐标—Win32版
第3节:你好!wxWidgets
第4节:深入分析基于框架窗口的应用
第5节:玩转主菜单
第6节:简简单单,状态栏
第7节:学工具栏,懂 MVC
第8节:基于对话框的应用
第9节:事件的静态与动态绑定
第10节:实战 Windows屏幕保护
第11节:基础工具:wxString
第12节:基础工具:日期与时间
课文封面
  • 一个关键区别:绝对时间长度与日历时间跨度之分;
  • 两个核心概念:时间点(Moment)与时间长度(或跨度);
  • 两个趣味功能:数字时钟与人生时光计算;
  • 三个重要类:wxDateTime、wxTimeSpan、wxDateSpan。

1. 关键类简介

  • wxDateTime :时间点(时刻/Moment)
  • wxTimeSpan : 绝对时间长度(以毫秒表达的两个时间点之间的长度)
  • wxDateSpan : 日历时间跨度(用以表达日历上两个时间点之间的跨度,比如多少周,多少月,多少年)

关键点:2025 年 2 月 1 日 0 点 0 分 0 秒 和 2025 年 1 月 31 日 23 点 59 分 59 秒,使用 wxTimeSpan表达,二者相差一秒,使用 wxDateSpan 表达,二者相差一天,也相差一月。

2. 视频1-数字时钟、常用方法

2.1 wxDateTime 构造

  • wxDateTime(); // 默认构造 :得到一个非法时间
  • wxDateTime(time_t t); // 从C的时间戳转换
  • wxDateTime(tm tm); // 从C的时间结构tm转换
  • wxDateTime(h, m, s, ms); // 指定时分秒毫秒(类型 wxDateTime_t),日期为当天
  • wxDateTime(wxDateTime_t day, Month month, int year, h=0, m=0, s=0, ms=0);

2.2 wxDateTime 设置

  • Now(); // 静态方法,取得当前日期时间;
  • Today(); // 静态方法 取得当前日期(时间为 0点0分0秒)
  • Set()、Set( time_t )、Set( tm )、Set(h, m, s, ms)、Set(day, month, year, h,m,s,ms); // 对应5个构造函数
  • SetYear(int year)、SetMonth(Month month)、SetDay(wxDateTime_t day)
    SetHour(h)、SetMinute(m)、SetSecond(s)、SetMillisecond(ms); // 设置各日期时间组成部分
  • SetToCurrent() // 设置为当前时间,相当于 Now() 的非静态版本;
  • ResetTime(); // 日期不变,时间归零(午夜零点零分零秒零毫秒)

2.3 wxDateTime 查询

  • 转回 C 的时间数据:GetTm() -> tm;GetTicks() -> time_t;
  • 取各组成部分:GetYear()、GetMonth()、GetDay()、GetHour()、GetMinute()、GetSecond()、GetMilisecond()、GetDateOnly();
  • 自动计算:GetWeekDay() -> WeekDay;GetDayOfYear() -> wxDateTime_t;GetWeekOfMonth(WeekFlags flag = Monday_First);GetWeekOfYear(WeekFlags flag = Monday_First)

2.4 wxDateTime 嵌套类型定义

  • 月份枚举:wxDateTime::Month : Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec, Inv_Month; 枚举值从 0 开始;
  • 星期枚举:wxDateTime::WeekDay : Sun, Mon, Tue, Wed, Thu, Fri, Sat, Inv_WeekDay;枚举值从0开始,且周日为每周第一天;
  • 星期标志枚举:wxDateTime::WeekFlags : 和周相关的计算时,用于指定每周的开始日为周一或周日,其值有 Default_First(美国默认周日开始、其他默认周一开始),Monday_First、Sunday_First;

2.5 wxDateTime 到 字符串

  • wxString Format (wxChar* const* fmt);
  • wxString FormatDate() ; // Format("%x");
  • wxString FormatTime(); // Format("%X");
  • wxString FormatISODate(); //YYYY-MM-DD
  • wxString FormatISOTime(); //HH:MM:SS

说明 :

  1. 需要转换为指定的格式日期、时间字符串时,所需要的格式化指示串(fmt) 说明,见:C/C++ strftime 函数;
  2. FormatDate() 相当于 Format("%x"),将得到类似 MM/DD/YY 的日期串;
  3. FormatTime() 相当于 Format("%X"),将得到类似 HH:MM:SS 的时间串;
  4. FormatISODate() 得到类似 YYYY-MM-DD 格式日期串,此格式符合 ISO-8601;
  5. FormatISOTime() 得到 HH:MM:SS 格式时间串,此格式符合 ISO-8601;
  6. 可自行组装出 YYYY-MM-DDTHH:MM:SS格式的,符合 ISO-8601 的日期时间串;

2.6 解析字符串至 wxDateTime

  • wxChar const* ParseFormat(wxChar const* s, const wxChar *format = wxDefaultDateTimeFormat, const wxDateTime& dateDef = wxDefaultDateTime); // 使用指定的格式,尝试解析字符串 s 的内容以获取日期时间值,解析失败的值,从 dateDef 中获取;解析失败返回空指针,否则返回解析结束于源串 s 的指针偏移;
  • wxChar const* ParseDateTime(wxChar const *datetime); 尽最多可能尝试,解析 datetime 串中的日期与时间信息;
  • wxChar const* ParseDate(wxChar const* date); 尽最多可能尝试,解析 date 串中的日期信息;
  • wxChar const* ParseTime(wxChar const* time); 尽最多可能尝试,解析 time 串中的时间信息;

重要:尽量在业务需求与设计上,限定仅需对符合 ISO 标准的字符串做转换。

3. 视频2-日期计算(含人生时光计算功能)

3.1 wxDateTime 判断、比较

  • bool IsValid() // 判断当前日期时间数据是否合法;
  • 相等判断:dt1 == dt2 ,或 dt1.IsEqualTo(dt2);
  • 大于判断:dt1 > dt2,或 dt1.IsLaterThan(dt2);
  • 小于判断:dt1 < dt2,或 dt1.IsEarlierThan(dt2);
  • 期间判断:dt >= dt1 && dt <= dt2,或 dt.IsBetween(dt1, dt2);
  • 严格期间判断:dt > dt1 && dt < dt2, 或 dt.IsStrictlyBetween(dt1, dt2);

注:以上两个期间判断参数,需满足 dt1 小于或等于 dt2。

  • 闰年判断:wxDateTime::IsLeayYear(year = Inv_Year);

IsLeayYear 是一个静态方法,需要一个入参(年份),但是,该入参默认值(一个非法年份)。唯一入参又有默认值,将千万该静态方法非常容易被误用为:

// IsLeayYear 的错误用法示例: wxDateTime now = wxDateTime::Now(); if ( now.IsLeapYear() ) // 错误,永远无法返回 true { wxMessageBox(L"今年是闰年"!); }
  • 工作日判断:IsWorkDay();

除时区、日历法(wxDateTime 有限支持从 Julian Day Number 转换)外,wxTimeDate 还可设置国别(国家、地区),并于国家(或地区)基础上,提供 IsWorkDay() 这样的方法——显然不可信。

3.2 时间点相减

wxDateTime - wxDateTime 固定得到一个 wxTimeSpan。 内含两个时间点之间存在多少毫秒(可正可负),并且,基于毫秒数(类型为 wxLongLong,类似 C++11 后的 long long),可折算为多少秒(同样为 wxLongLong)、多少分钟、小时、天(24小时)、周(7天);但不能换算为多少个月(因为月的天数不固定)。

3.3 时间点 ± 时间长度

时间点(wxDateTime)可以加减一个时间绝对长度(wxTimeSpan),或者加减一个日历时间长度(wxDateSpan)。

时间点加上或减去一个 wxTimeSpan,本质是在原时间点的基础上,加上或减去指定的毫秒数,从而得到另一个时间点。即,无论 wxTimeSpan 是多长,参加计算时,都是准确的毫秒数。

时间点加上或减去一个 wxDateSpan,能参加计算的偏移量,从一天开始,到一周、一月、一年。比如,假设时间点是 2月1日,加上一个月的偏移,将得到 3月1日——尽管,从2月1日开始往后到 3月1日,并不足30天。再有,假设当前时间点是 7月31日,减去1月,将自动得到6月30日(6月没有31日)。

wxDateSpan 可以指定组合的长度,比如,以下代码用来计算今天再过“一个月又十三天”后,是几月几日:

wxDateSpan ds; ds.SetMonths(1).SetDays(13); wxDateTime dt = wxDateTime::Today() + ds; // 今天+1月又13天

4. 关键代码

// 数字时钟 void HelloDateTimeDialog::OnTimer1Trigger(wxTimerEvent& event) { wxDateTime now = wxDateTime::Now(); wxString lable = now.Format(L"%Y/%m/%d %H:%M:%S"); this->TextTime->SetLabel(lable); this->Layout(); } // 时光计算 void HelloDateTimeDialog::OnButtonLifeClick(wxCommandEvent& event) { wxDateTime now = wxDateTime::Now(); wxDateTime birthday = wxDateTime(1, wxDateTime::Jun, 2004); // 生日:2004/6/1 // 判断一下 “是否出生了”…… if (now.IsEarlierThan(birthday)) { wxMessageBox(L"还没出生?", L"有问题吧!"); return; } int life; if (life = wxGetNumberFromUser(L"请基于自身的生活与健康状态和\n内心意愿……" , L"输入预估寿命长度(年):" , L"生命预估" , 79 // 使用我国人均寿命作为默认值 , now.GetYear() - birthday.GetYear() + 1 // 最小(当前岁数) , 150 // 最大: 150 , this); life == -1) { return; } // 告别日子 wxDateTime farewellDay = birthday + wxDateSpan().SetYears(life); wxTimeSpan daysPass = now - birthday; // 已经度过的时光 wxTimeSpan daysFuture = farewellDay - now; // 未来时光 wxString msg; msg << L"你生出于:" << birthday.Format(L"%Y年-%m月-%d日"); msg << L"\n自我评估寿命:" << life << L" 年"; msg << L"\n回首往昔,人生已度过匆匆的 " << daysPass.GetDays() << L" 天,计 " << daysPass.GetHours() << L" 小时"; msg << L"\n展望未来,人生还有 " << daysFuture.GetDays() << L" 个日日夜夜需要面对"; auto percen = static_cast<double>(daysPass.GetHours()) / (daysPass.GetHours() + daysFuture.GetHours()) * 100; msg << L"\n你的人生进度是:" << percen << L"%,请珍惜生命中的每一段快乐时光!"; wxMessageBox(msg, L"人生时光"); }