DateTime and TimeSpan module

Current version 1.0.0: DateTime.rbxm (11,1 KB)

About this module

This is a basic DateTime module that represents a date and a time, it doesn’t use os.date, and it can represent dates from 1 January 1 CE to 31 December 99999, it’s stored as if the time zone is UTC.

Why DateTime and TimeSpan if I’ve already got os.date?

This module has features os.date doesn’t have like addings days, months, and years, formatting etc.
This module is more convient when you want to add months/years of a date, as doing DateTime.new(2016, 2, 29):AddYears(1) will return DateTime.new(2017, 2, 28) but doing DateTime.new(2016, 2, 29):AddYears(4) will return DateTime.new(2020, 2, 29).

Features

It supports comparing dates

local dt1 = DateTime.DateTime.new(2000, 1, 2);
local dt2 = DateTime.DateTime.new(1999, 12, 31);
print(dt1 > dt2);
print(dt1 >= dt2);
print(dt1 < dt2);
print(dt1 == dt2)
dt2 = DateTime.DateTime.new(2000, 1, 2);
print(dt1 >= dt2);
print(dt1 == dt2);

true
true
false
false
true
true

and, adding and subtracting them

local dt1 = DateTime.DateTime.new(2000, 12, 30, 2, 42, 57);
local dt2 = DateTime.DateTime.new(1996, 5, 5, 1, 46, 39);
local ts1 = DateTime.TimeSpan.new(5, 11, 14, 17);
print(dt1 + ts1)
print(dt1 - ts1)
print(dt1 - dt2)

2001-01-04 13:57:14
2000-12-24 15:28:40
1700,00:56:18

Here are the list of operators supported in DateTime and TimeSpan module:

DateTime - DateTime = TimeSpan
DateTime - TimeSpan = DateTime
DateTime + TimeSpan = DateTime
TimeSpan + TimeSpan = TimeSpan
TimeSpan - TimeSpan = TimeSpan
TimeSpan * Integer = TimeSpan
TimeSpan / Integer = TimeSpan

It can also format dates

local dt1 = DateTime.DateTime.FromEpoch(1234567890);
print(dt1:Format('F'));

Friday, 13 February 2009 23:31:30

local dt1 = DateTime.DateTime.new(2017, 6, 11);
print(dt1:Format('MMMM d, y'))

June 11, 2017

Only the formatting date and time is appealing to me

Is it possible to format os.date?
Yes it is possible to format os.date and it’s simple actually, just convert it to DateTime

function FormatOsDate(t, format, dtfi)
	return DateTime.DateTime.FromOsDate(t):Format(format, dtfi)
end;

print(FormatOsDate(os.date('!*t', 1234567890), 'F'))

Friday, 13 February 2009 23:31:30

Patterns

It’ll check the basic first, if the format value doesn’t fit into basic, it’ll go to custom.

DateTime

Basic

Format Description
d Short date pattern
D Long date pattern
f Full date/time pattern (short time)
F Full date/time pattern (long time)
g General date/time pattern (short time)
G General date/time pattern (short time)
m M Month day pattern
y Y Year month pattern
t Short time pattern
T Long time pattern

Custom

Format specifier Description Example
a aa The AM/PM designator. 01:02:03 → AM; 13:45:30 → PM
d Day of the month from 1 to 31 1987-06-05 01:04:07 → 5
dd Day of the month from 01 to 31 1987-06-05 01:04:07 → 05
E EE EEE The abbreviated name for the day of the week 1987-06-05 → Fri
EEEE The full name for the day of the week 1987-06-05 → Friday
h The hour using 12-hour clock 13:45:30 → 1; 01:02:03 → 1
hh The hour using 12-hour clock 13:45:30 → 01; 01:02:03 → 01
H The hour using 24-hour clock 13:45:30 → 13; 01:02:03 → 1
HH The hour using 24-hour clock 13:45:30 → 13; 01:02:03 → 01
m The minute from 0 to 59 01:02:03 → 2
mm The minute from 00 to 59 01:02:03 → 02
M The month from 1 to 12 1987-06-05 01:04:07 → 6
MM The month from 01 to 12 1987-06-05 01:04:07 → 06
MMM The abbreviated name for the month 1987-06-05 01:04:07 → Jun
MMMM The full name for the month 1987-06-05 01:04:07 → June
s The second from 1 to 12 01:02:03 → 3
ss The second from 01 to 12 01:02:03 → 03
y The full year 1987-06-05 01:04:07 → 1987; 0012-03-04 → 12
yy The year from 00 to 99 1987-06-05 01:04:07 → 87; 2001-02-03 → 01
yyy The year as 3 minimum digit 1987-06-05 01:04:07 → 1987; 0012-03-04 → 012
yyyy The year as 4 minimum digit 1987-06-05 01:04:07 → 1987; 0012-03-04 → 0012
Literal string delimiter. '' for literal ' 1987-06-05 ('year' y, 'month' m, 'day' d) → year 1987, month 6, day 5
/ Date separator 1987-06-05 (dd/MM/y, DateTimeFormatInfo.Preset.de) → 05.06.1987
: Time separator 01:04:07 (HH:mm:ss, DateTimeFormatInfo.Preset.da) → 01.04.07

TimeSpan

Basic

Format Description
dhms Day hour minute second pattern
hms Hour minute second pattern
hmsf Hour minute second millisecond pattern
ms Minute second pattern
msf Minute second millisecond pattern
sf Second millisecond pattern
adhms Abbreivated day hour minute second pattern
ahms Abbreivated hour minute second pattern
ahmsf Abbreivated hour minute second millisecond pattern
amsf Abbreivated minute second pattern
amsf Abbreivated minute second millisecond pattern
asf Abbreivated second millisecond pattern
fdhms Full day hour minute second pattern
fhms Full hour minute second pattern
fhmsf Full hour minute second millisecond pattern
fmsf Full minute second pattern
fmsf Full minute second millisecond pattern
fsf Full second millisecond pattern

Custom

Format specifier|Description
-|-|-
d %d|Number of whole day in time interval
dd - dddddddd|Number of whole day in time interval with padding zeros as needed
h %h|Whole hours that aren’t counted as days
hh|Whole hours that aren’t counted as days that are zero padded
m %m|Whole minutes that aren’t counted as hours
mm|Whole minutes that aren’t counted as hours that are zero padded
s %s|Whole seconds that aren’t counted as minutes
ss|Whole seconds that aren’t counted as minutes that are zero padded
:|Time separator
,|Millisecond separator

What are DateTimeFormatInfo and TimeSpanFormatInfo?

These are for getting the month, the day names, the time separator and the date separator in DateTime.Format and the time separator and the milliseconds separator in TimeSpan.Format
Example:

local dtfi = DateTime.DateTimeFormatInfo.new 
{
	TimeSeparator = '.';
	DateSeparator = '/';
};
local dt = DateTime.DateTime.new(1987, 6, 5, 4, 3, 2);
print(dt:Format('F', dtfi))

1987 M06 5 04.03.02

More parts will be documented later

40 Likes

Hello there, @Blockzez! Very interesting module and will surely check out on my free time. Reminds me a lot of the built-in function Date in JavaScript. I have always wished there was a better time library in Lua but never had time to make one on my own due to working on multiple projects. Keep up the good work :+1:!

2 Likes

Updated the module: DateTime.rbxm (11,1 KB)
Update 1.0.1, 23 April 2020, 01:36:36 (CET):

  • Fixed negative TimeSpan bug
1 Like

Update 2.0.0a:
I decided to reimplement this

Do note many feature are different than in version 1, inputs are now in local time (which can be bypassed by .UtcYear, .UtcMonth, .UtcDay, .UtcHour, .UtcMinute, .UtcSecond and .UtcMillisecond).

Formatting (for dates only) patterns is similar to Unicode CLDR’s.

1 Like

Been getting this error: attempt to concatenate string with boolean line 284

ret = ret .. utc == nil and t[chr](self, data, i2 - i1) or t[chr](self, data, utc, i2 - i1);

(formatting)

Edit: Older versions work fine, it’s the new one.

This is an old library. I haven’t touched this for a year.
I probably forget to test it during release.
Apologies anyway.
(It was back when I used semicolons, when compound assignment didn’t exist, and I didn’t know about __mode)

Put a parenthesis around these. It’s a precedence issue. (EDIT: parenthesis changed after concatenation)

ret = ret .. (utc == nil and t[chr](self, data, i2 - i1) or t[chr](self, data, utc, i2 - i1));

You might, want to in line 35 change the proxy to table with weak key. Many of my libraries now do this so it can get garbage collected if there’s no more reference to that userdata. We use newproxy because I wanted to make it act like “read-only” so it’s because it’s supposed to be read-only and acts like a value type (even though it’s technically not).

local dt_proxy = setmetatable({ }, { __mode = "k" });

I might remake the module because I pretty much changed my programming style, with a possibility of making it rawequal and more prepared for edge cases.

2 Likes

does this module support modulo?

modulo of what? DateTime or TimeSpan (Duration)?
Why do you need to perform modulo of those?
It’s been a year, I probably have forgotten.

modulo is %

3 / 2 = 1.5 but 3 % 2 = 1

I said modulo of what? not what is modulo?.
I’ll repeat, why do you need to perform modulo of either of those, and to extend, what result do you expect for those value?

I was just wondering, never said I needed it

besides I’m guessing they return numbers anyways so modulo should work, I was just curious since you didn’t show it in the post

I know that is what you mean, didn’t realize at the time tho

Hello! I tried this fix and copied the line with the parenthesis, but it still shows the same error. Can you provide another solution? Thanks in advance!

I don’t know why I put the parenthesis after the and and not the .. operator in my fix.
I think it’s supposed to be the and or that acts like ternary conditional.

I edited it though I haven’t tested it (because it’s been a year and I don’t have it on me, I just quickly looked at the source code).

1 Like