kopia lustrzana https://github.com/peterhinch/micropython-samples
Add date module.
rodzic
e15e3219b2
commit
4691354a40
15
README.md
15
README.md
|
@ -42,6 +42,7 @@ and modules which are documented and supported.
|
|||
4.12 [Quaternions](./README.md#412-quaternions) Scale, move and rotate 3D objects with minimal mathematics.
|
||||
4.13 [A Pyboard power meter](./README.md#413-a-pyboard-power-meter) One of my own projects.
|
||||
4.14 [NTP time](./README.md#414-ntp-time) More portable than official driver with other benefits.
|
||||
4.15 [Date](./README.md#415-date) Small and simple classes for handling dates.
|
||||
5. [Module Index](./README.md#5-module-index) Supported code. Device drivers, GUI's, utilities.
|
||||
5.1 [uasyncio](./README.md#51-uasyncio) Tutorial and drivers for asynchronous coding.
|
||||
5.2 [Memory Device Drivers](./README.md#52-memory-device-drivers) Drivers for nonvolatile memory devices.
|
||||
|
@ -341,6 +342,20 @@ and point the MicroPython device at the local server with:
|
|||
ntptime.host="192.168.0.10" # Server address.
|
||||
ntptime.time()
|
||||
```
|
||||
## 4.15 Date
|
||||
|
||||
The official [datetime module](https://github.com/micropython/micropython-lib/tree/master/python-stdlib/datetime)
|
||||
is fully featured but substantial. This `Date` class has no concept of time,
|
||||
but is very compact. Dates are stored as a small int. Contrary to normal MP
|
||||
practice, properties are used. This allows basic arithmetic syntax while
|
||||
ensuring automatic rollover. The speed penalty of properties is unlikely to be
|
||||
a factor in date operations.
|
||||
|
||||
The `Date` class provides basic arithmetic and comparison methods. The
|
||||
`DateCal` subclass adds pretty printing and methods to assist in creating
|
||||
calendars.
|
||||
|
||||
The classes are documented [here](./date/DATE.md)
|
||||
|
||||
##### [Index](./README.md#0-index)
|
||||
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
# Simple Date classes
|
||||
|
||||
The official [datetime module](https://github.com/micropython/micropython-lib/tree/master/python-stdlib/datetime)
|
||||
is fully featured but substantial. This `Date` class has no concept of time,
|
||||
but is very compact. Dates are stored as a small int. Contrary to normal MP
|
||||
practice, properties are used. This allows basic arithmetic syntax while
|
||||
ensuring automatic rollover. The speed penalty of properties is unlikely to be
|
||||
a factor in date operations.
|
||||
|
||||
The `Date` class provides basic arithmetic and comparison methods. The
|
||||
`DateCal` subclass adds pretty printing and methods to assist in creating
|
||||
calendars.
|
||||
|
||||
[Return to main readme](../README.md)
|
||||
|
||||
# Date class
|
||||
|
||||
The `Date` class embodies a single date value which may be modified, copied
|
||||
and compared with other `Date` instances.
|
||||
|
||||
## Constructor
|
||||
|
||||
This takes a single optional arg:
|
||||
* `lt=None` By default the date is initialised from system time. To set the
|
||||
date from another time source, a valid
|
||||
[localtime/gmtime](http://docs.micropython.org/en/latest/library/time.html#time.localtime)
|
||||
tuple may be passed.
|
||||
|
||||
## Method
|
||||
|
||||
* `now` Arg `lt=None`. Sets the instance to the current date, from system time
|
||||
or `lt` as described above.
|
||||
|
||||
## Writeable properties
|
||||
|
||||
* `year` e.g. 2023.
|
||||
* `month` 1 == January. May be set to any number, years will roll over if
|
||||
necessary. e.g. `d.month += 15` or `d.month -= 1`.
|
||||
* `mday` Adjust day in current month. Allowed range `1..month_length`.
|
||||
* `day` Days since epoch. Note that the epoch varies with platform - the value
|
||||
may be treated as an opaque small integer. Use to adjust a date with rollover
|
||||
(`d.day += 7`) or to assign one date to another (`date2.day = date1.day`). May
|
||||
also be used to represnt a date as a small int for saving to a file.
|
||||
|
||||
## Read-only property
|
||||
|
||||
* `wday` Day of week. 0==Monday 6==Sunday.
|
||||
|
||||
## Date comparisons
|
||||
|
||||
Python "magic methods" enable date comparisons using standard operators `<`,
|
||||
`<=`, `>`, `>=`, `==`, `!=`.
|
||||
|
||||
# DateCal class
|
||||
|
||||
This adds pretty formatting and functionality to return additional information
|
||||
about the current date. The added methods and properties do not change the
|
||||
date value. Primarily intended for calendars.
|
||||
|
||||
## Constructor
|
||||
|
||||
This takes a single optional arg:
|
||||
* `lt=None` See `Date` constructor.
|
||||
|
||||
## Methods
|
||||
|
||||
* `time_offset` arg `hr=6`. This returns 0 or 1, being the offset in hours of
|
||||
UK local time to UTC. By default the change occurs when the date changes at
|
||||
00:00 UTC on the last Sunday in March and October. If an hour value is passed,
|
||||
the change will occur at the correct 01:00 UTC. This method will need to be
|
||||
adapted for other geographic locations.
|
||||
* `wday_n` arg `mday=1`. Return the weekday for a given day of the month.
|
||||
* `mday_list` arg `wday`. Given a weekday, for the current month return an
|
||||
ordered list of month days matching that weekday.
|
||||
|
||||
## Read-only properties
|
||||
|
||||
* `month_length` Length of month in days.
|
||||
* `day_str` Day of week as a string, e.g. "Wednesday".
|
||||
* `month_str` Month as a string, e.g. "August".
|
||||
|
||||
## Class variables
|
||||
|
||||
* `days` A 7-tuple `("Monday", "Tuesday"...)`
|
||||
* `months` A 12-tuple `("January", "February",...)`
|
||||
|
||||
# Example usage
|
||||
|
||||
```python
|
||||
from date import Date
|
||||
d = Date()
|
||||
d.month = 1 # Set to January
|
||||
d.month -= 2 # Date changes to same mday in November previous year.
|
||||
d.mday = 25 # Set absolute day of month
|
||||
d.day += 7 # Advance date by one week. Month/year rollover is handled.
|
||||
today = Date()
|
||||
if d == today: # Date comparisons
|
||||
# do something
|
||||
new_date = Date()
|
||||
new_date.day = d.day # Assign d to new_date: now new_date == d.
|
||||
print(d) # Basic numeric print.
|
||||
```
|
||||
The DateCal class:
|
||||
```python
|
||||
from date import DateCal
|
||||
d = DateCal()
|
||||
# Correct a UK clock for DST
|
||||
d.now()
|
||||
hour = (hour_utc + d.time_offset(hour_utc)) % 24
|
||||
print(d) # Pretty print
|
||||
x = d.wday_n(1) # Get day of week of 1st day of month
|
||||
sundays = d.mday_list(6) # List Sundays for the month.
|
||||
wday_last = d.wday_n(d.month_length) # Weekday of last day of month
|
||||
```
|
|
@ -0,0 +1,160 @@
|
|||
# date.py Minimal Date class for micropython
|
||||
|
||||
# Released under the MIT License (MIT). See LICENSE.
|
||||
# Copyright (c) 2023 Peter Hinch
|
||||
|
||||
from time import mktime, localtime
|
||||
|
||||
_SECS_PER_DAY = const(86400)
|
||||
def leap(year):
|
||||
return bool((not year % 4) ^ (not year % 100))
|
||||
|
||||
class Date:
|
||||
|
||||
def __init__(self, lt=None):
|
||||
self.now(lt)
|
||||
|
||||
def now(self, lt=None):
|
||||
self._lt = list(localtime()) if lt is None else list(lt)
|
||||
self._update()
|
||||
|
||||
def _update(self, ltmod=True): # If ltmod is False ._cur has been changed
|
||||
if ltmod: # Otherwise ._lt has been modified
|
||||
self._lt[3] = 6
|
||||
self._cur = mktime(self._lt) // _SECS_PER_DAY
|
||||
self._lt = list(localtime(self._cur * _SECS_PER_DAY))
|
||||
|
||||
def _mlen(self, d=bytearray((31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31))):
|
||||
days = d[self._lt[1] - 1]
|
||||
return days if days else (29 if leap(self._lt[0]) else 28)
|
||||
|
||||
@property
|
||||
def year(self):
|
||||
return self._lt[0]
|
||||
|
||||
@year.setter
|
||||
def year(self, v):
|
||||
if self.mday == 29 and self.month == 2 and not leap(v):
|
||||
self.mday = 28 # Ensure it doesn't skip a month
|
||||
self._lt[0] = v
|
||||
self._update()
|
||||
|
||||
@property
|
||||
def month(self):
|
||||
return self._lt[1]
|
||||
|
||||
# Can write d.month = 4 or d.month += 15
|
||||
@month.setter
|
||||
def month(self, v):
|
||||
y, m = divmod(v - 1, 12)
|
||||
self._lt[0] += y
|
||||
self._lt[1] = m + 1
|
||||
self._lt[2] = min(self._lt[2], self._mlen())
|
||||
self._update()
|
||||
|
||||
@property
|
||||
def mday(self):
|
||||
return self._lt[2]
|
||||
|
||||
@mday.setter
|
||||
def mday(self, v):
|
||||
if not 0 < v <= self._mlen():
|
||||
raise ValueError(f"mday {v} is out of range")
|
||||
self._lt[2] = v
|
||||
self._update()
|
||||
|
||||
@property
|
||||
def day(self): # Days since epoch.
|
||||
return self._cur
|
||||
|
||||
@day.setter
|
||||
def day(self, v): # Usuge: d.day += 7 or date_1.day = d.day.
|
||||
self._cur = v
|
||||
self._update(False) # Flag _cur change
|
||||
|
||||
# Read-only properties
|
||||
|
||||
@property
|
||||
def wday(self):
|
||||
return self._lt[6]
|
||||
|
||||
# Date comparisons
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.day < other.day
|
||||
|
||||
def __le__(self, other):
|
||||
return self.day <= other.day
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.day == other.day
|
||||
|
||||
def __ne__(self, other):
|
||||
return self.day != other.day
|
||||
|
||||
def __gt__(self, other):
|
||||
return self.day > other.day
|
||||
|
||||
def __ge__(self, other):
|
||||
return self.day >= other.day
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.year}/{self.month}/{self.mday}"
|
||||
|
||||
|
||||
class DateCal(Date):
|
||||
days = ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
|
||||
months = (
|
||||
"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December",
|
||||
)
|
||||
|
||||
def __init__(self, lt=None):
|
||||
super().__init__(lt)
|
||||
|
||||
@property
|
||||
def month_length(self):
|
||||
return self._mlen()
|
||||
|
||||
@property
|
||||
def day_str(self):
|
||||
return self.days[self.wday]
|
||||
|
||||
@property
|
||||
def month_str(self):
|
||||
return self.months[self.month - 1]
|
||||
|
||||
def wday_n(self, mday=1):
|
||||
return (self._lt[6] - self._lt[2] + mday) % 7
|
||||
|
||||
def mday_list(self, wday):
|
||||
ml = self._mlen() # 1 + ((wday - wday1) % 7)
|
||||
d0 = 1 + ((wday - (self._lt[6] - self._lt[2] + 1)) % 7)
|
||||
return [d for d in range(d0, ml + 1, 7)]
|
||||
|
||||
# Optional: return UK DST offset in hours. Can pass hr to ensure that time change occurs
|
||||
# at 1am UTC otherwise it occurs on date change (0:0 UTC)
|
||||
# offs is offset by month
|
||||
def time_offset(self, hr=6, offs=bytearray((0, 0, 3, 1, 1, 1, 1, 1, 1, 10, 0, 0))):
|
||||
ml = self._mlen()
|
||||
wdayld = self.wday_n(ml) # Weekday of last day of month
|
||||
mday_sun = self.mday_list(6)[-1] # Month day of last Sunday
|
||||
m = offs[self._lt[1] - 1]
|
||||
if m < 3:
|
||||
return m # Deduce time offset from month alone
|
||||
return int(
|
||||
((self._lt[2] < mday_sun) or (self._lt[2] == mday_sun and hr <= 1)) ^ (m == 3)
|
||||
) # Months where offset changes
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.day_str} {self.mday} {self.month_str} {self.year}"
|
Ładowanie…
Reference in New Issue