Skip to content

Commit

Permalink
feat: from_datetime,from_timestamp
Browse files Browse the repository at this point in the history
Signed-off-by: vvanglro <vvanglro@gmail.com>
  • Loading branch information
vvanglro committed Feb 6, 2024
1 parent fff14da commit d8b98f6
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 23 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ulid-rs-py"
version = "0.2.0"
version = "0.2.1"
description = "Use rust ulid crate to rewrite python ulid library"
authors = [
"vvanglro <vvanglro@gmail.com>"
Expand Down
19 changes: 13 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ pip install ulid-rs-py
## Quickstart

```python
from datetime import datetime
from ulid import new, from_uuid, from_parts, from_timestamp, from_string, PyUlid
from datetime import datetime, timezone
from ulid import new, from_uuid, from_parts, from_timestamp, from_datetime, from_string, PyUlid

# Generate ulid
py_ulid: PyUlid = new()
Expand All @@ -45,16 +45,23 @@ print(py_ulid.str())
assert py_ulid.str() == ulid_value

# From timestamp
datetime_value = datetime(2023, 7, 28)
py_ulid = from_timestamp(datetime_value)
timestamp_value = datetime(2023, 7, 28).timestamp()
py_ulid = from_timestamp(timestamp_value)
print(py_ulid.str())
print(py_ulid.timestamp())
assert py_ulid.timestamp() == 1690502400000
assert py_ulid.timestamp() == timestamp_value
print(py_ulid.randomness())

# From datetime
datetime_value = datetime(2023, 7, 28, hour=1, minute=20, tzinfo=timezone.utc)
py_ulid = from_datetime(datetime_value)
assert py_ulid.str()
assert py_ulid.datetime() == datetime(2023, 7, 28, hour=1, minute=20)
assert py_ulid.timestamp() == datetime_value.timestamp()

# From parts
datetime_value = datetime(2023, 7, 28)
py_ulid_tt = from_timestamp(datetime_value)
py_ulid_tt = from_timestamp(datetime_value.timestamp())
py_ulid = from_parts(py_ulid_tt.timestamp(), py_ulid_tt.randomness())
assert py_ulid.str() == py_ulid_tt.str()

Expand Down
46 changes: 37 additions & 9 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use chrono::{TimeZone, Timelike, Utc};
use chrono::{DateTime, Datelike, TimeZone, Timelike, Utc};
use pyo3::create_exception;
use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;
use pyo3::types::{PyDateAccess, PyDateTime, PyTimeAccess};
use std::time::{Duration, SystemTime};
use ulid::Ulid;
use uuid::Uuid;

Expand All @@ -28,8 +29,24 @@ impl PyUlid {
Ok(self.0 .0.to_be_bytes())
}

pub fn timestamp(&self) -> PyResult<u64> {
Ok(self.0.timestamp_ms())
pub fn timestamp(&self) -> PyResult<f64> {
let datetime: DateTime<Utc> = self.0.datetime().into();
Ok(datetime.timestamp() as f64)
}

pub fn datetime<'p>(&self, _py: Python<'p>) -> PyResult<&'p PyDateTime> {
let datetime: DateTime<Utc> = self.0.datetime().into();
PyDateTime::new(
_py,
datetime.year(),
datetime.month() as u8,
datetime.day() as u8,
datetime.hour() as u8,
datetime.minute() as u8,
datetime.second() as u8,
datetime.nanosecond(),
None,
)
}

pub fn randomness(&self) -> PyResult<u128> {
Expand All @@ -43,7 +60,7 @@ impl PyUlid {
pub fn increment(&self) -> PyResult<Option<PyUlid>> {
match self.0.increment() {
None => Ok(None),
Some(resp) => Ok(Option::from(self::PyUlid::new(resp))),
Some(resp) => Ok(Option::from(PyUlid::new(resp))),
}
}
}
Expand All @@ -69,21 +86,21 @@ fn from_string(_py: Python, value: &str) -> PyResult<PyUlid> {
#[pyo3(signature = (value))]
fn from_uuid(_py: Python, value: &str) -> PyResult<PyUlid> {
match _py.allow_threads(|| Uuid::parse_str(&value)) {
Ok(instance_uuid) => Ok(PyUlid::new(ulid::Ulid::from(instance_uuid))),
Ok(instance_uuid) => Ok(PyUlid::new(Ulid::from(instance_uuid))),
Err(err) => Err(InvalidUuidError::new_err(err.to_string())),
}
}

#[pyfunction]
#[pyo3(signature = (value))]
fn from_timestamp(_py: Python, value: &PyDateTime) -> PyResult<PyUlid> {
fn from_datetime(_py: Python, value: &PyDateTime) -> PyResult<PyUlid> {
let year = value.get_year();
let month = value.get_month() as u32;
let day = value.get_day() as u32;
let hour = value.get_hour() as u32;
let minute = value.get_minute() as u32;
let second = value.get_second() as u32;
let microsecond = value.get_microsecond() as u32;
let microsecond = value.get_microsecond();
_py.allow_threads(|| {
let dt = Utc
.with_ymd_and_hms(year, month, day, hour, minute, second)
Expand All @@ -96,11 +113,21 @@ fn from_timestamp(_py: Python, value: &PyDateTime) -> PyResult<PyUlid> {
})
}

#[pyfunction]
#[pyo3(signature = (value))]
fn from_timestamp(_py: Python, value: f64) -> PyResult<PyUlid> {
_py.allow_threads(|| {
let system_time = SystemTime::UNIX_EPOCH + Duration::from_secs(value as u64);
let ulid = Ulid::from_datetime(system_time.into());
Ok(PyUlid::new(ulid))
})
}

#[pyfunction]
#[pyo3(signature = (timestamp, randomness))]
fn from_parts(_py: Python, timestamp: u64, randomness: u128) -> PyResult<PyUlid> {
fn from_parts(_py: Python, timestamp: f64, randomness: u128) -> PyResult<PyUlid> {
_py.allow_threads(|| {
let ulid = Ulid::from_parts(timestamp, randomness);
let ulid = Ulid::from_parts(timestamp as u64 * 1000, randomness);
Ok(PyUlid::new(ulid))
})
}
Expand All @@ -111,6 +138,7 @@ fn _ulid_rs_py(_py: Python, module: &PyModule) -> PyResult<()> {
module.add_function(wrap_pyfunction!(from_string, module)?)?;
module.add_function(wrap_pyfunction!(from_uuid, module)?)?;
module.add_function(wrap_pyfunction!(from_timestamp, module)?)?;
module.add_function(wrap_pyfunction!(from_datetime, module)?)?;
module.add_function(wrap_pyfunction!(from_parts, module)?)?;
module.add_class::<PyUlid>()?;
module.add("DecodeError", _py.get_type::<DecodeError>())?;
Expand Down
16 changes: 13 additions & 3 deletions tests/test_ulid.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import uuid
import pytest
from datetime import datetime
from datetime import datetime, timezone
from ulid import (
new,
PyUlid,
Expand All @@ -10,6 +10,7 @@
from_timestamp,
DecodeError,
InvalidUuidError,
from_datetime,
)


Expand Down Expand Up @@ -67,15 +68,24 @@ def test_from_uuid():


def test_from_timestamp():
py_ulid = from_timestamp(datetime(1999, 1, 1))
timestamp = datetime(1999, 1, 1).timestamp()
py_ulid = from_timestamp(timestamp)
assert py_ulid.str()
assert type(py_ulid) is PyUlid
assert py_ulid.timestamp() == 915148800000
assert py_ulid.timestamp() == timestamp
assert py_ulid.randomness()
assert py_ulid.bytes()
assert py_ulid.increment()


def test_from_datetime():
datetime_value = datetime(2023, 7, 28, hour=1, minute=20, tzinfo=timezone.utc)
py_ulid = from_datetime(datetime_value)
assert py_ulid.str()
assert py_ulid.datetime() == datetime(2023, 7, 28, hour=1, minute=20)
assert py_ulid.timestamp() == datetime_value.timestamp()


def test_from_parts():
a = new()
py_ulid = from_parts(a.timestamp(), a.randomness())
Expand Down
1 change: 1 addition & 0 deletions ulid/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from_string,
from_uuid,
from_parts,
from_datetime,
from_timestamp,
PyUlid,
DecodeError,
Expand Down
2 changes: 1 addition & 1 deletion ulid/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.2.0"
__version__ = "0.2.1"
8 changes: 5 additions & 3 deletions ulid/_ulid_rs_py.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ class InvalidUuidError(ValueError): ...
class PyUlid:
def __repr__(self) -> str: ...
def bytes(self) -> List[int]: ...
def timestamp(self) -> int: ...
def timestamp(self) -> float: ...
def datetime(self) -> datetime: ...
def randomness(self) -> int: ...
def str(self) -> str: ...
def increment(self) -> Optional["PyUlid"]: ...

def new() -> PyUlid: ...
def from_string(value: str) -> PyUlid: ...
def from_uuid(value: str) -> PyUlid: ...
def from_timestamp(value: datetime) -> PyUlid: ...
def from_parts(timestamp: int, randomness: int) -> PyUlid: ...
def from_datetime(value: datetime) -> PyUlid: ...
def from_timestamp(value: float) -> PyUlid: ...
def from_parts(timestamp: float, randomness: int) -> PyUlid: ...

0 comments on commit d8b98f6

Please sign in to comment.