Kaksi koodauskerrosta muodostavat Unix-ajan. Ensimmäinen kerros koodaa ajanhetken skalaarisena reaalilukuna, joka kuvaa torstain 1. tammikuuta 1970 kello 00:00:00 UTC:n jälkeen kuluneiden sekuntien lukumäärää. Toinen kerros koodaa tämän luvun bittien tai desimaalilukujen sarjana.
Kuten UTC:ssä on tapana, tässä artikkelissa päivät merkitään gregoriaanisen kalenterin mukaan ja kunkin päivän sisällä lasketaan tunnit, minuutit ja sekunnit. Joissakin esimerkeissä esitetään myös kansainvälinen atomiaika (TAI, International Atomic Time), toinen aikajärjestelmä, jossa käytetään samoja sekunteja ja joka näytetään samassa muodossa kuin UTC, mutta jossa jokainen päivä on täsmälleen 86400 sekunnin pituinen ja menettää vähitellen synkronointinsa maapallon pyörimisen kanssa noin sekunnin vuosivauhdilla.
Ajan koodaaminen numeronaEdit
Unix-aika on yksittäinen merkkipitoinen luku, joka inkrementaalisesti kasvaa sekunnin välein, mikä tekee sen tallentamisesta ja manipuloinnista helpompaa tietokoneiden kannalta kuin tavanomaiset päivämäärän merkitsemisjärjestelmät. Tulkkiohjelmat voivat sitten muuntaa sen ihmisen luettavaan muotoon.
Unixin aikakausi on aika 00:00:00 UTC 1. tammikuuta 1970. Tässä määritelmässä on se ongelma, että UTC oli olemassa nykyisessä muodossaan vasta vuonna 1972; tätä asiaa käsitellään jäljempänä. Lyhyyden vuoksi loppuosassa käytetään ISO 8601 -päivämäärän ja -ajan muotoa, jossa Unixin aikakausi on 1970-01-01T00:00:00:00Z.
Unixin aikaluku on nolla Unixin aikakautena ja kasvaa tasan 86400:lla jokaista päivää kohden aikakautensa jälkeen. Näin ollen 2004-09-16T00:00:00Z, 12677 päivää epookin jälkeen, on Unix-aikaluku 12677 × 86400 = 1095292800. Tätä voidaan laajentaa myös taaksepäin epookista käyttämällä negatiivisia lukuja; näin ollen 1957-10-04T00:00:00Z, 4472 päivää ennen epookkia, on Unix-ajan luku -4472 × 86400 = -386380800. Tämä pätee myös päivien sisällä; aikaluku mihin tahansa vuorokauden aikaan on niiden sekuntien lukumäärä, jotka ovat kuluneet kyseisen päivän alkavasta keskiyöstä, lisättynä kyseisen keskiyöpäivän aikalukuun.
Koska Unix-aika perustuu aikakauteen, ja koska on yleinen väärinkäsitys, että Unix-aikakausi on ainoa aikakausi (jota usein kutsutaan nimellä ”aikakausi” ), Unix-aikaan viitataan toisinaan nimellä aikakausi.
KarkaussekunnitEdit
Ylläoleva järjestelmä tarkoittaa, että normaalina UTC-päivänä, jonka kesto on 86400 sekuntia, Unix-ajan numero muuttuu yhtäjaksoisesti keskiyöllä. Esimerkiksi yllä olevissa esimerkeissä käytetyn päivän lopussa ajan esitykset etenevät seuraavasti:
Kun tapahtuu karkaussekunti, UTC-päivä ei ole täsmälleen 86400 sekunnin pituinen, ja Unix-ajanumero (joka kasvaa aina täsmälleen 86400:lla joka päivä) kokee epäjatkuvuuden. Karkaussekunnit voivat olla positiivisia tai negatiivisia. Negatiivista karkaussekuntia ei ole koskaan julistettu, mutta jos sellainen julistettaisiin, negatiivisen karkaussekunnin sisältävän päivän lopussa Unixin aikanumero hyppäisi 1:llä seuraavan päivän alkuun. Päivän lopussa olevan positiivisen karkaussekunnin aikana, joka tapahtuu keskimäärin noin puolentoista vuoden välein, Unixin aikanumero kasvaa jatkuvasti seuraavaan päivään karkaussekunnin aikana ja hyppää sitten karkaussekunnin lopussa takaisin 1:llä (paluu seuraavan päivän alkuun). Esimerkiksi näin tapahtui tiukasti POSIX.1:n mukaisissa järjestelmissä vuoden 1998 lopussa:
Unixin aikanumerot toistuvat positiivista karkaussekuntia välittömästi seuraavalla sekunnilla. Unix-aikanumero 1483142400 on siis moniselitteinen: se voi viitata joko karkaussekunnin alkamiseen (2016-12-31 23:59:60) tai sen päättymiseen sekuntia myöhemmin (2017-01-01 00:00:00). Teoreettisessa tapauksessa, jossa negatiivinen karkaussekunti esiintyy, ei synny epäselvyyttä, vaan sen sijaan on olemassa joukko Unix-ajanumeroita, jotka eivät viittaa lainkaan mihinkään pisteeseen UTC-ajassa.
Unix-kello on usein toteutettu erityyppisellä positiivisen karkaussekunnin käsittelyllä, joka liittyy Network Time Protocoliin (NTP). Näin saadaan järjestelmä, joka ei ole POSIX-standardin mukainen. Katso lisätietoja jäljempänä olevasta NTP:tä koskevasta osiosta.
Käsiteltäessä ajanjaksoja, jotka eivät sisällä UTC-karkaussekuntia, kahden Unix-aikaluvun välinen ero on yhtä suuri kuin vastaavien aikapisteiden välisen ajanjakson kesto sekunteina. Tämä on yleinen laskentatekniikka. Jos kuitenkin esiintyy karkaussekunteja, tällaiset laskutoimitukset antavat väärän vastauksen. Sovelluksissa, joissa vaaditaan tällaista tarkkuutta, on tarpeen käyttää karkaussekuntitaulukkoa, kun käsitellään Unix-aikoja, ja usein on parempi käyttää muuta aikakoodausta, joka ei kärsi tästä ongelmasta.
Unix-aikamäärä on helppo muuntaa takaisin UTC-ajaksi ottamalla Unix-aikamäärän quotientti ja modulus modulo 86400. Osamäärä on päivien lukumäärä epookista ja moduuli on sekuntien lukumäärä kyseisen päivän keskiyön UTC-ajasta. Jos Unixin aikanumero on epäselvä positiivisen karkaussekunnin vuoksi, tämä algoritmi tulkitsee sen ajaksi juuri keskiyön jälkeen. Se ei koskaan tuota aikaa, joka on karkaussekunnin aikana. Jos Unix-ajan numero on virheellinen negatiivisen karkaussekunnin vuoksi, algoritmi tuottaa yhtä virheellisen UTC-ajan. Jos nämä olosuhteet ovat merkittäviä, niiden havaitsemiseksi on tarpeen tutustua karkaussekuntien taulukkoon.
Epäsynkroninen Network Time Protocol -pohjainen muunnos Muokkaa
Yleisesti Mills-tyylinen Unix-kello on toteutettu siten, että karkaussekuntien käsittely ei ole synkronoitua Unix-aikanumeron muutoksen kanssa. Ajanumero pienenee aluksi siellä, missä harppauksen olisi pitänyt tapahtua, ja hyppää sitten oikeaan aikaan 1 sekunti harppauksen jälkeen. Tämä helpottaa toteutusta, ja sitä kuvataan Millsin artikkelissa. Näin tapahtuu positiivisen karkaussekunnin yli:
Tämä voidaan purkaa oikein kiinnittämällä huomiota karkaussekunnin tilamuuttujaan, joka ilmoittaa yksiselitteisesti, onko karkausta jo tehty. Tilamuuttujan muutos on synkroninen harppauksen kanssa.
Vaikuttaa, että vastaava tilanne syntyy negatiivisen karkaussekunnin kohdalla, jolloin ohitettava sekunti on hieman liian myöhään. Hyvin lyhyesti järjestelmä näyttää nimellisesti mahdottoman aikaluvun, mutta tämä voidaan havaita TIME_DEL-tilamuuttujalla ja korjata.
Tällaisessa järjestelmässä Unixin aikaluku rikkoo POSIXia molempien karkaussekuntityyppien ympärillä. Karkaussekunnin tilamuuttujan kerääminen yhdessä aikanumeron kanssa mahdollistaa yksiselitteisen dekoodauksen, joten haluttaessa voidaan tuottaa oikea POSIX-aikanumero tai tallentaa koko UTC-aika sopivampaan muotoon.
Tätyylisen Unix-kellon käsittelyyn tarvittava dekoodauslogiikka dekoodaisi oikein myös samaa rajapintaa käyttävän hypoteettisen POSIX:n mukaisen kellon. Tämä saavutettaisiin ilmaisemalla TIME_INS-tila koko syötetyn karkaussekunnin ajan ja ilmaisemalla sitten TIME_WAIT-tila koko seuraavan sekunnin ajan toistamalla sekuntien laskentaa. Tämä edellyttää synkronista karkaussekuntien käsittelyä. Tämä on luultavasti paras tapa ilmaista UTC-aika Unix-kellomuodossa Unix-rajapinnan kautta, kun taustalla oleva kello ei pohjimmiltaan kärsi karkaussekunneista.
TAI-pohjainen muunnosEdit
Toisessa, paljon harvinaisemmassa Unix-aikojen säilyttämisen poikkeavassa vaihtoehdossa koodataan TAI UTC:n sijasta; jotkin Linux-järjestelmät on konfiguroitu tällä tavalla. Koska TAI:ssa ei ole karkaussekunteja ja jokainen TAI-päivä on täsmälleen 86400 sekuntia pitkä, tämä koodaus on itse asiassa puhdas lineaarinen lasku sekunneista, jotka ovat kuluneet vuodesta 1970-01-01T00:00:00 TAI. Tämä tekee aikaväliaritmiikasta paljon helpompaa. Näiden järjestelmien aika-arvot eivät kärsi siitä epäselvyydestä, joka on tiukasti POSIX:n mukaisissa järjestelmissä tai NTP-ohjautuvissa järjestelmissä.
Näissä järjestelmissä on tarpeen käyttää karkaussekuntitaulukkoa, jotta UTC:n ja pseudo-Unix-aikamuodon välillä voidaan muuntaa oikein. Tämä muistuttaa tapaa, jolla aikavyöhyketaulukoita on konsultoitava siviiliaikaan ja siviiliajasta muuntamiseksi; IANA:n aikavyöhyketietokanta sisältää karkaussekuntitietoja, ja samasta lähteestä saatavilla oleva esimerkkikoodi käyttää näitä tietoja muunnettaessa TAI-pohjaisten aikaleimojen ja paikallisen ajan välillä. Muunnos törmää myös määrittelyongelmiin ennen UTC:n nykymuodon alkamista vuonna 1972 (ks. kohta UTC-perusta jäljempänä).
Tämä TAI-pohjainen järjestelmä ei ole pinnallisesta samankaltaisuudestaan huolimatta Unix-aika. Se koodaa ajat arvoilla, jotka poikkeavat useita sekunteja POSIX-aika-arvoista. Versiota tästä järjestelmästä ehdotettiin sisällytettäväksi ISO C:n time.h
:een, mutta vain UTC-osa hyväksyttiin vuonna 2011. tai_clock
on kuitenkin olemassa C++20:ssä.
Luvun esittäminenEdit
Unixin aikanumero voidaan esittää missä tahansa muodossa, joka pystyy esittämään numeroita. Joissakin sovelluksissa luku esitetään yksinkertaisesti tekstimuotoisesti desimaalilukujen merkkijonona, mikä aiheuttaa vain vähäisiä lisäongelmia. Tietyt Unix-aikojen binääriset esitystavat ovat kuitenkin erityisen merkittäviä.
Unixin time_t
-tietotyyppi, joka edustaa ajanhetkeä, on monilla alustoilla perinteisesti 32-bittinen (mutta ks. alla) merkkipitoinen kokonaisluku, joka koodaa suoraan Unix-aikaluvun edellisessä kappaleessa kuvatulla tavalla. Koska se on 32-bittinen, se kattaa yhteensä noin 136 vuoden alueen. Pienin esitettävä päivämäärä on perjantai 1901-12-13 ja suurin esitettävä päivämäärä on tiistai 2038-01-19. Sekunnin kuluttua 03:14:07 UTC 2038-01-19 tämä esitystapa ylivuotaa. Tätä virstanpylvästä odotetaan huvittuneisuuden ja pelon sekoituksella – katso vuoden 2038 ongelma.
Joissain uudemmissa käyttöjärjestelmissä time_t
on laajennettu 64-bittiseksi. Tämä laajentaa esitettävissä olevia aikoja noin 293 miljardilla vuodella molempiin suuntiin, mikä on yli kaksikymmenkertainen määrä maailmankaikkeuden nykyiseen ikään nähden kumpaankin suuntaan.
Alunperin oli jonkin verran kiistaa siitä, pitäisikö Unixin time_t
:n olla allekirjoitettu vai allekirjoittamaton. Jos se olisi merkkaamaton, sen alue tulevaisuudessa kaksinkertaistuisi, mikä lykkäisi 32-bittistä ylivuotoa (68 vuodella). Tällöin se ei kuitenkaan pystyisi esittämään aikoja ennen aikakautta. Konsensus on, että time_t
on allekirjoitettu, ja tämä on tavanomainen käytäntö. QNX-käyttöjärjestelmän version 6 ohjelmistokehitysalustassa on allekirjoitukseton 32-bittinen time_t
, vaikka vanhemmissa versioissa käytettiin allekirjoitettua tyyppiä.
POSIX- ja Open Group Unix -spesifikaatioissa on mukana C-standardikirjasto, joka sisältää <time.h>
-otsikkotiedostossa määritellyt aikatyypit ja funktiot. ISO C -standardissa todetaan, että time_t
:n on oltava aritmeettinen tyyppi, mutta siinä ei määrätä mitään tiettyä tyyppiä tai koodausta sille. POSIX vaatii, että time_t
:n on oltava kokonaislukutyyppi, mutta ei määrää, että sen on oltava allekirjoitettu tai allekirjoittamaton.
Unixissa ei ole perinteitä esittää suoraan muita kuin kokonaislukuja Unix-aikalukuja binäärisinä murtolukuina. Sen sijaan sekuntia pienemmällä tarkkuudella olevat ajat esitetään käyttämällä yhdistettyjä tietotyyppejä, jotka koostuvat kahdesta kokonaisluvusta, joista ensimmäinen on time_t
(Unix-ajan kokonaisosa) ja toinen on aikaluvun murto-osa miljoonasosina (muodossa struct timeval
) tai miljardisosina (muodossa struct timespec
). Nämä rakenteet tarjoavat desimaalipohjaisen kiintopistemuotoisen dataformaatin, joka on hyödyllinen joissakin sovelluksissa ja triviaali muuntaa toisissa sovelluksissa.
UTC-pohjaEdit
Nykymuotoinen UTC, karkaussekunneilla, on määritelty vasta 1. tammikuuta 1972 alkaen. Sitä ennen, 1.1.1961 alkaen, oli käytössä vanhempi UTC-muoto, jossa oli satunnaisia aika-askeleita, jotka olivat epäkokonaislukuisia sekunteja, mutta myös UTC-sekunti oli hieman pidempi kuin SI-sekunti, ja sitä muutettiin määräajoin, jotta se vastaisi jatkuvasti maapallon pyörimisnopeutta. Ennen vuotta 1961 ei ollut UTC:tä ja ennen vuotta 1958 ei ollut laajalle levinnyttä atomaarista ajanmittausta; näinä aikakausina käytettiin jonkinlaista GMT:n approksimaatiota (joka perustui suoraan maapallon pyörimiseen) atomaarisen aika-asteikon sijasta.
Unix-ajan tarkka määritelmä UTC:n koodauksena on kiistaton vain sovellettuna UTC:n nykyiseen muotoon. Tämän UTC-muodon alkua edeltävä Unix-aikakausi ei vaikuta sen käyttöön tällä aikakaudella: päivien lukumäärä 1.1.1970 (Unix-aikakausi) ja 1.1.1972 (UTC:n alku) välisenä aikana ei ole kyseenalainen, ja päivien lukumäärä on ainoa, mikä on merkittävää Unix-ajan kannalta.
Unix-aika-arvojen, jotka ovat pienempiä kuin +63072000 arvot (eli ennen 1.1.1972), merkitystä ei ole tarkasti määritelty. Tällaisten Unix-aikojen perustana on paras ymmärtää olevan määrittelemätön UTC:n approksimaatio. Tuon aikakauden tietokoneissa oli harvoin riittävän tarkat kellot, jotta ne olisivat joka tapauksessa antaneet merkityksellisiä sekuntia pienempiä aikaleimoja. Unix-aika ei ole sopiva tapa esittää vuotta 1972 edeltäviä aikoja sovelluksissa, jotka vaativat sekunnin alittavaa tarkkuutta; tällaisissa sovelluksissa on ainakin määriteltävä, mitä UT:n tai GMT:n muotoa ne käyttävät.
Vuodesta 2009 lähtien on harkittu mahdollisuutta lopettaa karkaussekuntien käyttö siviiliajassa. Todennäköinen keino tämän muutoksen toteuttamiseksi on määritellä uusi aika-asteikko, jota kutsutaan kansainväliseksi ajaksi ja joka aluksi vastaa UTC:tä, mutta jossa ei sen jälkeen ole karkaussekunteja ja joka näin ollen pysyy vakioetäisyydellä TAI:sta. Jos näin tapahtuu, on todennäköistä, että Unix-aika määritellään jatkossa tämän uuden aika-asteikon mukaan UTC:n sijasta. Epävarmuus siitä, tapahtuuko tämä, ei tee tulevasta Unix-ajasta yhtään vähemmän ennustettavaa kuin se jo on: jos UTC:ssä ei olisi enää karkaussekunteja, tulos olisi sama.