„A dátumokat és időpontokat már kisgyerekeknek is megtanítjuk. Ugyan mi benne a nehéz?”
Sok programozó elkövette már ezt a hibát, és az ezt követő tapasztalat jellemzően rossz hatással van az egészségükre és a boldogságukra.
Aki nem triviális programozást végez dátumokkal és időkkel, az legalább legyen felkészülve arra, hogy megértse és mérsékelje a lehetséges problémákat.
Ez a dokumentum megpróbál áttekintést adni arról, hogyan kezeli az R a dátumokat és az időket, de a további részletekhez a teljes dokumentációt kell tanulmányoznod.
Adattudományi nyelvként az R-ben különös jelentőséggel bírnak a dátumok és az időpontok. Az adatok nagy része idősor formájában érkezik: a betegek napi száma egy járvány idején, a kormányok által jelentett havi foglalkoztatási adatok, madárpopulációk egy fészkelőtelepen, a jelentős gazdaságok éves GDP-je és végtelen sok további példa.
Ez azt jelenti, hogy az R egyszerűvé teszi a dátum- és időkezelést?
Sajnos nem!
A Base R dátum- és időkezelési funkcióinak rossz a hírük, és a legtöbbjüket ma már ritkán használják.
A lubridate csomag nagy előrelépés, de készülj fel egy nagy és bonyolult könyvtárra, amelyet problémák széles és bonyolult körének kezelésére terveztek.
A lubridate eredetileg önálló volt, ma már a tidyverse gyűjtemény része.
Töltsd be a library(lubridate) vagy a library(tidyverse) utasítással.
Ha segítségre van szükséged a lubridate használatához, ezek a források segíthetnek:
A date csupán egy nap, időinformáció nélkül.
Belsőleg a dátumokat az 1970-01-01 óta eltelt napok (előjeles) számaként tárolja.
today()
#> [1] "2026-04-22"
A datetime a dátumot, az időt és az időzónát egyesíti.
Belsőleg az 1970 eleje óta eltelt másodpercek számaként tárolja, UTC időzónában.
now()
#> [1] "2026-04-22 15:37:11 MST" # mid-afternoon in Arizona
Talán meglepő, de az R-nek nincs belső reprezentációja a csak időt tartalmazó értékekre.
date és datetime értékekkelA leggyakoribb dátum- és időkezelési igények talán ezek:
today() segítségével) és/vagy dátum-idő (a now() segítségével) lekérdezése.Sokféleképpen írhatjuk le a dátumokat és időket, és ezek általában kultúrafüggők. A csupa számból álló dátumok, mint a „7/6/23”, kétértelműek, zavaróak, és számos költséges hibához vezettek multinacionális szervezeteknél (a szerző ezt keserű személyes tapasztalatból mondja).
A nemzetközi szabványt (y-m-d) az ISO 8601 definiálja, két fő előnnyel:
Az adattudomány viszont arról szól, hogy bármilyen adattal dolgozunk, amit csak találunk, és gyakran értelmetlen azon panaszkodni, hogy mások hogyan formázzák.
A Base R rendelkezik konverziós függvényekkel, amelyek formátum stringet fogadnak (alapértelmezésben ISO 8601).
as.Date("2026-04-22")
#> [1] "2026-04-22"
as.Date("4/22/26", format = "%m/%d/%y")
#> [1] "2026-04-22"
as.POSIXct("2026-04-22 15:30:09 MST")
#> [1] "2026-04-22 15:30:09 MST"
A formátum stringek az évtizedek óta létező strptime() függvényre hasonlítanak, amelyet sok nyelv tartalmaz.
Az e szintaxist ismerő programozók opcionálisan használhatják a lubridate fast_strptime() függvényét.
Még hasznosabb, hogy a lubridate rengeteg segédfüggvényt kínál, amelyeknek a neve a mezők sorrendjét tükrözi.
Amint a függvények tudják, mely értékeket keressék és milyen sorrendben, a beolvasás lenyűgözően rugalmas.
mdy("4/22/26")
#> [1] "2026-04-22"
mdy("April 22nd 26")
#> [1] "2026-04-22"
mdy("Wednesday, April 22nd 2026")
#> [1] "2026-04-22"
mdy_hm("Wednesday, April 22nd 2026, 4:14 pm")
#> [1] "2026-04-22 16:14:00 UTC" # defaults to your OS timezone
Ha a programodban egy dátum/dátum-idő összetevői külön számokként vannak jelen, akkor azokat a make_date() vagy make_datetime() függvénnyel állíthatod össze.
Ezek azok az összetevők, amelyek megadhatók, a sorrendjükkel és az alapértelmezett értékeikkel:
make_datetime(
year = 1970L,
month = 1L,
day = 1L,
hour = 0L,
min = 0L,
sec = 0,
tz = "UTC"
)
#> [1] "1970-01-01 UTC"
make_datetime(2026, 4, 22, 17)
#> [1] "2026-04-22 17:00:00 UTC"
Természetesen a make_date() csak év, hónap és nap argumentumokat fogad.
Az R konzol a dátumokat/dátum-időket automatikusan string reprezentációként jeleníti meg. Ez általában kényelmes, de néha megtévesztő.
Belsőleg a dátumok és dátum-idők számok (néhány attribútummal kiegészítve), nem stringek. Ha dátumot (vagy dátum-időt) adunk vissza ott, ahol stringet várnak, az problémákat okoz, ezért tudnunk kell, hogyan végezzük el az átalakítást.
Egy egyszerű megközelítés a Base R as.character() függvényének használata, amely ISO 8601 formátumú stringet ad vissza.
Van továbbá egy format_ISO8601() függvény a dátum-időkhöz, amely lehetőséget kínál a kívánt pontosságra való kerekítésre.
today() |> as.character() # a date
#> [1] "2026-04-23"
now() |> as.character() # a datetime
#> [1] "2026-04-23 09:07:09.779319"
now() |> format_ISO8601() # defaults to nearest second
#> [1] "2026-04-23T09:07:38"
Kevés nem programozó akar ISO 8601 formátumú kimenetet látni, ezért elengedhetetlen a kultúraspecifikus dátum- és időformátumok kezelése.
A Base R biztosítja a format() függvényt, amely a sok más nyelvben megtalálható, tiszteletreméltó strftime() függvényekhez hasonló formátum stringeket használ.
format(today(), "%A, %d %B %Y")
#> [1] "Thursday, 23 April 2026"
Ez a Base R-es megközelítés csúnya, de megbízható. Íme néhány a leggyakoribb kódból:
| Kód | Eredmény |
|---|---|
| %d | A hónap napjának száma (5, 17, 28, stb.) |
| %a | A hét napjának rövidítése (Mon, Tue, Wed, stb.) |
| %A | A hét napjának teljes neve (Monday, Tuesday, stb.) |
| %w | A hét napjának száma (0-6, vasárnap a 0) |
| %u | A hét napjának száma (1-7, hétfő az 1) |
| %b | A hónap rövidítése (Jan, Feb, stb.) |
| %B | A hónap teljes neve (January, February, stb.) |
| %y | 2 jegyű év (pl. 89) |
| %Y | 4 jegyű év (pl. 1989) |
| %h | óra (24 órás formátum) |
| %m | perc |
| %s | másodperc |
| %z | eltolás az UTC-től |
| %Z | Időzóna (karakter) |
A Lubridate egy emberbarátabb alternatívát kíván nyújtani a stamp() függvénnyel, valamint a szűkebb stamp_date() és stamp_time() függvényekkel.
Ezek egy sablon stringet fogadnak, amely a kívánt kimenet példája, és egy egyedi függvényt hoznak létre az adott formátum előállításához.
A Lubridate megpróbálja kitalálni a formátumot, de gyakran kétértelműségekbe ütközik és rossz eredményt ad.
Légy felkészülve arra, hogy formátum stringeket (például "dmy") adj meg az egyértelműsítéshez.
Az Exercism-ön hasznos lehet a locale = "C" megadása is, hogy megkerüld a rendszer helyi beállításait és angolul dolgozz (ahogy a tesztek elvárják).
A helyi beállítások bonyolultak!
sf <- stamp("Saturday, June 27, 2026 at 14:45", locale = "C")
#> Multiple formats matched: "%A, %B %d, %Y at %H:%M"(1), "Saturday, %Om %d, %Y at %H:%M"(1), "Saturday, %B %d, %Y at %H:%M"(1), "%A, %Om %d, %Y at %H:%M"(0)
#> Using: "%A, %B %d, %Y at %H:%M"
sf(make_datetime(2023, 3, 29, 15, 0, 0))
#> [1] "Wednesday, March 29, 2023 at 15:00"
A Lubridate számos függvényt kínál egy dátum vagy dátum-idő elemeinek lekérdezéséhez vagy beállításához: kézenfekvőket, mint a year() és a second(), de specialisabbakat is, mint a quarter() és az epiweek().
Mindegyik ilyen függvény használható érték lekérdezéséhez, és a legtöbbjük érték beállításához is.
dt <- now()
dt
#> [1] "2026-04-23 15:47:11 MST"
# get
month(dt)
#> [1] 4
# set
month(dt) <- 11
dt
#> [1] "2026-11-23 15:47:11 MST"
A wday() (a hét napja) és a month() esetében az alapértelmezett kimenet egész szám, de:
labels = TRUE beállítása az aktuális helyi beállításnak megfelelő rövidített karakter stringet ad vissza.abbr = FALSE beállítása ezenfelül a teljes nevet adja vissza.d <- today()
d
#> [1] "2026-04-23"
wday(d)
#> [1] 5
wday(d, label = TRUE)
#> [1] Thu
#> Levels: Sun < Mon < Tue < Wed < Thu < Fri < Sat
wday(d, label = TRUE, abbr = FALSE)
#> [1] Thursday
#> Levels: Sunday < Monday < Tuesday < Wednesday < Thursday < Friday < Saturday
A címkék categorical variables (kategorikus változók), amelyek egy másik fogalom témáját képezik majd.
Az értékek a felsorolt Levels értékeire korlátozódnak, amelyek a rendezési sorrendet is mutatják.
A hét alapértelmezésben vasárnappal kezdődik, de ez a week_start argumentummal módosítható (használj 1-et a hétfőhöz).
dt <- make_datetime(2026, 4, 23)
# default behavior
wday(dt)
#> [1] 5
wday(dt, label = T)
#> [1] Thu
# set week_start to Monday
wday(dt, week_start = 1)
#> [1] 4
Néhány segédfüggvény TRUE/FALSE értéket ad vissza, például a leap_year() és a dst() (aktív-e a nyári időszámítás?).
A hónapok hossza változó, ezért a days_in_month() egész számként adja meg az értéket.
A date és a datetime értékek egyaránt egy pillanatot jelölnek az idővonalon.
Hogyan kezeljük az olyan fogalmakat, mint „tegnap ilyenkor” vagy „egy hét múlva”?
A más, tudományos irányultságú nyelveket ismerő tanulók találkozhattak már a Python datetime.timedelta típusával vagy a Julia Period típusával.
Ezzel szemben az R-nek nem kevesebb, mint három módja van az időtartamok ábrázolására, eltérő erősségekkel és korlátokkal. Mint mindig, most is azt válaszd, amelyik a legjobban illik a helyzetedhez.
A különféle időtartamok könnyen átalakíthatók egymásba az as.period(), az as_duration() és az as.interval() függvénnyel.
Az időtartamtípusok lekérdezhetők az is.period() és hasonló függvényekkel.
Egy period, például a hours(3), 3 órával növelheti az óraidőt.
A szabálytalanságokat, például a nyári időszámítást (DST), nagyrészt figyelmen kívül hagyja.
Minden period egy egység többes számú neve: years, weeks, stb.
d <- today()
d
#> [1] "2026-04-25"
d + weeks(2)
#> [1] "2026-05-09"
# dates are coerced to datetimes at midnight if necessary
d - hours(20)
#> [1] "2026-04-24 04:00:00 UTC"
hours(3)
#> [1] "3H 0M 0S"
Egy duration, például a dhours(3), 3 órával növeli a fizikai időt, figyelembe véve az olyan szabálytalanságokat, mint a DST, amelyek miatt az óraidő eltér a fizikai időtől.
Minden duration egy d előtagot ad a megfelelő period elé: dyears, dweeks, stb.
dhours(3)
#> [1] "10800s (~3 hours)"
Egy interval egy kezdő- és egy záróidő kombinációja, amelyet akár az interval() függvénnyel, akár a %--% infix operátorral hozhatsz létre.
# the next 2 lines were run about 33s apart, so now() is different
dt1 <- now()
dt2 <- now() + hours(1)
# interval(0 function)
ivl <- interval(dt1, dt2)
ivl
#> [1] 2026-04-24 16:24:54 MST--2026-04-24 17:25:27 MST
# infix operator
iv2 <- dt1 %--% dt2
iv2 == iv1
#> [1] TRUE
Az időzónák kezelése kihívást jelenthet. Az alábbi idézet a Python dokumentációjából származik, de ugyanúgy érvényes az R-re is:
„Az időigazítás szabályai világszerte inkább politikaiak, mint racionálisak, gyakran változnak, és az UTC-n kívül nincs minden alkalmazáshoz megfelelő szabvány.”
Néhány általános megjegyzés az időzónákról:
datetime mindig tartalmaz időzónát (tz); a date nem időzóna-tudatos.now() függvény kivétel: a rendszeróráról szerzi be a helyi idődet, a tz-t is beleértve.
Alapértelmezésben a helyi tz-t használja, de megadhatók mások is, például now("UTC").# timezone defaults to UTC
local <- ymd_hm("2026-04-25 10:32")
local
#> [1] "2026-04-25 10:32:00 UTC"
# timezone can be specified
AZ <- ymd_hm("2026-04-25 10:32", tz = "America/Phoenix")
AZ
#> [1] "2026-04-25 10:32:00 MST"
# same clocktime, different tz
local - AZ
#> Time difference of -7 hours
Az időzónákkal kapcsolatos aritmetika megbízhatóbb duration értékekkel, mint period értékekkel.
A dátumokkal és dátum-időkkel való munka általában sokkal könnyebb a lubridate-tel, mint a Base R-rel.
Ez a fogalom megpróbált áttekintést adni, de elkerülhetetlenül nagyon sok részletet kihagy.
Ismétlés kockázatát vállalva: a dokumentáció a barátod, használd bátran.
Ebben a feladatban egy New York-i szépségszalon időpont-ütemezőjén fogsz dolgozni, amely 2012. szeptember 15-én nyílt meg.
Öt részfeladat vár rád, és mindegyik az időpontok dátumaival foglalkozik. A időpontok 24 órás formátumban vannak (nincs AM/PM). Hogy az időzónákkal kapcsolatos problémák a lehető legegyszerűbbek legyenek, feltételezd, hogy minden UTC-t használ.
Valósítsd meg a schedule_appointment() függvényt, amely egy időpont szöveges reprezentációját alakítja át a megfelelő DateTime formátumra.
Ügyelj arra, hogy a bemenet az amerikai hónap/nap/év sorrendet követi.
julia> schedule_appointment("07/25/2023 13:45:00")
2023-07-25T13:45:00
Valósítsd meg a has_passed() függvényt, amely egy időpontot kap, és ellenőrzi, hogy az időpont a múltban van-e:
julia> has_passed(DateTime(1999, 12, 31, 9, 0, 0))
true
Valósítsd meg az is_afternoon_appointment() függvényt, amely egy időpontot kap, és ellenőrzi, hogy az délutánra esik-e (>= 12:00 és < 18:00):
julia> is_afternoon_appointment(DateTime(2023, 3, 29, 15, 0, 0))
true
Valósítsd meg a day_of_week() függvényt, amely a hét napjának sorszámát adja vissza.
A szalon vasárnap zárva tart, ezért a hétfő az 1. nap, a szombat pedig a 6.
# a Friday
day_of_week(make_datetime(2026, 06, 05, 12, 30, 00))
#> [1] 5
Ha egy vendég át szeretné tenni az időpontját, az automatikusan a következő péntekre kerül, ugyanabban az időpontban (mivel ez a hét leglassabb napja).
Valósítsd meg a reschedule() függvényt, amely az eredeti időpontot kapja meg (szöveges formában, hónap/nap/év sorrendben), és visszaadja az átütemezett időpontot.
# a Saturday
reschedule("06/13/2026 12:30:00")
#> [1] "2026-06-19 12:30:00 UTC"
Ha az eredeti időpont hétfő és csütörtök közé esett, akkor ezen a héten péntekre ütemezd át. Pénteki vagy szombati időpontok esetén a következő hét péntekjére ütemezd át.
Iratkozz fel az Exercism-re, hogy megtanuld és elsajátítsd a(z) R nyelvet 21 fogalom111 feladat segítségével, valódi emberi mentorálással, mindez ingyen.