在資料分析和軟體開發的領域中,時間的處理扮演著舉足輕重的角色。我將分享在 Python 和 SAS 中處理時間和日期資料的技巧與心得,並透過實際案例和程式碼範例,帶您深入瞭解如何在程式中有效地操作時間資料,彷彿掌握時間的魔法。
擷取時間的精髓:取得當前時間
在 Python 中,我們可以使用 time
模組來取得當前的時間戳記和時間物件,如同捕捉時間的瞬間。
import time
nl = '\n'
t_stamp = time.time()
now = time.localtime(t_stamp)
dsp_now = time.asctime(now)
print(
nl, '時間戳記:', t_stamp,
nl, '時間戳記資料型別:', type(t_stamp),
nl,
nl, '時間物件:', now,
nl,
nl, '格式化時間字串:', dsp_now,
nl, '格式化時間字串資料型別:', type(dsp_now)
)
time.time()
傳回一個浮點數,表示從 epoch(通常是 1970 年 1 月 1 日 00:00:00 UTC)到現在的秒數,如同時間的指紋。time.localtime()
將時間戳記轉換為 struct_time 物件,包含年、月、日、時、分、秒等資訊,如同時間的解剖圖。time.asctime()
將 struct_time 物件轉換為格式化的時間字串,如同時間的名片。
在 SAS 中,我們可以使用 DATETIME()
函式取得當前的日期和時間,如同翻閲時間的日曆。
data _null_;
now = datetime();
dow = put(datepart(now), weekdate17.);
tm = put(timepart(now), time9.);
yr = year(datepart(now));
bl = ' ';
all = catx(bl, dow, tm, yr); /* 使用 catx 連線字串並加入空格 */
put '日期時間:' all;
run;
DATETIME()
傳回一個數值,表示從 1960 年 1 月 1 日到現在的秒數,如同時間的里程碑。DATEPART()
和 TIMEPART()
分別提取日期和時間部分,如同時間的分割線。PUT()
函式將數值格式化為字串,如同時間的化妝師。 CATX()
函式用於連線字串,並在字串之間插入指定的分隔符,這裡使用空格作為分隔符,如同時間的連線符。
時間的藝術:格式化時間
Python 的 strftime()
方法可以將 struct_time 物件轉換為指定格式的字串,如同時間的雕刻家。
import time
now = time.localtime(time.time())
n1 = time.asctime(now)
n2 = time.strftime("%Y/%m/%d %H:%M", now)
n3 = time.strftime("%I:%M:%S %p", now)
n4 = time.strftime("%Y-%m-%d %H:%M:%S %Z", now)
print(
'\n', '時間物件:', now,
'\n',
'\n', '格式一:', n1,
'\n', '格式二:', n2,
'\n', '格式三:', n3,
'\n', '格式四:', n4
)
strftime()
使用格式化字串來指定輸出的格式,如同時間的範本。例如 %Y
表示四位數的年份,%m
表示月份,%d
表示日期,%H
表示 24 小時制的小時,%M
表示分鐘,%S
表示秒,%p
表示 AM/PM,%Z
表示時區,如同時間的密碼。
SAS 也提供了類別似的功能,可以使用 PUT()
函式和格式化字串來格式化時間,如同時間的魔術師。
data _null_;
now = datetime();
n1 = put(now, datetime19.);
n2 = put(datepart(now), yymmdds10.) || put(timepart(now), time6.);
n3 = put(timepart(now), timeampm11.);
n4 = put(now, e8601dt.); /* 使用 ISO 8601 格式 */
put '格式一:' n1 /
'格式二:' n2 /
'格式三:' n3 /
'格式四:' n4;
run;
SAS 的 PUT()
函式與 Python 的 strftime()
類別似,使用格式化字串來控制輸出,如同時間的指揮棒。DATETIME19.
格式化日期和時間,YYMMDDS10.
格式化日期,TIME6.
格式化時間,TIMEAMPM11.
格式化時間為 AM/PM 格式, E8601DT.
格式化日期時間為 ISO 8601 格式,如同時間的魔咒。
字串的奇蹟:字串轉時間
Python 的 strptime()
方法可以將時間字串轉換為 struct_time 物件,如同時間的翻譯家。
import time
t_str = "12:34:56 PM"
t = time.strptime(t_str, "%I:%M:%S %p")
hr = t.tm_hour
min = t.tm_min
sec = t.tm_sec
print(
'\n', '時間物件:', t,
'\n', '時間物件資料型別:', type(t),
'\n',
'\n', '小時:', hr,
'\n', '小時資料型別:', type(hr),
'\n',
'\n', '分鐘:', min,
'\n', '分鐘資料型別:', type(min),
'\n',
'\n', '秒數:', sec,
'\n', '秒數資料型別:', type(sec)
)
strptime()
的第一個引數是時間字串,第二個引數是格式化字串,用於指定時間字串的格式,如同時間的解碼器。
SAS 的 INPUT()
函式可以將字串轉換為時間值,如同時間的煉金術士。
data _null_;
t_str = '12:34:56';
t = input(t_str, time9.);
hr = hour(t);
min = minute(t);
sec = second(t);
put '時間值:' t timeampm11. /
'小時:' hr /
'分鐘:' min /
'秒數:' sec;
run;
INPUT()
函式的第一個引數是字串,第二個引數是 Informat,用於指定字串的格式,如同時間的模具。TIME9.
Informat 用於讀取時間值,如同時間的提取器。
透過以上範例,我們可以清楚地看到 Python 和 SAS 在時間處理方面的異同,如同時間的兩面。瞭解這些技巧,可以幫助我們更有效地處理時間資料,提升程式開發效率,如同掌握時間的鑰匙。
graph LR C[C] D[D] E[E] G[G] H[H] I[I] L[L] N[N] P[P] A[Python 時間處理] --> B(time 模組) B --> C{取得時間戳記} B --> D{取得時間物件} B --> E{格式化時間} A --> F(datetime 模組) F --> G{建立日期時間物件} F --> H{提取日期時間元件} F --> I{合併日期和時間} J(SAS 時間處理) --> K["DATETIME() 函式"] K --> L{取得當前日期時間} J --> M(PUT 函式) M --> N{格式化時間} J --> O(INPUT 函式) O --> P{字串轉時間}
希望這些技巧能幫助您在 Python 和 SAS 中更有效地處理時間資料,在資料科學的海洋中乘風破浪。
graph LR A[Python datetime] --> B(建立) A --> C(格式化) A --> D(元件提取) A --> E(合併) F[SAS datetime] --> G(建立) F --> H(格式化) F --> I(元件提取) F --> J(合併)
Python 與 SAS 的日期時間資料處理:深入比較
在資料科學的世界裡,日期和時間資料的處理是一項基本與重要的技能。我將以 Python 和 SAS 這兩種常用工具為例,帶您深入瞭解日期時間資料的處理技巧,並比較它們各自的優缺點。
解析日期時間組成:精準拆解
首先,我們來看看如何從日期時間資料中提取年、月、日、時、分、秒等組成部分。
在 SAS 中,我們可以使用 DATEPART
和 TIMEPART
函式來提取日期和時間部分。以下程式碼示範瞭如何使用這些函式:
data _null_;
dt_obj = '09Jan2019:19:34:56'dt;
date = datepart(dt_obj);
time = timepart(dt_obj);
year = year(date);
month = month(date);
day = day(date);
hour = hour(time);
minute = minute(time);
second = second(time);
put '日期: ' date yymmdd10. /
'時間: ' time time8. /
'年份: ' year /
'月份: ' month /
'日期: ' day /
'小時: ' hour /
'分鐘: ' minute /
'秒數: ' second;
run;
這段 SAS 程式碼首先定義了一個日期時間物件 dt_obj
。然後,使用 DATEPART
和 TIMEPART
函式分別提取日期和時間部分。接著,使用 YEAR
、MONTH
、DAY
、HOUR
、MINUTE
和 SECOND
函式提取年、月、日、時、分、秒。最後,使用 PUT
函式將結果輸出。
Python 則使用 datetime
模組來處理日期時間資料。以下程式碼示範瞭如何在 Python 中提取日期時間組成部分:
import datetime as dt
str = '2024-03-08 14:56:32'
dt_obj = dt.datetime.strptime(str, '%Y-%m-%d %H:%M:%S')
print(f'年份: {dt_obj.year}')
print(f'月份: {dt_obj.month}')
print(f'日期: {dt_obj.day}')
print(f'小時: {dt_obj.hour}')
print(f'分鐘: {dt_obj.minute}')
print(f'秒數: {dt_obj.second}')
這段 Python 程式碼首先將日期時間字串 str
使用 strptime()
函式轉換為 datetime
物件。然後,透過 datetime
物件的屬性 (year
、month
、day
、hour
、minute
、second
) 直接取得日期時間的各個組成部分。
字串與日期時間的相互轉換:資料型態的橋樑
在資料處理中,我們經常需要在字串和日期時間物件之間進行轉換。
Python 使用 strptime()
函式將字串轉換為日期時間物件,並使用 strftime()
方法將日期時間物件轉換為字串。
SAS 則使用 INPUT
函式搭配自定義的 informat 將字串轉換為日期時間物件,並使用 PUT
函式搭配格式化選項將日期時間物件轉換為字串。
Python Timedelta 物件:時間間隔的藝術
Python 的 timedelta
物件可以輕鬆計算兩個日期時間之間的差值。
import datetime as dt
date1 = dt.datetime(2024, 3, 8)
date2 = dt.datetime(2024, 3, 15)
timedelta = date2 - date1
print(timedelta.days) # 輸出: 7
這段程式碼計算了 date2
和 date1
之間的差值,並將結果儲存在 timedelta
物件中。timedelta.days
屬性可以取得時間差的天數。
SAS 中可以使用 INTCK
函式計算兩個日期時間之間的差值。
計算每月的第一個和最後一個工作日:實用技巧
計算每月的第一個和最後一個工作日是一個常見的需求。以下 Python 程式碼示範如何實作:
import datetime as dt
import calendar
def first_business_day(year, month):
first_day = dt.date(year, month, 1)
weekday = first_day.weekday()
if weekday < 5: # 0-4 代表週一到週五
return first_day
else: # 5-6 代表週六日
return first_day + dt.timedelta(days=7 - weekday)
def last_business_day(year, month):
last_day = calendar.monthrange(year, month)[1]
last_date = dt.date(year, month, last_day)
weekday = last_date.weekday()
if weekday < 5:
return last_date
else:
return last_date - dt.timedelta(days=weekday - 4)
year = 2024
month = 3
first_bday = first_business_day(year, month)
last_bday = last_business_day(year, month)
print(f'{year}年{month}月的第一個工作日:{first_bday}')
print(f'{year}年{month}月的最後一個工作日:{last_bday}')
first_business_day
函式首先取得每月的第一天。如果第一天是工作日,則直接傳回;否則,計算到下一個工作日的天數並傳回。last_business_day
函式首先取得每月的最後一天。如果最後一天是工作日,則直接傳回;否則,計算到上一個工作日的天數並傳回。
SAS 中可以使用 INTNX
函式搭配迴圈來計算每月的第一個和最後一個工作日。
透過以上範例,我們可以看到 Python 和 SAS 在處理日期時間資料方面各有千秋。Python 的 datetime
模組和 timedelta
物件提供了簡潔易用的 API,而 SAS 則提供了強大的函式和程式,可以處理更複雜的日期時間計算。 選擇哪種工具取決於您的具體需求和個人偏好。
from datetime import date, timedelta
def first_day_of_month(year, month):
"""回傳指定月份的第一天。"""
return date(year, month, 1)
def first_biz_day_of_month(year, month):
"""回傳指定月份的第一個工作日。"""
first_day = first_day_of_month(year, month)
weekday = first_day.weekday()
if weekday == 5: # 週六
return first_day + timedelta(days=2)
elif weekday == 6: # 週日
return first_day + timedelta(days=1)
else:
return first_day
def last_day_of_month(year, month):
"""回傳指定月份的最後一天。"""
if month == 12:
return date(year + 1, 1, 1) - timedelta(days=1)
else:
return date(year, month + 1, 1) - timedelta(days=1)
def last_biz_day_of_month(year, month):
"""回傳指定月份的最後一個工作日。"""
last_day = last_day_of_month(year, month)
weekday = last_day.weekday()
if weekday == 5: # 週六
return last_day - timedelta(days=1)
elif weekday == 6: # 週日
return last_day - timedelta(days=2)
else:
return last_day
這組函式提供計算每月第一天、最後一天、第一個工作日和最後一個工作日的實用方法。first_biz_day_of_month
和 last_biz_day_of_month
函式特別考慮了週末的情況,確保回傳值始終是工作日。
graph LR A[輸入年份和月份] --> B{第一天是否為週末?}; B -- 是 --> C[加 1 或 2 天]; B -- 否 --> D[即為第一個工作日]; C --> E[輸出第一個工作日]; D --> E; F[計算最後一天] --> G{最後一天是否為週末?}; G -- 是 --> H[減 1 或 2 天]; G -- 否 --> I[即為最後一個工作日]; H --> J[輸出最後一個工作日]; I --> J;
深入 Python 時間處理:Naive 與 Aware Datetime
在 Python 的時間處理中,“naive” 和 “aware” datetime 物件的概念至關重要,尤其在處理跨時區資料時。我將透過實際案例和 pytz
函式庫的應用,解析這兩種 datetime 物件的差異,並提供最佳實踐。
naive 與 Aware Datetime:核心差異
“naive” datetime 缺乏時區資訊,而 “aware” datetime 則明確指定了時區。這個差異在進行時間比較和運算時至關重要。
import datetime as dt
# 本地時間(naive)
dt_local = dt.datetime.now()
# UTC 時間(naive)
dt_naive = dt.datetime.utcnow()
# UTC 時間(aware)
dt_aware = dt.datetime.now(dt.timezone.utc)
print(f"dt_local: {dt_local}, tzinfo: {dt_local.tzinfo}")
print(f"dt_naive: {dt_naive}, tzinfo: {dt_naive.tzinfo}")
print(f"dt_aware: {dt_aware}, tzinfo: {dt_aware.tzinfo}")
dt_local
和 dt_naive
雖然表示不同時間,但都缺乏時區資訊,因此在某些操作中可能導致錯誤。dt_aware
則明確指定了 UTC 時區,確保時間計算的準確性。
pytz
函式庫:精確時區管理
pytz
函式庫提供更全面的時區處理功能,彌補了標準函式庫的不足。
from datetime import datetime
from pytz import timezone
dt_naive = datetime(2024, 1, 1, 12, 0, 0)
dt_est = timezone('US/Eastern').localize(dt_naive)
print(f"dt_naive: {dt_naive}, tzinfo: {dt_naive.tzinfo}")
print(f"dt_est: {dt_est}, tzinfo: {dt_est.tzinfo}")
pytz
的 localize()
方法不僅將 naive datetime 轉換為 aware datetime,還考慮了日光節約時間,確保時間的準確性。
graph LR A[naive Datetime] --> B(無時區資訊) C[Aware Datetime] --> D(明確時區) E[pytz] --> F{時區轉換}
透過以上案例,我們深入瞭解了 naive 和 aware datetime 的差異,以及 pytz
函式庫的重要性。在實務應用中,尤其在處理跨時區資料時,務必使用 aware datetime,並善用 pytz
函式庫,確保時間計算的準確性和可靠性。
在 Python 的時間處理中,理解 naive 和 aware datetime 的區別至關重要。使用 aware datetime 並搭配 pytz
函式庫,能確保程式在處理不同時區資料時保持準確性,避免潛在錯誤。
from datetime import datetime, timedelta
from pytz import timezone
import pytz
nl = '\n'
fmt = "%Y-%m-%d %H:%M:%S (%Z) %z"
new_york = timezone('US/Eastern')
shanghai = timezone('Asia/Shanghai')
dt_loc = new_york.localize(datetime(2018, 12, 23, 13, 0, 0))
dt_sha = dt_loc.replace(tzinfo=shanghai)
tm_diff = dt_loc - dt_sha
tm_bool = dt_loc == dt_sha
print(
nl, '紐約時間:' , dt_loc.strftime(fmt),
nl, '紐約時區資訊:' , dt_loc.tzinfo,
nl,
nl, '上海時間(錯誤示範):' , dt_sha.strftime(fmt),
nl, '上海時區資訊(錯誤示範):' , dt_sha.tzinfo,
nl,
nl, '時間差(錯誤示範):' ,tm_diff,
nl, '紐約時間 == 上海時間(錯誤示範):' , tm_bool
)
這段程式碼演示了replace()
方法在處理pytz
時區物件時的陷阱。我們設定了紐約和上海的時區,並建立了一個紐約時間的日期時間物件。接著,我們嘗試使用replace()
方法將其時區改為上海。然而,replace()
方法並沒有正確地處理時區轉換,導致時間差和比較結果不正確。
from datetime import datetime, timedelta
from pytz import timezone
import pytz
nl = '\n'
fmt = "%Y-%m-%d %H:%M:%S (%Z) %z"
new_york = timezone('US/Eastern')
shanghai = timezone('Asia/Shanghai')
dt_loc = new_york.localize(datetime(2018, 12, 23, 13, 0, 0))
dt_sha2 = dt_loc.astimezone(shanghai)
tm_diff = dt_loc - dt_sha2
tm_bool = dt_loc == dt_sha2
print(
nl, '紐約時間:' , dt_loc.strftime(fmt),
nl, '上海時間(正確示範):' , dt_sha2.strftime(fmt),
nl,
nl, '時間差(正確示範):' , tm_diff,
nl, '紐約時間 == 上海時間(正確示範):' , tm_bool
)
這段程式碼使用astimezone()
方法進行時區轉換。astimezone()
方法會根據時區差異調整日期和時間,確保轉換後的時間是正確的。
from datetime import datetime, timedelta
from pytz import timezone
import pytz
nl = '\n'
fmt = "%Y-%m-%d %H:%M:%S (%Z) %z"
new_york = timezone('US/Eastern')
dt_loc = new_york.localize(datetime(2018, 11, 4, 1, 0, 0))
minus_ten = dt_loc - timedelta(minutes=10)
before = new_york.normalize(minus_ten)
after = new_york.normalize(before + timedelta(minutes=20))
print(nl, '轉換前:' , before.strftime(fmt),
nl, '轉換後:' , after.strftime(fmt))
這段程式碼示範瞭如何使用normalize()
方法處理日光節約時間和標準時間的轉換。在美國,2018 年 11 月 4 日凌晨 1 點是從日光節約時間轉換到標準時間的時刻。normalize()
方法可以確保時間的正確性。
nl = '\n'
fmt = "%Y-%m-%d %H:%M:%S (%Z) %z"
new_york = timezone('US/Eastern')
tm_end = new_york.localize(datetime(2018, 11, 8, 0, 0, 0))
tm_start_est = tm_end - timedelta(weeks=1)
print(nl, '使用本地時區進行日期時間運算:',
nl, '起始時間:' , tm_start_est.strftime(fmt),
nl, '結束時間:' , tm_end.strftime(fmt))
tm_end_utc = tm_end.astimezone(pytz.utc)
tm_delta_utc = tm_end_utc - timedelta(weeks=1)
tm_start_edt = tm_delta_utc.astimezone(new_york)
print(nl, '使用 UTC 時區進行日期時間運算:',
nl, '起始時間:' , tm_start_edt.strftime(fmt),
nl, '結束時間:' , tm_end.strftime(fmt))
這段程式碼比較了使用本地時區和 UTC 時區進行日期時間運算的差異。在涉及日光節約時間轉換的情況下,使用本地時區直接進行運算可能會導致錯誤。最佳實務是先將時間轉換為 UTC,進行運算後再轉換回本地時區。
在 Python 中處理時區和日期時間,astimezone()
和normalize()
方法以及以 UTC 時間進行日期時間運算是確保程式碼正確性和可靠性的關鍵。
options tz='America/New_York';
data _null_;
tz_ny_id = tzoneid();
tz_ny_name = tzonename();
tz_ny_dst_name = tzonedstname();
tz_ny_st_name = tzonesttname();
tz_ny_off = tzoneoff();
tz_ny_off_dst = tzonesttoff();
tz_ny_off_st = tzonedstoff();
put '時區 ID: ' tz_ny_id /
'時區名稱: ' tz_ny_name /
'日光節約時間名稱: ' tz_ny_dst_name /
'標準時間名稱: ' tz_ny_st_name /
'UTC 偏移量: ' tz_ny_off /
'日光節約時間 UTC 偏移量: ' tz_ny_off_st /
'標準時間 UTC 偏移量: ' tz_ny_off_dst;
run;
這段 SAS 程式碼示範瞭如何使用 tzoneid()
、tzonename()
、tzonedstname()
、tzonesttname()
、tzoneoff()
、tzonedstoff()
和 tzonesttoff()
函式取得紐約的時區資訊。這些函式分別傳回時區 ID、名稱、日光節約時間名稱、標準時間名稱、UTC 偏移量、日光節約時間 UTC 偏移量和標準時間 UTC 偏移量。
options tz='';
data _null_;
local_utc_offset = tzoneoff();
dn_frm_utc = tzoneoff('America/Denver');
mo_frm_utc = tzoneoff('Africa/Mogadishu');
diff_tz_hr = abs((dn_frm_utc) - (mo_frm_utc)) / 3600;
put '丹佛 UTC 偏移量: ' dn_frm_utc /
'摩加迪沙 UTC 偏移量: ' mo_frm_utc /
'時區差異 (小時): ' diff_tz_hr /
'本地 UTC 偏移量: ' local_utc_offset;
run;
這段程式碼示範瞭如何使用 tzoneoff()
函式計算丹佛和摩加迪沙之間的時差。 透過計算兩個時區的 UTC 偏移量之差,並將結果除以 3600,即可得到時差(以小時為單位)。
透過理解 SAS 的 TIMEZONE
選項和相關函式,可以更精確地處理日期時間資料,避免潛在的錯誤。
避免時區的坑,從今天開始!
options tz='America/New_York';
data ny;
ny_dt = datetime();
ny_tz = tzonename();
put '紐約生效時區: ' ny_tz /
'紐約本地日期時間: ' ny_dt datetime19.;
run;
options tz='Asia/Shanghai';
data sha;
sha_dt = datetime();
sha_tz = tzonename();
put '上海生效時區: ' sha_tz /
'上海本地日期時間: ' sha_dt datetime19.;
run;
這段 SAS 程式碼示範瞭如何在不同時區下取得本地日期時間。首先,options tz='America/New_York';
設定目前 session 的時區為紐約。data ny;
建立一個名為 ny
的 dataset。ny_dt = datetime();
取得當前紐約的日期時間值,並儲存到變數 ny_dt
中。ny_tz = tzonename();
取得當前時區的名稱(即 ‘America/New_York’),並儲存到變數 ny_tz
中。put
陳述式將時區名稱和日期時間值輸出到 log 中,datetime19.
format 確保日期時間以易讀的格式顯示。
接著,options tz='Asia/Shanghai';
將時區切換到上海,後續程式碼以相同的邏輯取得上海的本地日期時間和時區名稱。
值得注意的是,SAS 日期時間值是自 1960 年 1 月 1 日起算的秒數。一旦建立,這個值本身並不會因為時區設定的改變而改變。改變時區設定只會影響日期時間值的顯示方式。
如果需要將一個時區的日期時間值轉換到另一個時區,可以使用 TZONEOFF
函式計算時差(以秒為單位),然後使用 INTNX
函式將時差加到原始日期時間值上。
data _null_;
/* 紐約時間 */
options tz='America/New_York';
ny_dt = datetime();
/* 將紐約時間轉換為上海時間 */
options tz='Asia/Shanghai';
sha_dt = intnx('second', ny_dt, tzoneoff(ny_dt,'Asia/Shanghai'));
put '紐約時間: ' ny_dt datetime19.;
put '上海時間: ' sha_dt datetime19.;
run;
這段程式碼示範瞭如何將紐約時間轉換為上海時間。首先設定時區為紐約,取得當前紐約時間。然後,將時區切換為上海,使用 TZONEOFF
函式計算紐約時間與上海時間的時差(以秒為單位)。TZONEOFF(ny_dt,'Asia/Shanghai')
傳回從紐約時間 ny_dt
轉換到上海時間所需的秒數偏移量。INTNX
函式則將這個偏移量加到紐約時間上,得到對應的上海時間。最後,程式碼將紐約時間和轉換後的上海時間輸出到 log。
這個例子清晰地展示瞭如何在 SAS 中處理不同時區的日期時間轉換,避免了常見的時區錯誤。透過 TZONEOFF
和 INTNX
函式的組合,可以精確地在不同時區之間轉換日期時間值。
透過以上程式碼範例和「內容解密」的詳細説明,讀者可以更深入地理解 SAS 中時區的處理方式,並掌握如何在程式中正確地處理不同時區的日期時間。