forked from cadey/xesite
147 lines
5.1 KiB
Markdown
147 lines
5.1 KiB
Markdown
---
|
|
title: "TempleOS: Date/Time Code"
|
|
date: 2019-06-22
|
|
---
|
|
|
|
# TempleOS: Date/Time Code
|
|
|
|
## Introduction
|
|
|
|
In the [last post](https://christine.website/blog/templeos-2-god-the-rng-2019-05-30)
|
|
we discussed `god`, the random number generator of TempleOS. This post is going to
|
|
cover the time handling subsystem of the TempleOS kernel. As mentioned [before](https://christine.website/blog/sorting-time-2018-08-26),
|
|
time is an unfortunately complicated thing, and legacy systems stapled onto
|
|
legacy systems complicate this a lot. It also doesn't help that we consider
|
|
one day 24 hours, even though it's more accurately slightly less than that.
|
|
Most people consider a year to be 365 days, but the actual movement of the ball
|
|
Earth around the Sun is closer to 365 days and about [six hours](https://www.scienceabc.com/eyeopeners/why-do-we-have-a-leap-day-leap-year-after-every-4-years.html).
|
|
To compensate for this, every 4 years we add a day to the calendar called a
|
|
Leap Day. This complicates time calculations. And this isn't even going to
|
|
cover time zones because I want you readers to have some shred of sanity left.
|
|
|
|
## Problem space description
|
|
|
|
Time is an unfortunately complicated thing to handle. The goal of this part of
|
|
the TempleOS kernel is to allow users to programmatically get the current "real
|
|
time", or the time of the day. This is sometimes called "wall clock time",
|
|
referring to the offices where Unix was developed having literal clocks on the
|
|
wall to tell you the time. When you read a clock off the wall, you get the
|
|
current time in hours, minutes and sometimes seconds. See this example below:
|
|
|
|
![an analog clock](/static/img/analog_clock.jpg)
|
|
|
|
This clock either says it is 2:51:59 or 14:51:59, as clocks only show a 12 hour
|
|
view of 24 hour days for historical reasons.
|
|
|
|
## How x86 hardware handles time
|
|
|
|
The x86 real-time clock keeps track of time with a similar kind of API. The OS
|
|
can query the real-time clock and get the following fields individually:
|
|
|
|
* Seconds
|
|
* Minutes
|
|
* Hours
|
|
* Weekday
|
|
* Day of month
|
|
* Month
|
|
* Last two digits of the year
|
|
* Sometimes the current century (19 or 20)
|
|
|
|
Note the last two digits of the year. This is returned because computers
|
|
historically did not keep track of the current century in order to save memory.
|
|
This famously caused the Y2K bug that [almost caused chaos globally](https://en.wikipedia.org/wiki/Year_2000_problem). Thankfully this problem was averted.
|
|
|
|
## How Linux handles time
|
|
|
|
Linux handles time by keeping track of [the number of seconds since January 1, 1970](https://en.wikipedia.org/wiki/Unix_time).
|
|
Traditionally programs use a signed 32 bit integer value for this. You might
|
|
notice that this is a drastically different time format than what the real-time
|
|
clock provides. When Linux reads from the real time clock, it does some math to
|
|
convert these real time values to a unix timestamp.
|
|
|
|
### The Rockchip calendar hack
|
|
|
|
Of course, this assumes that the hardware is behaving correctly. There have been
|
|
cases in the past where [buggy hardware required patches](https://lore.kernel.org/patchwork/patch/628142/).
|
|
If the hardware calendar and the software understanding of that calendar get out
|
|
of sync, things get really confused.
|
|
|
|
## How TempleOS handles time
|
|
|
|
TempleOS has two main time types, [CDateStruct](https://github.com/Xe/TempleOS/blob/master/Kernel/KernelA.HH#L193-L198)
|
|
and [CDate](https://github.com/Xe/TempleOS/blob/master/Kernel/KernelA.HH#L186-L190).
|
|
Let's look at each of these in more detail:
|
|
|
|
```c++
|
|
public class CDateStruct
|
|
{
|
|
U8 sec10000,sec100,sec,min,hour,
|
|
day_of_week,day_of_mon,mon;
|
|
I32 year;
|
|
};
|
|
|
|
public I64 class CDate
|
|
{
|
|
U32 time;
|
|
I32 date;
|
|
};
|
|
```
|
|
|
|
The `I64 class CDate` part of the definition makes the HolyC compiler align
|
|
instances of it across the size of a int64 value, or 8 bytes. This also makes
|
|
the compiler treat instances of it like an integer value.
|
|
|
|
Here are approximate Zig equivalents:
|
|
|
|
```zig
|
|
pub const CDateStruct = packed struct {
|
|
sec10000: u8,
|
|
sec100: u8,
|
|
sec: u8,
|
|
min: u8,
|
|
hour: u8,
|
|
day_of_week: u8,
|
|
day_of_mon: u8,
|
|
mon: u8,
|
|
year: i32,
|
|
};
|
|
|
|
pub const CDate = packed struct {
|
|
time: u32,
|
|
date: i32,
|
|
}
|
|
```
|
|
|
|
The [TempleOS documentation](https://templeos.holyc.xyz/Wb/Doc/TimeDate.html)
|
|
describes `CDate` like this:
|
|
|
|
```
|
|
TempleOS uses a 64-bit value, CDate, for date/time. The upper 32-bits are the
|
|
days since Christ. The lower 32-bits store time of day divided by 4 billion
|
|
which works out to 49710ths of a second. You can subtract two CDate's to get a
|
|
time span.
|
|
|
|
Use CDATE_FREQ to convert.
|
|
```
|
|
|
|
And it [describes the `date` field](https://templeos.holyc.xyz/Wb/Doc/Date.html)
|
|
of `CDate` like this:
|
|
|
|
```
|
|
Dates are 32-bit signed ints representing the number of days since the birth of
|
|
Christ. Negative values represent B.C.E. dates.
|
|
```
|
|
|
|
B.C.E. usually means "Before Common Era", which is a way to say "the time before
|
|
Jesus Christ was born". Christianity ended up being such a core part of global
|
|
culture that the calendar was reset because of it.
|
|
|
|
|
|
|
|
* CMOS real time clock API
|
|
* NowDateTimeStruct();
|
|
* How to read from the CMOS clock
|
|
* The bug in the time code
|
|
* Live Demo (Zig/wasm+JS)
|
|
|