temporalo v0.0.3
Temporalo
Temporalo - Another lightweight datetime processing package
The name comes from Esperanto, meaning "temporal" in English (exactly the incoming new ECMAScript Date/Time API).
Features:
- For browsers & Node.js & Deno
- ES module
- Lightweight
- Out of time zones, out of mind
- Proudly made in PRC
TODO:
- More APIs
- Tests
- TypeScript
- Error handling
- Support for ES2021 and lower
- IIFE and CMD packages?
Installation
npm install temporalo
yarn add temporalo
pnpm add temporalo
<script src="https://cdn.jsdelivr.net/npm/temporalo/dist/temporalo.umd.js"></script>
<script type="importmap">
{
"imports": {
"temporalo": "https://cdn.jsdelivr.net/npm/temporalo/dist/temporalo.modern.js"
}
}
</script>
const Temporalo = require('temporalo/dist/temporalo.cjs')
import Temporalo from 'temporalo/dist/temporalo.modern.js'
The Temporalo
instance
There are three ways to get a Temporalo
instance.
Using the constructor
new Temporalo()
.new Temporalo() new Temporalo(value) new Temporalo(dateString) new Temporalo(dateObject) new Temporalo(datoObject)
If the constructor is passed with:
a
Date
instance, it returns aTemporalo
instance of the same datetime value.a number or a string representing a datetime, it parses it using
new Date()
and returns aTemporalo
instance of the same datetime value.another
Temporalo
instance, it returns a clone of thatTemporalo
instance.no argument, then it returns a
Temporalo
instance of the current datetime in the local time zone.
Using the static method
Temporalo.from()
.This static method accepts the same argument that the constructor does and returns the same result that it returns.
As a returned value of FP style
Temporalo
instance methods.Most
Temporalo
instance methods return a newTemporalo
instance, so it will be perfect for FP-style method-chaining, leaving a more readable code and fewer errors.
The only property of the Temporalo
instance is value
, which is simply a string in the format of either YYYY-MM-DDTHH:mm:ss.sssZ
or ±YYYYYY-MM-DDTHH:mm:ss.sss
that complies to ISO 8601. Although reassigning this value is allowed, it is strongly NOT RECOMMENDED to do so, in that there might be unpredictable side effects that compromise the program. As a best practice, always use Temporalo
instances generated by the above three ways.
In the following part of this tutorial, we assume d
to be a Temporalo
instance with a date value of Feb. 22, 2022 and a time value of 22:22:22.222.
const d = new Temporalo('2022-02-22T22:22:22.222Z')
Formatting
So far there are three very basic formmating methods: date()
and time()
returning the date part and the time part, and format()
that accepts the same argument as Intl.DateTimeFormat()
constructor and returns the same result as Intl.DateTimeFormat.prototype.format()
. More customized formatters are on the way.
console.log([
d.date(),
d.time(),
d.format('en-US', { dateStyle: 'full', timeStyle: 'full', timeZone: 'UTC' })
])
/*
Result:
[
"2022-02-22",
"22:22:22",
"Tuesday, February 22, 2022 at 10:22:22 PM Coordinated Universal Time"
]
*/
Getting datetime info
The get()
method returns a plain object that describes datetime info with the following keys:
year
: An integer representing the year.month
: An integer from 1 to 12 representing the month.day
: An integer from 1 to 31 representing the day of the month.hours
: An integer from 0 to 23 representing the hours.minutes
: An integer from 0 to 59 representing the minutes.seconds
: An integer from 0 to 59 representing the seconds. Leap seconds are ignored.ms
: An integer from 0 to 999 representing the milliseconds.weekday
: An integer from 0 to 6 representing the milliseconds, which maps to weekdays from Sunday to Saturday.
console.log(d.get())
/*
Result:
{
year: 2022,
month: 2,
day: 22,
hours: 22,
minutes: 22,
seconds: 22,
ms: 222,
weekday: 2
}
*/
Sometimes when only one value of the datetime info is needed, it is natural to pass a unit to the get()
method, or even more conveniently, to use the unit methods as a grammar sugar.
Here is a table listing all ways to get one value of the datetime info.
Value to get | Using the get() method | Using the unit method | Returned Value |
---|---|---|---|
year | d.get('year') | d.year() | 2022 |
month | d.get('month') | d.month() | 2 |
day | d.get('day') | d.day() | 22 |
hours | d.get('hours') | d.hours() | 22 |
minutes | d.get('minutes') | d.minutes() | 22 |
seconds | d.get('seconds') | d.seconds() | 22 |
milliseconds | d.get('ms') | d.ms() | 222 |
weekday | d.get('weekday') | d.weekday() | 2 |
Setting date Info
Remember in the above chapters when I told you not to play with the value
property? What if you really need to change one or more values of a datetime like setting the year or day? Here comes the setter methods to save the day.
All setter methods returns a brand new Temporalo
instance based on the current one, with only one or more particular values are altered. Therefore, no value
is altered in the process, and side effects are eliminated.
One way to use the set()
method is to pass an object as its only argument. The object has a similar structure to the get()
method's result, with two differences:
You can send only a subset of the units, and for those not in the object, they stay the same as in the base instance.
weekday
makes no difference at all, because it is unable to set the weekday without changing other date values, so you may not need it in the object passed.
console.log([
d.set({ month: 10, day 10 }),
d.set('month', 10).set('day', 10),
d.month(10).day(10)
])
console.log(d)
/*
Result
[
Temporalo { value: "2022-10-10T22:22:22.222Z" },
Temporalo { value: "2022-10-10T22:22:22.222Z" },
Temporalo { value: "2022-10-10T22:22:22.222Z" }
]
Temporalo { value: "2022-02-22T22:22:22.222Z" }
*/
Still, you can just set one value at a time by passing a second argument to the set()
method, or use the unit methods. Since weekday
does not work here, the getter methods get('weekday')
and weekday()
have no setter counterparts.
Here is a table listing all ways to set one value of the datetime info.
Value to set | Using set method | Using unit method | Returned Value |
---|---|---|---|
year | d.set('year', 2020) | d.year(2020) | Temporalo { value: "2020-02-22T22:22:22.222Z" } |
month | d.set('month', 10) | d.month(10) | Temporalo { value: "2022-10-22T22:22:22.222Z" } |
day | d.set('day', 1) | d.day(1) | Temporalo { value: "2022-02-01T22:22:22.222Z" } |
hours | d.set('hours', 12) | d.hours(12) | Temporalo { value: "2022-02-22T12:22:22.222Z" } |
minutes | d.set('minutes', 15) | d.minutes(15) | Temporalo { value: "2022-02-22T22:15:22.222Z" } |
seconds | d.set('seconds', 15) | d.seconds(15) | Temporalo { value: "2022-02-22T22:22:15.222Z" } |
milliseconds | d.set('ms', 500) | d.ms(500) | Temporalo { value: "2022-02-22T22:22:22.500Z" } |
Note: It is OK to send out-of-range values to setters as long as they remain integers. When such values are passed, the setters will automatically calculate the result datetime. However, you should be very cautious and check the results, because they might not be what you expect.
console.log([
d.month(0),
d.day(-1),
d.hours(24)
])
/*
Result
[
Temporalo { value: "2021-12-22T22:22:22.222Z" },
Temporalo { value: "2022-01-30T22:22:22.222Z" },
Temporalo { value: "2022-02-23T00:22:22.222Z" }
]
*/
The formatter time()
also functions as a getter. When it receives a number, it keeps the date part and starts counting seconds from midnight. When it receives a time string, it combines that time with the date part.
console.log([
d.time(0),
d.time(3600),
d.time('08:00'),
d.time('11:11:11')
])
/*
Result
[
Temporalo { value: "2022-02-22T00:00:00.000Z" },
Temporalo { value: "2022-02-22T01:00:00.000Z" },
Temporalo { value: "2022-02-22T08:00:00.000Z" },
Temporalo { value: "2022-02-22T11:11:11.000Z" }
]
*/
Getting date range
Ranges are intervals of datetime with fixed starting points and ending points. They are especially helpful for setting presets in front-end date-pickers among many other uses.
As with the getter and setter methods, there are two kinds of methods to get a range. One is to use the range()
method, and the other is, of course, the grammar sugar unit methods. Since the bare unit methods are used to be setters, an underscore symbol (_
) is prefixed to the unit.
All range methods return a binary array of Temporalo
instances. The former represents the first instant of start date with a time of T00:00:00.000Z
, and the latter the last instant with a time of T23:59:59.999Z
.
Ranges only apply to date, so units under day
like hours
and minutes
are not allowed. Another two units, however, are introduced: week
and quarter
. A week is defined as a 7-day interval from Monday to Sunday according to ISO 8601, and a quarter is a following three-month interval beginning at January, April, July, or October.
Sometimes it is useful to get part of a range that ends on a particular day, like the part of the current year that has passed, which is where the untilNow
option comes in handy. In other circumstances, when a range that represents the following year, not the next year, is needed, the relative
option will help. In the relative mode, the untilNow
option has no effect, and day units like week
, month
, quarter
, and year
are no longer intervals with fixed positions, but with a fixed length, representing 7, 30, 90, 365 days respectively.
Here is a large cheat sheet of range examples for those who believe the above paragraphs TL;DR. (To make the table simple, only the date part of the results is given, just keep in mind they are actually arrays of two Temporalo
instances.)
Range to get | Using the range() method | Using the unit method | Result |
---|---|---|---|
current full day | d.range('day') | d._day() | 2022-02-22 ➡️ 2022-02-22 |
current full week | d.range('week') | d._week() | 2022-02-21 ➡️ 2022-02-27 |
current full month | d.range('month') | d._month() | 2022-02-01 ➡️ 2022-02-28 |
current full quarter | d.range('quarter') | d._quarter() | 2022-01-01 ➡️ 2022-03-31 |
current full year | d.range('year') | d._year() | 2022-01-01 ➡️ 2022-12-31 |
current week until now | d.range('week', 0, { untilNow: true }) | d._week(0, { untilNow: true }) | 2022-02-21 ➡️ 2022-02-22 |
current month until now | d.range('month', 0, { untilNow: true }) | d._month(0, { untilNow: true }) | 2022-02-01 ➡️ 2022-02-22 |
current quarter until now | d.range('quarter', 0, { untilNow: true }) | d._quarter(0, { untilNow: true }) | 2022-01-01 ➡️ 2022-02-22 |
current year until now | d.range('year', 0, { untilNow: true }) | d._year(0, { untilNow: true }) | 2022-01-01 ➡️ 2022-02-22 |
last full day | d.range('day', -1) | d._day(-1) | 2022-02-21 ➡️ 2022-02-21 |
last full week | d.range('week', -1) | d._week(-1) | 2022-02-14 ➡️ 2022-02-20 |
last full month | d.range('month', -1) | d._month(-1) | 2022-01-01 ➡️ 2022-01-31 |
last full quarter | d.range('quarter', -1) | d._quarter(-1) | 2021-10-01 ➡️ 2021-12-31 |
last full year | d.range('year', -1) | d._year(-1) | 2021-01-01 ➡️ 2021-12-31 |
next full day | d.range('day', 1) | d._day(1) | 2022-02-23 ➡️ 2022-02-23 |
next full week | d.range('week', 1) | d._week(1) | 2022-02-28 ➡️ 2022-03-06 |
next full month | d.range('month', 1) | d._month(1) | 2022-03-01 ➡️ 2022-03-31 |
next full quarter | d.range('quarter', 1) | d._quarter(1) | 2022-04-01 ➡️ 2022-06-30 |
next full year | d.range('year', 1) | d._year(1) | 2021-01-01 ➡️ 2021-12-31 |
last 7 days | d.range('week', -1, { relative: true }) | d._week(-1, { relative: true }) | 2022-02-14 ➡️ 2022-02-20 |
last 30 days | d.range('month', -1, { relative: true }) | d._month(-1, { relative: true }) | 2022-01-01 ➡️ 2022-01-31 |
last 90 days | d.range('quarter', -1, { relative: true }) | d._quarter(-1, { relative: true }) | 2021-10-01 ➡️ 2021-12-31 |
last 365 days | d.range('year', -1, { relative: true }) | d._year(-1, { relative: true }) | 2021-01-01 ➡️ 2021-12-31 |
next 7 days | d.range('week', 1, { relative: true }) | d._week(1, { relative: true }) | 2022-02-28 ➡️ 2022-03-06 |
next 30 days | d.range('month', 1, { relative: true }) | d._month(1, { relative: true }) | 2022-03-01 ➡️ 2022-03-31 |
next 90 days | d.range('quarter', 1, { relative: true }) | d._quarter(1, { relative: true }) | 2022-04-01 ➡️ 2022-06-30 |
next 365 days | d.range('year', 1, { relative: true }) | d._year(1, { relative: true }) | 2021-01-01 ➡️ 2021-12-31 |
Datetime calculation
Datetime calculation methods have a lot in common with setters. They both return a new Temporalo
instance based on the current one, and the argument structures are almost the same. While setters explicitly set the values of datetime info, these methods calculate the datetime with interval length, providing more flexibility.
There are two calculation methods: before()
and after()
. To use them, either pass one object or a unit with a value to it, and you will get the result. Their functions are easily seen considering the names, but the roles will reverse when negative values are passed.
Unit methods as grammar sugar also exist. They function the same as after()
and are prefixed with a dollar sign ($
). Allowed units are year
, month
, day
, hours
, minutes
, seconds
, and ms
.
Instead of a large cheatsheet, a few examples here will suffice.
console.log([
d.before('day', 2),
d.before('day', -2),
d.after('day', 2),
d.after('day', -2),
d.$day(2),
d.$day(-2)
])
/*
Result:
[
Temporalo { value: "2022-02-20T22:22:22.222Z" },
Temporalo { value: "2022-02-24T22:22:22.222Z" },
Temporalo { value: "2022-02-24T22:22:22.222Z" },
Temporalo { value: "2022-02-20T22:22:22.222Z" },
Temporalo { value: "2022-02-24T22:22:22.222Z" },
Temporalo { value: "2022-02-20T22:22:22.222Z" }
]
*/
Datetime interval calculation
Datetime interval calculation methods are not finished yet. In the plan, the API will be like this:
d.diff('day', d.$day(1)) // 1
d.diff('hours', d.$day(1)) // 24
d.diff('day', d.$hours(12)) // 0.5
d.diff('full', d.$hours(36)) // { day: 1, hours: 12 }
In the meanwhile, I am running out of grammar sugar symbols. Maybe double dollar sign ($$
)? Any ideas, guys?
Appendix: List of methods, units, and grammar sugars
unit | get(unit)unit() | set(unit, value)unit(value) | after(unit, value)before(unit, -value)$unit(value) | range(unit, offset)_unit(offset) | range(unit, offset,{ untilNow: true })_unit(offset, { untilNow: true }) | range(unit, offset,{ relative: true })_unit(offset, { relative: true }) |
---|---|---|---|---|---|---|
year | ✔︎ | ✔︎ | ✔︎ | ✔︎ | ✔︎ | ✔︎ |
month | ✔︎ | ✔︎ | ✔︎ | ✔︎ | ✔︎ | ✔︎ |
day | ✔︎ | ✔︎ | ✔︎ | ✔︎ | ✔︎ | |
hours | ✔︎ | ✔︎ | ✔︎ | |||
minutes | ✔︎ | ✔︎ | ✔︎ | |||
seconds | ✔︎ | ✔︎ | ✔︎ | |||
ms | ✔︎ | ✔︎ | ✔︎ | |||
weekday | ✔︎ | |||||
week | ✔︎ | ✔︎ | ✔︎ | ✔︎ | ||
quarter | ✔︎ | ✔︎ | ✔︎ |
type | method | input | output |
---|---|---|---|
constructor | new Temporalo() | undefinedstringnumberDate{}Temporalo{} | Temporalo{} |
constructor | Temporalo.from() | undefinedstringnumberDate{}Temporalo{} | Temporalo{} |
formatter | format() | undefined, undefinedstring, undefinedstring, Object{} | string |
formatter | date() | undefined | string |
formatter | time() | undefined | string |
getter | get() | undefined | Object{} |
getter | unit() | undefined | number |
setter | set() | Object{} | Temporalo{} |
setter | set() | 'string', number | Temporalo{} |
setter | unit() | number | Temporalo{} |
setter | time() | numberstring | Temporalo{} |
range getter | range() | string, undefined, undefinedstring, number, undefinedstring, number, Object{} | Temporalo{}, Temporalo{} |
range getter | _unit() | undefined, undefinednumber, undefinednumber, Object{} | Temporalo{}, Temporalo{} |
calculator | after() | string, number | Temporalo{} |
calculator | before() | string, number | Temporalo{} |
calculator | \$unit() | number | Temporalo{} |
misc | toDate() | undefined | Date{} |
misc | toJSON() | undefined | string |