## 🗐 DATETIME.E for Euphoria 3.1.1

Version 1.20, January/9/2018, by Shian Lee.

1. Introduction

This library provides date and time routines for any platform. It supports
dates and times from January 1, 1900 to December 31, 4900, according to the
Gregorian Calendar (the most widely used civil calendar today).

The Gregorian Calendar is accurate for civil use, not for precise
astronomical calculations. It was not fully adopted before the beginning of
the 20th century, therefore calculating dates before the year 1900 leads to
ambiguous results.

The library is using serial numbers to represent dates and times. A serial
number is an atom of the form (Days.Seconds).
Days, the integer part, must be in the range 2415021 to 3511113, which is
equivalent to 1/1/1900 to 12/31/4900 respectively.
Seconds, the fractional part, is in the range 0 to 0.99999, which is
equivalent to 0:00:00 to 23:59:59.

Out of range dates, before 1/1/1900 or after 12/31/4900, are serial numbers
of the form (0.Seconds). i.e. only the time part is significant.

The functions now() and datetime() are calculating and converting a sequence
of the form {year, month, day, hour, minute, second} into a serial number,
which later can be passed to other functions in this library.

When calculating date and time, each 86400 seconds are equivalent to 1 day.
The larger units (months/years) are always added or subtracted before the
smaller units (days). This method is intuitive, and it's compatible with the
professional edition of Microsoft(R) Visual Basic(TM) for MS-DOS(R).

You can use serial numbers *directly* for comparing, adding, subtracting,
(>, =, <, +, -) and sorting date and time like any other number. You can also
convert a serial number to an IEEE floating-point format, which is useful for
saving date/time in binary file or database: atom_to_float64(serial number)
can be used to store date and time, and atom_to_float32(floor(serial number))
can be used to store only the date part. Note that atom_to_float32() is not
precise enough to store the time part of a serial number in any case.

For developing this library, results of date and time calculations were
verified with Microsoft(R) Visual Basic(TM) for MS-DOS(R), and with an online
date/time calculator at https://www.timeanddate.com/. In both cases results
before the year 1900, or results which are based on a Julian Calendar, are
not relevant to this library.

Disclaimer
==========

Use this library at your own risk. The author will not be responsible for
any damage or data loss.

This library is tested on FreeDOS 1.1 operating system. The code or the
documentation might still contain errors or mistakes.

In the descriptions below, to indicate what kind of object may be passed in
and returned, the following prefixes are used:

x     - a general object (atom or sequence)

s     - a sequence

a     - an atom

i     - an integer

fn    - an integer used as a file number

st    - a string sequence, or single-character atom

2. Routines by Application Area

2.1 Serial Number
=================

These routines calculate and convert a date/time sequence to a serial number.

now             - return a serial number equal to current date/time

datetime        - return a serial number from a date/time sequence

2.2 Date/Time Info
==================

These routines extract information from a serial number.

dateinfo        - return a date/time sequence from a serial number

datediff        - return the difference between two serial numbers

sprintd         - return a formatted string sequence from a serial number

3. Alphabetical Listing of all Routines

--------------------------------<datediff>----------------------------------

Syntax:      include datetime.e
s = datediff(a1, a2)

Description: Return the difference between two serial numbers a1 and a2. s
is a sequence of the form:

{days, hours, minutes, seconds}

i.e. s is the absolute difference in days, hours, minutes and
seconds.

Comments:    For calculating a new date/time you should use datetime().

Example 1:

a1 = datetime({2010})
a2 = datetime({2020})

s = datediff(a1, a2)
-- s is {3652, 0, 0, 0}
-- i.e. 3652 days difference between 2010-Jan-1 to 2020-Jan-1

Example 2:

a1 = datetime({2017, 5, 1})
a2 = datetime({2017, 5 - 1, 15})

s = datediff(a1, a2)
-- s is {16, 0, 0, 0}
-- i.e. 16 days difference between 2017-May-1 to 2017-Apr-15

Example 3:

a1 = datetime({2017, 5, 1, 10, 20, 40})
a2 = datetime({2019, 5, 6, 30, 35, 45})

s = datediff(a1, a2)
-- s is {735, 20, 15, 5}
-- i.e. 735 days, 20 hours, 15 minutes and 5 seconds difference
-- between 2017-May-1, 10:20:40 to 2019-May-7, 6:35:45

Example 4:

a = now()

s = datediff(a, a - 100.5)
-- s is {100, 12, 0, 0}
-- i.e. 100 days and 12 hours (absolute) difference from now

--------------------------------<dateinfo>----------------------------------

Syntax:      include datetime.e
s = dateinfo(a)

Description: Return a date/time sequence from a serial number. s is a
sequence of the form:

{ year,  -- 4 digits year number (e.g. 2017)
month,  -- January = 1
day,  -- day of month, starting at 1
hour,  -- 0 to 23
minute,  -- 0 to 59
second,  -- 0 to 59
day of the week,  -- Sunday = 1
day of the year,  -- January 1st = 1
days in a month,  -- 1 to 31
days in a year}   -- 365 or 366

Comments:    You can tell that a year is a leap year, i.e. that February has
29 days instead of 28 days, when the field 'days in a year' is
equal 366.

Example 1:

s = dateinfo(now())
-- s is {2017, 3, 7, 8, 25, 55, 3, 66, 31, 365} for
--  Tuesday, 2017-Mar-7, 8:25:55

Example 2:

a = datetime({2017, 2, 16, 14, 19 ,15, "some extra info"})

s = dateinfo(a)
-- s is {2017, 2, 16, 14, 19, 15, 5, 47, 28, 365} for
--  Thursday, 2017-Feb-16, 14:19:15

Example 3:

s = dateinfo(2437127.78630)
-- s is {1960, 7, 11, 18, 52, 16, 2, 193, 31, 366} for
--  Monday, 1960-Jul-11, 18:52:16

s = dateinfo(0.78630)
-- s is {0, 0, 0, 18, 52, 16, 0, 0, 0, 0} for 18:52:16

s = dateinfo(2437127.00000)
-- s is {1960, 7, 11, 0, 0, 0, 2, 193, 31, 366} for
--  Monday, 1960-Jul-11, 0:00:00

--------------------------------<datetime>----------------------------------

Syntax:      include datetime.e
a = datetime(s)

Description: return a serial number from a date/time sequence s. s is a
sequence of the form:

{year, month, day, hour, minute, second}

Comments:    The default values {1900, 1, 1, 0, 0, 0} are used for missing
fields. Extra fields are ignored.

Any field outside valid range is evaluated to calculate a valid
date and time.

Use dateinfo() to return a date/time sequence from a serial
number.

Example 1:

a = datetime({})
-- a is 2415021.00000 for Monday, 1900-Jan-1, 0:00:00

a = datetime({17})
-- a is 0000000.00000 for ???, 0000-???-0, 0:00:00
-- i.e. the year 17 is out of supported range

a = datetime({2017})
-- a is 2457755.00000 for Sunday, 2017-Jan-1, 0:00:00

a = datetime({2017 - 40})
-- a is 2443145.00000 for Saturday, 1977-Jan-1, 0:00:00

a = datetime({2017 + 1050})
-- a is 2841259.00000 for Tuesday, 3067-Jan-1, 0:00:00

Example 2:

a = datetime({2017, 3, 2, 14, 19 ,15, "some extra info"})
-- a is 2457815.597 for Thursday, 2017-Mar-2, 14:19:15

a = datetime({2017, 3 - 24, 2, 14, 19 - 60, 15})
-- a is 2457084.555 for Monday, 2015-Mar-2, 13:19:15

a = datetime({2017, 3, 2, 14 + 49, 19 ,15 - 120})
-- a is 2457817.637 for Saturday, 2017-Mar-4, 15:17:15

a = datetime({5017, 3, 2, 14 + 49, 19 ,15 - 120})
-- a is 0.6369791667 for ???, 0000-???-0, 15:17:15
-- i.e. the year 5017 is out of supported range, so only the
--      time part is calculated and returned

Example 3:

a = datetime({1980, 5-12*10, 8, 12+24*50, 19+60*24 ,1-86400*2})
-- a is 2440764.513 for Friday, 1970-Jun-26, 12:19:01

-----------------------------------<now>------------------------------------

Syntax:      include datetime.e
a = now()

Description: Return a serial number equal to current date/time.

Comments:    Use dateinfo() to return a date/time sequence from a serial
number.

Example:

a = now()
-- a is 2457820.392 for Tuesday, 2017-Mar-7, 9:24:59

---------------------------------<sprintd>----------------------------------

Syntax:      include datetime.e
s = sprintd(st, a)

Description: Return a formatted string sequence from a serial number. st is a
format string, a is the serial number to be formatted. The basic
format specifiers are:

%yy    - print the year as a two-digit number (00-99)
%yyyy  - print the year as a four-digit number (1900-4900)

%m     - print the month as a number without leading zeros (1-12)
%mm    - print the month as a number with leading zeros (01-12)
%mmm   - print the month as an abbreviation (Jan-Dec)
%mmmm  - print the month as a full name (January-December)

%d     - print the day as a number without leading zeros (1-31)
%dd    - print the day as a number with leading zeros (01-31)
%ddd   - print the day as an abbreviation (Sun-Sat)
%dddd  - print the day as a full name (Sunday-Saturday)

%h     - print the hour without leading zeros (0-23)
%hh    - print the hour with leading zeros (00-23)
%H     - print the 12-hour clock without leading zeros (0-11)
%HH    - print the 12-hour clock with leading zeros (00-11)

%p     - print am with any hour before 12:00; pm with any hour
between 12:00 and 23:59
%P     - print AM with any hour before 12:00; PM with any hour
between 12:00 and 23:59

%t     - print the minute without leading zeros (0-59)
%tt    - print the minute with leading zeros (00-59)

%s     - print the second without leading zeros (0-59)
%ss    - print the second with leading zeros (00-59)

%w     - print the day of the week (1-7; Sunday = 1)
%e     - print the day of the year (1-366; January 1st = 1)
%c     - print the day of the century (1-36525; January 1st = 1))

%M     - print the numbers of days in a month (1-31)
%Y     - print the numbers of days in a year (365 days in a
common year; 366 days in a leap year)

%n     - print the date/time serial number (2415021.00000-
3511113.99999)

%%     - print the '%' character itself
%\$     - print nothing (terminates previous format specifier)

The following format specifiers allow you to fill up the field
with 1 to 5 leading zeros: %y, %m, %d, %h, %H, %t, %s, %w, %e,
%c, %M, %Y. (e.g. %y, %yy, %yyy, %yyyy, %yyyyy). To terminate a
format specifier string use %\$ (e.g. %ss%\$s).

Comments:    Some typical uses of sprintd() are:

1. Converting dates and times to strings.
2. Creating date and time stamps to save in a log file.
3. Displaying the date and time in a calendar or a clock.

sprintd() is not as fast as sprintf(). i.e. for printing the
time fast in a clock, it is recommended to use sprintf().

Example:

a = datetime({2017, 2, 3, 14, 53, 9})

s = sprintd("%yyyy-%mm-%dd %H:%tt:%ss %P\n", a)
-- s is "2017-02-03 2:53:09 PM\n" -- '\n' is new-line character

s = sprintd("%dddd, %yyyy-%mmm-%dd %h:%tt", a)
-- s is "Friday, 2017-Feb-03 14:53"

s = sprintd("%dddd, %mmmm %dd, %yyyy at %hh:%tt:%ss\n", a)
-- s is "Friday, February 03, 2017 at 14:53:09\n"

s = sprintd("Day %e in the year %yyyy.", a)
-- s is "Day 34 in the year 2017."

s = sprintd("[ %h%\$h\t%tm\t%s%\$s ]", a)
-- '\t' is tab character, so s is "[ 14h   53m     9s ]" if
-- printed at position(1, 1)